Blog
airflowproductionbest-practicesidempotencyreliabilitychecklist

Airflow 3 프로덕션 베스트 프랙티스 체크리스트

멱등성·자원 최적화·신뢰성을 점검하고, 배포 전 운영 준비도를 체크박스로 확인하는 Airflow 3 실전 연재의 마지막 편입니다.

Data Dynamics2026년 7월 6일16 min read

지금까지 12편에 걸쳐 Airflow 3의 아키텍처부터 테스트·CI/CD·보안까지 훑어왔습니다. 마지막인 이번 편은 새로운 기능을 배우는 게 아니라, 그 기능들을 실제 운영에 올리기 전에 한 번 더 점검하는 자리입니다. "로컬에선 잘 돌았는데 운영에서 터졌다"는 사고의 90%는 멱등성·자원·신뢰성 이 세 가지 중 하나를 놓쳐서 생깁니다.

이 글은 **"Airflow 3 실전 연재"의 12편(마지막)**입니다. 직전 편 테스트·CI/CD·보안에서 파이프라인을 안전하게 배포하는 방법을 다뤘다면, 이번 편은 그 파이프라인이 운영에서 견디는지를 점검합니다. 다음 편은 없습니다.

이 글에서 점검하는 것

  • 재실행해도 안전한 멱등(idempotent) 태스크 설계
  • 동시성·Pool·deferrable로 자원과 비용을 아끼는 법
  • retries·타임아웃·백필로 신뢰성을 확보하는 법
  • 배포 전에 한 번에 훑는 운영 준비도 체크리스트
  • 연재 전체를 한 장으로 정리한 지도

1. 멱등성 — 재실행해도 안전한 태스크

운영 파이프라인의 첫 번째 계명은 **멱등성(idempotency)**입니다. 같은 태스크를 몇 번을 다시 돌려도 결과가 똑같아야 한다는 뜻입니다. Airflow는 재시도(retry), 백필, 수동 clear & rerun 등 같은 태스크를 여러 번 실행하는 상황이 일상이기 때문에, 멱등하지 않은 태스크는 운영에서 반드시 사고를 냅니다.

핵심은 "추가(append)"가 아니라 "덮어쓰기(overwrite)"로 사고하는 것입니다. 매 실행이 자기가 책임지는 파티션 하나를 통째로 다시 쓴다고 생각하면 됩니다.

from airflow.sdk import dag, task
import pendulum
 
 
@dag(
    schedule="@daily",
    start_date=pendulum.datetime(2026, 1, 1, tz="UTC"),
    catchup=False,  # Airflow 3에서 기본값이 False로 바뀜
)
def daily_etl():
 
    @task
    def write_partition(data_interval_start=None):
        # logical_date 대신 data_interval로 파티션 경계를 잡는다
        ds = data_interval_start.format("YYYY-MM-DD")
        # 1) 먼저 해당 파티션을 비우고 (idempotent의 핵심)
        delete_partition(table="events", dt=ds)
        # 2) 그 파티션만 다시 채운다 — 몇 번을 재실행해도 결과 동일
        insert_partition(table="events", dt=ds, rows=extract(ds))
 
    write_partition()
 
 
daily_etl()

멱등성의 한 줄 요약: "INSERT가 아니라 DELETE 후 INSERT, 또는 MERGE/UPSERT로 쓴다."

참고로 Airflow 3에서는 execution_date가 제거되어 logical_date를 쓰지만, asset이나 수동 트리거 실행에서는 logical_dateNone일 수 있습니다. 따라서 파티션 키를 잡을 때는 가능하면 data_interval_start/data_interval_end를 쓰는 편이 안전합니다.

아래 흐름도는 안전한 재실행 파이프라인이 어떤 게이트를 거치는지 보여줍니다.

Loading diagram…

여기서 한 가지 더: 부분 실패(partial write)를 남기지 않는 것이 중요합니다. 파티션을 반쯤 쓰다가 죽으면 재실행 시 중복이 생깁니다. 가능하면 임시 위치에 쓴 뒤 마지막에 원자적으로 교체(atomic swap)하거나, 트랜잭션·INSERT OVERWRITE·MERGE처럼 원자성을 보장하는 연산을 쓰세요.

