日曜日, 9月 14, 2025
日曜日, 9月 14, 2025
- Advertisment -
ホームニューステックニュースナレッジワークにおけるデータ基盤の構成 - 2025/09

ナレッジワークにおけるデータ基盤の構成 – 2025/09


はじめに

ナレッジワークでエンジニアをしている @_sisisin です
2025/04 にデータ基盤チームへ移ってきて初めてのデータ基盤屋さんをやっています
主に基盤の整備やデータ活用の支援をしたりしています

この記事ではナレッジワークでのデータ基盤の現状を社内のメンバーへ共有する目的で書きました
内容的に公開できそうなので、Zenn に公開する形にしました

ということで、この 2025/09 時点のデータ基盤の構成とその思想・経緯を残しておきます

前提の説明 – ナレッジワークのサービスについて

ナレッジワークのサービス基盤について

ナレッジワークでは主に Google Cloud 上にサービス基盤を構築しています

余談ですが、Poetics社とのM&Aからシステム統合を進めているJamRollというサービスがAWS上にあります
こちらにもデータ活用のための基盤があるのですが、記事執筆時点で引き継いだばかりで何も手がつけられていないので ナレッジワークのサービス基盤に関連する部分に絞って説明します

データ基盤に関連する部分としては、以下のようなリソースを利用しています

  • CloudSQL (PostgreSQL)
  • BigQuery

また、開発環境から本番環境まで dev,qa,stg,prd の 4 つの環境を利用して開発・運用を行っています

インフラのコード管理には Terraform を利用しています

データ基盤が関わるナレッジワークの機能について

ナレッジワークは利用状況などの分析レポート出力機能を提供しています
この分析レポート出力機能ではデータ基盤から作成したデータを利用して出力を行っています

データ基盤の設計にはこれも加味する必要があります

データ基盤の全体像

サービス基盤が Google Cloud なので、データ基盤も同様に Google Cloud & BigQuery を中心に構築しています
データの抽出・変換・集計には dbt を利用しており、Cloud Run Job で実行しています

大まかに以下のような構成を取っています

論理レイヤーとして、 Data Sources , Data Lake , Data Warehouse , Data Marts , Data Consumers の 5 つに分けて表現しています

レイヤー 説明
Data Sources 元データが存在する場所。CloudSQL や GoogleAnalytics, スプレッドシートなど。
Data Lake Data Source から取得したデータを格納する場所。なるべく元の構造を保持する。ここから先は全て BigQuery でデータを扱う。
Data Warehouse 分析用に整形されたデータを格納する場所。dbt の Staging, Intermediate model が該当。
Data Marts 特定のビジネスユニットや目的に特化したデータを格納する場所。dbtのMart modelが該当。
Data Consumers データを利用するユーザーやシステム。BI ツールや分析ツールなど。先述の通り、ナレッジワークのサービスも含まれる。

また、物理インフラとして knowledgework-{env} , kwdp-elt , kwdp-mart の 3 つの Google Cloud Project が登場します

プロジェクト名 説明
knowledgework-{env} サービス基盤のプロジェクト。サービスの Data Lake もこちらに配置する
kwdp-elt データ基盤のコアプロジェクト。主に社内システムを Data Source とするデータの Data Lake 及び Data Warehouse を担う
kwdp-mart データ基盤のデータマート用プロジェクト。データ利用者向けの Data Marts を担う。BI ツールのホスティング等もここ。

全体の流れとして、以下のようになっています

  1. サービス基盤を含む各種データソースを何らかの手段で BigQuery へ取り込めるようにする( Data Sources ~ Data Lake レイヤー)
  2. BigQuery に取り込んだデータを dbt で変換・集計して分析に適した形にしてデータマートへ出力する( Data Lake ~ Data Warehouse ~ Data Marts レイヤー)
  3. 出力したデータを各種 BI ツールや分析ツールで参照・活用する( Data Marts ~ Data Consumers レイヤー)

以下、データの流れに沿って、各レイヤーについて詳しく説明していきます

Data Sources ~ Data Lake

  1. サービス基盤を含む各種データソースを何らかの手段で BigQuery へ取り込めるようにする( Data Sources ~ Data Lake レイヤー)

ということで、元データを BigQuery へ取り込む部分です

Data Source はサービス由来のものと、社内システム由来のもので大きく 2 系統に分かれます

サービス由来のデータ

