Skip to content
cover-dataengineering

Airflow on Kubernetes (1)

  • DataEngineering

📅 June 05, 2020

⏱️5 min read

최근 Airflow에는 Kubernetes 지원을 위해 다양한 컴포넌트들이 추가되고 있습니다. 이러한 변화의 흐름에 따라 Airflow를 Kubernetes 위에 배포하고 운영하는 방법에 대해 글을 작성해보고자 합니다. 이 글은 시리즈로 연재됩니다.


Airflow on Kubernetes

Airflow를 Kubernetes 인프라 위에서 운영하는 방법은 크게 두 가지로 나눌 수 있습니다. 이 글에서 소개할 방법은 CeleryExecutor의 각 모듈을 Kubernetes 위에 올리는 방식입니다. 기존에 운영하던 형태와 유사하기 때문에 쉽게 적용할 수 있으나 Celery에 대한 의존성이 강하다보니 완전히 Cloud Native한 형태는 아닙니다. 아키텍쳐는 가장 많이 사용하는 stable/airflow Helm Chart를 참고하였습니다. 이제 몇 가지 컴포넌트 설정과 함께 자세히 알아보겠습니다.


Config

Airflow는 airflow.cfg 파일 또는 AIRFLOW__[SECTOR]__[VARIABLES] 환경 변수를 통해 각 컴포넌트의 설정을 관리할 수 있었습니다. Helm Chart에서는 values.yaml의 config 필드를 통해 설정을 관리할 수 있습니다.

config:
  # CORE
  AIRFLOW__CORE__DEFAULT_TIMEZONE: "Asia/Seoul"
  AIRFLOW__CORE__PARALLELISM: "32"
  AIRFLOW__CORE__DAG_CONCURRENCY: "16"
  AIRFLOW__CORE__MAX_ACTIVE_RUNS_PER_DAG: "16"

  # WEBSERVER
  AIRFLOW__WEBSERVER__DEFAULT_UI_TIMEZONE: "Asia/Seoul"
  AIRFLOW__WEBSERVER__WORKER_REFRESH_INTERVAL: "60"

  # CELERY
  AIRFLOW__CELERY__WORKER_CONCURRENCY: "16"

  # SCHEDULER
  AIRFLOW__SCHEDULER__SCHEDULER_HEARTBEAT_SEC: "30"
  AIRFLOW__SCHEDULER__SCHEDULER_HEALTH_CHECK_THRESHOLD: "120"
  AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL: "30"
  AIRFLOW__SCHEDULER__RUN_DURATION: "10800"
  AIRFLOW__SCHEDULER__MAX_THREADS: "2"

위에 정의한 설정 변수들은 Airflow의 성능과 관련되어 있기 때문에 각자 할당된 리소스에 맞게 설정해주셔야 합니다. 자세한 내용은 공식문서 링크를 참고하시기 바랍니다. 위와 같은 방식으로 DAG에서 활용하는 connection, variables도 정의할 수 있습니다.


# config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: airflow-webserver-config
  namespace: airflow
data:
  webserver_config.py: |
    APP_THEME = "flatly.css"

---
# values.yaml
extraConfigmapMounts:
  - name: airflow-webserver-config
    mountPath: /opt/airflow/webserver_config.py
    configMap: airflow-webserver-config
    readOnly: true
    subPath: webserver_config.py

위와 같이 ConfigMap이나 Secret을 따로 만들고 참조하도록 연결하는 방식도 가능합니다. 특히 Airflow 1.10의 RBAC을 사용한다면 webserver_config.py를 통해 APP_THEME를 변경해줄 수 있는데 이런 경우에 extraConfigmap을 통해 적용할 수 있습니다.


airflow


제가 주로 사용하는 테마는 flatly.cssNAVBAR #18bc9c 컬러 조합입니다. 적용된 화면은 위와 같습니다. (+ 태그 기능도 1.10.10 버전에 추가되었습니다)


Celery Worker

celery


CeleryExecutor에서 worker는 실제 task를 수행을 담당하는 컴포넌트입니다. K8S에서는 celery worker가 StatefulSet으로 배포됩니다. 기존에는 worker가 AutoScalingGroup 등을 통해 인스턴스가 자동 확장되도록 구성했다면, K8S에서는 HorizontalPodAutoscaler를 통해 Pod 단위로 확장 가능하도록 구성할 수 있습니다.


workers:
  replicas: 1

  resources:
    requests:
      memory: "2Gi"

  autoscaling:
    enabled: true
    maxReplicas: 16
    metrics:
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

Airflow Ingress

보통 K8S 클러스터에 Ingress Controller를 설정하고 path를 통해 여러 서비스에 접속하는 경우가 많습니다. Airflow Chart 역시 Webserver와 Flower UI에 대한 ingress를 지원합니다. 저는 nginx-ingress controller를 사용해서 진행해보겠습니다. 아래 예시는 각자의 ingress-controller 설정에 맞게 바꾸시면 됩니다.


web:
  service:
    annotations: {}
    type: ClusterIP
    externalPort: 8080
    loadBalancerIP: ""
    loadBalancerSourceRanges: []

...

ingress:
  enabled: true
  web:
    annotations:
      kubernetes.io/ingress.class: nginx
      ingress.kubernetes.io/rewrite-target: /
      nginx.ingress.kubernetes.io/ssl-redirect: "false"

    path: "/airflow"
    host: "myloadbalancer-domain.com"

예를 들어 web path에 /airflow 라고 설정하셨다면, UI 접속 주소는 myloadbalancer-domain.com/airflow가 됩니다. flower도 위와 동일한 방식으로 설정하시면 됩니다.