2. 자원·비용 최적화

파이프라인이 늘어나면 다음 고민은 "워커가 부족하다" 또는 "클라우드 비용이 너무 나온다"로 옮겨갑니다. 자원을 아끼는 지렛대는 크게 세 가지입니다.

동시성 3계층. 3편(환경설정 & 최적화)에서 자세히 다뤘듯이 Airflow는 동시성을 3단계로 제어합니다.

설정범위역할
parallelism전체 시스템동시에 실행될 수 있는 태스크 총량
max_active_tasks_per_dagDAG당한 DAG 안에서 동시에 도는 태스크 수
max_active_runs_per_dagDAG당한 DAG의 동시 실행(run) 수

Pool. 특정 자원(예: 외부 DB 커넥션, 라이선스가 걸린 API)을 보호하려면 Pool로 동시 접근 수를 묶어두세요. priority_weight와 함께 쓰면 중요한 태스크가 먼저 슬롯을 잡습니다.

Deferrable operator. 8편(외부 시스템 연동)에서 본 것처럼, 외부 작업이 끝나길 그냥 기다리기만 하는 태스크라면 deferrable로 만들어 Triggerer에 넘기세요. 워커 슬롯을 점유하지 않고 비동기로 폴링하므로, "대기만 하는 태스크"가 워커를 잠그는 낭비를 없앱니다.

# 나쁜 예: 워커 슬롯을 점유한 채 sleep
sensor = SomeSensor(task_id="wait", mode="poke")
 
# 좋은 예: deferrable — Triggerer가 비동기로 대기, 워커는 자유
sensor = SomeSensor(task_id="wait", deferrable=True)

워커 오토스케일도 함께 고려하세요. KubernetesExecutor는 태스크별로 pod를 띄워 자연스럽게 스케일하고, CeleryExecutor는 큐 길이를 기준으로 워커 수를 늘리고 줄이도록(KEDA 등) 구성할 수 있습니다. 핵심 원칙은 같습니다: 놀고 있는 워커에 돈을 쓰지 말 것.

3. 신뢰성 — 실패를 다루는 법

운영은 "실패하지 않게" 만드는 게 아니라 "실패해도 복구되게" 만드는 일입니다.

  • 재시도(retries). 일시적 오류(네트워크·throttling)는 retriesretry_delay(지수 백오프 retry_exponential_backoff=True)로 대부분 자동 회복됩니다. 단, 멱등하지 않은 태스크에 retry를 거는 건 사고를 자동화하는 것이니, 1번 멱등성이 전제입니다.
  • 타임아웃. execution_timeout을 걸지 않으면 멈춰버린 태스크가 워커 슬롯을 영원히 점유합니다. 정상 실행 시간의 1.5~2배 정도를 기준으로 잡으세요. (Airflow 3에서 SLA는 제거되고 Deadline/deadline alerting으로 대체되었으니, 마감 기준 알림은 그쪽을 사용합니다.)
  • 알림. 10편(모니터링 & 운영)에서 다룬 대로 실패는 사람에게 닿아야 의미가 있습니다. on_failure_callback이나 알림 채널을 반드시 연결하세요.
  • 백필 전략. Airflow 3는 **스케줄러가 관리하는 백필(scheduler-managed backfill)**을 제공합니다. UI/API에서 트리거하면 스케줄러가 수행하므로, 과거 데이터를 다시 채울 때 동시성 한도를 넘기지 않습니다. 백필 역시 1번의 멱등성이 없으면 중복을 양산하니, 멱등성이 모든 것의 기반입니다.

4. 운영 준비도 체크리스트

배포 전, 아래 다섯 영역을 한 번씩 짚고 넘어가세요. 한 항목이라도 체크가 안 되면 그 부분이 다음 사고의 후보입니다.

Loading diagram…

