데이터 엔지니어의 Airflow 데이터 파이프라인 CI 테스트 개선기

Image not Found

들어가며

안녕하세요, 버즈빌 데이터 엔지니어 Abel 입니다. 이번 포스팅에서는 데이터 파이프라인 CI 테스트에 소요되는 시간을 어떻게 7분대에서 3분대로 개선하였는지에 대해 소개하려 합니다.



배경

이전에 버즈빌의 데이터 플랫폼 팀에서 ‘셀프 서빙 데이터 플랫폼 구축하기’ 에 대해 소개해 드렸었습니다. 해당 플랫폼에 데이터 파이프라인을 추가하는 방법에 대해 요약하자면 먼저 형식에 맞게 YAML 파일을 작성한 다음 깃허브를 통해 해당 YAML 파일에 대한 Pull Request를 생성한 후 배포하여 데이터 파이프라인을 추가하였습니다. 해당 과정 중에서 사람의 실수를 감지하고자 Pull Request 생성 시 자동으로 CI 테스트가 동작하게 됩니다. 하지만 해당 CI 테스트 과정은 저희 팀에서만 사용하는 것이 아닌 데이터 파이프라인을 생성하고자 하는 분들 모두에게 적용되는 만큼 다른 CI 테스트들보다 영향 범위가 넓었습니다. 이에 해당 CI 테스트를 개선하여 데이터 파이프라인을 관리하는 모든 분의 개발 효율을 향상해 보고 싶은 생각이 있었지만 다른 업무들에 비해 우선순위가 낮아 실제로 진행되지 못하고 있던 업무였습니다. 그러던 와중 잠시 기존 업무를 내려놓고 쌓여있던 기술 부채를 해소하고 업무 효율을 개선하는 버즈빌만의 벨로시티 스프린트라는 2주간의 시간이 주어져 CI 테스트 개선 작업을 진행할 수 있게 되었습니다.

데이터 파이프라인 CI 의 상태

우선 어떻게 CI 테스트를 개선할 것인가를 답하기 위해서는 현재 우리의 CI 테스트는 어떤 단계를 통하여 실행되고 있는가에 대한 파악이 필요합니다.

문제-제보-슬랙
위 사진은 저희 CI 테스트 실행 순서를 보여줍니다. 우선 컨테이너 이미지 레지스트리(Registry)에 업로드하기 위한 인증 단계인 login_ecr 과 실제로 이미지를 빌드하는 build_image 단계가 성공해야 합니다. 두 단계 모두 성공한다면 이후 flake8 을 통하여 코드 스타일을 검사하고 단순한 오류를 탐지하는 lint 단계와 테스트 코드를 실행시켜 주는 mypy 단계가 실행됩니다. 여기에 추가로 데이터 파이프라인 상에서 사용되는 쿼리가 추가되거나 수정된 파일만 추출해 주는 athena-process-changeset 단계가 성공하면 해당 단계에서 추출된 쿼리들의 문법 오류가 없는지 검증하는 pytest-and-validate-athena-query 단계가 실행됩니다. 그리고 모든 단계가 성공적으로 완료되어야 테스트 결과를 알리는 ci_required 단계가 실행되며 CI 테스트가 종료됩니다. 해당 실행 순서와 실행되는데 걸리는 시간을 통하여 우리는 어떤 단계들을 개선해야 할지 알 수 있습니다. 예를 들어 ci_required 가 실행되기 위해선 lint, mypy 와 pytest-and-validate-athena-query 단계가 모두 완료되어야 하기 때문에 가장 오래 걸리는 단계가 끝나기 전까지는 다른 단계들이 얼마나 빨리 끝나는지와는 전혀 무관합니다. 따라서 lint 와 mypy 단계는 pytest-and-validate-athena-query 가 개선되어 해당 단계들보다 소요 시간이 적어지기 전까진 개선 필요성이 매우 낮아지게 됩니다. 이러한 점들을 고려하면 현재 개선 필요성이 높은 단계는 build_image 와 pytest-and-validate-athena-query 입니다.



