ㅅㅇ
Airflow Pool로 Task 병목 해소 본문
운영 환경에서 특정 DAG가 장시간 실행되며 CPU와 메모리를 과도하게 점유하면서,
다른 도메인의 DAG까지 queued 상태로 밀리는 문제가 발생했습니다.
Airflow는 Executor가 허용하는 범위 내에서 가능한 많은 Task를 병렬 실행합니다.
이 과정에서 리소스를 많이 사용하는 Task가 Executor 자리를 선점하면,
상대적으로 가벼운 Task도 실행 기회를 얻지 못하는 문제가 발생합니다.
이 문제를 해결하기 위해 Pool을 활용한 논리적 격리 전략을 도입했습니다.
Pool 이란 ?
Pool은 CPU/RAM과 같은 물리 자원을 격리하는 기능이 아닙니다.
대신, Task 실행 개수를 논리적으로 제한하는 제어 장치입니다.
“물리적 격리”가 아니라 “논리적 실행 제어”
- 모든 Pool은 동일한 VM 자원을 공유하며,
- Pool이 제어하는 것은 오직 “동시 실행 가능한 Task 수” 입니다.
⇒ 즉, Pool은 시스템을 빠르게 만드는 기능이 아니라, 시스템 안정성을 확보하기 위한 실행 제어 전략입니다.
| 항목 | 설명 |
| 특징 | 논리적 제한 (물리적 자원 격리는 아님) |
| Pool | 동시에 실행 가능한 task 수 제한 |
| Slot | pool 내 동시 실행 가능 개수 |
| 기본 pool | default_pool (기본 128 slot) |
| 적용 단위 | Task 단위. Dag 단위로 할 수 있으나, 권장 x |
Task 단위로 사용하는 이유
DAG에는 가벼운 Task도 있고 무거운 Task도 존재합니다.
Pool은 리소스 특성이 다른 작업 단위를 격리하기 위한 장치이므로, Task 단위로 적용하는 것이 적합합니다.
DAG 단위로 Pool을 적용하면 가벼운 Task까지 함께 제한을 받아 병목이 전파되고, 세밀한 실행 제어가 어려워집니다.
언제 Pool을? 어떤 효과로?
1. 무거운 작업 격리하고 가벼운 Task 보호
Pool이 없으면 무거운 task가 executor를 모두 점유하게 되면서 다른 task 실행 기회를 빼앗습니다.
Pool을 분리하면,
- 무거운 task → 특정 Pool로 분리하여 해당 pool 안에서만 경쟁하게 합니다.
- 일반 task → default_pool에서 정상 실행
결과적으로 queue를 분리하는 효과를 얻습니다.
2. 동시에 실행되면 안되는 작업의 상한을 명확히 하고 싶을 때
DB write, 외부 API rate limit, GPU / 모델 로딩 등 동시 병렬로 실행되면 안되는 task 가 있을 수 있습니다.
이 때, slot 수를 걸어, 해당 pool 내에서 해당 작업이 N개까지만 실행하도록 상한을 걸 수 있습니다.
3. Dynamic Task 폭주 제어
Dynamic Task Mapping은 런타임에 수십~수백 개 task run 이 생성될 수 있기에, 해당 Dynamic Task Mapping task 는 pool 을 분리하는 것이 바람직합니다.
구현 방법
1. Pool 생성
- 대시보드에서도 생성 가능합니다.
airflow pools set ai_sem_pool 6 "AI semantic & embedding heavy tasks"
| 항목 | 설명 | 예시 값 |
| Pool name | pool 명 | ai_sem_pool |
| Slots | 해당 공용 자원 풀에서 실행 가능한 slot 갯수 | 6 |
| include_deferred | Include deferred 옵션 | 기본 False 권장 |
| description | 설명 | "AI semantic & embedding heavy tasks" |
include_deferred=True는 task 가 defer 상태에서도 slot을 점유하게 됩니다.
외부 세션 유지 등 특별한 경우가 아니면 False 권장합니다.
2. Task에 Pool 적용
| 옵션 | 의미 |
| pool | 사용할 pool 이름 |
| pool_slots | 해당 task가 점유할 slot 수 task 하나 당, 점유할 slot 수 인 것. |
@task(
pool="ai_sem_pool",
pool_slots=1
)
def generate_embedding(...):
...
예시 동작 플로우
slots 을 2개 설정하여 최대 동시에 task 2개만 실행가능한 pool 이 있을 때,
task1 에서 pool_slots=2 를 설정하고 실행한다면,
해당 task1의 run이 slot 2개를 동시에 점유합니다.
즉, pool 전체를 독점하여 다른 task instance 가 queue 상태일 것입니다.
Dynamic Task Mapping 에서 사용 시 주의
핵심 원칙
- Pool 전체 slots는 해당 task의 max_active_tasks 설정을 고려해 충분히 확보해야 합니다.
- Task 설정 시 pool_slots=1로 고정
- → mapped task가 여러 slot을 동시에 점유하지 않도록
주의
1. Pool 전체 slots 이 작을 때
pool 의 slots 이 넉넉하지 않다면, dynamic task 병렬 처리 제기능을 하지 못할 것입니다.
2. 실행 병렬 수 결정 요소
Task의 실제 동시 실행 개수는 다음 순서로 제한됩니다.
| 순위 | 제한 요소 |
| 1 | pool slots |
| 2 | max_active_tasks_per_dag |
| 3 | parallelism |
| 4 | 가용 CPU 자원 |
즉, 상위 제한이 하위 제한보다 작으면, Pool 설정은 의미가 없습니다.
예시 :
max_active_tasks_per_dag = 10
ai_sem_pool slots = 3
→ 해당 dag 의 task 가 최대 10개까지는 실행되도록 기대하지만, pool slot 제한으로 실제로 최대 3개만 실행될 수 있습니다.
3. Pool은 실행 제어이지 생성 제어가 아님
Pool은 실행 수를 제어할 뿐, task 생성 수를 줄여주지는 않습니다.
mapped task 의 수가 많으면 Scheduler 메모리 증가, UI 느려지는 문제가 발생할 수 있는데, 해당 문제를 해결해주진 않습니다.
운영 중 확인 방법
Pool 상태 확인
airflow pools list
실행 중 Task 조회
SELECT
ti.dag_id,
ti.task_id,
ti.state,
ti.pool
FROM task_instance ti
WHERE ti.pool='ai_sem_pool'
AND ti.stateIN ('running','queued');
- running → slot 점유 중
- queued → slot 대기 중
Pool은 성능 최적화 도구가 아니라,
과도한 병렬 실행으로부터 시스템을 보호하는 안정성 제어 메커니즘입니다.
적절한 Task를 적절한 Pool로 분리하는 전략은
Airflow 운영 안정성을 유지하는 데 있어 핵심적인 설계 요소라 할 수 있습니다.
'DEV > Airflow' 카테고리의 다른 글
| Airflow 로컬 IDE 디버깅 환경 구축 (0) | 2026.02.20 |
|---|
