[k8s] Airflow on EKS — Helm, Git Sync, Spark 연동

Woong·2026년 4월 14일

Docker, k8s

목록 보기
36/38

개요

  • EKS 에 Airflow 를 Helm 으로 설치하고, Git Sync 로 DAG 를 배포하는 구조를 정리
    • KubernetesExecutor 사용 — 고정 컴포넌트(웹/스케줄러/DB) 는 온디맨드 노드, 태스크 파드는 Karpenter 가 자동 증설
    • Git Sync 로 DAG 레포지토리를 주기적으로 동기화
    • Spark operator 연동 — Airflow worker 에서 SparkApplication CRD 생성 가능

아키텍처

  • KubernetesExecutor 를 사용하는 이유
    • 태스크마다 독립된 Pod 으로 실행 → 리소스 격리
    • Karpenter 가 태스크 Pod 요구사항에 맞는 노드를 자동 프로비저닝
    • 태스크가 없으면 노드도 자동 종료 → 비용 절감

커스텀 Airflow 이미지 빌드

  • 기본 이미지에 필요한 의존성을 추가한 커스텀 이미지를 빌드
Dockerfile
FROM apache/airflow:2.11.0

RUN pip install --no-cache-dir \
    "apache-airflow==2.11.0" \
    "apache-airflow-providers-cncf-kubernetes==10.5.0" \
    "apache-airflow-providers-standard==1.10.3" \
    "apache-airflow-providers-common-compat>=1.7.2" \
    "beautifulsoup4>=4.12.0" \
    "lxml>=5.0.0" \
    "slack-sdk>=3.27.0"
  • apache-airflow-providers-cncf-kubernetes : KubernetesPodOperator, SparkKubernetesOperator 사용에 필요
ECR 업로드
AWS_PROFILE="<profile>"
AWS_REGION="ap-northeast-1"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text --profile "$AWS_PROFILE")

# ECR 로그인
aws ecr get-login-password \
  --region "$AWS_REGION" \
  --profile "$AWS_PROFILE" \
| docker login --username AWS --password-stdin \
  "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"

# 빌드 & 푸시
REPO_NAME="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/apache/airflow"
IMAGE_TAG=2.11.0-custom

docker build --platform linux/amd64 -t "${REPO_NAME}:${IMAGE_TAG}" .
docker push "${REPO_NAME}:${IMAGE_TAG}"
  • sha256 digest 확인 (values.yaml 에 digest 를 지정하는 경우)
aws ecr describe-images \
  --region "${AWS_REGION}" \
  --repository-name "apache/airflow" \
  --image-ids imageTag="${IMAGE_TAG}" \
  --query 'imageDetails[0].imageDigest' \
  --output text

Helm 설치

Secret 생성
  • Git Sync 인증 정보와 AWS 자격증명을 Secret 으로 관리
apiVersion: v1
kind: Secret
metadata:
  name: airflow-dag-repo-secret
  namespace: airflow
type: Opaque
stringData:
  GITSYNC_USERNAME: "<git_username>"
  GITSYNC_PASSWORD: "<git_access_token>"
  AWS_ACCESS_KEY_ID: "<aws_access_key>"
  AWS_SECRET_ACCESS_KEY: "<aws_secret_key>"
  AWS_DEFAULT_REGION: "ap-northeast-1"
kubectl apply -f secret.yaml
Helm install
helm repo add apache-airflow https://airflow.apache.org
helm upgrade --install airflow apache-airflow/airflow \
  --namespace airflow \
  --create-namespace \
  -f values.yaml

values.yaml 주요 설정

Executor
executor: "KubernetesExecutor"
커스텀 이미지
images:
  airflow:
    repository: <account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/apache/airflow
    tag: 2.11.0-custom
    digest: sha256:<digest>   # 버전 업데이트시 갱신
    pullPolicy: IfNotPresent
고정 컴포넌트 노드 스케줄링
  • 웹서버, 스케줄러 등 고정 컴포넌트는 On-Demand 노드에 배치
nodeSelector:
  nodegroup: "<on_demand_node_pool>"
tolerations:
  - key: "nodegroup"
    operator: "Equal"
    value: "<on_demand_node_pool>"
    effect: "NoSchedule"
Worker (태스크 Pod)
  • KubernetesExecutor 이므로 별도 worker 는 사용하지 않음
    • 태스크마다 Pod 이 생성되고, Karpenter 가 노드를 자동 프로비저닝
workers:
  nodeSelector: {}    # 비워두면 Karpenter 가 자동으로 적절한 노드에 배치
  tolerations: []
Git Sync (DAG 배포)
  • DAG 레포지토리를 sidecar 로 주기적으로 동기화
dags:
  persistence:
    enabled: false    # Git Sync 사용 시 PVC 불필요
  gitSync:
    enabled: true
    repo: "https://git.example.com/team/airflow-dag-repo.git"
    branch: next
    ref: next
    depth: 1
    period: 5s
    credentialsSecret: "airflow-dag-repo-secret"
    containerName: git-sync
    uid: 65533
  • period: 5s : 5초마다 Git 변경사항 확인

  • credentialsSecret : 앞에서 생성한 Secret 참조

  • depth: 1 : shallow clone 으로 빠르게 동기화

  • DAG 레포지토리 구조 예시

airflow-dag-repo/
├── human_resource/
│   └── dag.py
├── game_doc_insight/
│   └── dag.py
└── ...

Spark 연동

  • Airflow worker (태스크 Pod) 에서 SparkApplication CRD 를 생성할 수 있도록 RBAC 설정
Role & RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: airflow-worker-spark-access
  namespace: spark
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch", "patch"]
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get"]
  - apiGroups: ["sparkoperator.k8s.io"]
    resources: ["sparkapplications"]
    verbs: ["create", "get", "list", "watch", "delete"]
  - apiGroups: ["sparkoperator.k8s.io"]
    resources: ["sparkapplications/status"]
    verbs: ["get"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: airflow-worker-spark-access
  namespace: spark
subjects:
  - kind: ServiceAccount
    name: airflow-worker
    namespace: airflow
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: airflow-worker-spark-access
kubectl apply -f airflow-worker-spark-access.yaml
권한 확인
# SparkApplication 생성 가능 여부
kubectl auth can-i create sparkapplications.sparkoperator.k8s.io \
  -n spark --as system:serviceaccount:airflow:airflow-worker

# Pod 로그 조회 가능 여부
kubectl auth can-i get pods/log \
  -n spark --as system:serviceaccount:airflow:airflow-worker

삭제

helm delete airflow --namespace airflow

reference

0개의 댓글