Airflow DAG 복구 실패: 한밤중 데이터 파이프라인 장애 해결 과정

새벽 2시, 모니터링 알림이 울렸다. Airflow DAG이 꼬여서 데이터 파이프라인이 멈춰 섰다. 다음 날 아침 대시보드에 올라가야 할 핵심 지표가 모두 공백으로 남을 상황, 그 순간의 아찔함은 데이터 엔지니어라면 누구나 한 번쯤 겪어봤을 공포일 것이다. 특히 Airflow DAG을 여러 개 운영하다 보면, 의존성이 복잡하게 얽힌 파이프라인 하나가 꼬이기 시작하면 연쇄적으로 실패가 전파되며 한밤중의 악몽이 현실이 된다. 오늘은 실제로 프로덕션 환경에서 Airflow DAG이 꼬여 데이터 파이프라인 전체가 마비됐던 경험과, Airflow DAG 복구 실패를 해결하기 위해 적용한 단계별 대응법을 상세하게 공유한다. 같은 실수를 반복하지 않기 위한 진단법과 예방 전략까지, 현실적인 팁을 모두 담았다.

📌 요약: 이 글에서 다루는 내용

✅ Airflow DAG이 꼬이는 3가지 대표적인 원인
✅ 실제 사례로 보는 장애 발생 과정 (타임스탬프 기준)
✅ DAG 복구를 위한 5단계 실전 진단법
✅ 파이프라인 장애를 예방하는 운영 환경별 비교 분석
✅ 한밤중에도 당황하지 않는 모니터링 & 알림 전략

1. 왜 Airflow DAG은 갑자기 꼬이는가: 근본 원인 파헤치기

Airflow DAG이 꼬이는 현상은 단순히 코드 오류 때문만이 아니다. 운영 환경에서 발생하는 DAG 장애는 대부분 예기치 못한 외부 요인과 스케줄링 로직의 충돌에서 비롯된다. 필자가 겪은 장애의 발단은 다소 사소했다. 특정 외부 API의 응답 지연이 발생했고, 해당 태스크의 retries(재시도 횟수)가 소진되면서 DAG이 실패로 마킹됐다. 문제는 여기서 끝나지 않았다. 다음 실행 주기에도 같은 태스크가 다시 실행되며, 누적된 실패가 전체 파이프라인의 백필(Backfill)을 유발한 것이다.

실제로 장애 상황을 분해해 보면, DAG이 꼬이는 핵심 원인은 크게 세 가지다. 첫째, 태스크 간 의존성 설정 오류다. 예를 들어 depends_on_past=True 옵션을 과도하게 사용하면 이전 실행이 실패할 때마다 후속 실행이 모두 블로킹된다. 둘째, 리소스 경합이다. 동시에 실행되는 DAG이 많아지면 Worker 프로세스가 고갈되고, 태스크가 대기열에서 멈춰 있는 ‘Hanging’ 상태가 발생한다. 셋째, 스케줄 간격과 데이터 도착 시간의 불일치다. 데이터 소스가 지연되는데 DAG의 schedule_interval이 이를 고려하지 않으면, 반복적인 Partial Failure가 발생한다. 추가로, 특정 태스크의 실패가 전체 DAG로 번지는 것을 미리 차단하려면 trigger_rule='all_done' 같은 흐름 제어 옵션을 설계 단계에서 고려해 볼 수 있다.

1-1. 장애의 시작: 단일 태스크 실패가 전체로 번지기까지

내가 겪은 사례는 schedule_interval='@daily'로 설정된 매출 집계 DAG이었다. 새벽 1시에 실행되도록 설계돼 있었지만, 전날 밤 원천 데이터 업데이트가 30분 지연되면서 태스크의 execution_timeout에 걸렸다. 해당 태스크는 재시도를 2회로 설정해 두었지만, 재시도 동안 DAG의 다른 다운스트림이 이미 실행을 기다리며 슬롯을 점유하고 있었다.

결과적으로, 실패한 태스크가 재시도되는 동안 새로 스케줄된 DAG 실행이 파이프라인에 진입하며 슬롯 충돌이 발생했다. 이때부터 모든 DAG 실행이 서로의 자원을 기다리며 데드락(Deadlock) 상태에 빠졌고, 결국 데이터 파이프라인 전체가 멈추고 말았다. 가장 무서운 점은 하나의 DAG이 꼬인 것이 아니라, 의존성 그래프를 통해 여섯 개의 관련 DAG이 연쇄적으로 실패했다는 것이다.