개선 작업

build_image 단계

빌드 캐싱 안정화

데이터 파이프라인 리포지토리(Repository)에서는 CI 테스트용과 배포용 이미지로 두 가지 이미지를 빌드하고 있습니다. 이 중 CI 테스트 개선과 관련된 CI 테스트용 이미지 빌드 단계를 살펴보았을 때 빌드마다 빌드에 걸리는 시간의 편차가 매우 커서 최소와 최대의 차이가 2배 이상 나는 상황이었습니다. 시간이 오래 걸린 케이스들에서 이미지 빌드 로그를 보았을 때 변경사항이 전혀 없는 이미지 레이어가 캐싱이 되지 않아 다시 빌드 과정을 거치고 있는 상태였습니다. 바로 캐시 데이터를 관리하는 리포지토리를 확인해 보았으나 해당 브랜치의 이미지에 대한 캐시 데이터가 정상적으로 업로드되어 있는 것을 확인했습니다. 이때, 캐시 데이터에 붙은 태그를 보고 데이터 파이프라인에서 관리되는 이미지에서 캐시 데이터 문제의 원인을 깨달았습니다. 위에 현상에서 설명한 것과 같이 데이터 파이프라인 리포지토리에서는 두 가지 이미지를 빌드하고 있습니다. 하지만 이미지 캐싱은 브랜치 단위로 관리되고 있었고 이로 인해 CI 테스트용 이미지와 배포용 이미지 중 먼저 업로드된 캐시 데이터가 늦게 업로드된 캐시 데이터에 의해 덮어씌워져 먼저 빌드 된 이미지의 경우 그다음 빌드 시 캐시 데이터를 사용할 수 없는 상황이었습니다. 간헐적으로 CI 테스트용 이미지가 캐싱 없이 빌드를 하게 되는 상황으로 인해 CI 테스트에 걸리는 시간의 편차가 심하였습니다. 이를 개선하고자 브랜치, 도커파일명, 빌드 타깃을 기준으로 캐시 데이터를 관리할 수 있도록 변경하였고 이를 통하여 build_image 단계에 소요되는 시간이 안정되었습니다.

문제-제보-슬랙
안정화 이전
문제-제보-슬랙
안정화 이후

의존성 개선

데이터 파이프라인 이미지 빌드 과정에서는 캐시 데이터 다운로드 → 이미지 빌드 → 이미지 푸시 → 캐시 데이터 생성 → 캐시 데이터 푸시 순서로 진행되었습니다. 이미지와 캐시 데이터를 따로 관리하는 이유는 이미지만 빌드 완료되면 바로 배포가 될 수 있도록 하고 이후 캐시 데이터를 관리하기 위함이었습니다. 하지만 CI 테스트에서는 이미지 빌드 과정 전체에 대한 의존성을 띠고 있었고 이 때문에 캐시 데이터 푸시까지 완료된 이후 CI 테스트가 진행되었습니다. 이를 개선하고자 배포 시 감지하는 방식과 동일한 방식을 CI 테스트에 적용하여 이미지 빌드 과정에 의존성을 두는 것이 아닌 이미지 푸시 여부에 의존성을 주었고 이를 통해 캐시 데이터와 관련된 처리 시간을 절약할 수 있었습니다.

pytest-and-validate-athena-query 단계

불필요한 커맨드 제거

