데이터 파이프라인용 Airflow를 관리형 서비스(MWAA/Composer)에서 직접 운영하는 Kubernetes로 이관하며 겪은 운영 이슈(스케줄러 CPU 폭증, 워커 OOM, 사라진 실행 로그)와 해결을 다룬 경험기다.
핵심 포인트- GitSync로 DAG를 추가하자 스케줄러 CPU가 급등(개발용은 675%)했는데, min_file_process_interval을 올려 DAG 파싱 파일 I/O 빈도를 낮춰 해결했다.
- 워커 OOM은 K8S의 오버커밋과 메모리(압축 불가 자원) 경합에서 비롯됐고, Container/Kubelet/OS 3계층 OOM 구조를 이해한 뒤 resource request/limit과 Taint/Tolerations로 노드를 분리해 안정화했다.
- 실행 로그가 404로 사라진 이슈는 BigQuery 지연으로 태스크가 scheduled 상태에 오래 머물다 CeleryExecutor에 못 가고 1차 실행이 타임아웃돼 로그가 안 남은 것이 원인이었다.
- 워커를 1대→2대로 스케일 아웃해 동시 처리량을 늘려 지연·로그 누락을 해소했고, 관리형 대비 고정 비용이 약 50% 감소했다.
상세 정리- 전환 배경: 빠른 구축을 위해 CSP SaaS로 시작한 Airflow를 기술 성숙도를 높여 직접 관리하는 K8S로 이관해 성능·비용 최적화를 노렸다.
- 구성: Helm으로 배포하고 스케줄러·트리거가 작업을 Redis 큐에 발행하며, Pod/Celery 워커가 실행하고 Metadata DB로 상태를 동기화한다.
- CPU 이슈: DAG 추가 후 스케줄러 CPU가 0.25→0.45 core(80%), 개발용은 0.4→2.7 core(675%)로 급등했다. 스케줄러가 DAG 파일을 지속 파싱하는 게 원인이었다.
- CPU 해결: 변경 반영이 조금 늦어도 되는 상황이라 min_file_process_interval을 상향해 파일 I/O를 줄여 CPU를 낮췄다. Airflow 2.0부터 GitSync DAG를 직렬화해 Metadata DB에 저장하는 점도 영향을 줬다.
- OOM 구조: 메모리는 압축 불가 자원이라 경합 시 장애로 이어지고, Container(limit 초과) → Kubelet(Hard Eviction, 파드 축출) → OS(OOM score 높은 프로세스 종료) 3계층에서 OOM이 발생한다.
- OOM 해결: 메모리에 request/limit을 설정해 Container 단계에서 조기 대응하게 하고, 네임스페이스 LimitRange/ResourceQuota는 컴포넌트별 요구가 달라 부적합하다고 판단해 대신 Taint/Tolerations로 애플리케이션별 노드를 격리했다.
- 로그 실종: DAG가 제때 실행 못하고 늦게 몰려 실행되며 1차 실행 로그가 404였다. worker 로그에 정상은 [queued], 문제는 [scheduled] 상태로 남아 있었다.
- 로그 원인: 공식 문서상 태스크는 스케줄러에서 scheduled → Executor에서 queued가 되는데, BigQuery 지연으로 작업이 밀려 scheduled에서 CeleryExecutor로 못 가고 1차 실행이 타임아웃돼 로그가 없었다.
- 로그 해결: 워커 동시 처리량 상한이 원인이라, 스케일 업(worker_concurrency) 대신 K8S에 맞게 워커를 2대로 스케일 아웃해 지연을 확연히 줄였다.
- 효과: 기술 역량 향상과 함께 관리형 고정 비용이 노드 사용 비용으로 바뀌며 약 50% 감소했고, 로그 수집·모니터링 고도화 필요성을 체감했다.
왜 읽나Airflow를 관리형에서 K8S로 옮겨 직접 운영하려는 데이터 엔지니어·SRE에게 스케줄러 CPU·워커 OOM·로그 실종의 원인 진단과 K8S 자원 정책 대응을 알려주는 경험기.