2. 실전 복구 사례: 한밤중의 5단계 응급 처치

장애가 확인된 새벽 2시 17분, 우리 팀은 즉시 다음과 같은 순서로 복구 작업을 진행했다. 가장 중요한 것은 패닉에 빠지지 않고 논리적으로 원인을 좁혀 나가는 것이다. 순서를 어기면 오히려 문제만 키울 수 있다. 아래는 필자가 실제로 수행한 5단계 진단 및 복구 프로세스다.

Step 1: DAG 실행 상태의 시각적 하위 트리 확인

Airflow UI의 Graph View에서 실패한 태스크의 업스트림/다운스트림을 한눈에 파악했다. 실패가 특정 태스크에서 방사형으로 퍼지고 있는지, 특정 분기(DAG Branch)에서만 발생하는지 확인하여 문제의 범위를 국한시켰다.

Step 2: Task Instance의 로그 분석과 에러 코드 추적

실패한 태스크의 로그를 열어 Task timed out, ConnectionRefusedError, Resource temporarily unavailable 등 실제 에러 메시지를 확인했다. 로그만으로 원인을 특정하기 어렵다면 airflow tasks test 명령어로 해당 태스크를 단독 실행해 재현해 보는 것도 효과적이다. 이 단계에서 외부 API 지연이 아닌, 내부 데이터베이스 Connection Pool 고갈이 근본 원인임을 발견했다.

Step 3: 실행 중인 DAG 일시 중지 및 슬롯 확보

이미 실행 중이지만 꼬인 다른 DAG을 모두 Pause 처리하고, Airflow Scheduler를 재시작하여 Worker 프로세스의 점유된 슬롯을 강제로 해제했다. 만약 대기 중인 태스크가 여전히 슬롯을 점유하고 있다면, airflow tasks clear로 해당 태스크 상태를 초기화해 슬롯을 회수할 수도 있다. 이때 소스 코드 배포는 절대 하지 않았다. 코드 변경은 장애 상황에서 새로운 변수를 만들기 때문이다.

Step 4: 선행 조건 완료 후 수동 백필(Backfill) 실행

누락된 데이터 기간을 특정하여 airflow dags backfill 명령어로 수동 재실행을 진행했다. 이때 백필의 실행 순서는 반드시 데이터 의존성이 깊은 태스크부터 순차적으로 진행되어야 한다. 한꺼번에 Parallel로 실행하면 다시 리소스 경합이 발생한다.

Step 5: 복구 완료 후 데이터 무결성 검증

복구된 DAG이 성공 상태로 마킹되더라도, 데이터 자체가 올바른지 SQL 쿼리로 검증했다. DAG이 성공했더라도 데이터 품질이 보장되지 않는 경우가 많으며, 특히 백필 데이터는 중복 기록 위험이 있다. 이를 위해 Primary Key 중복 검사를 즉시 수행했다.

3. DAG 실행의 안정성: 리소스 관리와 스케줄링 설계 비교 분석

이번 장애를 교훈 삼아 우리 팀은 운영 환경의 파이프라인 아키텍처를 재점검했다. 특히, DAG이 꼬이지 않도록 예방하기 위한 스케줄링 설정 전략을 표로 정리했다. 아래 비교표를 통해 어떤 조건에서 어떤 설정이 더 안정적인지 파악할 수 있다.

비교 항목 권장 안정 설정 장애 유발 설정
실행 타임아웃 execution_timeout 30분 + retries 3회 Timeout 미설정 또는 retries 1회
백필 전략 비즈니스 시간대 기준 파티션 단위 백필 누락 기간 전체를 한 번에 백필
동시성 제어 max_active_runs=1, pool 분리 사용 max_active_runs 제한 없음
의존성 옵션 데이터 존재 여부를 센서로 확인 후 실행 depends_on_past=True 무분별 적용
리소스 추적 실행 전 남은 슬롯을 확인하는 트리거 규칙 무조건 즉시 실행 트리거

위 표에서 알 수 있듯이, 단순히 코드가 잘 작동하는 것만으로는 파이프라인이 안정적으로 운영되지 않는다. 특히 max_active_runspool 설정은 DAG이 꼬이는 상황을 방지하는 가장 효과적인 방어막이다. 필자는 이번 장애 이후 모든 주요 DAG에 별도의 Pool을 할당하여 리소스 경합을 원천적으로 차단했다. Pool을 나눌 때는 우선순위가 높은 DAG에 더 많은 슬롯을 배분하고, 그 외 DAG는 여유 슬롯을 적게 잡아 등급별로 운영하는 방식을 적용했다.

