← 학습 카테고리

Learn

Airflow

151개 모듈 · 현재 92번째

Airflow 모듈 92/151 airflow-learn-92

스케줄러 기본 동작과 다중 스케줄러(HA)

Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation administration-and-deployment/scheduler.rst (개요 ~ Database Requirements 절)

이 모듈을 다 읽으면

  • 스케줄러의 기본 루프 동작과 스케줄 간격(interval) 종료 시점 트리거 원칙을 설명할 수 있다
  • 풀 슬롯이 충분할 때 우선순위가 무시될 수 있는 이유를 설명할 수 있다
  • HA 스케줄러가 DB row-level lock으로 동시성을 제어하는 원리와 DB별 지원 여부를 설명할 수 있다

스케줄러는 모든 Dag와 태스크를 모니터링하며 의존성이 충족된 태스크 인스턴스를 트리거하는 상시 서비스다. 2.0.0부터는 별도 합의 알고리즘 없이 메타데이터 DB의 row-level lock만으로 여러 스케줄러를 동시에 운영하는 HA 구성을 지원한다.

스케줄러의 기본 동작

Airflow 스케줄러는 모든 태스크와 Dag를 모니터링하다가 의존성이 충족되면 태스크 인스턴스를 트리거한다. 내부적으로는 지정된 Dag 디렉터리의 모든 Dag와 동기화 상태를 유지하는 서브프로세스를 띄운다. 기본적으로 1분에 한 번씩 Dag 파싱 결과를 수집하고 트리거 가능한 태스크가 있는지 확인한다.

스케줄러는 프로덕션 환경에서 상시 서비스로 실행되도록 설계되었으며, ``airflow scheduler`` 명령 하나로 시작할 수 있고 ``airflow.cfg``의 설정을 사용한다. 태스크를 실제로 실행하는 데는 구성된 Executor를 쓴다. 첫 Dag Run은 Dag 내 태스크들의 최소 ``start_date``를 기준으로 생성되고, 이후 실행은 Dag의 timetable을 따른다.

핵심 포인트

  • 스케줄러는 기본적으로 1분마다 Dag 파싱 결과를 수집하고 트리거 가능한 태스크를 확인한다
  • airflow scheduler 명령으로 시작하는 상시 서비스이며 구성된 Executor로 태스크를 실행한다
  • 첫 Dag Run은 태스크들의 최소 start_date 기준, 이후는 timetable을 따라 생성된다

스케줄 간격 종료 후 트리거되는 이유

cron이나 timedelta 스케줄을 쓰는 Dag는 그 스케줄이 커버하는 기간이 끝나야 태스크를 트리거한다. 예를 들어 ``@daily``로 설정된 잡은 하루가 끝난 뒤에 실행된다. 이 방식은 그 기간에 필요한 데이터가 완전히 준비된 뒤에 Dag가 실행되도록 보장하기 위함이며, UI에서는 마치 Airflow가 하루 늦게 태스크를 실행하는 것처럼 보인다.

예를 들어 하루 스케줄을 쓰는 Dag에서 데이터 인터벌이 2019-11-21에 시작하는 실행은 ``2019-11-21T23:59`` 이후에 트리거된다. 즉 스케줄러는 시작 날짜로부터 한 스케줄 주기 **뒤**, 인터벌이 끝나는 시점에 잡을 실행한다.

핵심 포인트

  • cron/timedelta 스케줄은 인터벌이 끝나야(예: @daily는 하루가 끝나야) 트리거되어 UI에서는 하루 늦어 보인다
  • 이는 그 인터벌에 필요한 데이터가 완전히 준비된 후 실행을 보장하기 위한 설계다

풀 슬롯과 태스크 우선순위

스케줄러는 가능한 한 빨리 태스크를 스케줄링하도록 높은 처리량을 목표로 설계되었다. 이를 위해 스케줄러는 풀에 남은 빈 슬롯 수를 확인하고 한 번의 반복(iteration)에서 그 수만큼만 태스크 인스턴스를 스케줄링한다. 이는 대기 중인 스케줄 대상 태스크가 큐 슬롯보다 많을 때만 태스크 우선순위가 실제로 작동한다는 것을 의미한다. 따라서 낮은 우선순위 태스크와 높은 우선순위 태스크가 같은 배치에 속해 있으면, 낮은 우선순위 태스크가 먼저 스케줄링되는 경우도 있을 수 있다.

