Airflow 아키텍처 개요 (컴포넌트와 배포 형태)
Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation core-concepts/overview.rst
이 모듈을 다 읽으면
- Airflow의 필수 컴포넌트(스케줄러, Dag 처리기, Dag 번들, API 서버, 메타데이터 DB)와 선택 컴포넌트(워커, 트리거러, 플러그인)를 구분해 설명할 수 있다
- Dag 처리기가 스케줄러와 분리되어 있는 이유를 보안 관점에서 설명할 수 있다
- 워커가 태스크마다 새 서브프로세스를 띄우는 방식이 워커 사이징에 어떤 의미를 갖는지 설명할 수 있다
Airflow는 Dag(방향성 비순환 그래프)로 표현되는 워크플로를 빌드하고 실행하는 플랫폼이다. 이 모듈은 Airflow 3의 구성 요소(스케줄러, Dag 처리기, Dag 번들, API 서버, 메타데이터 DB, 선택적으로 워커·트리거러·플러그인)와 이들이 단일 머신 배포부터 분산 배포, Dag 처리기가 분리된 배포까지 어떻게 조합되는지, 그리고 태스크가 실제로 워커에서 어떤 방식(서브프로세스 단위)으로 실행되는지를 다룬다.
필수 컴포넌트
최소 구성의 Airflow 설치는 다섯 가지 컴포넌트로 이루어진다. 첫째, 스케줄러(scheduler)는 예약된 워크플로를 트리거하고 태스크를 익스큐터(executor)에 제출하는 역할을 한다. 익스큐터는 별도의 프로세스가 아니라 스케줄러의 설정 속성이며 스케줄러 프로세스 안에서 함께 동작한다. 둘째, Dag 처리기(Dag processor)는 Dag 번들에서 Dag 파일을 파싱해 메타데이터 데이터베이스에 직렬화한다. 셋째, Dag 번들(Dag bundle)은 Dag 처리기가 Dag 파일을 읽어오는 대상이자 워커가 올바른 버전의 Dag 파일에 접근할 수 있게 해주는 저장소로, 기본값은 로컬 디스크의 폴더다. 넷째, API 서버(API server)는 REST API를 제공하고 Dag·태스크의 상태를 조회·트리거·디버깅할 수 있는 UI를 제공하며, Task SDK를 사용하는 태스크들이 메타데이터 DB에 직접 접근하지 않고도 상태를 Airflow로 전달할 수 있게 중개한다. 다섯째, 메타데이터 데이터베이스(보통 PostgreSQL이나 MySQL)는 태스크·Dag·Variable의 상태를 저장하며, Airflow가 동작하려면 반드시 구성되어야 한다.
Airflow 3에서 Dag 처리기는 모든 배포에서 필수 컴포넌트로 항상 별도의 독립 프로세스로 실행된다. 이는 Airflow 2 시절 스케줄러가 Dag 파일을 직접 파싱하던 것과 다른 지점이다.
핵심 포인트
- 필수 컴포넌트는 스케줄러, Dag 처리기, Dag 번들, API 서버, 메타데이터 DB 다섯 가지다
- 익스큐터는 별도 컴포넌트가 아니라 스케줄러 프로세스 안에서 동작하는 설정 속성이다
- Airflow 3부터 Dag 처리기는 모든 배포에서 필수이며 항상 독립 프로세스로 실행된다
선택 컴포넌트와 보안을 위한 컴포넌트 분리
선택 컴포넌트로는 워커(worker), 트리거러(triggerer), 플러그인(plugin) 폴더가 있다. 워커는 스케줄러가 할당한 태스크를 실행하며, 기본 설치에서는 스케줄러의 일부로 동작할 수도 있고, CeleryExecutor에서는 장기 실행 프로세스로, KubernetesExecutor에서는 파드로 실행될 수 있다. 트리거러는 지연(defer)된 태스크를 asyncio 이벤트 루프 안에서 실행하며, 지연 태스크를 쓰지 않는 기본 설치에서는 필요하지 않다. 다만 Human-in-the-loop 태스크는 트리거러를 쓰지 않고 스케줄러가 관리하는 ``awaiting_input`` 상태에서 대기한다. 플러그인 폴더는 Airflow 기능을 확장하는 방법이며 스케줄러·Dag 처리기·트리거러·API 서버가 모두 읽는다.
분산 배포에서는 컴포넌트를 분리함으로써 보안이 강화된다. 예를 들어 Dag 처리기를 스케줄러에서 분리하면, 스케줄러는 Dag 번들에 접근할 수 없고 Dag 작성자가 제출한 코드를 실행할 수 없게 된다. API 서버 역시 Dag 번들에 직접 접근하지 않으며, UI의 Code 탭에 표시되는 코드는 메타데이터 DB에서 읽어온다. API 서버는 Dag 작성자가 제출한 임의의 코드를 실행할 수 없고, Deployment Manager가 설치한 패키지나 플러그인으로 설치된 코드만 실행할 수 있다. Operations User는 UI에만 접근할 수 있고 Dag를 트리거·모니터링할 수는 있지만 Dag를 작성할 수는 없다. 로컬 디스크 Dag 번들 백엔드는 버저닝을 지원하지 않으므로 Dag 처리기와 워커가 서로 다른 버전의 Dag를 보는 경우가 생길 수 있는데, Git 같은 버전 관리 Dag 번들 백엔드를 쓰면 스케줄러가 태스크를 디스패치할 때 특정 번들 버전을 고정할 수 있어 이 문제를 해결한다.
핵심 포인트
- 워커·트리거러·플러그인 폴더는 선택 컴포넌트이며, Human-in-the-loop 태스크는 트리거러 없이 스케줄러의 awaiting_input 상태에서 대기한다
- Dag 처리기를 스케줄러에서 분리하면 스케줄러가 Dag 작성자의 코드를 실행할 수 없게 되어 보안이 강화된다
- 로컬 디스크 Dag 번들은 버저닝을 지원하지 않아 Dag 처리기와 워커가 다른 버전을 볼 수 있으며, Git 같은 버전 관리 번들은 이를 해결한다
태스크 실행 아키텍처: 워커는 태스크마다 새 서브프로세스를 띄운다
워커는 태스크 코드를 자기 자신의 프로세스 안에서 절대 직접 실행하지 않는다. 태스크 인스턴스마다 새로운 서브프로세스를 시작하고, 이를 감독(supervise)하다가 태스크 인스턴스가 끝나면 종료시킨다. 이는 태스크가 어떤 언어로 작성되었는지와 무관하게 항상 성립하며, 서브프로세스의 종류만 언어에 따라 달라진다 — Python은 포크된 Python 인터프리터, Java는 완전히 새로운 JVM 인스턴스, Go는 컴파일된 태스크 바이너리의 새 프로세스다.
어떤 것도 태스크 인스턴스 사이에서 풀링되거나 재사용되지 않으므로, N개의 동시 태스크 인스턴스는 N개의 서브프로세스(플러스 태스크 코드 자체가 추가로 생성하는 프로세스)만큼의 비용이 든다. 따라서 워커를 사이징한다는 것은 워커 프로세스 자체가 아니라, 한 워커가 동시에 실행하는 태스크 인스턴스의 피크 개수를 기준으로 자원을 예산 책정하는 일이다. 또한 워커 프로세스만이 태스크의 단명(short-lived) 자격 증명을 보유하고 Execution API와 통신하는 유일한 주체이므로, 태스크 코드를 실행하는 서브프로세스는 어떤 언어로 작성되었든 메타데이터 데이터베이스에 직접 접근하지 않는다.
핵심 포인트
- 워커는 태스크마다 새 서브프로세스(Python은 포크된 인터프리터, Java는 새 JVM, Go는 새 바이너리 프로세스)를 띄우고, 끝나면 종료시킨다
- 서브프로세스는 태스크 인스턴스 간에 재사용되지 않으므로, 워커 사이징은 동시 실행 태스크 인스턴스의 피크 개수를 기준으로 해야 한다
- 태스크 코드를 실행하는 서브프로세스는 메타데이터 DB에 직접 접근하지 않으며, 워커 프로세스만 단명 자격 증명을 들고 Execution API와 통신한다