🔖

アプリケーションDBのメタデータをデータ基盤に自動で伝播させる仕組みを構築しました

に公開

はじめに

ナレッジワークでエンジニアをしている @_sisisin です。

前回の記事「ナレッジワークにおけるデータ基盤の構成 - 2025/09」でデータ基盤の全体構成について説明しました。
その中で、BigQueryのポリシータグを利用したアクセス制御について軽く触れましたが、今回はそのポリシータグを含むメタデータを自動伝播する仕組みについて詳しく説明します。

背景と課題

ナレッジワークはエンタープライズ企業を主たるターゲットとして開発・営業を行っているため、高いセキュリティレベルを求められています。
会社としてもISMSやプライバシー認証を取得するなどの活動をしております。
データ基盤もその例に漏れず、顧客の個人情報などが理由なく閲覧できないような制御をする必要があります。

さて、ナレッジワークではデータ基盤にBigQueryを利用しているため、ポリシータグという機能を利用して列ごとに閲覧制御をかけています。
このポリシータグが手動での付与によって運用されていたのですが、当然手動での運用には限界があります。

そこで、今回、アプリケーションDB(PostgreSQL)からdbt mart層まで、メタデータとポリシータグを自動伝播させることで、開発者が一度定義すればデータ基盤全体で活用できるようにすることを目標として自動化をしました。

システム概要

対象となるメタデータ

以下の2種類のメタデータを伝播させています:

  • テーブル・カラムのコメント
  • カラムごとの閲覧ポリシー(BigQueryポリシータグ)

メタデータの伝播フロー

前回の記事で説明した通り、ナレッジワークのデータ基盤では以下のような流れでデータが処理されています

PostgreSQL (CloudSQL)
  ↓ Datastreamによるレプリケーション
BigQuery Data Lake
  ↓ dbt
BigQuery Data Marts

そして、このデータの流れの上でメタデータは以下のような流れで伝播させるようにしました

  1. PostgreSQL 上でメタデータ定義
  2. PostgreSQLからメタデータ抽出・Data Lake層のBigQueryへ同期
  3. Data Lake層のBigQueryのメタデータを元にdbt sources.yml自動生成
  4. sources.yml から staging modelへ反映
  5. dbt staging → martへ伝播

順を追って実装の詳細を説明していきます

1. PostgreSQL 上でメタデータ定義

PostgreSQLではCOMMENT ON句を利用してテーブルや列のコメントを定義できます。
このコメントの中に、ポリシータグの情報をJSON形式で埋め込む形でメタデータを定義出来るようにしました。

comment on column users.name is 'ユーザーの名前
%kw_meta_v1%{
  "mask_policy": "pii"
}';

%kw_meta_v1% という文字列からコメントの末尾までにJSONを書くようなフォーマットにし、JSONの中に列へのメタ情報を与えられるようにしています
執筆時点の実装では mask_policy のみが定義可能で、これはBigQueryのポリシータグに対応させています。
mask_policynone という値も定義可能としており、「マスク不要」であることを明示できるようにしています。

このコメントを書き損ねたら意味がないので、CI上でマイグレーションファイルに対して テキスト系の型の列にはメタデータコメントを必須化する というチェック処理を入れて、開発時に漏れがないようにしています。

2. PostgreSQLからメタデータ抽出・Data Lake層のBigQueryへ同期

アプリケーション側のリポジトリでGitHub Actionsを使って、定期的にメタデータをBigQueryへ同期させます。

具体的には以下の処理を行います。

  1. CI上でDB Migrationを実行して、メタデータを登録したDBをCI上に構築
  2. DBへ接続してメタデータを吸い出し
  3. メタデータをBigQueryのテーブルへ反映

2,3は同期スクリプト内で処理されています。
2はINFORMATION_SCHEMAを利用してコメントを吸い出し、3では BigQuery SDKを利用してテーブル・列のコメントとポリシータグを更新しています。

3 〜 5 BigQueryのメタデータをdbt mart層まで伝播

ここからはデータ基盤プロジェクトのリポジトリの範囲になります
以下の3つのステップをまとめて1つのGitHub Actionsで実装しています

3. Data Lake層のBigQueryのメタデータを元にdbt sources.yml自動生成
4. sources.yml から staging modelへ反映
5. dbt staging → martへ伝播
  • Data Lake層のBigQueryのメタデータを元にdbt sources.yml自動生成
    • 前の手順にてメタデータが同期されたBigQueryのテーブルを読み、dbt用のsources.ymlをメタデータ込みで自動生成します。
    • アプリケーションとデータ基盤は別リポジトリになっているので、BigQueryを介してメタデータをやりとりしています。
  • sources.yml から staging modelへ反映
    • dbt 上で source macro を利用しているモデル(staging model)を特定し、該当モデルのyamlへメタデータを移植します。
  • dbt staging → martへ伝播
    • dbt-osmosisを利用してstaging modelから各dbt modelへのメタデータを伝播します。

BigQuery→sources.yml→staging modelの部分は自前で実装しています。

BigQuery→sources.ymlについて

dbt-osmosisはsources.ymlを部分的に生成する機能があるのですが、新規に対象テーブルが出来たときに増やしてくれたり、ポリシータグを定義したりはしてくれません。
そのため、BigQueryの対象となるデータセットを参照して対応するsources.ymlを完全に自動生成する処理を実装しています。

sources.yml→staging modelについて

これまたdbt-osmosisがBigQueryのポリシータグをsources.ymlからstaging modelへ伝播してくれません。
なのでこちらもまた自前で実装する判断を取っています

