Snowflakeにおけるdbtのタグ管理を自動化!dbt-elementaryを活用したvalidationの仕組み
この記事は、ナウキャスト Advent Calendar 2025 の1日目の記事です。
はじめに
こんにちは。ナウキャストでデータエンジニアをしている島尻です。
データ基盤の開発・運用の現場において、dbt(data build tool) を活用するケースは多いと思います。私が参画しているプロジェクトでも dbt を導入しており、ジョブの実行制御のために dbt の tags を活用しています。
この tags 機能は非常に便利ですが、開発者が dbt の model に対して自由に設定ができてしまいます。そのため、新しい model を追加して tags を付与する際にタイプミスをしてしまったり、設計にない tags を開発者が勝手に追加してしまったりということがあり得ます。
そのため、tags を利用する場合は、早めに設計を固めて validation の仕組みを整備することが重要と考えます。(そもそもディレクトリ指定などで事足りる場合は、tags の利用を最小限にするという選択も必要です)
本記事ではそんな dbt tags の validation の実装例を紹介します。
なお、先にネタバレをしておくと、以下の記事の内容を大いに参考にさせていただきました。こちらを Snowflake 用に置き換えて書いています。
実装内容のサマリ
今回実装した仕組みは 「許可された tags 以外が使われていないか」を CI(継続的インテグレーション)上で自動的に検証する というものです。
dbt の基本機能である seed や test に加えて、dbt の Data Observability のパッケージである dbt-elementary を組み合わせて実装しています。なお、CI は GitHub Actions で実行する前提です。
CI パイプラインの全体像は以下の通りです。
実装の詳細
ここからは具体的な実装内容を紹介します。
dbt seed: 許可された tags の一覧を定義
dbt seed を利用して、「どの tags が有効なのか」を定義するマスタデータを作成します。開発者側で簡単に追加・PR上でレビューがしやすいように、seedのCSVファイルで定義します。
以下のようなイメージです。
※連携元システム別、DWH のレイヤー別、実行頻度別で tags を設計している例
tag_name,description,tag_group
system/systemA,システムAのモデル,system
system/systemB,システムBのモデル,system
layer/staging,ステージング層のモデル,layer
layer/intermediate,中間層のモデル,layer
layer/mart,マート層のモデル,layer
schedule/daily,日次実行モデル,schedule
schedule/monthly,月次実行モデル,schedule
これまで使われていない新たな tags を付与する場合には、この seed ファイルにレコードを追加する必要があります。
dbt-elementary: 現在の tags 情報を取得
dbt の各 model で付与されている tags の一覧を取得するために、dbt-elementary パッケージを利用します。
今回は dbt_models というメタデータテーブルを利用します。dbt-elementary の利用用途が tags の validation のみの場合、dbt run --select elementary.edr.dbt_artifacts.dbt_models という形で指定して実行することで、この dbt_models テーブルのみを生成できます。
このテーブルには dbt Project 内の全 model のメタデータが格納されており、tags カラムに dbt tags の情報が配列形式で保存されます。
例えば、先述の seed_tag_description.csv を元に tags をつけている dbt model が存在する場合、以下のようなテーブルが作成されます。(2カラム、関連レコードのみ抜粋)
| UNIQUE_ID | TAGS |
|---|---|
| model.test_dbt_project.table1 | ["system/systemA", "layer/staging", "schedule/daily"] |
| model.test_dbt_project.table2 | ["system/systemA", "layer/intermediate", "schedule/daily"] |
| model.test_dbt_project.table3 | ["system/systemB", "layer/staging", "schedule/daily"] |
※UNIQUE_IDの部分は、model.{dbt_project.ymlで定義されたproject name}.{テーブル名}となっています。
検証用の model 作成
「現在の全 model の tags」と「許可された tags(seed 定義)」を比較し、許可されていない tags を持っている model を抽出する View を作成します。
以下は Snowflake での実装イメージです。(config は省略)
WITH
tag_list AS (
SELECT
tag.value::string as tag,
listagg(alias, ', ') WITHIN group (
ORDER BY unique_id
) AS related_model_sample
FROM
{{ ref("dbt_models") }},
LATERAL FLATTEN(input => parse_json(tags)) tag
GROUP BY tag
),
final AS (
SELECT *
FROM tag_list
LEFT JOIN {{ ref("seed_tag_description") }} USING (tag)
)
SELECT *
FROM final
version: 2
models:
- name: metadata_dbt_tags_information
description: "List of dbt tags with sample related models and seed-based descriptions"
columns:
- name: tag
description: "dbt tag"
tests:
- not_null
- unique
- relationships:
to: ref('seed_tag_description')
field: tag
- name: description
description: "Description maintained in seed_tag_description"
tests:
- not_null
以下は実際に作成される View のイメージですが、許可されていない tag(seed_tag_description に存在しない tag)が使用されている場合、description と tag_group カラムが NULL になります。
正常なケース(すべての tag が許可されている場合):
| tag | related_model_sample | description | tag_group |
|---|---|---|---|
| system/systemA | model.test_dbt_project.table1, model.test_dbt_project.table2 | システムAのモデル | system |
| system/systemB | model.test_dbt_project.table3 | システムBのモデル | system |
| layer/staging | model.test_dbt_project.table1, model.test_dbt_project.table3 | ステージング層のモデル | layer |
| layer/intermediate | model.test_dbt_project.table2 | 中間層のモデル | layer |
| schedule/daily | model.test_dbt_project.table1, model.test_dbt_project.table2, model.test_dbt_project.table3 | 日次実行モデル | schedule |
dbt test で失敗するケース(許可されていない tags が使用されている場合):
| tag | related_model_sample | description | tag_group |
|---|---|---|---|
| system/systemA | model.test_dbt_project.table1, model.test_dbt_project.table2 | システムAのモデル | system |
| system/systemB | model.test_dbt_project.table3 | システムBのモデル | system |
| system/systemC | model.test_dbt_project.table4 | NULL | NULL |
| layer/staging | model.test_dbt_project.table1, model.test_dbt_project.table3 | ステージング層のモデル | layer |
| layer/intermediate | model.test_dbt_project.table2 | 中間層のモデル | layer |
| layer/unknown | model.test_dbt_project.table5 | NULL | NULL |
| schedule/daily | model.test_dbt_project.table1, model.test_dbt_project.table2, model.test_dbt_project.table3 | 日次実行モデル | schedule |
上記の例では、system/systemC と layer/unknown という許可されていない tag が使用されているため、description と tag_group が NULL となり、schema.yml で定義された not_null テストが失敗します。また、relationships テストも失敗します。
CI のワークフローへの組み込み
具体的なワークフローの yaml ファイルの例は割愛しますが、GitHub Actions の CI ジョブとして定義する際は、以下の流れでコマンドを実行していく形となります。
dbt run --select elementary.edr.dbt_artifacts.dbt_modelsdbt seed --select seed_tag_descriptiondbt run --select metadata_dbt_tags_informationdbt test --select metadata_dbt_tags_information
PR 作成後に tags の validation チェックが走り、許可されていない tags が使用されている場合は自動的に CI エラーになるようなイメージです。これにより、tags の品質向上、レビュー負荷の軽減を図ることができます。
Makefile 等で一連のコマンドを定義しておくと、ローカルで validation を試行してから PR にあげる、というプロセスが踏めるようになり、コマンド管理もできるので便利です。
まとめ
本記事では dbt-elementary や seed を利用して dbt tags の validation を行う仕組みをご紹介しました。
dbt tags は何も考えずに導入すると無法地帯になるリスクがあるため、利用する場合は tags の整合性担保や PR レビュー負荷の軽減のために、今回のような validation の仕組みを早めに導入することをおすすめします!
Discussion