Airflow DAG 배포 자동화: GitSync, 패키지 배포, 버전 관리
DAG 배포가 어려운 이유
Airflow를 처음 도입하면 DAG 배포는 간단해 보인다. 파이썬 파일을 dags/ 폴더에 복사하면 끝이다. 하지만 팀이 커지고 환경이 늘어나면 다음 문제가 생긴다.
배포 창(sync window): Airflow scheduler는 dags/ 폴더를 주기적으로 스캔한다. 파일을 직접 복사하면 scheduler가 인식하기까지 시차가 생기고, 그 사이에 다른 버전의 DAG이 동시에 파싱될 수 있다.
패키지 의존성 충돌: DAG마다 다른 버전의 라이브러리를 쓰는 경우 공유 Python 환경에서 충돌이 발생한다. Airflow 자체 의존성과 custom operator 의존성이 섞이는 경우도 있다.
환경 분리: dev, staging, prod 환경의 DAG이 서로 다른 브랜치 또는 설정을 써야 하는데, 수동 복사는 실수를 유발한다.
버전 추적: 어떤 버전의 DAG이 어느 환경에 배포됐는지 기록이 없으면 사고 원인 분석이 어렵다.
이 문제를 해결하는 방법은 크게 세 가지로 나뉜다.
| 방식 | 동작 | 장점 | 단점 |
|---|---|---|---|
| Image bake | DAG 파일을 Docker 이미지에 포함 | 버전 고정, 재현성 | 배포마다 이미지 빌드 필요, 빠른 반영 어려움 |
| GitSync | git-sync sidecar가 주기적으로 git pull | 빠른 반영, 구성 단순 | DAG만 버전 관리; 패키지 의존성 별도 관리 필요 |
| PVC mount | 공유 PersistentVolume에 rsync/cp | 구성 단순 | 스케일 아웃 시 공유 PVC 관리 복잡 |
대부분의 Kubernetes 기반 Airflow 환경에서는 GitSync가 기본 선택이다. 빠른 DAG 반영이 가능하면서 패키지 의존성은 이미지에 굽는 방식으로 분리할 수 있다.
GitSync 사이드카 패턴
GitSync는 Kubernetes sidecar 컨테이너로 실행된다. Scheduler, Worker, Triggerer, DAG processor 파드마다 git-sync 컨테이너가 함께 실행되어 emptyDir 볼륨을 통해 DAG 파일을 공유한다.
KubernetesExecutor를 사용할 때는 Worker Pod가 task 실행 때마다 새로 생성된다. 이때 git-sync는 init container로 동작해 Pod 시작 시 한 번만 pull한다.
Helm 차트로 GitSync 설정하기
공식 Apache Airflow Helm 차트는 dags.gitSync로 GitSync를 활성화한다. Helm 차트 공식 문서는 dags.persistence.enabled=false와 dags.gitSync.enabled=true를 함께 설정하라고 안내한다.
# values.yaml (핵심 설정만 발췌)
dags:
persistence:
enabled: false # PVC 대신 git-sync 사용
gitSync:
enabled: true
repo: "[email protected]:myorg/airflow-dags.git"
branch: main
rev: HEAD
depth: 1 # shallow clone, 속도 절약
wait: 10 # 동기화 간격 (초)
subPath: "dags" # 저장소 내 DAG 파일 경로
containerName: git-sync
uid: 65533
# SSH 인증 (권장: deploy key 사용)
sshKeySecret: airflow-ssh-secret
images:
airflow:
repository: myregistry/airflow
tag: "2.9.2-providers-v1.5" # 패키지 포함 커스텀 이미지
pullPolicy: IfNotPresentSSH 인증 설정
공개 저장소가 아니면 SSH deploy key 또는 HTTPS token이 필요하다.
# 1. SSH 키 생성
ssh-keygen -t ed25519 -f airflow-gitsync-key -N ""
# 2. GitHub repository → Settings → Deploy keys 에 공개 키 등록
cat airflow-gitsync-key.pub
# 3. 비밀 키를 Kubernetes Secret으로 등록
kubectl create secret generic airflow-ssh-secret \
--from-file=gitSshKey=airflow-gitsync-key \
-n airflow
# 4. known_hosts 추가 (MITM 방지)
# Helm 차트 dags.gitSync.knownHosts 에 github.com 의 ssh 공개 키 지문 입력Helm 차트 공식 문서는 dags.gitSync.knownHosts를 설정하지 않으면 man-in-the-middle 공격에 취약해질 수 있다고 경고한다.
CI/CD 파이프라인: GitHub Actions 연동
GitSync는 DAG 코드를 자동 반영하지만, 배포 전에 코드가 올바른지 검증해야 한다. GitHub Actions를 사용한 전형적인 파이프라인은 다음과 같다.
# .github/workflows/dag-ci.yml
name: DAG CI
on:
push:
branches: [main, staging]
paths:
- 'dags/**'
- 'plugins/**'
- 'requirements*.txt'
pull_request:
branches: [main, staging]
paths:
- 'dags/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Python 설치
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: 의존성 설치
run: |
pip install apache-airflow==2.9.2 \
apache-airflow-providers-google==10.3.0 \
apache-airflow-providers-amazon==8.7.0
pip install -r requirements-dev.txt
- name: DAG 파싱 검증
run: |
python -c "
import os, glob, importlib.util, sys
dag_files = glob.glob('dags/**/*.py', recursive=True)
errors = []
for f in dag_files:
try:
spec = importlib.util.spec_from_file_location('dag', f)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
except Exception as e:
errors.append(f'{f}: {e}')
if errors:
print('\n'.join(errors))
sys.exit(1)
print(f'OK: {len(dag_files)} DAGs parsed successfully')
"
- name: flake8 lint
run: flake8 dags/ plugins/ --max-line-length=120
- name: DAG 단위 테스트
run: pytest tests/ -v --tb=short패키지가 변경됐을 때는 이미지 빌드도 같이 트리거한다.
build-image:
needs: validate
if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[rebuild]')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Docker 빌드 및 push
run: |
docker build -t myregistry/airflow:2.9.2-providers-${{ github.sha }} .
docker push myregistry/airflow:2.9.2-providers-${{ github.sha }}
- name: Helm 업데이트
run: |
helm upgrade airflow apache-airflow/airflow \
--set images.airflow.tag=2.9.2-providers-${{ github.sha }} \
--reuse-values \
-n airflow커밋 메시지에 [rebuild]를 붙일 때만 이미지를 다시 빌드하는 방식이다. 대부분의 DAG 코드 변경은 GitSync로만 반영되므로 이미지 빌드 없이 배포된다.
커스텀 패키지와 Provider 관리
Airflow의 패키지는 두 종류다.
- Airflow Provider:
apache-airflow-providers-*패키지. Google, AWS, Slack, dbt 등 외부 시스템 연동 operator와 hook을 제공한다. - Custom library: 팀에서 만든 공통 operator, hook, sensor.
Dockerfile: 커스텀 이미지 구성
FROM apache/airflow:2.9.2-python3.11
# 공식 provider
RUN pip install --no-cache-dir \
apache-airflow-providers-google==10.3.0 \
apache-airflow-providers-amazon==8.7.0 \
apache-airflow-providers-slack==7.3.0
# 사내 공통 라이브러리 (private PyPI 또는 wheel)
COPY dist/myorg_airflow_common-1.5.0-py3-none-any.whl /tmp/
RUN pip install --no-cache-dir /tmp/myorg_airflow_common-1.5.0-py3-none-any.whl
# 추가 의존성
COPY requirements.txt /requirements.txt
RUN pip install --no-cache-dir -r /requirements.txt이미지 태그에 provider 버전을 반영하면 어떤 패키지 버전이 배포됐는지 추적하기 쉽다.
airflow:2.9.2-providers-v1.5 # 이미지 버전
^ ^
Airflow 사내 패키지 버전requirements.txt 핀닝 전략
# requirements.txt
apache-airflow-providers-google==10.3.0 # exact pin
apache-airflow-providers-amazon==8.7.0
pandas>=2.0,<3.0 # 범위 허용버전 범위를 너무 느슨하게 두면 자동 업그레이드가 예상치 못한 동작을 유발할 수 있다. 주요 패키지는 exact pin을 권장하고, 월 단위 의존성 업데이트 PR을 별도로 만드는 편이 안전하다.
DAG 버전 관리 전략
GitSync는 항상 최신 commit을 pull한다. 이 특성은 DAG 버전 관리 방식을 결정한다.
브랜치 기반 환경 분리
각 환경(dev, staging, prod)이 다른 브랜치를 추적한다.
# dev 환경 values.yaml
dags:
gitSync:
branch: develop
# staging 환경
dags:
gitSync:
branch: staging
# prod 환경
dags:
gitSync:
branch: main장점: 브랜치 전략이 환경 분리와 직접 연결된다. 단점: 브랜치 간 병합 충돌 관리가 필요하다.
태그 기반 배포
staging 승인 후 prod에는 태그로 배포한다.
# 배포 승인 시 태그 생성
git tag -a v2026.07.08.1 -m "2026-07-08 1차 배포"
git push origin v2026.07.08.1# prod 환경: 태그 추적
dags:
gitSync:
rev: v2026.07.08.1 # HEAD 대신 특정 태그태그를 사용하면 prod가 특정 커밋에 고정되어 예측 가능성이 높아진다. 단점은 매 배포마다 Helm 값을 바꿔야 한다는 점이다. 이를 해결하기 위해 GitHub Actions에서 태그 생성과 Helm 업데이트를 자동화할 수 있다.
DAG 파일 내 버전 명시
DAG 파일에 버전 정보를 명시하면 어떤 버전의 코드로 실행됐는지 추적이 가능하다.
from airflow import DAG
DAG_VERSION = "2026-07-08-v1"
with DAG(
dag_id="my_pipeline",
tags=["version:" + DAG_VERSION, "team:data-eng"],
# ...
) as dag:
passAirflow UI의 DAG 목록에서 태그로 필터링할 수 있어 버전 확인이 편리하다.
배포 문제 진단
1. DAG이 반영되지 않는다
GitSync가 pull했는지 확인한다.
# git-sync 컨테이너 로그 확인
kubectl logs <scheduler-pod> -c git-sync -n airflow --tail=50
# emptyDir 볼륨의 실제 파일 확인
kubectl exec <scheduler-pod> -c airflow-scheduler -n airflow -- \
ls -la /opt/airflow/dags/
# DAG 파일이 심볼릭 링크로 연결됐는지 확인
kubectl exec <scheduler-pod> -c airflow-scheduler -n airflow -- \
ls -la /git/Airflow scheduler는 dag_dir_list_interval(기본 300초)마다 dags/ 디렉터리를 스캔한다. GitSync 간격과 scheduler 스캔 간격이 맞지 않으면 반영 시간이 길어진다.
# airflow.cfg
[scheduler]
dag_dir_list_interval = 60 # 기본 300초, 빠른 반영 원하면 줄인다2. DAG 파싱 에러
scheduler 로그나 Airflow UI의 "DAG Import Errors" 탭에서 확인한다.
# scheduler에서 직접 파싱 테스트
kubectl exec <scheduler-pod> -c airflow-scheduler -n airflow -- \
python -c "
from airflow.models import DagBag
bag = DagBag('/opt/airflow/dags', include_examples=False)
print('import errors:', bag.import_errors)
print('dags found:', list(bag.dags.keys()))
"3. 패키지 없음 에러
커스텀 이미지에 패키지가 없으면 DAG 파싱 자체가 실패한다.
# 이미지에서 패키지 확인
kubectl exec <scheduler-pod> -c airflow-scheduler -n airflow -- \
pip show apache-airflow-providers-google패키지를 추가해야 한다면 Dockerfile을 수정하고 이미지를 다시 빌드해야 한다. GitSync로는 해결할 수 없다.
배포 요약: DAG vs 패키지 변경 구분
DAG 파일 변경과 패키지 변경은 배포 경로가 다르다. 이를 구분하지 않으면 불필요한 이미지 빌드가 늘거나, 반대로 패키지 변경이 배포되지 않는 상황이 생긴다.
| 변경 유형 | 배포 방법 | 소요 시간 | 재시작 필요 |
|---|---|---|---|
| DAG 파이썬 파일 수정 | GitSync 자동 반영 | 5~60초 | 불필요 |
| 새 DAG 추가 | GitSync 자동 반영 | 5~60초 | 불필요 |
| provider 패키지 추가/변경 | 이미지 빌드 + Helm upgrade | 5~20분 | Pod 재시작 |
| Airflow 버전 업그레이드 | 이미지 빌드 + Helm upgrade + DB migrate | 30분+ | DB 마이그레이션 포함 |
| Airflow 설정(airflow.cfg) 변경 | Helm values 변경 + Pod 재시작 | 5~10분 | Pod 재시작 |
GitSync는 DAG 코드 변경만 빠르게 반영하는 경로다. 패키지나 환경 자체가 바뀌면 이미지 빌드 경로를 밟아야 한다. 이 두 경로를 CI/CD 파이프라인에서 명확히 분리해 두면 실수로 잘못된 경로를 밟는 사고를 줄일 수 있다.
References
- Apache Airflow Docs, "Manage DAG Files (Helm Chart)": https://airflow.apache.org/docs/helm-chart/stable/manage-dag-files.html
- Apache Airflow Helm Chart, "Parameters Reference": https://airflow.apache.org/docs/helm-chart/stable/parameters-ref.html
- Medium (Ankercloud Engineering), "Airflow on Argo CD — a Step-by-Step GitOps Guide with git-sync for DAGs": https://medium.com/ankercloud-engineering/airflow-on-argo-cd-a-step-by-step-gitops-guide-with-git-sync-for-dags-464bd0d7e2f5
- Medium (Apache Airflow), "Streamlining Airflow Deployment: Automating CI/CD with GitHub Actions": https://medium.com/apache-airflow/streamlining-airflow-deployment-automating-ci-cd-with-github-actions-5602f1062783
- Google Cloud, "Test, synchronize, and deploy your DAGs from GitHub (Cloud Composer)": https://docs.cloud.google.com/composer/docs/composer-2/dag-cicd-github
- GitHub Discussions apache/airflow, "Efficient CI/CD strategies for Airflow management in production": https://github.com/apache/airflow/discussions/30272
- Spark Code Hub, "Airflow DAG Versioning Strategies": https://www.sparkcodehub.com/airflow/advanced/dag-versioning