설정(Configuration)

  • catchup=False를 의도적으로 확인했다(기본값이지만 DAG별로 명시)
  • 동시성 3계층(parallelism·max_active_tasks_per_dag·max_active_runs_per_dag)을 부하에 맞게 설정했다
  • 자원 보호가 필요한 태스크는 Pool로 묶었다
  • 모든 태스크에 execution_timeout이 있다
  • executor 선택(Local/Celery/Kubernetes/Edge, 또는 hybrid)이 워크로드와 맞다

고가용성(HA)

  • 스케줄러를 2개 이상 띄웠다(단일 장애점 제거)
  • API server·DAG processor·Triggerer가 각각 독립 프로세스로 분리·다중화되어 있다
  • 메타데이터 DB가 매니지드/복제 구성이며 백업·복구를 실제로 테스트했다
  • DAG bundle(git 등) 동기화가 모든 컴포넌트에서 일관된다

모니터링(Monitoring)

  • 핵심 메트릭(스케줄러 하트비트, 태스크 큐 적체, 실패율)을 수집한다
  • 로그가 중앙 저장소로 모인다(워커가 죽어도 로그가 남는다)
  • 실패·지연 알림이 사람에게 닿는 경로가 있다
  • DAG versioning으로 어떤 버전이 돌았는지 추적 가능하다

보안(Security)

  • REST API 인증(JWT)과 권한이 설정되어 있다(구 experimental API 미사용)
  • 시크릿(커넥션·변수)이 평문이 아니라 시크릿 백엔드로 관리된다
  • 워커가 메타데이터 DB에 직접 접속하지 않고 Task Execution API로 통신한다

테스트(Testing)

  • DAG 무결성(import·순환 의존성) 테스트가 CI에 있다
  • 핵심 비즈니스 로직에 단위 테스트가 있다
  • 배포는 CI/CD 게이트를 통과해야만 운영에 반영된다

자세한 보안·테스트 항목은 11편(테스트·CI/CD·보안)을 참고하세요.

5. 연재 전체 지도

12편의 여정을 한 장으로 정리합니다. 필요한 주제로 언제든 되돌아오세요.

한 줄 핵심링크
0왜 Airflow 3인가, 언제 쓰나개요
1Scheduler·API server·DAG processor·Triggerer 구조아키텍처
2HA를 갖춘 클러스터로 띄우기클러스터 구성
3동시성·Pool·executor 튜닝환경설정 & 최적화
4Task SDK로 DAG 작성하는 정석DAG 작성
5스크립트·파라미터·에러·재실행·날짜 기준DAG 고급 테크닉
6Asset 기반 데이터 인지 스케줄링스케줄링 & Asset
7XCom으로 태스크 간 데이터 전달XCom
8외부 시스템 연동 & deferrable외부 연동
9REST API로 원격 스케줄 제어REST API & 원격
10메트릭·로그·알림으로 운영하기모니터링 & 운영
11테스트·CI/CD·시크릿 보안테스트·CI/CD·보안
12프로덕션 준비도 점검(이 글)

6. 다음 학습 방향

이 연재는 끝났지만, Airflow는 계속 발전합니다. 다음 단계로는 이런 것들을 권합니다.

  • 공식 문서를 자주 들여다보세요. 특히 Apache Airflow 공식 문서의 best practices·release notes는 버전이 올라갈 때마다 바뀌므로, 설정 키나 동작 변경은 항상 자기 버전 문서로 확인하는 습관이 안전합니다.
  • Provider 패키지 생태계를 파악하세요. AWS·GCP·Snowflake·dbt 등 대부분의 외부 연동은 provider 패키지로 제공됩니다. 직접 구현하기 전에 이미 있는 operator/hook을 먼저 찾아보면 코드가 훨씬 줄어듭니다.
  • EdgeExecutor와 hybrid executor 같은 3.x의 새 실행 모델은 원격·엣지 워크로드가 있다면 직접 실험해 볼 가치가 있습니다.

마지막 한 줄: 멱등성을 기반으로, 자원을 아끼고, 실패를 전제로 설계하라. 이 세 문장이 프로덕션 Airflow의 거의 전부입니다.

여기까지 함께 와주셔서 감사합니다. 이 연재가 여러분의 데이터 파이프라인을 조금 더 단단하게 만드는 데 보탬이 되었기를 바랍니다.