4. 한밤중의 악몽을 없애는 예방 전략: 모니터링과 알림

기술적으로 복구는 끝났지만, 같은 사건이 재발하지 않도록 하는 시스템 마련이 진정한 해결책이다. 이번 경험에서 가장 크게 느낀 것은 ‘장애 감지 속도’가 복구 시간을 결정한다는 사실이다. 단순히 이메일 알림만 켜둔 상태는 실시간 대응에 한계가 있다. 지금은 다음과 같은 모니터링 전략을 모든 데이터 파이프라인에 적용하고 있다.

4-1. 태스크 수준 알림이 아닌 파이프라인 흐름 알림

개별 태스크 실패 이메일은 수신함을 마비시킬 뿐이다. 핵심은 DAG 실행의 ‘완료율’과 ‘예상 완료 시간’을 함께 트래킹하는 것이다. 현재는 Airflow의 on_failure_callback을 커스터마이즈하여 슬랙 채널에 실패 태스크 이름뿐만 아니라, 해당 실패가 전체 파이프라인에 미칠 영향 범위(다운스트림 수)를 함께 전송한다. 이때 실패한 태스크의 로그 URL도 메시지에 포함하면, 알림을 받는 사람이 즉시 로그를 확인하고 빠르게 대응할 수 있다.

이를 위해 운영 중인 파이프라인을 세 가지 등급으로 나누어 등급별로 알림 채널과 대응 수준을 분리했다. 최우선 등급은 즉시 전화 알림이 가고, 일반 등급은 슬랙 알림으로 넘겨 휴일에는 방치할 수 있도록 했다. 이 전략을 통해 한밤중에 발생하는 사소한 태스크 실패로 인한 불필요한 호출을 줄이고, 진짜 위험 상황에만 집중할 수 있게 되었다.

5. DAG이 이미 꼬였다면? 빠른 의사결정을 위한 체크리스트

만약 지금 당신이 Airflow DAG이 꼬인 상황에서 이 글을 보고 있다면, 아래 체크리스트를 그대로 따라 하라. 문제를 해결하는 것보다 더 중요한 것은 병목 지점을 빠르게 찾는 것이다.

  • 실패 태스크의 로그 중 ‘시작 시간’을 확인해라. 만약 시작 시간이 지연됐다면 스케줄러 문제가 아니라 리소스 문제다.
  • 데이터 소스의 적재 적시성을 확인하라. 데이터 원천 자체가 늦어졌는지, 우리 파이프라인에서 처리량이 부족한지 구분하라.
  • Airflow Scheduler 로그에서 Launched 카운트가 비정상적으로 증가했는지 보라. DAG이 꼬일 때는 태스크가 무한 재실행되며 로그가 비정상적으로 찍힌다.
  • DB 커넥션 풀과 메모리 사용률을 즉시 모니터링하라. DAG 장애는 DB 연결 소진이나 메모리 누수 같은 공통 원인에서 자주 비롯된다.
  • 일단 DAG을 비활성화하라. 정확한 원인을 모르겠다면, 코드를 고치려고 하지 말고 먼저 Schedule을 꺼라. 추가 피해를 막는 것이 우선이다.

6. 결론: DAG이 꼬이는 것은 예고된 재앙이다

Airflow DAG이 꼬이는 상황은 단순한 우연이 아니다. 운영 환경의 복잡도가 증가하고 데이터 의존성이 강해질수록, 파이프라인은 흔히 한 번쯤 대규모 실패를 겪게 된다. 이번 한밤중의 복구 경험은 우리 시스템의 구조적 약점을 정확히 짚어준 값진 기회였다. 귀찮다고 리소스 풀 설정을 미루거나, 모니터링 임계값을 대충 잡아두는 순간, 더 큰 사고가 발생한다.

데이터 파이프라인 운영에서 중요한 것은 ‘실패하지 않는 파이프라인’이 아니라, ‘실패해도 빠르게 복구할 수 있는 능력’이다. 지금 당신의 Airflow에 execution_timeout이 제대로 걸려 있는지, depends_on_past가 남용되고 있지 않은지 점검하라. 그리고 이 글이 Airflow DAG 복구 실패를 경험한 모든 분께 실질적인 참고가 되기를 바란다. 내일 아침 대시보드가 깨끗하게 채워져 있을 때의 안도감은, 밤샘 복구의 고통을 충분히 상쇄할 것이다.

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다