dbtを導入して小規模チームでも運用可能なデータマネジメント体制を構築した話

はじめに

こんにちは。株式会社High Linkのデータユニットマネージャーの芦川 (@assy) です。

私たちのチームでは、データを強みとした事業価値創出を促進するために、データ基盤の整備やデータマネジメント、全社的なデータ利活用レベルの引き上げに取り組んでいます。

データマネジメントをしていると、「誰が作ったかわからない野良のテーブルが乱立している」ことや「BigQueryコンソール上でviewを定義してしまってコードレビューができない」さらには、「テーブル間の依存関係がわからず削除できない」といった課題にぶつかる方は多いんじゃないでしょうか。

私たちもまさにこのような問題に直面し、導入したのがdbtです。

今回は、dbtの導入に至る経緯や選定の理由、dbtをどう活用しているのかといった話を共有させて頂こうと思います。

私たちのようにデータマネジメントにがっつり人的リソースを割けないスタートアップにおいては、少人数でもメンテナンス可能な仕組みづくりが重要になってきます。

dbtを活用したデータマネジメントについて少しでもご参考になれば幸いです。

dbtとは?

dbt (data build tool) とは、簡単に説明すると ELT (Extract, Load, Transform) の T の部分を担うツールです。

www.getdbt.com

つまり、EL(データ転送)は他のツールに任せ、あくまでBigQuery等Data Warehouseに保存されたデータに対してTransformを行うツールです。

ちなみに私たちはサービスDBデータに対するELをEmbulk 、Tをdbtが担うという構成をとっています。

dbt はテーブルの定義を主にSQLで記述します。SQLが書ければパイプラインを簡単に記述できるのは利点と言えるでしょう。

また、コード内でPython用テンプレートエンジンJinjaを利用することができるため、SQLだけでは記述が難しい処理や、マクロを定義して処理を再利用することも可能です。

GitHubと連携したCI/CDが実現できコードベースの管理ができるのも嬉しいポイント。

dbt導入の経緯

抱えていた課題

私たちは1年ほど前からBigQueryをData Warehouseとして採用し、本格的にデータ基盤の整備に取り組み始めました。

データパイプラインを整備し、BigQuery内のテーブルに対して Lake層、Warehouse層、Mart層 の3段構成を定義し、データマネジメントを行なってきました。

しかしながら、コンソール上で定義したテーブルが増えてくるに従い、次のような問題が露呈してきました。

  • コンソール上でテーブル定義を自由にできてしまうため、クエリのレビューの仕組みがない(担当者同士がケアするしかない)
  • テーブル定義の変更履歴を管理できない
  • テーブル同士の依存関係のトラッキングが大変
  • テーブル定義のドキュメントをアドホックに管理するのが大変

特に当時の運用ポリシーでは、事業の担当者がある程度自由にテーブルを増やすことができるようにしていたため、このまま野良のテーブルが増えていくと管理がますます難しくなることは目に見えていました。

こういった課題を解決するために、仕組みの改善が求められました。

なぜdbtを選んだのか

やりたかったこと

  • テーブル定義をGitHub上で管理して、CI/CDの基盤を整えたい
  • データリネージを構成したい
  • ドキュメントを自動生成したい
  • なるべくコストを抑えたい

これらを満たすツールを探していたところ、Dataformとdbtが該当し、2択で迷いました。

私たちはBigQueryを利用しているため、Dataformが無料かつ必要十分な機能を備えていたので第一候補に上がりました。

しかしながら2022年9月現在、DataformはGCPへの統合作業が進んでおり、新規サインアップを停止しています。Waitlistに登録しましたがなかなか動きがなかったのでdbtを使うことにしました。

dbtはOSS版(無料)と、dbt cloudというマネージド版(有料)の2種類があります。私たちはdbt自体をメンテナンスするリソース的余裕はなかったため、dbt cloudを選択しました。