サービス基盤が各環境分あるため、環境ごとに扱えるようにする必要があります
そのため、サービス用の knowledgework-{env} プロジェクトに Data Lake レイヤーの BigQuery Dataset を配置する、という構成を取っています

データソースとして主に、CloudSQL, GoogleAnalytics, CloudLogging を扱っています

CloudSQL

Google Cloud の Datastream を利用して、CloudSQL(PostgreSQL)のデータを BigQuery へストリーミングで取り込んでいます
ナレッジワークでは複数のバックエンドサービスが動いており、PostgreSQL の database,schema も新規サービスが立ち上がるタイミングで作られることが多いです
そのため、これらのリソースは Terraform module 化しており、PostgreSQL リソースを追加するタイミングで 一緒に Datastream,BigQuery などの関連リソースが作成されるようにしています

GoogleAnalytics,CloudLogging
  • GoogleAnalytics: BigQuery Export 機能を利用して BigQuery へエクスポートしています
  • CloudLogging: Log Sink 機能を利用して LogBucket へ出し、BigQuery の Linked Dataset 機能を利用して BigQuery 上で参照できるようにしています

社内システム由来のデータ

これは物によって様々なので、ほぼ個別に対応しています
現状マネージドサービスは使ってません(そもそも検討も出来てないですが・・・)

一例として GitHub のデータを紹介しますと、以下のような作りにしています

  1. GitHub の Webhook でイベントを Cloud Run で受けて Request Body をそのまま PubSub へ流す
  2. BigQuery Subscription で PubSub のメッセージを BigQuery へ取り込む

dora-team/fourkeys のリポジトリと近い構成になっていますね

https://github.com/dora-team/fourkeys

Request Body をそのまま PubSub へ流し、BigQuery 上で JSON 型の列に入れています。こうしておくことでスキーマの変更への追従をしないでも全てのデータが常に取れるようにしています
どのデータソースに対しても同じアプローチをしており、基本的に JSON 列に未加工でデータを入れるようにしています

最近はこういうのが生成 AI で簡単に作れてしまうので、今のところは割とカジュアルにデータ取り込みを作り込んでいく方向でやっています

Data Lake ~ Data Warehouse ~ Data Mart

  1. BigQuery に取り込んだデータを dbt で変換・集計して分析に適した形にしてデータマートへ出力する( Data Lake ~ Data Warehouse ~ Data Marts レイヤー)

ここからは dbt の世界になります
dbt の詳細な設定やモデリングの話よりは、どちらかというとインフラリソースの説明とデータの流れを中心に解説していきます

主なdbtを動かすリソース構成

dbtはCloud Scheduler と Cloud Workflows でワークフローを定義し、その中で Cloud Run Job を起動して実行しています

さて、サービスのプロジェクトが 4 環境あるのに対して、データ基盤用のプロジェクトは 1 つにしています
ではどのようにして各環境のデータの transform をしているかというと、 kwdp-elt プロジェクト内で 4 環境に対応した Cloud Run Job をそれぞれ動かす構成としています


4環境向けのCloud Run Jobの図

Docker Imageは共通で、Dockerfileで以下のような dbt コマンドをエントリポイントとしたものを利用します



ENTRYPOINT ["/app/.venv/bin/dbt"]

これらのCloud Run Jobはmainブランチへのマージドリガーで一律デプロイされるようにしており、変更を素早く反映できるようにしています
サービスの機能に利用しているアウトプットもあるのにマージトリガーでいいのかという論点はありますが、サービス側の作りで一定リスク回避ができるような仕組みになっているのでOKとしています

こぼれ話 – 元々はサービス基盤の方にdbt Cloud Run Jobを作っていた

元々は サービス基盤のある knowledgework-{env} の方でdbtのJobを構築していました
ナレッジワークはモノレポなので、デプロイの仕組みなどがある程度ある・利用が限定されていたなどの理由からそうなっていたようです

しかし、実際に運用していると以下のような課題が出てきていました

  • dbtのデプロイのライフサイクル・仕組みの問題: サービス向けに構築されたデプロイパイプラインを流用していたことに起因して若干歪な設定になっていたり、prd環境まですぐにデプロイして良いのにデプロイトリガーがサービス準拠なので手間が多い
  • Terraformのデプロイライフサイクルがデータ基盤の開発サイクルと噛み合わない: 例えば本番用のdbt Jobへ追加の権限を付与する、といった場合にもサービス向けのTerraformのデプロイを待つ必要がある
  • 社内システムのデータなどの扱い: GitHubのデータなどをサービス基盤上で扱うのは直感に反するし、セキュリティ要件が全く異なる中での権限制御がやりにくい
    • そうやって kwdp-elt プロジェクトが最初に生まれ、そちらにもdbtを作成した。が、そうすると今度はdbtのメンテナンスがサービス基盤とデータ基盤で2重管理になる

