ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Hands-On] 도커 환경의 Airflow(에어플로우) 스케쥴러 고가용성(High Availability) 알아보기
    Data 2020. 12. 3. 23:44
    반응형

    | 이 글은 제가 작성한 원문(영어)을 번역한 글입니다.

     

    오늘은 데이터 엔지니어링에서 Workflow Management 툴로 사용되는 에어플로우(Apache Airflow)에 대해 전달 드리려고 합니다. 

     

    에어플로우는 Apache Nifi, Streamsets 등과 비슷한 기능을 가지며,  데이터 엔지니어링에서 의존성을 가지는 다양한 작업의 실행을 DAG이라는 개념으로 묶어 관리의 관리를 도와주는 기능합니다. Streamsets은 프로덕션 레벨을 위해서는 비용을 지불하여야하고, Apache Nifi는 GUI로 관리가 가능한 반면 코드 제어하는 부분에 어려움이 있기 때문에, Python 기반의 Airflow는 데이터 파이프라인에서 작업 관리를 위해 많이 사용되곤 합니다. 작업을 실행하는 워커(Worker)나 웹서버(Web Server)는 쉽게 이중화하여 고가용성을 확보할 수 있지만 스케쥴러는 SPOF(Single point of Failure)로 여겨졌습니다. 여기서는 Clairvoyant에서 제공한 Airflow Failover Controller를 통해 도커환경에서 고가용성을 테스트하여 에어플로우 스케쥴러 고가용성을 고민하시는 분들이 손쉽게 POC를 진행하실 수 있도록 기술하겠습니다.

     

    관련한 코드는 제 깃헙에 공유되어 있습니다.

    컨테이너 구조

    git clone https://github.com/kadensungbincho/airflow-ha-docker
    cd airflow-ha-docker
    docker-compose up -d

    위 코드를 실행하시면 아래  이미지와 같은 컨테이너들이 실행됩니다:

    docker-compose up 시 컨테이너 구조

    • Master: 웹서버, 스케쥴러(master 또는 worker 중 하나에 생성), 스케쥴러 페일오버 컨트롤러 및 워커가 실행되는 컨테이너
    • Worker: 워커, 스케쥴러 페일오버 컨트롤러, 스케쥴러(master 또는 worker 중 하나에 생성)
    • DB: Mysql이 실행되는 컨테이너
    • RabbitMQ: 스케쥴러가 실행하는 작업들이 큐에 저장되어 워커가 가져가 작업을 실행함

     

    기존의 에어플로우 클러스터와 다른 점은 고가용성을 위해 스케쥴러가 실행될 수 있는 대상이 되는 노드들에 스케쥴러 페일오버 컨트롤러 프로세스가 생성된 부분입니다. 이 스케쥴러 페일오버 컨트롤러는 주기적으로 현재 스케쥴러가 위치한 노드에 ssh로 접근하여 프로세스를 확인하고, 만약에 프로세스가 존재하지 않는다면 내부적 알고리즘을 통해 스케쥴러 프로세스를 다시 생성해줍니다. 스케쥴러 프로세스의 재생성과 관련해서는 아래에서 좀 더 자세하게 확인해보도록 하겠습니다.

     

    728x90

    도커 컴포즈 파일 구조

    아래는 위의 컨테이너를 생성할 때 사용된 도커 컴포즈 파일입니다. db, rabbitmq, master, worker 4개의 컨테이너가 존재하며 db와  rabbitmq는 유저명, 비밀번호 및 데이터베이나(혹은 vhost) 정도 설정되었습니다.

    version: '3.7'
    
    services:
      db:
        image: mysql:8.0
        command: --default-authentication-plugin=mysql_native_password
        environment:
          - MYSQL_ROOT_PASSWORD=airflow
          - MYSQL_USER=airflow
          - MYSQL_PASSWORD=airflow
          - MYSQL_DATABASE=airflow
        ports:
          - "3306:3306"
    
      rabbitmq:
        image: rabbitmq:3.8.0-management
        environment:
          - RABBITMQ_DEFAULT_USER=airflow
          - RABBITMQ_DEFAULT_PASS=airflow
          - RABBITMQ_DEFAULT_VHOST=airflow
        ports:
          - "15672:15672"
    
      master:
        build:
          context: ./airflow
          dockerfile: Dockerfile
        image: puckel/docker-ha-airflow:1.10.9
        init: true
        hostname: master
        depends_on:
          - db
          - rabbitmq
        volumes:
          - ${PWD}/airflow/airflow.cfg:/usr/local/airflow/airflow.cfg
          - ${PWD}/airflow/sshd_config:/etc/ssh/sshd_config
          - ${PWD}/airflow/.ssh:/usr/local/airflow/.ssh
        ports:
          - "8080:8080"
          - "2020:22"
        command: master
    
      worker:
        build:
          context: ./airflow
          dockerfile: Dockerfile
        image: puckel/docker-ha-airflow:1.10.9
        init: true
        hostname: worker
        depends_on:
          - master
        ports:
          - "2021:22"
        volumes:
          - ${PWD}/airflow/airflow.cfg:/usr/local/airflow/airflow.cfg
          - ${PWD}/airflow/sshd_config:/etc/ssh/sshd_config
          - ${PWD}/airflow/.ssh:/usr/local/airflow/.ssh
        command: worker

    에어플로우 master와 worker와 관련해서는 다음의2가지 사항을 조금 더 추가하였습니다:

    • "init: true": 부재 시, sshd가 생성하는 수많은 자식프로세스를 제대로 reap하지 못해 좀비프로세스를 생성합니다. 이는 도커가 기본적으로 foreground PID 1으로 프로세스를 실행하며 보통 자식프로세스 reap을 PID 1이 부재하게 됩니다.
    • volume: 스케쥴러 페일오버 컨트롤러는 기본적으로 ssh를 통해 다른 노드에 있는 스케쥴러 프로세스의 active 여부를 확인하기에 스케쥴러 실행 대상이 되는 노드 간에 ssh가 가능해야합니다. ssh 커맨드로 바로 접근이 가능하도록 sshd_config과 key들을 하드코딩 해두었습니다(테스트용).
    PID 1 is responsible for making sure that children are reaped. In your container, by default your main process will be PID 1. Since you probably won’t be handling reaping of children from your application, you can end up with zombie processes in your container. There are a few solutions to this problem. The first is to run an init system in the container of your own choosing —one that is capable of handling PID 1 responsibilities. s6, runit, and others described in the preceding Note can be easily used inside the container.
    - Docker Up & Running, Sean P. Kane & Karl Matthias

     

    고가용성 확인

    아래에서는 실행되고 있는 스케쥴러 프로세스를 죽여서(kill), 스케쥴러 페일오버 컨트롤러가 잘 동작하는지 확인해 보겠습니다.

     

    먼저, localhost:8080에 접속하면 아래와 같이 Airflow UI를 확인할 수 있습니다.

     

    디폴트 상태에 컨테이너들의 프로세스

     

    아래 커맨드를 통해 master 컨테이너를 확인하면,

    docker exec -it master /bin/sh
    ps aux

     

    아래와 같은 프로세스들을 확인할 수 있습니다:

    • PID1: 다른 프로세스들을 spawn하는 ‘docker-init’ed entrypoint process
    • PID7: 스케쥴러 프로세스가 존재하는지 주기적으로 체크하는 스케쥴러 페일오버 컨트롤러 프로세스. 만약 스케쥴러가 죽었다면, 스케쥴러를 spawn함
    • PID15: ssh daemon
    • PID32: 웹서버
    • Others: gunicorn worker processes for webserver

    이번에는 worker 컨테이너를 확인하면,

    docker exec -it worker /bin/sh
    ps aux

     

    master와 다르게 많은 worker(Celery) 프로세스와 아래 2가지 특징적인 프로세스의 생성을 확인하실 수 있습니다:

     

    worker 컨테이너의 스케쥴러 프로세스 Kill 시

    그렇다면 worker 컨테이너 스케쥴러 프로세스를 kill하고 다시 스케쥴러 프로세스가 잘 spawn되는지 확인해보겠습니다.

     

    아래에서 예상과 같이 master 컨테이너에 스케쥴러 프로세스가 재생성되는 부분 확인하실 수 있습니다:

     

    위에서 도커환경에서 간단하게 에어플로우 스케쥴러 페일오버 컨트롤러를 통해 스케쥴러의 고가용성을 확보하는 테스트를 진행해 보았습니다. 그러한 라이브러리가 있다고 해도, 로컬에서 환경을 세팅하고 테스트하고 POC(proof of concept)을 진행하는 데에 번거롭고 시간이 많이 소요되게 되는데요. 이 공유를 통해, 그러한 시간을 아끼실 수 있으셨으면 좋겠습니다.

     
    반응형
Kaden Sungbin Cho