Executor란 무엇인가 — 유형과 LocalExecutor
Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation core-concepts/executor/index.rst - Executor, Executor Types; core-concepts/executor/local.rst 전체
이 모듈을 다 읽으면
- Executor가 스케줄러 프로세스와 어떤 관계인지 설명할 수 있다
- 로컬 실행기와 원격 실행기(큐/배치형, 컨테이너형)의 장단점을 비교할 수 있다
- LocalExecutor의 parallelism 파라미터와 fork/spawn 모드 차이를 설명할 수 있다
Executor는 태스크 인스턴스를 실제로 실행하는 pluggable 컴포넌트이며 [core] executor 설정으로 교체한다. 이 모듈은 Executor가 스케줄러 프로세스와 맺는 관계, 로컬/원격(큐·배치형, 컨테이너형) 실행기의 트레이드오프, 그리고 기본 실행기인 LocalExecutor의 parallelism과 워커 스폰 방식을 다룬다.
Executor의 역할과 설정
Executor는 태스크 인스턴스를 실행하는 메커니즘이다. 공통 API를 갖고 있어 설치 요구사항에 따라 교체(pluggable)할 수 있으며, [core] executor 옵션으로 지정한다. 내장 실행기는 이름만으로(예: executor = KubernetesExecutor), 커스텀·서드파티 실행기는 파이썬 클래스의 모듈 경로로 지정한다. 현재 설정된 실행기는 `airflow config get-value core executor` 명령으로 확인할 수 있으며, 기본값은 LocalExecutor다.
신규 사용자가 흔히 오해하는 지점이 있다. 로컬/원격 실행기 중 하나를 쓰려면 별도의 실행기 프로세스를 반드시 띄워야 한다고 생각하기 쉽지만, 그렇지 않다. 실행기 로직은 스케줄러 프로세스 '안에서' 동작하며, 선택된 실행기에 따라 태스크를 로컬에서 실행할지 아닐지가 달라질 뿐이다.
핵심 포인트
- Executor는 태스크 인스턴스를 실제로 실행하는 메커니즘이며 [core] executor 설정으로 교체 가능한 pluggable 컴포넌트다
- 실행기 로직은 스케줄러 프로세스 '안에서' 동작한다 — 별도 프로세스를 반드시 띄워야 하는 것은 아니다
- airflow config get-value core executor 명령으로 현재 설정된 실행기를 확인할 수 있다
로컬 실행기 vs 원격 실행기 (큐/배치형 vs 컨테이너형)
로컬 실행기는 스케줄러 프로세스 안에서 태스크를 직접 실행한다. 장점은 사용이 쉽고 빠르며 지연이 매우 낮고 설정 요구사항이 적다는 것이고, 단점은 기능이 제한적이며 스케줄러와 자원을 공유한다는 것이다. Airflow는 기본값으로 LocalExecutor를 탑재하며, 소규모 단일 머신 운영에는 적합하지만 다중 머신/클라우드 운영에는 원격 실행기 중 하나를 쓰는 것이 권장된다.
원격 실행기는 다시 두 갈래로 나뉜다. 첫째, 큐/배치형(Queued/Batch) 실행기는 태스크를 중앙 큐에 보내고 원격 워커가 이를 가져가 실행한다. 워커는 대체로 상시 대기(persistent)하며 한 번에 여러 태스크를 처리한다. 장점은 워커를 스케줄러 프로세스에서 분리해 견고성을 높이고, 대형 호스트로 많은 태스크를 비용 효율적으로 처리할 수 있으며, 워커가 늘 대기 중이면 지연도 낮게 유지할 수 있다는 것이다. 단점은 공유 워커에서 noisy neighbor(자원·환경 설정 경합) 문제가 생길 수 있고, 워크로드가 일정하지 않으면 유휴 워커나 과잉 스케일링 비용이 발생하며 스케일 관리 부담이 생긴다는 것이다. 대표 예로 CeleryExecutor, BatchExecutor, EdgeExecutor가 있다.
둘째, 컨테이너형(Containerized) 실행기는 태스크마다 컨테이너/파드 안에서 애드혹으로 실행되며, 태스크가 큐잉될 때 격리된 실행 환경이 배포된다. 장점은 태스크마다 격리되어 noisy neighbor 문제가 없고, 태스크별로 시스템 라이브러리·바이너리·자원량 등을 맞춤 설정할 수 있으며, 워커가 태스크 실행 기간에만 살아있어 비용 효율적이라는 것이다. 단점은 컨테이너·파드가 뜨는 동안 시작 지연이 있고, 짧고 작은 태스크를 대량으로 돌리면 오히려 비쌀 수 있으며, 워커 관리는 필요 없지만 대신 쿠버네티스 같은 클러스터 자체를 관리해야 한다는 것이다. 대표 예로 KubernetesExecutor, EcsExecutor가 있다.
핵심 포인트
- LocalExecutor는 기본값이며 소규모 단일 머신 운영에 적합하지만 스케줄러와 리소스를 공유한다
- 큐/배치형 원격 실행기(Celery, Batch, EdgeExecutor)는 상시 대기 워커풀을 쓰므로 지연은 낮지만 워크로드가 일정하지 않으면 유휴 자원 비용이 발생한다
- 컨테이너형 실행기(Kubernetes, ECS)는 태스크마다 격리된 실행 환경을 제공해 noisy neighbor 문제가 없지만 컨테이너/파드 기동 지연이 있다
LocalExecutor 상세: parallelism과 워커 스폰 방식
LocalExecutor는 스케줄러 노드 위에서 통제된 방식으로 프로세스를 스폰해 태스크를 실행한다. parallelism 파라미터는 스폰되는 프로세스 수를 제한해 노드가 과부하되지 않게 하며 반드시 0보다 커야 한다. [core] parallelism 설정으로 조정하고 기본값은 32다. LocalExecutor는 시작(start) 시점에 self.parallelism 개수만큼 프로세스를 스폰하고, task_queue로 태스크 유입과 워커 간 작업 분배를 조율한다. 워커는 준비되는 즉시 태스크를 가져가며, LocalExecutor 생명주기 동안 태스크를 기다리며 대기하다가, 셧다운 호출을 받으면 poison token을 받아 종료된다.
워커 스폰 방식은 멀티프로세싱 시작 방식에 따라 다르다. fork 모드(리눅스 기본값)는 Copy-on-Write(COW)로 인한 메모리 스파이크를 막기 위해 parallelism만큼의 워커를 한꺼번에 스폰한다. spawn 모드(macOS·Windows 기본값)는 다수 프로세스를 동시에 스폰하는 오버헤드를 피하기 위해 필요할 때마다 워커를 하나씩 스폰한다.
LocalExecutor 워커는 스케줄러의 서브프로세스로 스폰되므로, 컨테이너화된 환경에서는 이것이 스케줄러 프로세스의 과도한 메모리 소비처럼 보일 수 있고 OOM(메모리 부족)으로 인한 컨테이너 재시작을 유발할 수 있다. 컨테이너의 자원 한도에 맞춰 parallelism 값을 조정해야 한다. 여러 스케줄러가 [core]에 executor=LocalExecutor로 설정되어 있으면 각 스케줄러가 자신의 LocalExecutor를 돌리므로 태스크가 여러 머신에 분산 처리되는데, 한 스케줄러가 재시작되면 다른 스케줄러들이 고아가 된 태스크를 인식하고 재시작/실패 처리하기까지 시간이 걸릴 수 있다는 점을 고려해야 한다.
이전 버전 Airflow에는 무제한 parallelism(self.parallelism = 0) 옵션이 있었지만, 스케줄러 노드 과부하를 막기 위해 Airflow 3.0.0부터 이 옵션은 제거되었다.
핵심 포인트
- parallelism은 0보다 커야 하며 기본값은 32이고 [core] parallelism으로 설정한다
- fork 모드(리눅스 기본)는 시작 시 parallelism만큼 한꺼번에 워커를 스폰해 COW 메모리 스파이크를 막고, spawn 모드(macOS/Windows 기본)는 필요할 때마다 하나씩 스폰한다
- LocalExecutor 워커는 스케줄러의 자식 프로세스이므로 컨테이너 환경에서는 스케줄러의 메모리 사용량으로 보여 OOM 재시작을 유발할 수 있다
- Airflow 3.0.0부터 parallelism=0(무제한) 옵션은 제거되었다