dbt cloud はdeveloperアカウント1名までは無料で、チームで利用したい場合、developerアカウント1つにつき月間$50かかります。

www.getdbt.com

まずは個人アカウントではじめてみて、良さそうであればチームアカウントに変更するのがおすすめです。

dbtを活用したテーブル運用ポリシー

dbtの導入と併せて、テーブル管理がしやすくなるよう運用ポリシーの改善を行いました。

具体的には、warehouse層/mart層のテーブルに関しては全てデータエンジニアリングチームがdbtで管理するようにし、野良のテーブルが乱立しないようにしました。

担当者が直接テーブルを生成できないようにすると一定スピード感は落ちますが、データマネジメントチームが管理する対象を明らかにすることで運用をしやすくするメリットのほうが大きいと判断しました。

dbtのコードをGitHubで管理しているため、新しいテーブルを定義する際には必ずコードレビューが入り、品質が担保される仕組みになっています。

また、コミットログが残るため、誰によっていつどのような変更が行われたかは明白です。

dbt活用の工夫

初めて利用する際には dbt公式のチュートリアルから読み始めました。

docs.getdbt.com

また、BigQuery向けのチュートリアルとして、以下の記事も非常に参考になりました。

zenn.dev

これらを参考にしつつ、実際に運用する上で工夫した点やTipsについてご紹介したいと思います。

ディレクトリ構成

dbt公式ドキュメントのHow we structure our dbt projectsを参考にしつつ、自分たちに合わせて一部改変して構成しています。

├── dbt_project.yml
├── macros
└── models
    ├── source
    ├── warehouses
    └── marts
  • source … ソースモデルとステージングモデル(データモデリングの最小単位)の定義ファイルを格納
  • warehouses … warehouse層に相当するテーブルの定義ファイルを格納
  • marts … mart層に相当するテーブル定義ファイルを格納

dbt_project.ymlディレクトリごとにモデルのconfigを一括定義できるので、カテゴリごとにディレクトリを分けておくと管理が容易になります。

How we structure our dbt projectsについては翻訳、要約してくださっている以下の記事も参考にさせていただきました。 zenn.dev

BigQueryにおけるカスタムスキーマの活用

dbt cloudはデフォルトで、Environment (dbtの実行環境設定) で定義されたデータセットに全てのテーブルを保存します。(後述の target_schema に相当)

しかしながら、テーブルごとに出力先データセットを制御したいケースは多々あります。

カスタムスキーマを利用すると、モデルごとに保存先データセットを変更することが可能になります。デフォルトでは次の表のように、 <target_schema>_<custom_schema> ように _ で連結されたデータセット名になります。

Target schema Custom schema Resulting schema
<target_schema> None <target_schema>
analytics None analytics
dbt_alice None dbt_alice
<target_schema> <custom_schema> <target_schema>_<custom_schema>
analytics marketing analytics_marketing
dbt_alice marketing dbt_alice_marketing

https://docs.getdbt.com/docs/building-a-dbt-project/building-models/using-custom-schemas

