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

Dagster の“ネイティブ統合”が効いた話(dlt / dbt + リバースETL)

    こんにちは!Scene Live bq事業本部のまっすーです。

    2025年からデータサイエンティスト兼データエンジニアとして、Scene Liveに中途入社しました。bqというサービスのLLMを用いた検証・研究をおこなったり、プロダクトに関するデータ収集から利活用までの『データ基盤』を作ったりしています。

    連載:「モダンデータスタック続編」第 3 回 / 全 3 回
    第 1 回:なぜ Airflow から Dagster に乗り換えたのか
    第 2 回:Dagster 移行、500 テーブル超を止めずに引っ越した話

    既刊:【1ヶ月半で構築】bq事業のモダンデータスタック


    はじめに

    データ関連の連載、最終回です。

    第 1 回で 「なぜ Dagster に乗り換えたのか」、第 2 回で 「どうやって 500 テーブル超を引っ越したのか」を書きました。今回はその仕上げで、「移行してから何が良くなったのか」、特に第 1 回で挙げた ネイティブ統合 が、実際どう効いているかの話です。

    「ネイティブ統合」と言われてもピンとこないと思うので、Airflow 時代と何が違うのか、何ができるようになったのかを、できるだけ具体的に書きます。


    この記事の前提(用語の整理)

    第 1 回・第 2 回で出てきた用語(Cloud Composer / CRJ / CRS / BigQuery / Airflow / Dagster / Asset / Pipes / dbt / dlt)は引き続き使います。本記事で初めて深く触れる用語を以下に整理します。


    • Asset Lineage(アセット系統図): Asset 同士の依存関係を 1 枚の DAG(有向非巡回グラフ)として可視化したもの。「このテーブルを作るには、どのテーブルが必要で、その元はどこから来ているか」が一目で分かる

    • dlt(data load tool): Python で「外部からデータを取り込む」処理を書く OSS。dbt と似た開発体験で取り込みパイプラインが書ける。第 1 回でも軽く触れた通り、Dagster 移行と同時に導入した

    • @dbt_assets: dagster-dbt が提供する Python デコレータ。dbt プロジェクトを 1 行(に近い記述)で 「dbt のモデル 1 つ = Dagster の Asset 1 つ」 に変換してくれる

    • @dlt_assets: 同様に dagster-dlt が提供するデコレータ。dlt の取り込み source 1 つを Dagster の Asset 1 つに対応させる

    • スキーマドリフト: 取り込み元(API や DB)のテーブル構造が、知らないうちに変わってしまうこと。カラムが増えた・減った・型が変わった等。dlt はこれを検知して通知できる


    「ネイティブ統合」って結局何が違うの?

    第 1 回で「dbt / dlt / リバース ETL とのネイティブ統合が強力だった」と書きました。これがピンと来ないと、本記事は楽しめないので、まずここを丁寧に。
    Airflow 時代の「外部実行」と、Dagster の「ネイティブ統合」を並べてみます。

    Airflow 時代(外部実行):

    Airflow の DAG 内
    └─ task: dbt を実行する
    └─ 内部: dbt build というシェルコマンドを叩く
    └─ dbt が 500 を超えるモデルを順番に処理(Airflow からは見えない)

    Airflow から見えるのは「dbt build というタスクが成功したか失敗したか」の 1 ビット情報だけ。中で 500 を超えるモデルがどう動いたかは、dbt 側のログを別途見にいかないと分かりません。


    Dagster(ネイティブ統合):

    Dagster の Definitions
    └─ @dbt_assets: dbt プロジェクトを「Asset の集まり」として宣言
    └─ Dagster は 500 モデル 1 つ 1 つを「個別の Asset」として認識
    └─ どのモデルが何秒で動いたか・どこで失敗したかが Asset 画面で全部見える

    つまり、Dagster がツールを「外から呼ぶ」のではなく、ツールの単位(dbt モデル / dlt source)がそのまま Dagster の管理単位(Asset)になる。これが「ネイティブ統合」です。

    画像

    以下のように、DagsterのUIの中で、依存関係(Lineage)が見える、しかもasset単位で。

    画像

    この差が、実運用でどう効くかを 3 つに分けて見ていきます。


    dbt: 1 モデル = 1 Asset の威力

    500 を超える dbt モデルが、500 個の Asset として Dagster の UI に並ぶ。これだけ聞くと「いやそれ多すぎでしょ」と思うかもしれません。実際、最初は私もそう思いました。
    ところが運用してみると、「全部 Asset」だからこそできることが圧倒的に多かったんです。

    • 個別リトライができる: 500 モデルのうち 1 つだけが失敗したとき、その 1 モデルだけを再実行できる(Airflow 時代は「dbt build を全部やり直す」になりがちだった)

    • 実行時間がモデル単位で見える: どのモデルが遅いか・最近遅くなったかが Asset 画面でひと目で分かる

    宣言は驚くほど短く、5 行ほどで dbt プロジェクトの全モデルが Dagster Asset になります。

    @dbt_assets(manifest=Path("target/manifest.json"))
    def my_dbt_assets(context, dbt: DbtCliResource):
        yield from dbt.cli(["build"], context=context).stream()
    画像

    以下はモデル・Asset単位で実行時間や他Assetとの関係等が見えている画像です。

    画像

    dlt: 1 source = 1 Asset で取り込みも統一

    dlt 側も同じ構造です。dlt で書いた取り込み source(API / CloudSQL / GCS など)が、そのまま Dagster の Asset として並びます。
    これがなぜ嬉しいかというと、取り込み(dlt)と変換(dbt)が同じ Asset の世界で繋がるから。

    [dlt source: notion_pages] → [dbt model: bronze_notion]
                → [dbt model: silver_notion]
                → [dbt model: gold_notion]

    この矢印が、Dagster の Asset Lineage に自動で現れます。dlt と dbt を別々のツールとして書いているのに、Dagster の中では 1 枚の DAG に繋がる。

    そして、第 2 回で書いた スキーマドリフトの Slack 通知 も、ネイティブ統合のおかげで実装が楽でした。dlt が「あ、カラムが増えた」と検知すると、Dagster の Asset イベントとしてその情報が拾えるので、そこから Slack を叩くだけ。Airflow 時代だったら、dlt(当時はカスタムスクリプト)の中で通知ロジックを書く必要があったところです。


    リバース ETL: 出口まで Asset で繋ぐ

    うちでは Bronze → Silver → Gold という 3 階層でデータを加工した最後に、業務 SaaS やプロダクト DB にデータを書き戻すというステップがあります。これがリバース ETL です。
    このリバース ETL のツールも、Dagster の Asset として統合されています。つまり、

    [dlt 取り込み] → [dbt 変換 (Bronze〜Gold)] → [リバース ETL: SaaS / DB へ書き戻し]

    この一連の流れが、1 つの Asset Lineage として Dagster の UI で見られる。データが「外から来て、加工されて、外に出ていく」全部が 1 枚で見えるわけです。

    画像

    これは Airflow 時代だと不可能でした。dlt 相当のスクリプト・dbt・リバース ETL 相当のスクリプトを、別々の DAG タスクとして並べることはできても、「データ単位の依存関係」としては繋がっていなかったからです。


    “全部 Asset” だからできること

    ここまで dbt / dlt / リバース ETL を個別に見てきましたが、ネイティブ統合の本当の威力は 「3 つを同じ単位で扱える」 ことです。

    具体的に効いているシーンを 3 つだけ。


    1. 失敗時の影響範囲が秒で特定できる

    朝、Slack にエラー通知が来る。Dagster の UI を開くと、どの Asset が失敗して、どの下流 Asset が止まっているかが 1 画面で見える。「あ、このダッシュボードに使ってる Gold テーブルまで波及してるな」が即座に分かる。

    画像

    Airflow 時代は「失敗した DAG を見る → 中の dbt のログを見る → どのモデルが失敗したか特定 → 影響範囲を SQL で調査」という流れで、軽く 30 分〜1 時間かかっていました。これが数分になりました。


    2. リトライが Asset 単位

    失敗した Asset だけを Dagster の UI からポチっと再実行できる。Airflow 時代は「DAG 全体を再実行」「dbt build をやり直す」など、必要以上に大きい単位で動かすことが多かった。


    3. 観測性が統一される

    dlt の取り込み時間も、dbt のモデル実行時間も、リバース ETL の書き戻し時間も、全部同じ Asset 画面で見られる。「最近、データが揃うのが遅くなってきたな」と思ったら、Asset の実行時間履歴を眺めれば原因が分かる。


    実際にコストはどう変わったか

    第 1 回で「Composer の固定料金が重かった」と書きました。その答え合わせを、最後に少しだけ。
    移行前後で、Composer がやっていた仕事 (オーケストレーター本体) にかかるインフラ費用を比較すると、こうなりました。具体的な金額は伏せて、移行前 (Cloud Composer の月額) を 100 とした相対値 で書いていきます。

    画像

    つまり、 Composer 時代を基準に 約 4 割削減 (▲42%) 。月で見ると Composer の固定料金がまるごと消えて、代わりに Dagster の Cloud Run (常駐 CRS 3 つ) + Cloud SQL (バックエンド DB) が新規発生した結果、約 6 割のコストで同じ仕事ができている、という構図です。
    Cloud SQL が新たに増えているのは、Dagster は内部状態 (実行履歴・スケジュール・センサー状態など) を PostgreSQL に保存する設計 だからです。Composer はフルマネージドで内部に PostgreSQL を持っていたので、「Composer の月額」にこの分が含まれていただけで、Dagster を自前ホストする以上は、PostgreSQL のコストは “Dagster の一部” として読んでおくのがフェアですね。

    しかも、これでも まだ調整中です 。移行直後はリソース設定を保守的に振っていたぶん、Cloud Run のコストが高めに出ていました。直近 1〜2 週間でも、CRS の min_instance (常時起動するインスタンス数) や CPU/メモリ設定をチューニングして少しずつ下げてきていて、第 1 回で言っていた “削減 ”は、まだ終わっていない というのが正直なところです。

    並行運用や本番デプロイで 3 日止めたぶんの “痛み” はあったわけですが、オーケストレーター本体のコストを長期的に約 4 割削減できる のは、規模が大きくなるほど効いてくるはずで、ここは移行して良かったと思っています。

    ※ なお、データ基盤全体ではこれ以外に GCS / BigQuery などのコストもありますが、それらは Dagster 移行とは別の要因 (データ量の増減・dlt 取り込み方式変更など) で動くため、ここでは触れていません。


    ネイティブ統合の限界

    ここまでメリットを並べてきましたが、当然限界もあります。誠実に書いておきます。

    • Dagster バージョンに引きずられる: dagster-dbt は Dagster のメジャーアップデートで API が変わることがある。dbt 側は問題なく動いていても、Dagster 側で書き直しが必要になることが起こりうる

    • ツール固有の機能が薄まる: dlt / dbt それぞれが持っている細かい機能(特殊な実行モードや CLI オプション)が、ネイティブ統合の薄いラッパー越しでは使いにくいことがある。素の dbt build ではできることが、@dbt_assets 経由だとひと工夫必要、みたいな

    • 学習コストは結局かかる: Asset の概念に慣れるまでは、「dbt は dbt として、dlt は dlt として書ければいいじゃん」と感じるはず。Asset の世界観に乗ったときの恩恵が大きいぶん、乗るまでのハードルはある

    ただし、これらの限界は AI コーディング環境(Claude Code + dagster-io/skills プラグイン)でかなり緩和される、というのは第 1 回でも書いた通りです。書き換えが必要なときも、AI が「ここをこう変えればいい」と教えてくれるので、自力ですべて追う必要はありません。


    連載のおわりに

    3 回にわたって、Airflow から Dagster への移行の話を書いてきました。

    第 1 回: なぜ乗り換えたか(コスト・設計思想・ネイティブ統合)
    第 2 回: どう引っ越したか(環境別の段階移行と、3 日止めた話)
    第 3 回(本記事): 移行後に何が良くなったか(ネイティブ統合の威力)

    データ基盤の “作った話” は前回の note 記事で書 きましたが、“作ってからの話” は今回の連載でようやくまとめられました。データ基盤は作って終わりじゃなく、運用しながら育てていくものなので、これからも何かやらかしたら正直に書こうと思います。

    最後まで読んでいただきありがとうございました。

    なお、次回は連載をいったん離れて、単発で 「データの消費者は『ヒト』だけじゃない ─ AI ネイティブなデータ基盤の実態」 という記事を書く予定です。今回の連載で整えた基盤を、LLM がデータの消費者として登場し始めている話 ─ 例えば GCP のコストデータを LLM が分析して SRE 的なアドバイスを Slack に投げてきたり、評価ロジックの劣化を LLM が見張っていたり ─ の事例を紹介します。

    その後に、LLM 運用基盤としての Langfuse に関する連載を予定しています。お楽しみに!


    連載目次


    まだまだ発展途上のデータ基盤。これからも生成AI・LLMを使った改善を続けていきます!
    データ基盤に興味がある方、LLMを活用した開発やデータ分析に興味がある方、ぜひお話ししましょう。


    ▼Scene Live 採用情報

    ▼bq事業のエンジニア採用

    この記事が参加している募集

     
     
    株式会社Scene Live・New Standard室のまっすーです。データエンジニア兼データサイエンティストとして、データ基盤作り・分析設計・LLMOpsまで幅広く取り組んでいます!

    あなたへのおすすめ