문제-제보-슬랙
해당 단계를 살펴보면 우선 airflow의 dag를 실행시키기 위한 airflow 초기화 과정 이후 테스트를 실행시키고 데이터 파이프라인 과정 속에서 사용하는 쿼리들이 문법적으로 문제가 없는지 검증하는 과정을 거칩니다. 이때, 실제로 테스트를 하는 시간은 1분도 채 안 되지만 환경 설정에만 1분 40초를 소모하게 됩니다. 해당 커맨드의 경우 airflow.db라는 sqlite 용 파일 하나를 생성하게 되는데 해당 파일의 경우 Airflow 버전에 따라서 변경되게 되며 동일한 버전에서는 모든 CI 테스트 과정 속에서 동일한 파일을 생성합니다. 이러한 특성을 기반으로 Airflow 버전에 기반한 캐싱을 할까 고민을 하였습니다. 하지만 Airflow 버전 업그레이드가 자주 발생하는 일이 아니었기 때문에 캐싱 시 해당 Airflow 버전을 알아내는 작업을 하는 것보다 해당 파일을 수동으로 관리하는 것이 설정 및 운영 난이도가 훨씬 낮았습니다. 이러한 결정을 통하여 리포지토리에 직접 airflow db init 의 결과물인 airflow.db 파일을 저장하였고 이를 통하여 CI 테스트 과정에서 제거하여 1분 40초의 시간을 절약하였습니다.

병렬 실행

기존에 pytest 와 아테나 쿼리 검증 단계를 순차적으로 진행하였던 이유가 airflow db init 의 결과물을 사용해야 했기에 같은 환경을 공유해야 했고 아테나 쿼리 검증 단계에 소요되는 시간이 길지 않았기에 순차적으로 실행될 수 있게 해두었습니다. 하지만 airflow db init 의 결과물을 리포지토리에 저장해두었기 때문에 더 이상 같은 환경을 공유할 필요가 없어졌습니다. 이를 통하여 pytest 와 아테나 쿼리 검증 단계를 병렬로 처리하여 CI 테스트에 걸리는 시간을 단축시킬 수 있었습니다.

mypy 단계 개선

pytest-and-validate-athena-query 단계가 개선되며 기존 3분 후반대에 걸리던 시간이 1분 중반까지 개선되었습니다. 이로 인해 pytest의 추가적인 개선보다 1분 후반대의 시간이 걸리는 mypy 개선 우선 순위가 높아져 mypy 개선 작업을 진행하였습니다.

캐싱

mypy의 경우 별다른 설정을 해주지 않아도 커맨드가 실행되는 디렉터리 하위의 .mypy_cache에 캐시 데이터를 관리합니다. 해당 경로를 깃허브 액션에서 지원하는 캐시 저장소를 통하여 브랜치 단위로 관리하였고 이를 통하여 1분 50초 대의 소요 시간을 1분 10초 대까지 개선하였습니다. 이때, 깃허브 액션의 캐시 저장소는 최대 10GB까지만 지원하며 용량 초과 시 과거 데이터는 삭제되며 7일 이상 접근되지 않은 캐시 데이터 또한 삭제되는 점을 유의하여 사용해야 됩니다. 예를 들어 수 GB의 이미지를 깃허브 액션 캐시 저장소를 통해 관리하는 경우 동시에 여러 브랜치에서 작업하는 경우 캐싱이 정상적으로 동작하지 않으며 오히려 캐싱 하는데 시간이 더 많이 소요될 수도 있습니다.



CI 테스트 개선 결과

기존에 최대 7분이 걸리던 CI 테스트가 안정적으로 3분 중반대가 나오게 되며 약 50%의 개선을 이루어냈습니다. 이를 통하여 데이터 엔지니어뿐 아니라 데이터 파이프라인을 생성하고 관리하는 모든 분들의 업무 효율을 증가시킬 수 있었습니다.

문제-제보-슬랙



마무리하며

실제 CI 테스트 개선 작업을 진행한지 반 년 정도 지났는데 확실히 개선 전에 비해 CI 테스트를 대기한다고 소모되는 시간이 확실히 줄어들고 이를 통해 업무 집중의 질도 향상되었습니다. 업무를 해결하기 위한 과정을 개선하는 작업은 이후 업무 효율에만 도움이 될 뿐 아니라 지금까지 당연하게 여겼던 업무 과정을 다시 한번 전반적으로 점검하는 기회가 되었습니다. 이번에는 CI 테스트 개선을 중점으로 개선하였지만 또 다른 업무 과정 속 비효율을 개선해 나아가며 이에 대해 소개 드릴 수 있으면 좋을 것 같습니다.

You May Also Like

버즈빌, 아마도 당신이 원하던 회사!

지원하기