実現方法としては、 dbt compileで吐き出したmanifest.jsonを利用してsource→stagingの繋がりを特定し、同名カラムマッピングでポリシータグを伝播しています。[1]

ポリシータグが絡む列(≒text系の列)では列のリネームが発生しにくい[2] ため、単に同名の列への伝播というシンプルなロジックで十分と判断しました。

設計時に検討した他の選択肢

メタデータ定義について

PostgreSQLのCOMMENT ON句を利用する以外に、外部ファイルとして定義して同期する方法も検討しました

外部ファイル利用のメリットとして以下のような点が挙がります

  • エディタ・CIフレンドリー
  • メタコメントの一覧性が高い(ファイルを見ればよいため)
  • Dataplexのようなカタログツール用のyamlとして直接記述する、といった事までやるとメタデータを管理する上で便利

一方で、デメリットもそれなりにありました

  • 例えばテーブル追加したときにファイルが適切に更新されているかを保証するのが相応に手間
    • DBの状態を見たうえでファイルと比較して・・・みたいな処理が必要となる。
  • 段階的に導入するのが手間
    • ファイルの状態を保証するCIを入れるとして、既存のテーブルに対してのケアが求められる一手間がある
  • 自動生成・バリデーションといった、開発体験を向上する仕組みを手元で動かすのにローカルでDBを立てる必要がある
    • 上記の保証する仕組みを流用して自動生成を作るとして、それらを動かすのにはDBを立てる必要がある
    • ナレッジワークでは割と手元でサービスを動かさずにdev環境にデプロイして動かすスタイルで開発する人がいるので、そういった人にとってはこの手間は割と大きなオーバーヘッドになってしまう
  • テーブルの状態と同期させるのがCOMMENTに比べて面倒
    • COMMENTはテーブル・列が消えたら一緒に消えてくれる

COMMENT ON句を利用する場合、デメリットはJSONをフォーマッタなどが効かない環境で書かされるぐらいで済みます
メリットとして、テーブル・列追加に対してシームレスにpiiかどうかを考えれば良い(コードの位置が離れていない)といった開発体験の良さもあって、であればCOMMENT ON句を利用する方が良いかなと思いました

BigQueryを介したメタデータ伝播について

アプリケーションのリポジトリからデータ基盤のリポジトリへメタデータを伝播させる方法として、アプリのリポジトリのGitHub Actionsから直接データ基盤のリポジトリを操作する方法もありえるかなと思いました

ここはコード・リポジトリで直接的に依存させるよりは、BigQueryという共通のデータストアを介して疎結合にした方が良いと考えました
アプリケーション側はメタデータ込みでDataLakeレイヤーにデータを出す、という役割
データ基盤は受け取ったメタデータを適切に扱う役割

という役割分担で考えるのがシンプルだろう、という判断です

sources.ymlからのメタデータの伝播について

先に少し触れたように、dbt-osmosisはsources.ymlを生成する機能があります
であれば、dbt-osmosisへコントリビュートして機能を充実させることで問題を解決するアプローチもありえましたが、これは見送りました

理由として、2025年8月(設計を検討していた時期)時点で以下のような状況があったためです

  • dbt-osmosisの最新版(v1.1.17)のリリース日が2025/4/24
  • 最終リリースから2025/7/8にかけて改修が入っていたが、リリースされていない
  • 最終commitも7/8を最後に止まっている

forkするにしても最新commitをベースにすると動く保証があるか怪しいレベルで大きな改修が入っているし、逆にv1.1.17をベースにするのは地獄・・・
という状況
逆にsources.yml -> staging modelだけ実現できれば良いのであれば大きな仕様はいらないはずなので自作に振るほうがコスパいいだろう、という判断でした

それにsources.ymlのsourceの完全な自動生成もdbt-osmosisの機能として落とし込むのは難しい・自作するほうが取り回しやすい、というのもありました
(例えばどのデータセットのテーブルを自動生成対象にするのか、みたいな話になるが、そもそもdbt-osmosisはsources.ymlの生成はオマケ機能っぽい立ち位置に見えるので、大局的に見て設計に落とし込むのにディスカッションの必要な機能になるんじゃないか、という感じのことを考えました)

運用してみての所感と今後の課題

概ね意図通りに運用できており、問題なく回せています。
とりあえず目下のデータ閲覧ポリシー面で満たしたい要求は一定満たせたので、その点では良かったかなと思います。

残る課題として、dbt上でのカラムリネームにdbt-osmosisが対応できていないので完全に自動化しきれていない・人手によるチェックが必要、という点があります。
一応dbt Fusionがカラムリネージに対応しているようなので、これを外から利用できるのであれば解決できるのではないか、と期待しています。
・・・が、dbt-osmosisがdbt-core 1.10(≒dbt Fusion)に対応していないため、dbt Fusionへバージョンアップするのには先にdbt-osmosisをなんとかしなきゃいけないというジレンマに陥っています。(キツイ)
dbt model同士のメタデータ伝播をdbt-osmosisに頼っている以上は、dbt-osmosisのバージョンアップを待つか自力でforkして対応するかはしないといけなさそうでここは頭が痛いところですね・・・
将来どう対応していくかは今のところノーアイデアです。難しいですね。

とまあなんだか締まらない感じですが、今回は以上です。
ここまでお読みいただきありがとうございました。

脚注
  1. より正確には、「リネージ処理を実装しようとしたが、完全に動かすのは難しいので限定的な実装となった」というのが実情です。 ↩︎

  2. staging modelの列のリネームの典型例はこちら https://docs.getdbt.com/best-practices/how-we-structure/2-staging#staging-models を参照のこと ↩︎

GitHubで編集を提案
株式会社ナレッジワーク

Discussion