dbt

Junior

Как настроить profiles.yml в dbt так, чтобы креды никогда не попадали в git?

dbt намеренно разделяет «что строить» и «куда подключаться» на два файла — один коммитится, второй не должен коммититься никогда. Вот как это разделение должно работать.

Читать на:
Захардкожено в profiles.yml Закоммиченный .env файл Утечка через логи CI
Частые способы, которыми креды к хранилищу оказываются там, где не должны — примерно в таком порядке, не измеренная статистика.

dbt_project.yml описывает ваш проект и лежит в git. profiles.yml описывает подключение к хранилищу и по умолчанию лежит вообще вне любого репозитория. Смешение этих двух файлов — самая частая причина, по которой креды оказываются закоммичены.

Где лежит profiles.yml и почему это не случайность

По умолчанию dbt ищет profiles.yml в ~/.dbt/ — не в директории проекта, и не в том, что dbt init кладёт под контроль версий. Можно указать другое расположение через переменную окружения DBT_PROFILES_DIR (полезно в CI, где домашней директории как таковой нет), но расположение по умолчанию — это и есть смысл: оно физически отделяет данные подключения от кода, который пушится в GitHub.

# ~/.dbt/profiles.yml — никогда внутри самого проекта dbt my_project: target: dev outputs: dev: type: snowflake account: "{{ env_var('DBT_ACCOUNT') }}" user: "{{ env_var('DBT_USER') }}" password: "{{ env_var('DBT_PASSWORD') }}" role: transformer database: analytics warehouse: transforming schema: dbt_dev threads: 4

Ключ my_project: сверху должен совпадать со значением profile: в вашем dbt_project.yml — это единственная связь между двумя файлами, и это имя, а не секрет.

env_var() — это и есть весь трюк

Любое значение, которое не должно быть читаемо тем, кто может сделать cat файла, идёт через env_var('SOME_NAME') вместо того, чтобы быть вписанным буквально. Локально это означает экспорт переменной в профиле shell'а или в локальном .env файле, который в .gitignore (direnv или dotenv отлично для этого подходят). В CI это означает установку тех же имён как зашифрованных секретов в том, что запускает ваши задачи dbt — секреты GitHub Actions, коннекшены Airflow или эквивалент вашего оркестратора — никогда как открытый текст в файле workflow.

Что часто пропускают: у env_var() есть необязательный второй аргумент как значение по умолчанию — env_var('DBT_THREADS', '4') — это нормально для несекретных настроек, но если не задать его для переменной с паролем, отсутствующий секрет громко упадёт с ошибкой «env var required but not provided» вместо тихого подключения от имени какого-то другого пользователя. Не давайте секретам значения по умолчанию.

Где это всё ещё ломается

Два реальных пробела, которые это само по себе не закрывает. Во-первых, ничто не мешает коллеге добавить второй блок target с буквальным паролем «просто пока» — profiles.yml сам по себе dbt не линтует, так что тут нужна привычка код-ревью, а не только шаблон. Во-вторых, dbt debug проверяет только тот target, на который вы сейчас указываете; опечатка в редко используемом target (например, разовый output staging) может лежать сломанной месяцами, пока кто-то наконец не запустит его. Ни то, ни другое — не проблема dbt, которую нужно чинить: opti-pipe вообще не трогает креды или конфиг подключения, только run_results.json, который прогон производит уже после того, как успешно подключился.

Посмотрите, как это выглядит на вашем собственном пайплайне.

Загрузите реальный event log Spark, run_results.json от dbt или экспорт метрик Flink — и получите конкретные рекомендации, которые нужно одобрить, а не ещё одно эмпирическое правило.