비-Python 언어 SDK 개요 (Stub Task, Coordinator, 실행 모델)
Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation authoring-and-scheduling/language-sdks/index.rst - Non-Python Task SDKs (전체, 실험적 기능)
이 모듈을 다 읽으면
- Stub Task, Coordinator, Language Runtime 세 구성 요소의 역할을 구분할 수 있다
- queue 파라미터가 태스크를 어떤 코디네이터로 라우팅하는지 설명할 수 있다
- ExecutableCoordinator가 어떤 언어에만 적용 가능한지, 그 이유를 설명할 수 있다
Airflow Dag는 항상 파이썬으로 정의되지만 개별 태스크의 구현은 Java/Go/TypeScript 같은 다른 언어로 작성할 수 있다 - Dag 작성자는 @task.stub으로 태스크가 어디서 구현되는지만 선언하고, 실제 실행은 워커가 큐에 매핑된 코디네이터를 통해 해당 언어의 런타임을 호출하는 방식으로 이뤄진다.
세 가지 구성 요소: Stub Task, Coordinator, Language Runtime
현재 제공되는 언어 SDK는 세 가지다 - JVM 언어(예: Java, `JavaCoordinator` 사용, 최소 런타임 JRE 17), Go(`ExecutableCoordinator` 사용, 별도 런타임 불필요 - 네이티브 바이너리), TypeScript(`NodeCoordinator` 사용, 최소 런타임 Node.js 22).
실행 모델은 세 부분으로 이뤄진다. Stub task는 `@task.stub` 데코레이터로 선언하며, 스케줄러 입장에서는 일반 Airflow 태스크와 완전히 동일하게 취급된다 - 의존성, 재시도, 풀 등 다른 모든 태스크 레벨 기능에 똑같이 참여한다. 유일한 차이는 워커가 함수 정의 안의 파이썬 코드를 실행하지 않고, 대신 *코디네이터*에 실행을 위임한다는 점이다.
Coordinator는 `[sdk] coordinators` 설정에 등록되는 파이썬 객체로, Airflow 워커의 일부로 취급된다. 워커가 stub 태스크를 집어들면, 그 태스크에 지정된 `queue`에 매핑된 코디네이터를 찾아 그 코디네이터로 태스크를 실행한다. 코디네이터는 대상 언어의 런타임을 관리하고, 거기서 오는 메시지를 전달하며, 결과를 Airflow로 다시 중계하는 역할을 한다 - 모든 코디네이터는 `BaseCoordinator`를 상속한다.
Language runtime은 코디네이터가 태스크 인스턴스마다 하나씩 호출하는 단명(short-lived) 런타임이다. 대부분의 경우 비-파이썬 언어로 구현된 실행 파일의 서브프로세스이며, 워크로드를 식별하는 메시지를 받아 태스크를 실행하고 코디네이터를 프록시 삼아 워커 프로세스와 통신한다.
핵심 포인트
- 표에 나온 최소 런타임 요구사항이 언어별로 다르다 - Java는 JRE 17, Go는 별도 런타임 불필요(네이티브 바이너리), TypeScript는 Node.js 22가 필요하다
- Stub task는 스케줄러 입장에서 일반 태스크와 동일하게 의존성·재시도·풀 등 모든 태스크 레벨 기능에 참여하며, 유일한 차이는 워커가 함수 본문의 파이썬 코드를 실행하지 않고 코디네이터에 실행을 위임한다는 점이다
- Coordinator는 Airflow 워커의 일부로 취급되며, 태스크의 queue 값에 매핑된 코디네이터가 대상 언어 런타임을 관리하고 메시지를 중계한다
Stub Task 선언과 UI에서 보이는 코드
Stub 태스크는 여전히 파이썬 태스크 선언이므로, 일반 Dag나 태스크에서 쓸 수 있는 모든 파라미터를 그대로 쓸 수 있다 - `queue`, `retries`, `retry_delay`, `execution_timeout`, `pool` 등. 태스크 간 의존성도 파이썬 Dag 파일에서 그대로 정의한다. `queue` 파라미터가 어떤 코디네이터로 라우팅될지 결정하고, 그 외 `@task` 키워드 인자는 평소처럼 Airflow의 스케줄러/워커가 그대로 존중한다.
Stub 태스크가 만든 XCom 값은 다운스트림 파이썬 태스크에서 볼 수 있고 그 반대도 마찬가지다. 다만 XCom *의존성*(순서)은 파이썬 Dag 안에서 선언되는 반면, 실제 값을 읽고 쓰는 코드는 각 언어 구현에서 명시적으로 작성해야 한다.
stub 태스크를 포함한 Dag에 대해, Airflow UI의 Code 뷰는 stub 선언을 포함한 파이썬 Dag 파일만 소스로 보여준다 - 비-파이썬 구현 소스는 UI 어디에도 표시되지 않으며, 이를 확인하려면 프로젝트 저장소나 번들에 포함된 빌드 아티팩트를 직접 봐야 한다. 이는 버그가 아니라 의도된 아키텍처 결정이다.
핵심 포인트
- stub 태스크의 queue 파라미터가 어떤 코디네이터로 라우팅될지 결정하고, 그 외 retries/pool/execution_timeout 등 일반 @task 키워드 인자는 평소처럼 동작한다
- XCom의 의존성(순서)은 파이썬 stub Dag에서 선언되지만, 실제 값을 읽고 쓰는 코드는 각 언어 구현에서 명시적으로 작성해야 한다
- Airflow UI의 Code 뷰는 stub 선언을 포함한 파이썬 Dag 파일만 보여주며, 비-파이썬 구현 소스는 UI 어디에도 표시되지 않는다 - 의도된 아키텍처 결정이다
Coordinator 설정과 새 컴파일 언어 SDK 구현
코디네이터는 `airflow.cfg`(또는 환경 변수)의 `[sdk]` 섹션에 등록한다. `coordinators`는 코디네이터 논리 이름을 클래스와 kwargs에 매핑하는 JSON 객체다 - `classpath`는 워커가 import할 수 있어야 하고, `kwargs`는 코디네이터 생성자에 그대로 전달된다. `extra`는 코디네이터 자체에는 전달되지 않지만 다른 컴포넌트가 필요에 따라 참조하는 부가 정보다 - 예를 들어 KubernetesExecutor는 `extra.pod_template_file`로 특정 큐의 워커 파드를 특정 파드 템플릿으로 띄우고, `extra.worker_container_repository` + `extra.worker_container_tag`로 그 큐의 워커 베이스 이미지(예: JVM이 포함된 이미지)를 오버라이드하는 데 쓴다.
`queue_to_coordinator`는 Celery 큐 이름을 코디네이터 이름에 매핑하는 JSON 객체다 - 하나의 코디네이터가 여러 큐를 서비스할 수 있지만, 한 큐는 코디네이터 하나에만 매핑될 수 있다.
`ExecutableCoordinator`는 번들 파일을 직접 실행하는 방식으로 태스크를 실행하며, 따라서 실행 시점에 별도의 언어 런타임·가상머신·인터프리터가 전혀 필요 없는 독립 실행 바이너리를 만드는 컴파일 언어(Go, Rust, C, C++, Zig 등)에만 맞는다. JVM 언어처럼 바이트코드로 컴파일되어 실행 시점에 JRE가 필요한 언어는 이 코디네이터에 맞지 않고, 대신 `JavaCoordinator`가 그 역할을 담당한다.
새로운 컴파일 언어를 이 방식으로 지원하려면, 코디네이터가 소비하는 공용 온디스크 번들 포맷(`AFBNDL01` 푸터, 바이너리 무결성 해시, `dag_id`/`task_id` 목록을 담은 `airflow-metadata.yaml` 매니페스트)으로 번들을 만들고, 코디네이터의 IPC 프로토콜(`--comm`/`--logs` 소켓 인자)을 구현해야 한다. 이 스펙은 매니페스트용 머신 판독 가능 JSON Schema와 함께 공개되어 있으며, Go SDK가 이 스펙을 따르는 참조 구현이다.
핵심 포인트
- [sdk] coordinators는 코디네이터 이름→classpath/kwargs/extra를 매핑하고, queue_to_coordinator는 큐 이름→코디네이터 이름을 매핑한다 - 하나의 코디네이터가 여러 큐를 서비스할 수 있지만 한 큐는 코디네이터 하나에만 매핑된다
- extra는 코디네이터 생성자에는 전달되지 않고 다른 컴포넌트가 참조하는 부가 정보다 (예: KubernetesExecutor가 extra.pod_template_file로 큐별 워커 파드 템플릿을 결정)
- ExecutableCoordinator는 실행 시점에 별도 런타임/VM/인터프리터가 전혀 필요 없는 독립 실행 바이너리(Go, Rust, C, C++, Zig 등)에만 맞으며, JRE가 필요한 JVM 언어는 대신 JavaCoordinator가 담당한다
- 새로운 컴파일 언어 SDK를 추가하려면 AFBNDL01 푸터·무결성 해시·airflow-metadata.yaml 매니페스트를 갖춘 공용 번들 포맷을 만들고 코디네이터의 IPC 프로토콜(--comm/--logs)을 구현해야 하며, Go SDK가 그 참조 구현이다