Перейти к содержанию

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
Bash
dbt source freshness  # проверка свежести

Чеклист dbt CI/CD

  1. CI на каждый PRdbt build ловит ошибки до мержа
  2. Slim CIstate:modified+ для скорости
  3. Изолированная схема — каждый PR строит в своей схеме
  4. CD на merge в main — автоматический деплой
  5. Артефакты — сохранять manifest для slim CI
  6. Source freshness — проверять свежесть источников
  7. Тесты — not_null, unique, accepted_values на каждом столбце
  8. Blue-green — деплой без даунтайма

Проверь себя


Источники