개요
현재 회사에서 Workflow Management 툴로 Airflow를 도입하였고 DAG를 통해 ETL과 같은 Job을 관리하고 있다. 데이터 플랫폼 구축의 초기 단계이기 때문에 하나의 Repository에서 모든 DAG를 관리하였고 배포도 직접 서버에 접속해서 수동으로 배포하고 있었고 이번에 DW로 BigQuery를 선정하게 되면서 원본 데이터들을 ETL을 통해 DW로 적재하거나 DM을 만드는 DAG가 점점 늘어날 것이고 DAG를 개발하는 인원도 점차 늘어날 것이라고 판단해서 이번에 Airflow DAG에 대한 CI/CD를 구축하기로 결정하게 되었고 CI/CD를 구축하기까지의 여정에 대해 포스팅 해보려고 한다.
CI/CD란?
보통 개발이 끝난 application이 있다면 이를 실제 서버에 배포하는 것 까지의 단계별로 진행하는 절차가 있는데 보통 빌드 -> 테스트 -> 배포라는 순서를 따른다. 예를 들어, Java로 API를 개발했다면 실행시키기 위해 Jar 파일로 build 하는 과정이 필요할 것이고 이후 Unit Test나 Integration Test를 거쳐서 로직에 문제가 없는지 파악하기 위해 테스트를 할 것이다. 이후 테스트에 문제가 없다면 배포 서버에 Jar 파일을 이동시켜서 실행시킴으로써 배포가 완료될 것이다.
예전에는 CI/CD 라는 개념이 없어서 위의 과정을 수동으로 사람이 직접 했고 이로 인해 Human Error가 발생할 여지가 있고 오래걸린다는 단점이 있었다. 현재에 와서 DevOps라는 새로운 영역이 등장하고 CI/CD를 통해 빌드부터 배포까지 자동화를 할 수 있게 되었다. 이로 인해 개발자는 개발에만 집중하면 되었고 DevOps 담당자들이 구축해놓은 CI/CD 파이프라인에 태우기만 하면 되는 것이다.
그래서 CI/CD가 뭐냐. 먼저 CI는 Continuous Integration의 약자로 지속적인 통합을 의미한다. 위의 절차에서 CI는 빌드 -> 테스트 과정에 대해서 자동화한 것이라고 생각하면 된다. VCS로 git을 많이 사용하기 때문에 git을 예로 들면, 개발자가 git을 통해 CI를 실행시킬 브랜치로 merge하게 되면 빌드부터 테스트까지 자동으로 이루어지게 되는 것이다. 개발자는 git을 통해 merge나 push만 하면 되기 때문에 지속적으로 빌드와 테스트를 할 수가 있고 배포가 가능한 상태의 결과물을 준비할 수 있다. 그래서 지속적으로 배포 가능한 상태를 유지한다는 의미로 지속적인 통합이라는 말을 사용하는 것이다.
CD는 Continuous Deployment로 지속적인 배포를 의미하는데, CI를 통해 배포 가능한 결과물이 만들어졌으면 그것을 배포 서버에 결과물을 이동시켜서 배포하는 것까지를 자동화하는 것을 의미한다. 여기서 CD는 Continuous Delivery도 가능한데 Deployment와의 차이점은 배포 서버에 배포하기 전에 배포 가능한 상태의 결과물을 어떤 저장소에 준비시키는 것까지를 자동화하고 실제 결과물을 서버에 배포하는 것은 수동으로 진행하도록 하는 것을 의미한다. 이를 사용하는 이유는 만약 Production 환경의 서버가 준비가 잘 안 되어 있는데 누군가 git을 통해 merge하여 CI/CD를 동작시켰을 때, 배포를 진행하게 되는 risk가 있기 때문이다. 그렇기 때문에 각자 상황에 맞는 CI/CD 파이프라인을 구축해야 한다.
Airflow DAG CI/CD 설계
Airflow의 DAG 읽는 방식
그러면 이제 본론으로 넘어와서 Airflow DAG CI/CD는 어떻게 구축할 것인가? 먼저 Airflow가 DAG를 어떻게 읽어들이는지에 대해 알 필요가 있다. Airflow는 기본적으로 airflow.cfg 이라는 설정 파일을 통해 설정을 할 수 있는데 DAG를 작성한 .py 파일이 어떤 디렉토리에 있는지 정의하는 부분이 있는데 Airflow는 정의한 디렉토리 하위의 파이썬 파일을 읽어서 DAG를 읽어들인다. 우리는 DAG를 작성한 파이썬 파일에 대해서 CI/CD를 진행할 것이고 Airflow 자체 설정에 대해서는 개별로 볼 것이다.
DAG 코드 관리
그러면 DAG를 코드로 어떻게 관리할까 했을 때, 두 가지 방안이 있다. 하나의 Repository에서 관리하는 방법이고 두 번째는 각 DAG별로 여러 개의 Repository로 나누는 방법이다. 전자의 경우 전형적인 하나의 Application을 개발할 때의 git 전략들을 사용해서 하나의 Repository를 여러 명의 사용자가 관리하는 방법이다. 후자의 경우 좀 특이한 방법으로 우리는 Airflow 라는 이미 만들어진 Application이고 파이썬 스크립트만 작성하면 되기 때문에 가능한 방법인데 후자로 할 경우 DAG 개발자들 입장에서는 해당 DAG의 Repository를 다른 DAG들과의 의존성 없이 개발할 수 있어 코드 관리에 수월함이 있다는 장점이 있다. 단점으로는 DAG가 많아지게 되면 Repository가 많이 생긴다는 것이 있다. 전자와 후자를 비교했을 때 전자의 경우 git의 의존도가 높고 개발자들간의 커뮤니케이션이 매우 중요하지만 후자를 할 경우 git의 의존도가 많이 낮아지고 단순한 push만 필요하기 때문에 DAG 개발이 개발자 뿐만 아니라 분석가들도 개발할 수 있다는 점을 보았을 때 후자를 택하게 되었다.
구축 방식
그렇다면 여러 개의 Repository를 생성해서 DAG를 작성한다고 했을 때, Airflow 에 어떻게 배포할 것인가를 고민해 봤을 때, 나는 다음과 같은 방법을 사용했다. Airflow를 쿠버네티스 환경에서 올리는 경우에는 sidecar-container라는 것을 이용하는 경우가 있다고 한다. 쿠버네티스에서는 Pod 위에 여러 개의 컨테이너가 띄워지고 내려가기 때문에 수시로 컨테이너가 띄워질 때마다 배포가 이루어져야 하기 때문에 이것은 비용 효율적이지 못하고 리소스가 낭비된다. 그래서 고정적인 컨테이너로 sidecar-container를 띄워서 해당 컨테이너로 배포를 하면 그 DAG 파일들을 해당 pod의 컨테이너들이 공유하는 방식을 사용한다.
우리는 Airflow를 Celery Executor를 이용하여 master-worker 구조의 클러스터로 띄워져 있기 때문에 위의 구조에 착안해서 각 노드마다 주기적으로 S3와 sync하는 컨테이너를 실행시켜서 실시간으로 동기화하는 방식으로 하기로 결정을 했다.
위와 같이 각 Airflow가 설치된 노드의 로컬 디렉토리와 컨테이너의 볼륨을 마운트하여 컨테이너가 주기적으로 s3에서 DAG 파일들을 가져오면 Airflow가 해당 DAG들을 읽는 방식이다. 이 외에도 git과 sync하는 방식도 있지만 여러 개의 Repository를 만들었기 때문에 해당 수만큼의 git-sync container가 필요해져서 비효율적이라고 판단하여 s3를 sync하는 방식을 채택했다.
그러면 s3가 최종 배포 지점이니 s3에 해당 DAG 파일들이 도착하는 것 까지 CI/CD 파이프라인을 구축해야 한다. 각 Repository에서 Push가 일어날 때 마다 해당 Repository의 파이썬 코드들이 S3로 도착해야 하고 Build 과정은 따로 필요없기 때문에 Test 이후 바로 S3로 해당 코드들을 이동시키면 된다.
위와 같이 Build 과정이 없기 때문에 CI에서는 Test만 이루어지고 CD과정이 S3로 배포하는 과정이 된다.
DAG CI/CD 구축
CI/CD Tool 선정
설계가 끝났으니 이제 Tool을 선정해서 구현을 해야 한다. CI/CD tool은 Jenkins, CircleCI 등 다양한 툴이 있는데 현재 회사는 Bitbucket을 사용하고 있고 Bitbucket pipeline 솔루션을 사용할 수 있는 상태이다. 그래서 나는 Bitbucket과 통합이 쉬운 점, 구현이 간단한 점, CI/CD가 복잡하지 않은 점을 고려하여 Bitbucket pipeline을 사용하기로 결정했다.
Bitbucket pipeline은 bitbucket-pipelines.yml 파일만 있으면 간단하게 CI/CD 파이프라인을 구성할 수 있다.
# This is an example Starter pipeline configuration
# Use a skeleton to build, test and deploy using manual and parallel steps
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: python:3.8
pipelines:
branches:
master:
- parallel:
- step:
name: 'Test'
caches:
- pip
script:
- if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; else pip install pytest; fi
- if [ -d "tests" ]; then pytest -v ./tests/*; fi
# The following deployment steps will be executed for each pipeline run. To configure your steps and conditionally deploy see https://support.atlassian.com/bitbucket-cloud/docs/configure-bitbucket-pipelinesyml/
- step:
name: 'Deployment to dev'
deployment: Development
# trigger: 'manual'
script:
- echo "DAG Deploy to S3 bucket to sync dev airflow"
# sync your files to S3
- pipe: atlassian/aws-s3-deploy:1.1.0
variables:
AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION
S3_BUCKET: '$S3_BUCKET/$BITBUCKET_REPO_SLUG'
LOCAL_PATH: '$(pwd)'
DELETE_FLAG: 'true'
EXTRA_ARGS: "--exclude=* --include=dags/* --include=modules/*"
- step:
name: 'Deployment to qa'
deployment: QA
trigger: 'manual'
...
- step:
name: 'Deployment to prod'
deployment: Production
trigger: 'manual'
...
위와 같이 yml 파일을 작성하면 아래와 같이 Bitbucket-pipeline UI에서 확인할 수 있다.
그리고 Bitbucket-pipelines.yml 파일 외에도 Bitbucket에서 Repository 별로 설정이 필요한데 Web UI를 통해 설정을 할 수도 있지만 Bitbukcet에서 REST API를 제공도 하고 있다. 나는 Web UI로 Repository를 생성하고 설정하는 이러한 과정이 매우 귀찮은 과정이라고 느끼게 되었고 REST API를 활용하여 설정을 자동화하고 싶어서 Python을 이용하여 Repository를 설정하는 것을 프롬프트로 만들어 생성부터 설정까지 자동화를 시켰다.
위와 같이 CI/CD 구축을 완료하면 개발자는 각 Repository에서 개발하고 git을 통해 코드를 push하면 해당 CI/CD 파이프라인을 이용하게 되고 배포를 원할 때 버튼 클릭하나로 해당 환경에 배포가 가능해졌다.
Airflow 로컬 환경 구축
CI/CD를 완성하고 보니 DAG 개발을 위해서는 Airflow 환경 구축이 필요한데 해당 환경을 로컬에서 직접 구축하려면 상당히 번거로운데 그래서 로컬 환경에서 Airflow 환경 구축에 대한 자동화를 시작하게 되었다.
먼저, 해당 사례가 있는지 구글 검색을 통해 찾아보았고 쏘카의 기술 블로그에서 비슷한 고민을 한 것을 찾게 되었다.
쏘카는 쿠버네티스 환경에서 Airflow를 구축했는데 로컬 개발 환경에 대해서 처음에는 개발을 위해 브랜치를 만들면 그것이 트리거 되어 인스턴스에 Airflow를 띄우고 개발이 끝난 브랜치를 merge하고 삭제하면 인스턴스가 내려가는 방식을 했다고 한다. 하지만 브랜치를 지우지 않거나 브랜치를 만들어놓고 다른 업무를 수행하면서 브랜치를 방치함으로써 리소스가 낭비되는 경우가 많았다고 하고 그래서 로컬에서 docker로 구축하도록 바꿨다고 한다. 그래서 해당 내용을 참고하여 docker가 좋은 방안이라고 생각해 Airflow를 docker container로 띄우는 것을 자동화하는 코드를 작성하게 되었다.
Airflow를 docker container로 한 번에 띄우기 위해 docker-compose.yml 을 Airflow에서 제공하는 docker-compose.yml 파일을 참고하여 작성하였고 로컬 환경에서 관리하는 shell script를 작성하였다. 예를 들어, ./manage.sh setup 이라는 명령을 사용하면 docker-compose를 사용하여 Airflow의 컨테이너들을 띄우게 된다. 그러면 로컬에서 해당 환경의 dags라는 폴더 하위에 DAG Repository를 clone하여 개발을 하면 로컬의 Airflow에서 해당 DAG들을 테스트할 수 있다.
마치며
CI/CD라는 것은 들어서 알고만 있었지 직접 구축하면서 CI/CD가 왜 필요한지 왜 중요한지 더 잘 알게 되었고 DevOps라는 직무의 필요성도 느끼게 되었다. 데이터 엔지니어라는 직무라는 것은 참 애매한 직무인 것 같다. DevOps를 할 수도 있고 개발을 할 수도 있고... 현재 회사 다닌지 5개월차가 되어 가는데 데이터 엔지니어라고 딱 선을 긋는 것 보다는 소프트웨어 엔지니어인데 기술 분야가 데이터 쪽이라고 이해하는 것이 더 맞는 것 같다. 현재, 데이터를 Serving 하는 API나 검색 API와 같은 Application 에 대한 CI/CD도 준비하고 있는데 완성되고 정리가 되면 포스팅을 한 번 작성해 봐야 겠다.
'Data > Airflow' 카테고리의 다른 글
[Airflow] EC2 -> EKS 마이그레이션 (0) | 2024.08.01 |
---|---|
[Airflow] Airflow 성능 관련 설정 값 정리 (0) | 2023.12.19 |
[Airflow] 로그 기록 시간대 문제 (0) | 2023.04.05 |
[Airflow] DAG란? (0) | 2022.05.22 |
[Airflow] Apache Airflow 설치하기 (0) | 2022.05.16 |