Airflow Auth

Airflow 에서는 다양한 인증 방식을 지원하지만 여기에서는 가장 기본이 되는 Password Auth 방식으로 배포하겠습니다. 새로 추가된 RBAC 설정도 함께 추가해보겠습니다. 먼저 extraPipPackages 설정을 통해 의존성 패키지를 설치해주고 상단에 환경 변수도 추가해줍니다.


config:
  AIRFLOW__WEBSERVER__RBAC: "True"
  AIRFLOW__WEBSERVER__AUTHENTICATE: "True"
  AIRFLOW__WEBSERVER__AUTH_BACKEND: "airflow.contrib.auth.backends.password_auth"

...

web:
  extraPipPackages:
    - "flask-bcrypt"
    - "flask-oauthlib>=0.9"

이제 로그인할 사용자를 추가해주어야 합니다. Scheduler Pod의 Bash에서 create_user 명령어를 통해 생성해주시면 됩니다.


$ kubectl exec \
  -it \
  --namespace airflow \
  --container airflow-scheduler \
  Deployment/airflow-scheduler \
  /bin/bash

$ airflow create_user \
--username=admin \
--email=test@example.com \
--password=mypassword \
--role=Admin \
--firstname=test \
--lastname=park

Airflow IAM Role

AWS EKS와 같은 클라우드 서비스 위에 배포한다면 각 컴포넌트의 세부 권한을 지정해주어야 합니다. 만일 Pod에 IAM Role을 할당하지 않는다면 Airflow는 클러스터의 기본 IAM Role인 EKS worker 설정을 따르게 됩니다. 따라서 보안을 신경쓰셔야 한다면 설정하는 것이 바람직합니다. 특히 Airflow에서 다른 AWS Managed Service(EMR, Athena, Lambda)와 연계하는 DAG이 존재하신다면 필수적입니다.


serviceAccount:
  create: true
  name: "airflow"
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789999:role/airflow

...

securityContext:
  fsGroup: 1000

values.yaml에는 포함되어 있지 않지만 각 컴포넌트마다 securityContext를 지정해주셔야 IAM Role을 매핑할 수 있습니다. IAM Role for Service Account가 내부적으로 K8S TokenProjection을 사용하기 때문에 설정을 안하면 토큰을 읽을 수 없다는 오류가 발생합니다. IAM Role 설정에 대한 자세한 내용은 EKS 공식 문서를 참고하시기 바랍니다.


DAGs

Airflow는 Scheduler가 DAG 파일을 주기적으로 동기화하며 문법적 오류가 없는지 체크하는 역할을 수행합니다. 단일 노드에서는 로컬에 있는 DAG 파일을 읽으면 되지만 K8S에서는 worker pod가 여러 노드에 걸쳐있기 때문에 모두 같은 DAG 파일을 바라보도록 하는 동기화 설정이 필요합니다. Helm Chart에서는 이를 지원하기 위해 두 가지 옵션을 제공합니다.


1. Git-Sync Sidecar

# git-sync sidecar
dags:
  git:
    url: ssh://git@repo.example.com/example.git
    repoHost: repo.example.com
    secret: airflow-git-keys
    privateKeyName: id_rsa

    gitSync:
      enabled: true
      refreshTime: 60

첫 번째 방식은 git-sync 사이드카 컨테이너를 활용하는 방법입니다. 간단히 말하자면 주기적으로 외부 저장소를 당겨오는 방식으로 git 인증이 필요합니다. 사이드카 패턴이 생소하시다면 이전에 작성한 분산 컨테이너에서의 디자인 패턴 글을 참고하시기 바랍니다.


2. Shared Persistent Volume

# EFS PV, PVC
apiVersion: v1
kind: PersistentVolume
metadata:
  name: airflow-dags
  namespace: airflow
  labels:
    name: airflow-dags
    storage: airflow
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 0.0.0.0 <- EFS endpoint
    path: "/airflow"

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: airflow-dags
  namespace: airflow
  labels:
    storage: airflow
spec:
  storageClassName: ""
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  selector:
    matchLabels:
      name: airflow-dags

---
# shared persistent volume
dags:
  persistence:
    enabled: true
    existingClaim: "airflow-dags"
    accessMode: ReadWriteMany
    size: 1Gi

두 번째 방식은 EFS와 같은 공유 파일시스템을 활용한 방법입니다. EFS의 특정 경로에 DAG 파일을 저장하고 마운트를 통해 모든 Pod이 같은 경로를 바라보도록 설정하는 방식입니다. 저는 EFS PV와 PVC를 먼저 추가한다음 existingClaim을 통해 참조하도록 설정해주었습니다.


Deploy

필요한 설정을 완료했다면 배포는 아래 Helm 명령어를 통해 할 수 있습니다. 가능하다면 데이터베이스는 external로 사용하는 방법을 추천드립니다. DB 암호는 secret을 통해 생성하고 참조하도록 설정해주시면 됩니다.


helm install stable/airflow \
--version 7.1.1 \
--namespace airflow \
--name airflow \
-f ./values.yaml

배포 이후에 namespace를 보면 아래와 같은 Pod이 존재하는걸 확인할 수 있습니다.


airflow-po


이 글에서 언급한 설정은 FIXME 주석을 해두었으니 궁금하신분들은 https://github.com/Swalloow/airflow-helm 저장소를 확인하시기 바랍니다.

← PrevNext →
  • Powered by Contentful
  • COPYRIGHT © 2020 by @swalloow