dbt: CI/CD и slim CI¶
Зачем это DE?¶
Без CI/CD каждый dbt run в проде — русская рулетка. Сломанная модель ломает витрину, а витрина ломает дашборд CEO. CI ловит ошибки до мержа в main, CD деплоит автоматически, slim CI делает это за секунды, а не часы.
Компоненты dbt CI/CD¶
Text Only
┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐
│ PR │ ──→ │ CI │ ──→ │ Merge │ ──→ │ CD │
│ Branch │ │ (test) │ │ to main│ │ (deploy)│
└─────────┘ └──────────┘ └─────────┘ └──────────┘
│
┌──────┴──────┐
│ dbt build │
│ --select │
│ state:modified+│
│ (slim CI) │
└─────────────┘
CI Pipeline (GitHub Actions)¶
Полный CI¶
YAML
# .github/workflows/dbt-ci.yml
name: dbt CI
on:
pull_request:
paths:
- 'models/**'
- 'macros/**'
- 'tests/**'
- 'snapshots/**'
- 'dbt_project.yml'
- 'packages.yml'
jobs:
dbt-ci:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: ci_db
POSTGRES_USER: ci_user
POSTGRES_PASSWORD: ci_pass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dbt
run: pip install dbt-postgres
- name: Install packages
run: dbt deps
- name: Seed test data
run: dbt seed --target ci
- name: Build and test
run: dbt build --target ci
- name: Generate docs
run: dbt docs generate --target ci
profiles.yml для CI¶
YAML
# profiles.yml
my_project:
target: ci
outputs:
ci:
type: postgres
host: localhost
port: 5432
user: ci_user
password: ci_pass
dbname: ci_db
schema: "pr_{{ env_var('GITHUB_PR_NUMBER', 'local') }}"
threads: 4
prod:
type: postgres
host: "{{ env_var('DB_HOST') }}"
port: 5432
user: "{{ env_var('DB_USER') }}"
password: "{{ env_var('DB_PASSWORD') }}"
dbname: analytics
schema: public
threads: 8
Slim CI — state:modified¶
Проблема¶
Проект с 200 моделями. PR меняет 1 модель. Полный CI строит все 200 — 20 минут. Slim CI строит только изменённую + зависимые — 30 секунд.
Как работает¶
Bash
# 1. Скачать артефакты прода (manifest.json)
# 2. dbt build с селектором state:modified+
dbt build --select state:modified+ --defer --state ./prod-artifacts/
| Селектор | Что строит |
|---|---|
state:modified |
Только изменённые модели |
state:modified+ |
Изменённые + все downstream (зависимые) |
state:new |
Только новые модели |
--defer — использование prod-данных¶
--defer говорит: «Если модель не в state:modified+, бери данные из прода, а не строй заново».
Text Only
PR изменил: stg_orders
Зависимости: fct_revenue → stg_orders + dim_customer
С --defer:
stg_orders → строится в CI-схеме (изменённая)
dim_customer → берётся из прода (не изменена)
fct_revenue → строится в CI-схеме (downstream)
GitHub Actions — slim CI¶
YAML
# .github/workflows/dbt-slim-ci.yml
name: dbt Slim CI
on:
pull_request:
paths: ['models/**', 'macros/**', 'tests/**']
jobs:
slim-ci:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: ci_db
POSTGRES_USER: ci_user
POSTGRES_PASSWORD: ci_pass
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install dbt-postgres
- run: dbt deps
# Скачать prod manifest
- name: Download prod artifacts
uses: actions/download-artifact@v4
with:
name: dbt-prod-manifest
path: ./prod-state/
continue-on-error: true # первый PR — нет артефактов
# Slim CI: только изменённые + downstream
- name: dbt build (slim)
run: |
if [ -f ./prod-state/manifest.json ]; then
dbt build --select state:modified+ \
--defer --state ./prod-state/ \
--target ci
else
dbt build --target ci
fi
CD — деплой в прод¶
Стратегия: Blue-Green¶
Text Only
┌───────────────────────┐
│ analytics.public │ ← текущий прод (blue)
└───────────────────────┘
dbt run --target prod → analytics.public_new (green)
dbt test --target prod → тесты на green
↓ если ОК
ALTER SCHEMA public RENAME TO public_old;
ALTER SCHEMA public_new RENAME TO public;
DROP SCHEMA public_old CASCADE;
GitHub Actions — CD¶
YAML
# .github/workflows/dbt-deploy.yml
name: dbt Deploy
on:
push:
branches: [main]
paths: ['models/**', 'macros/**']
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install dbt-postgres
- run: dbt deps
- run: dbt run --target prod
- run: dbt test --target prod
# Сохранить артефакты для slim CI
- name: Upload manifest
uses: actions/upload-artifact@v4
with:
name: dbt-prod-manifest
path: target/manifest.json
retention-days: 30
Артефакты dbt¶
После каждого dbt run в target/ появляются:
| Файл | Содержимое | Для чего |
|---|---|---|
manifest.json |
Полная модель проекта (модели, тесты, макросы, зависимости) | Slim CI (state comparison) |
run_results.json |
Результаты последнего запуска (время, статус, rows) | Мониторинг, алерты |
catalog.json |
Метаданные таблиц (столбцы, типы, описания) | Документация |
sources.json |
Статус source freshness | Мониторинг свежести |
Source freshness в CI¶
YAML
# models/sources.yml
sources:
- name: raw
freshness:
warn_after: {count: 12, period: hour}
error_after: {count: 24, period: hour}
loaded_at_field: _loaded_at
tables:
- name: orders
- name: customers
Чеклист dbt CI/CD¶
- CI на каждый PR —
dbt buildловит ошибки до мержа - Slim CI —
state:modified+для скорости - Изолированная схема — каждый PR строит в своей схеме
- CD на merge в main — автоматический деплой
- Артефакты — сохранять manifest для slim CI
- Source freshness — проверять свежесть источников
- Тесты — not_null, unique, accepted_values на каждом столбце
- Blue-green — деплой без даунтайма