これらの課題を解消するために、 kwdp-elt プロジェクトへ1本化した、という経緯があります

dbtを利用したデータの流れ

さて、dbt projectで各環境向けにprofiles.ymlでtargetを定義しておいて、 dbt-kw-{env} JobをWorkflowから起動する時に DBT_TARGET 環境変数を与えて挙動を環境ごとに変えています

具体的には以下のようなリソースが環境別に切り替わります

  • sources.ymlに定義されるdbt Source: knowledgework-{env}.* (prdのみ kwdp-elt.* も含める)
  • staging,intermediate modelの出力先dataset: kwdp-elt.dbt_kw_{env}.* (prd のみ kwdp-elt.dbt)
  • mart modelの出力先dataset:
    • for サービス: knowledgework-{env}.dbt_marts_*
    • for 社内利用: kwdp-mart.dbt_*

dbtの中間データ(staging, intermediate model)も kwdp-elt プロジェクト内に dbt_kw_{env} (prdのみ単に dbt)datasetを定義しています
中間データについては外部から触れられないようにしており、基本的にdbtのモデル開発者にのみ閲覧権限を付与しています

dbtの最終出力データ(mart model)は、用途に応じて knowledgework-{env}.dbt_marts_* dataset か kwdp-mart.dbt_* dataset に出力しています

実際のmodelの例を挙げて、どのようにデータが流れているかを説明します

例1: ナレッジワークの分析レポート出力機能向けの出力

以下の図は分析レポート出力用に定義された usage_analysis_logged_in_data のmart modelのリネージグラフです

このモデルを target: dev で実行した場合

  • まず Source として knowledgework-dev プロジェクト上にサービスのPostgreSQLからレプリケーションしている knowledgework-public datasetの activities , group_users , groups , users テーブルを参照
  • staging レイヤーとして kwdp-elt.dbt_kw_dev.stg_users といったstaging modelを作成
  • intermediate レイヤーとして kwdp-elt.dbt_kw_dev.int_usage_analysis_logged_in_data intermediate modelを作成
  • 最終出力として knowledgework-dev.dbt_marts_usage_analysis.usage_analysis_logged_in_data dataset へ出力

というような参照・出力になります

例2: 開発生産性の可視化用のGitHub集計データ出力

以下の図は社内で生産性の可視化に利用しているGitHubのデータを集計した engneering_merged_pull_requests のmart modelのリネージグラフです

このモデルは、データソースがGitHub Webhook経由で取り込んだテーブルとなっていて環境が1つしかないため、 target: prd でのみ実行されるように制御しています
さて、実際に動かすと

  • Sourceとして kwdp-elt.github.events_raw を参照
  • baseレイヤーとして、一度 kwdp-elt.dbt.base_github__events modelを作成
  • stagingレイヤーとして、イベント種別ごとに kwdp-elt.dbt.stg_github__pull_request_events などのstaging modelを作成
  • intermediateレイヤーとして、 kwdp-elt.dbt.int_pull_requests intermediate modelを作成
  • 最終出力として kwdp-mart.dbt_engneering.engrneering_merged_pull_requests dataset へ出力

といった流れになります
ここでは Sourceが kwdp-elt プロジェクトにあるため、サービス用のプロジェクトは参照されていません

Data Lake から Data Martまでの流れのまとめ

事前にData LakeレイヤーとしてBigQueryへ集約したデータを元に、dbtを利用して集計結果をData Martレイヤーとして出力するまでの流れを見てきました

ざっくりまとめると、 kwdp-elt プロジェクトで4環境分のdbtのCloud Run Jobがそれぞれいい感じに動いて環境に応じた出力先へ出力している、という感じです

Data Marts ~ Data Consumers

  1. 出力したデータを各種 BI ツールや分析ツールで参照・活用する( Data Marts ~ Data Consumers レイヤー)

dbtでデータを出すまで追ってきました
データ基盤の終着点、データ利用側の話をします

サービス向けの出力・社内利用向けの出力でそれぞれ大きく異なるので分けて説明します

サービス向けの出力

