dbt は「同じコードを別の環境に安全に適用する」ための仕組みを標準で備えています。コア(dbt Core)では profiles.yml の target と Jinja コンフィグで、dbt Cloud では Environment(Development / Deployment)と Job の組み合わせで実現します。
この記事では、安定的な本番運用と試験対策の両方で落とし穴になりやすい点(スキーマ分離、権限、deferral による Slim CI、テストの厳格度切替)を、具体例と運用パターンで解説します。
dbt では「環境」を2層で扱います。ローカル/CLI では profiles.yml の outputs と target(例: dev/stg/prod)で接続先やスキーマを切り替えます。dbt Cloud では Environment(Development または Deployment)として資格情報・権限・対象プロジェクトを束ね、Job を特定 Environment に紐づけます。
重要なのは、物理的な分離(データベース/カタログ/スキーマ/データセット単位)を明確にすることです。これにより、開発者が誤って本番オブジェクトを上書きする事故を防げます。Snowflake なら別 Database/Schema、BigQuery なら別 Dataset、Databricks(Unity Catalog)なら Catalog/Schema での分離が基本方針です。
| 環境 | 目的 | 物理配置(例) | 権限ポリシー |
|---|---|---|---|
| dev | 個人開発・実験 | SNOWFLAKE: DB=ANALYTICS, SCHEMA=DEV_<user> | 開発者にCREATE可、本番データはREADのみ |
| stg | 結合テスト・リハーサル | BIGQUERY: project=analytics-stg, dataset=core_stg | 限定メンバーのみWRITE可 |
| prod | 消費・配信 | DATABRICKS: catalog=prod, schema=marts | CI/CDのみWRITE、閲覧者はREAD |
環境分離の典型パターン(論理→物理)
dbt_project.yml: 環境名をスキーマに反映する(安全な衝突回避)
models:
+materialized: table
# カスタム: スキーマ名に target.name を強制付与
macro-paths: ["macros"]
最も事故が多いのは「同じスキーマに dev と prod が混在」する状態です。generate_schema_name マクロで target.name を必ず付与し、物理オブジェクトの衝突を防ぎます。個人開発は dev_<user> のように個人単位のスキーマを発行するのが安全です。
データウェアハウスごとの特性に合わせます。Snowflake は Database/Schema の2階層、BigQuery は Project/Dataset、Databricks(Unity Catalog)は Catalog/Schema/Volume など。prod はできるだけ上位階層から独立させるとロール管理が容易になります。
| 命名方針 | 例 | メリット/注意 |
|---|---|---|
| schema = <base>_<target> | core_dev / core_stg / core_prod | 単純で覚えやすい。prod でも接頭辞が付く点を許容できるか検討 |
| user スキーマ | dev_alice, dev_bob | 並行開発に強い。権限管理とクリーンアップ運用が必要 |
| 階層分離優先 | Snowflake: DB=ANALYTICS_DEV/STG/PROD | 最も安全。コスト管理や権限制御が明瞭 |
macros/generate_schema_name.sql の例
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if custom_schema_name is not none -%}
{{ default_schema }}_{{ custom_schema_name }}_{{ target.name }}
{%- else -%}
{{ default_schema }}_{{ target.name }}
{%- endif -%}
{%- endmacro %}
dbt Core/CLI では profiles.yml の outputs に dev/stg/prod を定義し、target でどれを使うかを切り替えます。各 output に接続先、スキーマ/データセット、ロール/権限を明示します。環境変数は env_var で読み込みます。
Snowflake/BigQuery/Databricks いずれでも、prod は書き込み主体を CI/CD(サービスアカウント)に限定し、開発者は読み取り中心にします。これにより、誤更新や秘密情報の露出を防げます。
profiles.yml(例:Snowflake)
my_project:
target: dev
outputs:
dev:
type: snowflake
account: {{ env_var('SF_ACCOUNT') }}
user: {{ env_var('SF_USER') }}
password: {{ env_var('SF_PASSWORD') }}
role: DEV_ROLE
database: ANALYTICS
warehouse: DEV_WH
schema: core
stg:
type: snowflake
account: {{ env_var('SF_ACCOUNT') }}
user: {{ env_var('CI_USER') }}
password: {{ env_var('CI_PASSWORD') }}
role: STG_ROLE
database: ANALYTICS_STG
warehouse: CI_WH
schema: core
prod:
type: snowflake
account: {{ env_var('SF_ACCOUNT') }}
user: {{ env_var('DEPLOY_USER') }}
password: {{ env_var('DEPLOY_PASSWORD') }}
role: PROD_ROLE
database: ANALYTICS_PROD
warehouse: PROD_WH
schema: marts
dbt Cloud では、Environment に接続情報・ロール・デプロイ可否をまとめ、Job は必ず特定の Environment に紐づきます。Development Environment は開発者が IDE で使い、Deployment Environment は CI/CD やスケジュール実行で使用します。
本番 Job は承認フローや限定ロールで保護し、ステージング Job は PR 単位で実行(CI)してから本番 Job を走らせる二段構成にします。UI のラベルは時期により変わる可能性がありますが、Environment と Job の紐付け・分離という設計原則は安定しています。
Job コマンド例(Cloud/CLI 共通イディオム)
dbt build --target prod --select tag:marts+
PR で全リビルドをすると高コストです。deferral と state を組み合わせる Slim CI では、過去の本番(またはステージング)成果物を参照しつつ、変更影響範囲だけを再ビルドします。これにより、速く安定した検証が可能になります。
dbt Cloud の Job では『Defer to a previous run state』相当の設定があり、参照元として prod の最新アーティファクトを指定します。CLI では --defer と --state オプションを使います。
Slim CI(PR)での典型コマンド
dbt build \
--target stg \
--select state:modified+ \
--defer \
--state path/to/prod_artifacts
環境に応じてテストの厳格度や GRANTS を切り替えます。prod では致命的テストで失敗させ、dev では警告や無効化でスピードを優先します。dbt の tests は Jinja で enabled/severity を環境条件にできます。モデルの grants も target.name で条件分岐可能です。
破壊的変更(カラム削除など)はステージングでデータ検証を済ませ、prod では段階的リリースかリネーム+移行で安全に進めます。スケジュール Job は前段のステージング成功をトリガーにし、ロールバックはアーティファクトの固定バージョンを参照します。
環境別の tests/grants 設定例
# tests(schema.yml)
version: 2
models:
- name: fct_orders
columns:
- name: order_id
tests:
- not_null:
name: not_null_order_id
enabled: "{{ target.name != 'dev' }}"
severity: "{{ 'error' if target.name == 'prod' else 'warn' }}"
# grants(dbt_project.yml または models/*.yml のモデル設定)
models:
marts:
+grants:
select: "{{ ['ANALYST_ROLE'] if target.name == 'prod' else [] }}"
Analytics Engineer
問題 1
PR の CI で、過去の本番成果物を参照しつつ変更影響範囲だけを再ビルドしたい。最も適切なコマンドはどれか。
正解: A
Slim CI の定石は state:modified+ で変更と依存を選択し、--defer と --state で本番アーティファクトを参照して未変更ノードの再ビルドを避ける。B は本番に直接書き込み、C は全更新でコスト過大、D はビルドを伴わないため不十分。
dbt Cloud の Environment と profiles.yml の target は何が違いますか?
profiles.yml の target は CLI/ローカルでの接続先やスキーマ切替を表し、dbt Cloud の Environment はクラウド側での資格情報・ロール・ランタイムを束ね、Job と厳密に紐づく運用単位です。原理は同じでも適用層が異なります。
ステージングを省略して dev→prod に直接デプロイしても良いですか?
推奨しません。ステージングはスキーマ変更や依存関係の検証、データ品質テストのリハーサルに不可欠です。特に破壊的変更や増分モデルでは、stg での Slim CI による差分検証が本番事故の主要な防波堤になります。
prod と stg を同じ Database/Project 内の別スキーマで運用しても安全ですか?
最小要件は満たせますが、本番の権限・コスト・データ保持をより強固に分離するには上位階層(Snowflake の別 Database、BigQuery の別 Project、Databricks の別 Catalog)を分ける方が安全です。可能なら階層も分離してください。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
dbt Model の基礎: SQL で定義する変換の最小単位
Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...
dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点
dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...
dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点
dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...
dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト
Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....
dbt_project.yml の読み方:主要設定と命名を最短で掴む
dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...