メインコンテンツへスキップ
見出し画像

dbtとCubeで構築する本番環境から独立した検証環境

    株式会社movでエンジニアをしている小野です。

    弊社のデータ基盤では、BigQueryとdbtを使ってデータマートを構築し、Cubeをセマンティックレイヤーとして利用しています。

    最近、弊社が提供する「口コミコム」でも、分析機能のデータ取得にCubeを利用するようになりました。

    しかし、当時は本番DB由来のデータマートとCubeしかなく、staging環境に登録した企業や店舗のデータを使った動作確認ができませんでした。

    そこで今回、dbtのモデルとCubeの定義を共通化しながら、データの参照先と実行環境を本番から分離する構成を整備しました。

    この記事では、その設計と実装、構築後に発生した問題について紹介します。

    staging環境用の検証環境が欲しい

    口コミコムでは、分析指標の定義や集計ロジックを一元管理するため、BigQueryのデータマートとCubeを利用する取り組みを進めています。

    その中で、本番環境の社内向けデモ画面の一部で、データが正しく表示されない事象が発生しました。

    通常はリリース前にstaging環境で動作確認していますが、今回はstaging専用のデータマートとCubeがありませんでした。
    stagingから本番向けのCubeを利用しても本番のデータマートを参照するため、stagingに登録した企業や店舗のデータを使った検証ができなかったのです。

    この反省を活かし、次回からリリース前に本番と同じ仕組みで動作確認できるよう、staging専用のデータマートとCubeを構築することにしました。

    本番とstagingのデータフローを分離する

    まずは、どのように環境を分離するかを検討しました。

    本番とstagingのデータを1つのデータマートに保存し、環境を表す列で区別する方法も考えられます。

    画像
    env列で環境を表現する案

    しかし、この方法では各クエリで環境を絞り込む必要があります。フィルタを付け忘れたり、環境間でIDが重複したりすると、意図しないデータを取得する可能性があります。

    今回は、データマートを環境ごとに分け、Cubeも別々のDeploymentを利用する構成にしました。

    画像
    採用した専用データマート/Cubeを作る案

    dbtモデルは共通化したい

    dbtでは、データマートを生成するためのSQLを「モデル」として管理します。

    本番とstagingで同じデータマートを生成する場合、環境ごとにSQLを複製すると、次の問題が発生します。

    • 保守コスト増加:SQLの修正が両方の環境で必要になる

    • 定義の不整合:片方だけ修正されると、集計ロジックが異なってしまう

    • 検証の信頼性低下:stagingで確認した処理が、本番と同じとは限らなくなる

    そこで、SQLは共通化し、データの参照先だけを環境によって切り替えることにしました。

    dbtでは、テーブル名をSQLに直接記述する代わりに、source()で参照先を指定できます。

    例えば、口コミ数を集計するモデルなら、次のように記述できます。

    SELECT
      company_id,
      COUNT(*) AS review_count
    FROM {{ source('app', 'reviews') }}
    GROUP BY company_id

    source('app', 'reviews')は、dbtで定義したデータの参照先を表します。

    実際に参照するテーブルは設定によって切り替えられるため、モデルのSQLを変更せずに、本番とstagingで同じ集計処理を利用できます。

    staging専用のデータマートとCubeを構築する

    dbtでstagingデータマートを生成

    dbtのtargetに応じて、元データの参照先とデータマートの書き込み先を切り替えるようにしました。

    例えば、元データの参照先は次のように定義できます。

    schema: "{{ 'staging_raw' if target.name == 'stg' else 'production_raw' }}"

    書き込み先は、profiles.ymlで環境ごとに設定します(抜粋)。

    outputs:
      prod:
        dataset: production_mart
      stg:
        dataset: staging_mart

    これにより、--target stgを指定するだけで、stagingの元データからstaging専用のデータマートを生成できます。SQL自体は本番と共通です。

    データマートは次の2つの方法で更新します。

    • 定期更新:Cloud Composerで週1回、stagingの元データを反映する

    • モデル変更時:dbt Cloudで、モデルの変更がmainにマージされた際に、影響するモデルをbuildする

    Cube CloudにShared Deploymentを追加

    本番用とは別に、staging専用のCube Deploymentを作成しました。データの接続先やデプロイを環境ごとに分離し、本番に影響を与えずに検証できるようにするためです。なお、口コミコムのqa環境も、このstaging用のDeploymentを共用しています。

    本番ではDedicated Deploymentを利用していますが、stagingは利用頻度が低いため、低コストなShared Deploymentを選びました。

    料金の目安(執筆時点、Starterプランの場合)は、Dedicatedが約0.60ドル/時間、Sharedが約0.15ドル/時間です。さらにSharedは30分間アクセスがないと自動停止するため、稼働時間も抑えられます。

    Cubeの定義は本番と共通にし、独自に用意した環境変数CUBEJS_DBT_TARGET_SCHEMAの値によって、参照するデータマートを切り替えるようにしました。

    最後に、Cubeから取得した集計値がBigQueryのデータマートと一致することを確認しました。

    CubeのViewが見つからないエラーが発生した

    構築後、staging環境で新しく追加した指標を取得しようとすると、Cube側で該当するViewが見つからず、画面が正常に表示されない問題が発生しました。

    原因を調べるため、Cubeのモデル定義と、本番・stagingそれぞれのデプロイ状況を確認しました。コードにはViewが追加され、本番のCubeにも反映されていましたが、stagingのCubeだけ古い定義のままでした。

    データマートは自動更新されていたのですが、staging用Cubeのデプロイ作業は自動化の対象から漏れていたのです。

    そこで、GitHub Actionsにstaging用Cubeの自動デプロイを追加しました。
    dbt Cloudでstagingデータマートのbuildが成功したらCubeをデプロイする構成です。

    これでようやくデータマートとCubeのstaging環境が完成しました。

    まとめ

    今回は、dbtとCubeを使って、本番環境から独立した検証環境を構築した事例を紹介しました。

    Cubeのデプロイ漏れは、エラーの報告を受けるまで気がつきませんでした。
    今後は、環境構築時の疎通確認だけでなく、データマートやCubeの変更が正しく反映されているかも確認する必要があると学びました。


    弊社、株式会社movでは各職種の仲間を募集しています!
    少しでもご興味を持っていただけましたら、ぜひカジュアル面談でお話しさせてください!

    あなたへのおすすめ