dbtを利用したデータの流れ にて、mart modelの出力先は knowledgework-{env}.dbt_marts_* だと述べました
実際には2025/09現在は分析レポート出力機能向けに dbt_marts_usage_analysis dataset へのみ出力しています

これはもし追加で参照したいという話になった場合には別のdatasetを作成する、という思想での命名となっています
この辺は「ユースケースに応じたData Martを作成する」という原則に則っている形です
こうすることで、機能を提供するサーバーごとに閲覧権限を与えるように出来るし、参照するべきテーブルの役割も明確になります

こぼれ話 – レポート機能のサービスレベル

お客様向けの機能としてレポート出力できるようにしているため、もちろん当日集計が失敗したらインシデントとなります
ではどのぐらいの時間までに終わっていればいいのか?という話ですが、これもPdMと話し合って決めて運用しています

論点は様々あったのですが、最終的にはdailyで実行しているdbtジョブが正午までに集計が終わっていない場合はインシデント扱いとする、という話に着地しました

さらにこぼれ話をすると、Google Analyticsのデータが日本時間で午前9時をまたがないと前日分のデータが入らない(!)(UTC 0:00起点なんやろなあ)ため、dbtジョブは午前9時台で実行しています
いざ失敗したときに復旧までの時間が短くて緊張感がありますね

社内利用向けの出力

社内利用向けの出力は kwdp-mart.dbt_* dataset へ出力しています
mart datasetはなるべく細かく分けるようにしています
これは、利用者・用途・オーナーシップを明確にするためです
誰がどこから参照しているかわからない状態になると運用面で問題が起きることが多いため、なるべく細かく分けるようにしています

datasetは以下のような tf で定義しており、データ利用したいオーナーシップを持つチームごとに必要なだけ権限を払い出す、という構成が自然と出来るようにしています

locals {
  
  mart_datasets_config = {
    platform_and_sre = {
      dataset_id_suffix = "platform_and_sre"
      friendly_name     = "mart for Platform and SRE analytics"
      description       = "Platform and SRE領域向け分析用データマート"
      viewers = concat(
        module.const.users.data_developer_members,  
        module.const.users.data_user_members
      )
    }
  }
}

resource "google_bigquery_dataset" "mart_datasets" {
  for_each = local.mart_datasets_config

  project       = var.project_id
  dataset_id    = "dbt_${each.value.dataset_id_suffix}"
  friendly_name = each.value.friendly_name
  description   = each.value.description
  location      = var.region
}

閲覧は主にlightdash,redashから行っています
redashが昔から利用されていたのですが、いくつか課題があるために新規ではlightdashの利用をするようにしています

こぼれ話 – redashの課題

歴史的経緯で間違ったクエリがあったり、ロジックが散逸していたり、といったよくある話もありますが、それよりも苦しいのが以下の点です

redashはログインユーザーの権限でBigQueryへクエリが出来ない
(まあlightdashでこれが出来るようになったのも最近なのですが)

ここまでの説明では特に言及してきませんでしたが、BigQueryのポリシータグを利用したデータのアクセス制御も細かく運用しています
お客様の個人情報は当然自由に見れてはいけませんよね、というやつです

さてredashの話ですが、redashでBigQueryへクエリを実行する場合、redashの設定でサービスアカウントを設定し、その権限でクエリが実行されます
つまり、限定的に権限を払い出すといったことが非常にやりにくくなっています

例えばお客様の問い合わせ対応で特定の開発者にのみ個人情報閲覧の権限を払い出したい、みたいなことがやりにくいですね
redash上の個人情報閲覧用グループ、といったものを作ってそこに所属させて・・・みたいなことはやってはいるのですが・・・

lightdashでは、Google Loginをしていれば、そのユーザーの権限でBigQueryへクエリ実行が出来るため、権限制御が非常にやりやすくなっています

dbtとの親和性の話などもあるのですが、こういった事情があってlightdashを新規では利用する、という方針で進んでいます

まとめ

ナレッジワークの現在のデータ基盤について、データの論理・物理レイヤーを示し、データの流れを追っていく形で説明をしました

ホントはdbtの話とか権限制御とか書こうと思ってたのですが、データの流れを追うだけでかなりの分量になってしまったので割愛しました
そのへんは追々書いていきたいな〜という気持ちです(気持ちだけ)

ここまで読んでいただき、ありがとうございました


KNOWLEDGE WORK Blog Sprint、明日9/15の執筆者はバックエンドエンジニアのnasumさんになります



Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -