Go SDK - 번들 빌드/패키징과 배포, 코디네이터 설정
Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation authoring-and-scheduling/language-sdks/go.rst - Go entry point, Coordinator configuration, Building and packaging, Cross-platform builds, Deploying, ExecutableCoordinator configuration, Limitations (약 133-427줄)
이 모듈을 다 읽으면
- Go 번들의 엔트리 포인트(RegisterDags)가 매니페스트와 실제 실행 코드의 정합성을 어떻게 보장하는지 설명할 수 있다
- airflow-go-pack으로 번들을 빌드/패킹하는 과정과 크로스 플랫폼 빌드 시 유의점을 설명할 수 있다
- ExecutableCoordinator의 kwargs와 Go SDK의 알려진 제약을 설명할 수 있다
Go 번들의 엔트리 포인트는 bundlev1.BundleProvider.RegisterDags 하나로 dag_id/task 등록의 유일한 진실 공급원이 되어 매니페스트가 실제 바이너리 동작과 어긋날 수 없게 하며, airflow-go-pack이 빌드와 AFBNDL01 메타데이터 푸터 부착을 한 번에 처리하고, ExecutableCoordinator는 executables_root를 재귀적으로 스캔해 매니페스트의 dag_id를 매칭하고 무결성 해시를 검증한 뒤 번들을 실행한다.
엔트리 포인트 - RegisterDags가 유일한 진실 공급원
Go 번들은 `bundlev1.BundleProvider`를 구현해 자신의 Dag와 태스크를 등록한다. `RegisterDags(dagbag)`에서 `dagbag.AddDag("simple_dag")`로 등록하는 `dag_id`는 반드시 파이썬 Dag의 `dag_id`와 일치해야 하고, 이어서 `AddTask(extract)`처럼 등록하는 각 태스크는 함수 이름을 그대로 `task_id`로 쓰며 그 Dag의 `@task.stub` 함수 이름과 일치해야 한다. `main()`은 `bundlev1server.Serve(&myBundle{})` 한 줄이면 된다.
`RegisterDags`가 등록하는 `dag_id`와 태스크 이름 목록이 곧 이 번들이 실행할 수 있는 유일한 목록이므로, 여기서 생성되는 매니페스트는 실제 바이너리가 실행하는 내용과 절대 어긋날 수 없다.
핵심 포인트
- RegisterDags가 등록하는 dag_id/태스크 이름이 곧 번들이 실행할 수 있는 유일한 목록이므로, 이로부터 생성되는 매니페스트는 실제 바이너리 동작과 어긋날 수 없다
- 등록한 dag_id는 반드시 파이썬 Dag의 dag_id와 일치해야 하고, 등록한 각 태스크 이름은 그 Dag의 @task.stub 함수 이름과 일치해야 한다
빌드와 패키징 (airflow-go-pack)
그냥 `go build`만으로도 실행 가능한 바이너리는 나오지만, 배포 가능한 번들(바이너리 + embed된 소스 + 매니페스트)이 되려면 `airflow-go-pack`을 거쳐야 한다. 이 패커는 바이너리를 컴파일한 뒤 embed된 메타데이터 푸터를 덧붙여, 코디네이터가 바이너리를 실행하지 않고도 `dag_id`들을 읽을 수 있는 하나의 실행 파일을 만든다. 이 온디스크 포맷(`AFBNDL01` 푸터와 `airflow-metadata.yaml` 매니페스트)은 모든 네이티브 실행 언어 SDK가 공유하는 번들 포맷이다.
`airflow-go-pack`은 Go 1.24의 `tool` 디렉티브로 배포되므로 전역 설치가 필요 없다 - 번들 모듈의 `go.mod`에 `tool github.com/apache/airflow/go-sdk/cmd/airflow-go-pack`을 추가하고 `go tool airflow-go-pack`으로 실행하면 되며, 이렇게 하면 프로젝트별로 패커 버전을 고정할 수 있다. `go tool airflow-go-pack ./example/bundle -- -trimpath -tags=prod`처럼 빌드와 패킹을 한 번에 할 수 있고, `--` 뒤의 플래그는 그대로 `go build`에 전달된다. `--output <경로>`를 쓰면 패킹된 번들을 코디네이터가 스캔하는 `executables_root` 디렉터리로 바로 쓸 수 있다.
핵심 포인트
- airflow-go-pack은 Go 1.24의 tool 디렉티브로 배포되어 전역 설치 없이 go.mod에 추가하고 go tool airflow-go-pack으로 실행하며, 이를 통해 프로젝트별로 패커 버전을 고정할 수 있다
- 순수 go build만으로는 배포 가능한 번들이 되지 않는다 - AFBNDL01 푸터와 airflow-metadata.yaml 매니페스트를 부착하는 airflow-go-pack을 거쳐야 코디네이터가 실행 없이 매니페스트를 읽을 수 있는 하나의 실행 파일이 완성된다
크로스 플랫폼 빌드와 배포
번들을 실행할 워커가 빌드 머신과 다른 OS/아키텍처를 쓰는 경우가 흔하다 (예: Apple Silicon 노트북에서 Linux 호스트로 배포). 이때 `--goos`/`--goarch`를 넘기면 패커가 대신 크로스 빌드를 해준다.
대안으로, 이미 빌드된 바이너리를 `--executable`/`--source`로 패킹할 수도 있다. 패커는 보통 바이너리를 `--airflow-metadata` 인자로 실행해 매니페스트를 읽어내는데, 크로스 컴파일된 바이너리는 빌드 머신에서 실행할 수 없다. 이럴 때는 그 바이너리를 실행할 수 있는 머신에서 미리 매니페스트를 생성한 뒤, `--airflow-metadata`로 그 매니페스트 파일을 패커에 넘겨야 한다 (`--executable`은 `--goos`/`--goarch`나 `--` 뒤의 `go build` 플래그와는 함께 쓸 수 없다 - 이미 빌드된 바이너리를 패킹하는 것이지 새로 빌드하는 게 아니기 때문이다).
배포는 패킹된 번들을 `executables_root`로 지정된 디렉터리에 복사하거나 마운트하는 것으로 충분하다. `ExecutableCoordinator`는 그 디렉터리들을 재귀적으로 스캔해, 들어온 `dag_id`를 각 번들의 매니페스트와 매칭한 뒤 번들의 무결성 해시를 검증하고 일치하는 번들을 실행한다. 번들은 파일명이 아니라 트레일러의 매직 바이트로 식별되므로(Linux/macOS는 확장자 없음, Windows는 `.exe`), 워커에 배포된 파일의 이름 자체는 무관하다.
핵심 포인트
- 빌드 머신과 실행 워커의 OS/아키텍처가 다르면(예: Apple Silicon에서 Linux 배포) --goos/--goarch로 크로스 빌드하거나, 크로스 컴파일된 바이너리를 실행할 수 있는 머신에서 미리 매니페스트를 생성해 --airflow-metadata로 패커에 넘겨야 한다 (--executable은 --goos/--goarch와 배타적)
- ExecutableCoordinator는 번들을 파일명이 아니라 트레일러의 매직 바이트로 식별하므로, 워커에 배포된 파일의 이름 자체는 무관하다
코디네이터 설정과 알려진 제약
`ExecutableCoordinator`에 전달되는 핵심 kwargs는 `executables_root`(필수, 번들을 재귀적으로 스캔할 하나 이상의 디렉터리, 문자열/경로/리스트 모두 허용)와 `task_startup_timeout`(기본값 10.0초, 번들 서브프로세스가 실행 후 연결하기까지 기다리는 시간 - 제약된 하드웨어 등에서 번들 기동이 느리면 늘려야 함)이다.
코디네이터는 Airflow 워커의 일부이므로, `[sdk]` 설정과 `executables_root`의 번들 파일은 태스크가 실제로 실행되는 곳에만 있으면 된다 - `CeleryExecutor`라면 Celery 워커에 두는 것으로 충분하고, `LocalExecutor`라면 태스크가 스케줄러 프로세스 안에서 실행되므로 스케줄러가 읽을 수 있는 곳에 둬야 한다. API 서버와 Dag 프로세서에는 이 설정이 필요 없다.
알려진 제약으로, 파이썬 stub Dag는 여전히 필수다 - 실행 API가 아직 비-파이썬 언어의 Dag 구조를 나르지 못해 태스크 이름과 의존성은 파이썬에서 `@task.stub`으로 선언해야 하며, 이는 문서화된 알려진 제약이다.
핵심 포인트
- executables_root(필수, 여러 디렉터리 가능)와 task_startup_timeout(기본 10.0초, 제약된 하드웨어 등에서 번들 기동이 느리면 늘려야 함)이 ExecutableCoordinator의 핵심 kwargs다
- 코디네이터는 워커의 일부이므로 [sdk] 설정과 번들 파일은 태스크가 실제로 실행되는 곳에만 있으면 된다 - LocalExecutor는 태스크가 스케줄러 프로세스 안에서 돌아가므로 스케줄러가 읽을 수 있는 곳에 둬야 하고, API 서버와 Dag 프로세서에는 필요 없다
- 실행 API가 아직 비-파이썬 언어의 Dag 구조를 나르지 못하므로, 태스크 이름과 의존성은 여전히 파이썬 stub Dag(@task.stub)로 선언해야 한다는 것이 문서화된 알려진 제약이다