핵심 포인트

  • 스케줄러는 한 번의 반복에서 풀의 빈 슬롯 수만큼만 태스크를 스케줄링한다
  • 대기 태스크 수가 큐 슬롯 수보다 많아야만 우선순위가 실제로 영향을 준다
  • 같은 배치 안에서는 낮은 우선순위 태스크가 높은 우선순위 태스크보다 먼저 스케줄링될 수 있다

다중 스케줄러(HA) 개요

2.0.0부터 Airflow는 성능과 복원력을 위해 여러 스케줄러를 동시에 실행하는 것을 지원한다. HA 스케줄러는 기존 메타데이터 DB를 최대한 활용하도록 설계되었다 — 스케줄러 간 직접 통신이나 Raft/Paxos 같은 합의 알고리즘, Zookeeper/Consul 같은 별도 합의 도구를 쓰지 않음으로써 운영 표면적을 최소화했다.

스케줄러는 직렬화된(serialized) Dag 표현을 사용해 스케줄링 결정을 내리며, 대략적인 스케줄링 루프는 다음과 같다: 새 DagRun이 필요한 Dag를 확인해 생성하기, 스케줄 가능한 TaskInstance나 완료된 DagRun을 찾기 위해 DagRun 배치를 검사하기, Pool 한도와 기타 동시성 한도를 지키면서 스케줄 가능한 TaskInstance를 선택해 실행을 위해 큐에 넣기.

핵심 포인트

  • 2.0.0부터 다중 스케줄러 동시 실행을 지원하며, 별도 합의 알고리즘/도구 없이 기존 메타데이터 DB만으로 동작한다
  • 스케줄링 루프는 새 DagRun 생성 → 스케줄 가능 TaskInstance/완료 DagRun 검사 → Pool/동시성 한도를 지키며 큐잉의 순서로 진행된다

DB 요구사항과 row-level lock

PostgreSQL 12+ 또는 MySQL 8.0+ 사용자는 별도 설정 없이 원하는 만큼 스케줄러를 실행할 수 있다. 성능과 처리량을 유지하기 위해 스케줄링 루프 중 메모리 내에서 많은 계산을 하는 부분이 있는데(매 TaskInstance마다 DB 왕복이 너무 느리기 때문), 이때 여러 한도가 잘못 지켜지지 않도록 오직 하나의 스케줄러만 이 크리티컬 섹션에 들어가도록 DB row-level lock(``SELECT ... FOR UPDATE``)을 사용한다.

이 크리티컬 섹션은 TaskInstance가 scheduled 상태에서 executor로 큐잉되는 동안 동시성/Pool 한도를 지키는 구간이며, Pool 테이블의 모든 행에 대해 row-level 쓰기 락을 요청하는 방식으로 획득된다(``SELECT * FROM slot_pool FOR UPDATE NOWAIT``와 대략 동등하나 정확한 쿼리는 조금 다르다).

PostgreSQL 12+와 MySQL 8.0+는 완전히 지원되어 "최적"의 경험을 제공한다. (버전 민감) MariaDB는 10.6.0 전까지 ``SKIP LOCKED``나 ``NOWAIT`` SQL 절을 구현하지 않았다 — 이 기능들이 없으면 다중 스케줄러 실행이 지원되지 않고 데드락 에러가 보고된 바 있다. 10.6.0 이후는 다중 스케줄러와 적절히 동작할 수도 있지만 테스트되지는 않았다. Microsoft SQL Server는 HA로 테스트된 적이 없다.

핵심 포인트

  • PostgreSQL 12+/MySQL 8.0+는 추가 설정 없이 다중 스케줄러를 완전히 지원한다
  • 크리티컬 섹션은 Pool 테이블 전체 행에 대한 row-level 쓰기 락(SELECT...FOR UPDATE)으로 단일 스케줄러만 진입하게 강제한다
  • (버전 민감) MariaDB는 10.6.0 이전에는 SKIP LOCKED/NOWAIT 미지원으로 다중 스케줄러가 지원되지 않으며, MS SQL Server는 HA로 테스트되지 않았다