[dbt 101] Day 4: 오케스트레이션, Airflow와 dbt의 우아한 결합
지금까지 우리는 dbt를 이용해 로컬 환경에서 데이터를 모델링하고 테스트했다. 이제 이 로직을 매일 아침 9시에 자동으로 실행시켜야 한다. 여기서 지난주에 다뤘던 파이프라인의 지휘자, Apache Airflow가 등장할 차례다.
하지만 dbt를 Airflow에서 실행하는 방법은 여러 가지가 있다. 오늘은 가장 원시적인 방법부터, 2026년 현재 업계 표준으로 자리 잡은 Cosmos를 이용한 통합 방법까지 다룬다.
1. 고전적인 방식: BashOperator (Monolithic)
가장 직관적이고 쉬운 방법은 Airflow의 BashOperator를 사용하는 것이다. 단순히 터미널 명령어를 Airflow에게 대신 치게 만드는 방식이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from airflow import DAG
from airflow.operators.bash import BashOperator
with DAG(dag_id="dbt_bash_operator_example", ...) as dag:
dbt_run = BashOperator(
task_id="dbt_run",
bash_command="cd /path/to/dbt/project && dbt run"
)
dbt_test = BashOperator(
task_id="dbt_test",
bash_command="cd /path/to/dbt/project && dbt test"
)
dbt_run >> dbt_test
문제점: 블랙박스(Black Box)
이 방식은 dbt 프로젝트가 아무리 커도 Airflow UI 상에서는 단 하나의 Task(dbt_run)로 보인다.
- 가시성 부족: 100개의 모델 중 99번째 모델이 실패해도, Airflow는 단순히 “dbt_run 실패”라고만 표시한다. 어떤 모델이 문제인지 보려면 긴 로그를 뒤져야 한다.
- 재시도 불가: 실패한 모델 하나만 재시도(Retry)할 수 없다. 전체
dbt run을 처음부터 다시 돌려야 한다.
2. 모던한 방식: Astronomer Cosmos
우리가 원하는 것은 dbt의 Lineage(의존성 그래프)가 Airflow의 DAG로 그대로 표현되는 것이다. stg_customers가 성공해야 customers 모델이 실행되도록 Task를 쪼개고 싶다.
과거에는 manifest.json 파일을 파싱 해서 동적으로 DAG를 생성하는 코드를 직접 짰지만, 이제는 Cosmos(astronomer-cosmos)라는 강력한 오픈소스 라이브러리가 이 작업을 대신해 준다.
Cosmos 설정 및 구현
먼저 라이브러리를 설치한다. pip install astronomer-cosmos
이제 DAG 파일에서 Cosmos의 DbtDag를 불러와 설정하면 끝이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import os
from datetime import datetime
from cosmos import DbtDag, ProjectConfig, ProfileConfig, ExecutionConfig
from cosmos.profiles import SnowflakeUserPasswordProfileMapping
# dbt 프로젝트 경로 설정
dbt_project_path = "/usr/local/airflow/dbt/my_project"
profile_config = ProfileConfig(
profile_name="default",
target_name="dev",
profile_mapping=SnowflakeUserPasswordProfileMapping(
conn_id="snowflake_default", # Airflow Connection ID 사용
profile_args={"schema": "public"},
),
)
# Cosmos DAG 생성
dbt_cosmos_dag = DbtDag(
project_config=ProjectConfig(dbt_project_path),
profile_config=profile_config,
execution_config=ExecutionConfig(
dbt_executable_path=f"{os.environ['AIRFLOW_HOME']}/dbt_venv/bin/dbt",
),
# 일반적인 Airflow DAG 설정
dag_id="dbt_cosmos_example",
start_date=datetime(2026, 1, 1),
schedule_interval="@daily",
catchup=False,
)
Cosmos가 가져온 변화
위 코드를 배포하면 Airflow UI는 마법 같은 변화를 보여준다.
- Task Group 자동 생성: dbt의
models/폴더 구조가 Airflow의 Task Group으로 매핑된다. - 세분화된 제어: 특정 모델 하나만 실패하면, 그 모델만 클릭해서
Clear하고 재시도할 수 있다. - 의존성 시각화: dbt의
ref()관계가 Airflow의 화살표(Edge)로 자동 변환된다.
3. 베스트 프랙티스: 가상 환경 분리
Airflow와 dbt는 둘 다 Python 기반이고, 의존성 패키지 충돌(Dependency Hell)이 자주 발생한다. 따라서 실무에서는 Cosmos를 사용할 때 가상 환경(Virtualenv)을 분리하는 것을 권장한다.
- Airflow는 시스템 Python 환경에서 실행된다.
- dbt는 별도의
venv에 설치한다. - Cosmos의
ExecutionConfig에서dbt_executable_path를 해당 venv의 dbt 실행 파일 경로로 지정한다.
이렇게 하면 Airflow 버전 업그레이드가 dbt 실행에 영향을 주지 않고, 반대의 경우도 마찬가지다.
마치며
Airflow와 dbt의 결합은 데이터 엔지니어링의 생산성을 극대화한다. Cosmos를 통해 우리는 “모델링은 dbt에서, 스케줄링과 관제는 Airflow에서”라는 명확한 역할 분담을 시각적으로도 구현할 수 있게 되었다.