LLM WikiAccess-protected knowledge portal

WIKI

CI/CD 파이프라인 기초: 데이터 플랫폼 팀의 배포 자동화

데이터 팀에서 CI/CD가 어려운 이유 소프트웨어 개발팀에서 CI/CD는 이미 기본 인프라다. 그런데 데이터 플랫폼 팀에서는 여전히 "배포는 수동으로"인 경우가 많다. 왜일까? 데이터 파이프라인의 특성이 다르기 때문이다. 코드 변경이 일으키는 장애가 즉시 드러나지 않는다. dbt 모델이 잘못 수정되어도 테이블은 조용히 틀린 값을 뱉는다. Airflow DAG에서 의존성이 잘못 설정되어도 당장 에러가 나지 않고 다음 날 새벽에야

경로human/study/content/ci-cd-data-platform/01-cicd-fundamentals-data-platform-deployment.md
카테고리Study
태그#airflow #cicd #data #deployment #fundamentals #infra #platform #study

데이터 팀에서 CI/CD가 어려운 이유

소프트웨어 개발팀에서 CI/CD는 이미 기본 인프라다. 그런데 데이터 플랫폼 팀에서는 여전히 "배포는 수동으로"인 경우가 많다. 왜일까?

데이터 파이프라인의 특성이 다르기 때문이다. 코드 변경이 일으키는 장애가 즉시 드러나지 않는다. dbt 모델이 잘못 수정되어도 테이블은 조용히 틀린 값을 뱉는다. Airflow DAG에서 의존성이 잘못 설정되어도 당장 에러가 나지 않고 다음 날 새벽에야 재처리가 필요한 상황이 된다. 스키마가 바뀌면 수십 개의 다운스트림 파이프라인이 함께 깨진다.

이 문제를 해결하는 것이 데이터 플랫폼의 CI/CD다. 코드가 프로덕션에 닿기 전에 자동으로 검증하고, 배포를 추적 가능하고 되돌릴 수 있게 만든다.


CI/CD의 개념 정리

CI와 CD는 다른 개념이다. 혼용되는 경우가 많으므로 정확히 구분해야 한다.

CI — 지속적 통합 (Continuous Integration)
개발자가 코드를 중앙 저장소에 올릴 때마다 자동으로 빌드·테스트를 실행한다.
목적: 코드 변경이 기존 코드를 깨지 않는다는 것을 빠르게 확인한다.
데이터 팀 예시: PR 열리면 dbt compile → dbt test → 스키마 검증 자동 실행.
CD — 지속적 배포 (Continuous Delivery / Deployment)
CI를 통과한 코드를 자동(또는 원클릭)으로 프로덕션에 반영한다.
Delivery: 배포 준비 완료, 사람이 승인. Deployment: 완전 자동화.
데이터 팀 예시: main 브랜치에 merge되면 DAG 자동 배포, dbt 모델 자동 run.
CI와 CD 개념 구분

데이터 플랫폼 CI/CD 전체 흐름

전형적인 데이터 플랫폼 CI/CD 파이프라인의 흐름은 다음과 같다.

개발자 코드 작성 PR 오픈 CI 단계 1. Lint / 스키마 검증 2. 단위 테스트 실행 3. Slim CI (변경 모델만) 4. PR 리뷰 + 승인 CD 단계 5. 스테이징 배포 6. 통합 테스트 / 검증 7. 프로덕션 배포 8. 배포 후 모니터링 프로덕션 Airflow DAGs dbt 모델 Spark 잡 롤백 알림 · 모니터링 피드백
데이터 플랫폼 CI/CD 파이프라인 흐름

구성 요소별 CI/CD 전략

dbt 모델의 CI/CD

dbt는 데이터 플랫폼 팀이 CI/CD를 도입하기 가장 쉬운 영역이다. SQL 모델, 테스트, 문서가 코드로 관리되기 때문이다.

PR 시 실행할 CI 단계:

# GitHub Actions 예시: .github/workflows/dbt-ci.yml
name: dbt CI

on:
  pull_request:
    paths:
      - 'dbt/**'

jobs:
  dbt-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dbt
        run: pip install dbt-bigquery

      - name: dbt compile (문법 검사)
        run: dbt compile --profiles-dir .

      - name: Slim CI — 변경된 모델만 빌드·테스트
        run: |
          dbt build \
            --select state:modified+ \
            --defer \
            --state ./prod-artifacts \
            --profiles-dir .

핵심 개념: Slim CI

state:modified+는 "현재 PR에서 변경된 모델과 그 하위 의존 모델만" 선택한다. 전체 dbt 프로젝트를 매 PR마다 실행하면 비용과 시간이 크게 소모된다. Slim CI는 이를 변경 영향 범위로 제한한다.