実装時はカスタムスキーマschema (https://docs.getdbt.com/reference/resource-configs/schema)というconfigで定義します。

models:
  jaffle_shop: # the name of a project
    marketing:
      +schema: marketing

しかしながら、これはあくまで target_schema は固定されたまま custom_schema がsuffixになる仕様なので、 custom_schema 自体をデータセット名にしたい場合はうまくいきません。

少し発展的ですが、 generate_schema_name というmacroを書き換えることで、 custom_schema が与えられたときのデータセット名生成ルールを変更することが可能です。

例えば、以下のようなコードを macros/get_custom_schema.sql として配置します。

{% macro generate_schema_name(custom_schema_name, node) -%}
    {{ generate_schema_name_for_env(custom_schema_name, node) }}
{%- endmacro %}

https://docs.getdbt.com/docs/building-a-dbt-project/building-models/using-custom-schemas#advanced-custom-schema-configuration

generate_schema_name_for_env はdbt側が提供している関数で、これを用いることで、 prod 環境のときは custom_schema_name で与えられたデータセット名になり、それ以外の環境では custom_schema_name は無視されるようにすることができます。

私たちはこれを利用して出力データセットの設定を行なうようにしています。

Aliasを活用してテーブル名を変更する

dbtはデフォルトではファイル名がテーブル名になります。 alias configを用いることで、ファイル名に依存せずに保存するテーブル名を自由に設定することができます。

Model Config Database Identifier
ga_sessions.sql \<None> "analytics"."ga_sessions"
ga_sessions.sql {{ config(alias='sessions') }} "analytics"."sessions"

もちろんカスタムスキーマと併用することも可能です。

これにより任意のデータセットに任意のテーブル名で保存することが可能になります。

-- This model will be created in the database with the identifier `sessions`
-- Note that in this example, `alias` is used along with a custom schema
{{ config(alias='sessions', schema='google_analytics') }}

select * from ...

https://docs.getdbt.com/docs/building-a-dbt-project/building-models/using-custom-aliases

BigQueryのコンソール上でdescriptionを参照できるようにする

dbtは定義ファイルにモデルのdescriptionを記述しておくことで、dbt上で閲覧できるドキュメントを自動生成してくれます。

とはいえ、分析者目線では外部のドキュメントを参照しに行かずともBigQuery上でdescriptionが参照できたほうが便利ですよね。

dbtでは persist_docs configを有効にすることで実現できます。

例えば dbt_project.yml に以下のようにconfigを追加します。

models:
  +persist_docs:
    relation: true
    columns: true

https://docs.getdbt.com/reference/resource-configs/persist_docs

relationtrue にすると、テーブルのdescriptionが反映され、 columnstrue にするとカラムのdescriptionが反映されます。

テーブルのdescriptionとカラムのdescriptionを反映した例

Materializationの活用

dbtにはmaterializationという、定義したモデルをData Warehouseに保存する際のテーブル作成方法が設定できます。BigQueryの場合はデフォルトでビューとして保存されます。

こちらはmaterialized というconfigで設定が可能です。

models:
  jaffle_shop:
    +materialized: table
    example:
      +materialized: view

https://docs.getdbt.com/docs/building-a-dbt-project/building-models/materializations

  • View: ビューとして保存する
  • Table: 実体テーブルとして保存する
  • Incremental: 前回の dbt run からの差分更新を行う
  • Ephemeral: モデル定義はするがデータウェアハウスに反映したくない場合に利用

基本的にはViewで構成していますが、ユーザー行動データのようなストリーミングで日々増えていくデータに対応するwarehouse層のテーブルを定義する際にはincremental modelを活用しています。こちらの詳細は別の記事でご紹介できればと思います。

さいごに

dbtを導入し、開発・運用のフローを整備することでチームでのデータマネジメントは格段にやりやすくなりました。

私たちのようなスタートアップでは特に、データマネジメントに大きなリソースを割くことは難しいです。だからこそ早い段階から小規模チームでも運用可能な体制を整備することは、中長期的には大きな一歩になると考えています。

実際に使ってみた感想としては、dbtは多機能ですが銀の弾丸ではなく、利用者側の工夫や学習がある程度必要なものであると感じました。

しかしながら、dbtを導入することはただ管理がしやすくなる以上に、チームで共通のツールを採用し、プロセスを統一化できることに大きな価値があると思います。

実際、dbtを導入してからは、dbtを軸にデータマネジメントの議論が盛んに行われるようになりました。 この点において、dbtを導入するという意思決定は非常によかったと感じています。

High Linkではデータ基盤の整備やデータマネジメントを通して、一緒に全社的なデータ利活用レベルを引き上げていけるエンジニアを募集しております!

https://herp.careers/v1/highlink/RIGo8XhyyCPQ?utm_source=hatena&utm_medium=blog&utm_campaign=dbt-data-managementherp.careers