データエンジニアリング — Argo Workflows 並列化 × dbt 品質ゲート × リトライ戦略

2026-05-13 (Day 30) 水曜 C: データエンジニアリング ★★★☆☆ Argo Workflows v3.5 / dbt Core 1.8+ CronWorkflow 設計

概要

Argo 並列実行

同じ steps リスト内に複数エントリを並べると並列実行。staging 3モデルを並列化し18分 → 8分に短縮。

🚧

dbt test 品質ゲート

dbt test --select staging を独立ステップとして配置。失敗時は marts へ進まず、重複データのレポート混入を防ぐ。

🔁

指数バックオフリトライ

retryStrategy で最大3回・2分〜8分のバックオフ。BigQuery 429/500 エラーを自動吸収。

🕐

タイムゾーン修正

timezone: "Asia/Tokyo" に変更するだけで JST 05:00 実行が正しく動く。

問題

毎朝5時に実行される日次販促データパイプラインで以下の問題が発生しています。

#問題影響
1staging内の3モデルが独立しているのに直列実行合計約18分かかっている
2dbt test が一切実行されていない先月 stg_orders に重複レコードが混入し売上数字が2倍に見えるバグが発生
3リトライ設定がないBQ 429/500 エラーで手動再実行が必要
4タイムゾーンが UTCJST 5:00実行を意図していたが実際はJST 14:00に実行されている

要件

  1. 並列化: staging内の3モデルを並列実行し、全て完了してからmartsを実行
  2. データ品質ゲート: staging実行後にdbt testを実行し、テスト失敗時はmartsへ進まない
  3. リトライ設定: 各ステップに最大3回のリトライ(指数バックオフ: 2分〜)
  4. タイムゾーン修正: JST 05:00に正しく実行されるよう修正
期待する回答形式: 改善後のYAML全文 + 各変更点の説明 + 実務上の注意点

現状の YAML(問題あり)

直列実行・品質ゲートなし・リトライなし・タイムゾーン誤りの4問題が潜んでいます。
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: promo-pipeline-daily
spec:
  schedule: "0 5 * * *"
  timezone: "UTC"          # ← 問題: UTCのためJST 14:00に実行される
  workflowSpec:
    entrypoint: pipeline
    templates:
      - name: pipeline
        steps:
          - - name: extract
              template: bq-extract
          - - name: transform-a
              template: dbt-run-staging  # ← 問題: staging全体を1ステップで直列
          - - name: transform-b
              template: dbt-run-marts    # ← 問題: testなしにmartsへ進む
          - - name: load-report
              template: export-to-gcs

      - name: bq-extract
        container:              # ← 問題: retryStrategyなし
          image: gcr.io/project/pipeline:latest
          command: [python, extract.py]

      - name: dbt-run-staging
        container:
          image: gcr.io/project/dbt:latest
          command: [dbt, run, --select, staging]

      - name: dbt-run-marts
        container:
          image: gcr.io/project/dbt:latest
          command: [dbt, run, --select, marts]

      - name: export-to-gcs
        container:
          image: gcr.io/project/pipeline:latest
          command: [python, export.py]

ヒント(段階的開示)

ヒント1 — 方向性
Argo Workflowsの steps は各ステップが「リスト内リスト」構造になっている。同じ階層(同じ - レベル)に並べると並列実行、別の行に並べると直列実行になる。データ品質ゲートは「staging並列グループ → dbt test → marts」という順序で表現できる。
ヒント2 — 並列化の書き方
✗ 直列(各々別リスト)
steps:
  - - name: step-a
  - - name: step-b  # 直列
  - - name: step-c  # 直列
✓ 並列(同じリスト内)
steps:
  - - name: step-a  # 同じリスト内
    - name: step-b  # = 並列実行
    - name: step-c  # = 並列実行
ヒント3 — リトライ設定とタイムゾーン
retryStrategy:
  limit: "3"
  retryPolicy: "Always"
  backoff:
    duration: "2m"
    factor: "2"
    maxDuration: "10m"

# タイムゾーン修正
schedule: "0 5 * * *"
timezone: "Asia/Tokyo"   # JSTのまま書ける

JSTはUTC+9。timezone: "Asia/Tokyo" を指定すれば cron の時刻をJSTで直接書ける。UTCで指定したい場合は "0 20 * * *"(前日UTC 20:00)にする必要がある。

パイプライン構造図 — 改善後のDAGフロー

改善後: 並列化 + 品質ゲート + リトライ ① extract bq-extract ② staging 並列実行(3モデル同時) stg_orders retry: 3回 / backoff 2m stg_customers retry: 3回 / backoff 2m stg_promotions retry: 3回 / backoff 2m ③ データ品質ゲート 🚧 dbt test --select staging | 失敗 → STOP ④ dbt marts テスト通過後のみ実行 → export-to-gcs(省略) 従来: 18分(直列) 改善後: ≈8分(並列) timezone: "Asia/Tokyo" schedule: "0 5 * * *" → JST 05:00

模範解答(改善後 YAML 全文)

apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: promo-pipeline-daily
spec:
  schedule: "0 5 * * *"
  timezone: "Asia/Tokyo"   # 修正①: UTC→Asia/Tokyo でJST 05:00実行
  workflowSpec:
    entrypoint: pipeline
    templates:
      # ── メインフロー ──────────────────────────────
      - name: pipeline
        steps:
          # Step1: 抽出(直列)
          - - name: extract
              template: bq-extract

          # Step2: staging 3モデルを並列実行(修正②)
          - - name: stg-orders
              template: dbt-stg-orders
            - name: stg-customers
              template: dbt-stg-customers
            - name: stg-promotions
              template: dbt-stg-promotions

          # Step3: データ品質ゲート(修正③)- 失敗したらここで止まる
          - - name: quality-gate
              template: dbt-test-staging

          # Step4: martsはstagingテスト通過後のみ実行
          - - name: transform-marts
              template: dbt-run-marts

          # Step5: レポート出力
          - - name: load-report
              template: export-to-gcs

      # ── 抽出 ─────────────────────────────────────
      - name: bq-extract
        retryStrategy:   # 修正④: 全ステップにリトライ設定
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "2m"
            factor: "2"
            maxDuration: "10m"
        container:
          image: gcr.io/project/pipeline:latest
          command: [python, extract.py]

      # ── staging 並列ステップ ───────────────────────
      - name: dbt-stg-orders
        retryStrategy:
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "2m"
            factor: "2"
            maxDuration: "10m"
        container:
          image: gcr.io/project/dbt:latest
          command: [dbt, run, --select, stg_orders]

      - name: dbt-stg-customers
        retryStrategy:
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "2m"
            factor: "2"
            maxDuration: "10m"
        container:
          image: gcr.io/project/dbt:latest
          command: [dbt, run, --select, stg_customers]

      - name: dbt-stg-promotions
        retryStrategy:
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "2m"
            factor: "2"
            maxDuration: "10m"
        container:
          image: gcr.io/project/dbt:latest
          command: [dbt, run, --select, stg_promotions]

      # ── データ品質ゲート ──────────────────────────
      - name: dbt-test-staging
        retryStrategy:
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "2m"
            factor: "2"
            maxDuration: "10m"
        container:
          image: gcr.io/project/dbt:latest
          command: [dbt, test, --select, staging]

      # ── marts ─────────────────────────────────────
      - name: dbt-run-marts
        retryStrategy:
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "2m"
            factor: "2"
            maxDuration: "10m"
        container:
          image: gcr.io/project/dbt:latest
          command: [dbt, run, --select, marts]

      # ── レポート出力 ──────────────────────────────
      - name: export-to-gcs
        retryStrategy:
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "2m"
            factor: "2"
            maxDuration: "10m"
        container:
          image: gcr.io/project/pipeline:latest
          command: [python, export.py]

各変更点の説明

1 タイムゾーン修正
timezone: "UTC"timezone: "Asia/Tokyo" に変更。schedule: "0 5 * * *" のままで JST 05:00 に実行される。UTCで指定したい場合は "0 20 * * *" にする必要があった。
2 staging 並列化
元は直列3ステップ(約18分)→ 並列実行で最も時間のかかるモデルの時間(仮に8分)まで短縮。Argoの steps リスト内に複数の - エントリを同レベルで並べることで並列になる。
3 データ品質ゲート追加
dbt test --select staging を独立ステップとして staging後・marts前に配置。dbt test が1件でも失敗するとArgoのステップが非ゼロ終了コードを返し、後続の marts ステップへ進まない。先月の売上2倍バグ(重複レコード混入)は dbt testunique テストで検知可能だった。
4 リトライ設定
各 template に retryStrategy を追加。limit: "3" で最大3回リトライ、backoff で指数バックオフ(2分→4分→8分)を設定。BigQuery の 429(レートリミット)や 500(一時エラー)はこれで吸収できる。
実務上の注意点:
  • retryStrategy はWorkflowレベルではなく template レベルに書くことで、ステップごとに異なるポリシーを設定できる
  • 品質ゲートで失敗した場合、Slack アラート等の通知ステップを onExit で追加するとインシデント対応が速くなる
  • dbt Mesh 導入時は --select のパス指定がプロジェクトをまたぐため注意

ポイント解説

1 Argo Workflowsの並列/直列制御
steps の各要素(- - で始まる)は直列、同じ - リスト内の複数エントリは並列。DAGテンプレートを使うとより複雑な依存関係をグラフで表現できるが、線形パイプラインには steps で十分。
2 データ品質ゲートの重要性
dbt test をパイプラインに組み込まないと、上流データの問題が下流レポートまで伝播する。not_null / unique / accepted_values / relationships の4テストを staging 全モデルに設定するのが最低ライン。
3 タイムゾーンの罠
Cron のデフォルトは UTC が多い。timezone フィールドを明示的に設定しないとJSTユーザーが想定外の時刻に実行される。特に深夜バッチでは日付跨ぎのバグにつながる。
4 指数バックオフの設計
duration(初期待機時間)・factor(倍率)・maxDuration(上限)の3パラメータで制御。BigQueryの429エラーはほぼ数分で解消するため、2m→4m→8mは実用的。maxDuration を設定しないとリトライ間隔が際限なく伸びる。

実務への応用

  • 毎朝5時の販促効果集計バッチはまさにこの構成で動いている
  • staging並列化により、従来18分かかっていた処理が最長ステップ(例: stg_orders 8分)まで短縮 → レポート完成がJST 5:13頃(従来5:23頃)に
  • データ品質ゲートに dbt test --select staging --store-failures を追加すると、失敗レコードがBigQueryテーブルに保存されデバッグが容易になる
  • Argo Workflows の onExit テンプレートにSlack/PagerDuty通知を追加することで、品質ゲート失敗時に即時アラートが飛ぶ

今日のまとめ

Argo Workflows の steps 並列化・dbt test による品質ゲート・リトライ戦略・タイムゾーン設定の4点がデータパイプラインの信頼性と効率性を決定する基本構成要素。

特にデータ品質ゲートをパイプラインに組み込むことは、下流レポートへの誤データ伝播を防ぐための最重要実装である。「バグが発生した後で品質テストを追加」ではなく、最初から設計段階で組み込むことが実務の正解。

自己評価

自分の回答

気づき・メモ