--defer --state는 변경되지 않은 모델의 결과를 프로덕션 아티팩트에서 참조한다. 테스트에 필요한 데이터를 다시 만들지 않아도 된다.

Airflow DAG의 CI/CD

DAG 배포는 두 가지 전략이 있다. GitSync패키지 배포다.

GitSync 방식: Airflow 서버가 Git 저장소를 직접 폴링하여 최신 DAG를 동기화한다. 간단하고 설정이 쉽다. 단점은 main 브랜치에 잘못된 DAG가 들어가면 즉시 프로덕션에 반영된다는 것이다.

GitSync 설정 (values.yaml, Helm)
dags:
  gitSync:
    enabled: true
    repo: https://github.com/myorg/data-platform
    branch: main
    subPath: dags/
    period: 60  # 60초마다 동기화

패키지 배포 방식: CI가 통과한 코드만 배포 대상이 된다. DAG를 파이썬 패키지로 만들어 Artifactory 또는 PyPI에 업로드하고, 배포 시 Airflow 환경에서 pip install로 가져온다. 더 안전하지만 파이프라인이 복잡하다.

DAG 배포 전 필수 검증:

# dags/tests/test_dag_integrity.py
import pytest
from airflow.models import DagBag

@pytest.fixture
def dag_bag():
    return DagBag(dag_folder="dags/", include_examples=False)

def test_no_import_errors(dag_bag):
    assert not dag_bag.import_errors, (
        f"DAG import errors: {dag_bag.import_errors}"
    )

def test_dag_tasks_have_owners(dag_bag):
    for dag_id, dag in dag_bag.dags.items():
        assert dag.default_args.get("owner"), f"{dag_id} has no owner"

def test_dag_has_tags(dag_bag):
    for dag_id, dag in dag_bag.dags.items():
        assert dag.tags, f"{dag_id} has no tags"

이 테스트는 CI에서 반드시 실행해야 한다. DagBag 로드 에러는 Airflow 스케줄러가 시작될 때 전체 DAG 파싱에 영향을 줄 수 있다.


환경 전략: dev → staging → production

개발 (dev)
개발자 로컬 또는 개인 클라우드 환경
dbt target: dev (개인 스키마)
Airflow: 로컬 Docker Compose
소규모 샘플 데이터
스테이징 (staging)
CI 통과 후 자동 배포
프로덕션과 동일 구조, 제한된 데이터
통합 테스트 실행 환경
QA / 데이터 검증
프로덕션 (prod)
수동 승인 또는 자동 배포
실제 데이터, 실제 소비자
롤백 플랜 필수
배포 후 모니터링
데이터 플랫폼 환경 구성 전략

dbt 환경별 프로파일 분리:

# profiles.yml
my_project:
  target: dev
  outputs:
    dev:
      type: bigquery
      project: mycompany-dev
      dataset: "dbt_{{ env_var('DBT_USER', 'default') }}"
    staging:
      type: bigquery
      project: mycompany-staging
      dataset: dbt_staging
    prod:
      type: bigquery
      project: mycompany-prod
      dataset: dbt_prod

개발자마다 자신의 데이터셋(dbt_alice, dbt_bob)에서 작업하면 서로 간섭이 없다.


데이터 파이프라인의 테스트 계층

코드 테스트와 달리, 데이터 파이프라인 테스트는 데이터 자체의 품질도 검증해야 한다.

E2E 데이터 검증
비쌈·느림
통합 테스트
dbt test, 스테이징 파이프라인
dbt 스키마 테스트
not_null, unique, accepted_values, relationships
단위 테스트 (빠름)
dbt unit test, DAG 무결성 검사, SQL 함수 테스트
데이터 파이프라인 테스트 피라미드

dbt 단위 테스트 예시 (dbt 1.8+):

# models/order_summary.yml
models:
  - name: order_summary
    config:
      contract:
        enforced: true
    columns:
      - name: order_id
        data_type: int64
        constraints:
          - type: not_null

unit_tests:
  - name: test_order_total_calculation
    model: order_summary
    given:
      - input: ref('orders')
        rows:
          - {order_id: 1, amount: 100, discount: 10}
          - {order_id: 2, amount: 200, discount: 0}
    expect:
      rows:
        - {order_id: 1, net_amount: 90}
        - {order_id: 2, net_amount: 200}

스키마 변경의 안전한 배포

데이터 파이프라인에서 가장 위험한 배포는 스키마 변경이다. 컬럼 삭제, 타입 변경, 테이블 이름 변경은 다운스트림 파이프라인을 즉시 깨뜨린다.

안전한 스키마 변경 원칙:

✅ 안전한 변경 (하위 호환)
  - 새 컬럼 추가 (nullable)
  - 컬럼에 기본값 추가
  - 뷰(View) 추가

⚠️ 주의가 필요한 변경
  - NOT NULL 컬럼 추가 (기존 행 마이그레이션 필요)
  - 인덱스 추가 (DDL 잠금 위험)

❌ 위험한 변경 (Breaking Change)
  - 컬럼 삭제
  - 컬럼 타입 변경 (암묵적 캐스팅 주의)
  - 테이블/뷰 이름 변경
  - 컬럼 이름 변경

CI에서 Schema Change Detection:

# GitHub Actions에서 스키마 변경 감지
- name: Check breaking schema changes
  run: |
    dbt run-operation check_schema_changes \
      --args '{"state": "./prod-artifacts"}'
    # breaking change 감지 시 CI 실패

dbt의 --defer + --state 조합을 활용하면 프로덕션 메타데이터와 PR의 변경 사항을 비교하여 breaking change를 자동으로 감지할 수 있다.


배포 전략: Blue-Green과 Canary

데이터 파이프라인에서 무중단 배포는 소프트웨어보다 복잡하다. 데이터를 두 벌 만들기 어렵기 때문이다.

dbt 모델의 Blue-Green 배포:

-- 새 모델을 별도 스키마에 먼저 배포
-- prod_green.order_summary 빌드 및 검증
-- 검증 후 뷰를 이용해 참조를 전환

CREATE OR REPLACE VIEW prod.order_summary AS
  SELECT * FROM prod_green.order_summary;
-- 이제 다운스트림은 인식 없이 새 테이블을 바라봄

Airflow DAG의 점진적 배포:

DAG 배포 자체는 원자적이다. 그러나 실행 중인 Task가 있는 상태에서 DAG를 변경하면 mid-flight 작업이 의도치 않은 코드로 실행될 수 있다. 안전한 패턴:

1. 새 DAG ID로 새 버전 배포 (old_dag_v1 → old_dag_v2)
2. old_dag_v1은 paused 처리
3. old_dag_v2가 안정적으로 동작 확인 후 old_dag_v1 삭제

롤백 전략

모든 배포에는 롤백 플랜이 있어야 한다.

dbt 롤백:

# 이전 prod 아티팩트로 롤백
git checkout <prev-commit> -- dbt_project.yml models/
dbt run --target prod

# 또는 Iceberg/Delta Lake time travel로 이전 스냅샷으로 복원
ALTER TABLE prod.order_summary RESTORE VERSION AS OF <timestamp>;

Airflow DAG 롤백:

GitSync 환경이라면 Git revert로 DAG 코드를 이전 버전으로 되돌리는 것이 가장 빠르다.

git revert <bad-commit>
git push origin main
# GitSync가 60초 내로 반영

배포 후 모니터링

배포가 성공적인지 판단하는 기준을 사전에 정의해야 한다.

배포 후 15~30분 이내 확인 항목
□ 프로덕션 DAG 실행 성공 여부
□ dbt 모델 행 수 이상 없음 (전날 대비 ±20% 이내)
□ 핵심 KPI 지표 이상 없음 (대시보드 확인)
□ 다운스트림 파이프라인 에러율 증가 없음
□ slow query 증가 없음
□ 알림 채널에 에러 메시지 없음

배포 알림을 Slack에 자동으로 보내는 GitHub Actions 예시:

- name: Notify deployment status
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    text: |
      dbt 프로덕션 배포 ${{ job.status }}
      커밋: ${{ github.sha }}
      배포자: ${{ github.actor }}
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
  if: always()

팀별 CI/CD 성숙도 단계

레벨 0 — 수동 배포
dbt run, DAG 업로드 모두 수동. 테스트 없음. 배포 로그 없음.
증상: "어제 배포했는데 왜 틀렸지?" 사고가 반복된다.
레벨 1 — 기본 CI
PR마다 dbt compile + dbt test 자동 실행. DAG 무결성 검사 추가.
목표: 문법 오류와 명백한 테스트 실패를 PR 단계에서 차단.
레벨 2 — 환경 분리 + CD
dev / staging / prod 환경 분리. main 머지 시 스테이징 자동 배포.
Slim CI로 변경 영향 범위만 테스트. 배포 알림 채널 운영.
레벨 3 — 완전 자동화 + 관측성
스테이징 검증 통과 후 프로덕션 자동 배포. 스키마 변경 자동 감지.
배포 후 자동 데이터 품질 검사. 롤백 원클릭화. 배포 이력 추적.
데이터 팀 CI/CD 성숙도 모델

Open Questions


References