エンジニアリング
Dagu v2.7.0 の内側: 再現しやすいワークフロー
Dagu v2.7.0 は、アクションのバージョン、ファイル、ツール、入力、出力、実行証跡をワークフローの契約へ移し、実行を再現しやすくします。
Dagu v2.7.0 の内側: 再現しやすいワークフロー
Dagu v2.7.0 は、単に再利用可能なアクションを追加したリリースではありません。中心にあるのは再現性です。
ワークフローは、特定のマシンにたまたま置かれているスクリプト、誰かが去年入れた CLI、あるワーカーだけが持つチェックアウトに依存している限り、再現しやすいとは言えません。動くことはあります。何か月も動くことさえあります。しかし別のワーカーに移ったり、別のチームメンバーが実行したり、深夜の障害対応で原因を追うとき、どの前提が壊れたのか分からなくなります。
v2.7.0 は、そうした前提をワークフローの契約の中へ引き上げるリリースです。完全な決定性ではありません。外部 API、データベース、時刻は変わります。それでも、アクションのバージョン、必要なファイル、使うツール、入力と出力の形、残す成果物は、見える場所にあり、別の実行環境へ移せるべきです。
問題は隠れた状態
v2.7.0 以前の Dagu でも本格的なワークフローは実行できました。問題は、どれだけ多くの本番挙動が DAG の外に置かれていたかです。
- このステップはワーカーに
jqが入っている前提で動く - このシェルスクリプトは DAG の隣に存在する必要がある
- このアクションはワーカーが持っていないかもしれないファイルに依存する
- このサブワークフローは JSON を返すが、呼び出し側は読み取り場所を知っている必要がある
- 分散ワーカーは、親ワーカーと同じチェックアウトを持っている場合だけ DAG を実行できる
どれも珍しい前提ではありません。ただ、前提がワークフローの外にあるほど、実行は再現しにくくなります。YAML を見ても、その実行を成立させたものが何だったのか分からないからです。
Dagu Actions はワークフローパッケージ
Dagu Actions とサードパーティアクションは、別の DAG から参照して呼び出せるワークフローパッケージです。パッケージには dagu-action.yaml、実行する workflow.yaml、必要な補助ファイルを含められます。
実行時には、Dagu がバージョン付きアクション参照を解決し、マニフェストを読み、呼び出し側の with 値を入力スキーマで検証し、アクションの DAG をサブ DAG として実行します。完了後は出力オブジェクトを出力スキーマで検証し、親 DAG が参照できる形で公開します。
これにより、アクションは再利用できるだけでなく、入力、実行、出力の境界を宣言できます。ここがより重要な点です。
パッケージは実行と一緒に移動する
Dagu は分散構成で動かせます。親 DAG があるワーカーで始まり、アクションのサブ DAG が別のワーカーで実行されることがあります。その場合、子ワーカーはアクションのファイルを実行前に受け取る必要があります。
v2.7.0 では、親ワーカーがアクションを解決すると、Dagu がアクションのワークスペースを具体化し、制限付きのバンドルとしてまとめます。必要ならコーディネーターがそのバンドルを実行と一緒に子ワーカーへ渡し、子ワーカーは実行前に展開します。
ワーカーが事前にリポジトリ構成を知っている必要はありません。その実行に必要なアクションパッケージを受け取ればよいのです。
- 圧縮後 64 MiB、展開後 256 MiB が標準上限
- ファイル数は標準で 8192 個まで
.gitディレクトリは除外- シンボリックリンクと特殊ファイルは拒否
- パスは正規化され、ワークスペース外へ逃げられない
ツールも契約の一部になる
スクリプトは小さな CLI ツールに依存しがちです。jq、yq、リリース補助ツール、診断ツール、変換ツール。多くの環境では、それらはワーカーイメージ、README、誰かの記憶の中にあります。これも隠れた状態です。
v2.7.0 では DAG に必要なツールを宣言できます。Dagu のツール管理は aquaproj/aqua を基盤にし、Dagu バイナリの中で、ツールセットのハッシュ計算、ワーカー内キャッシュへのインストール、解決済みコマンドのマニフェスト作成、PATH への追加を行います。
結果として、ワークフローは「jq が入っているはず」と期待するのではなく、「このバージョンの jq が必要」と宣言できます。サブ DAG やアクションパッケージは呼び出し側のツールを継承しません。必要なツールは、そのパッケージ自身が宣言します。
出力は stdout の慣習に頼らない
ワークフローのデータフローは、最初は気軽に始まりがちです。スクリプトが JSON を出し、別のステップが stdout を読む。誰かがどの行を見るべきか覚えている。後から変更しようとすると、どこかの呼び出し元を壊すかもしれないため誰も触りたがりません。
v2.7.0 では、アクション境界を越える値により明確な戻り道があります。ステップは stdout.outputs で stdout から値を公開でき、outputs.write でワークフロー出力を直接書けます。親 DAG はアクション境界を通して結果を読みます。
これは、スキーマで検証された入力、DAG 実行、スキーマで検証された出力という、関数シグネチャに近い形をアクションへ与えます。
成果物で実行を検査できる
再現性は、同じ実行を始められることだけではありません。実行が終わったあと、何が起きたかを理解できることでもあります。
v2.7.0 は artifact.write、artifact.read、artifact.list、stdout/stderr からの成果物キャプチャを追加しました。レポート、変換済みデータ、ログ、クエリ結果、その他の証跡を、実行履歴に残しやすくします。
組み込みアクションが小さなシェル慣習を減らす
シェルは今後も土台です。Dagu はすべてのスクリプトをフレームワークコードに変えたいわけではありません。ただ、ファイル確認、コピー、HTTP 待機、成果物保存、データ抽出、DuckDB クエリ、別 DAG の enqueue など、ワークフローの小さな共通操作は、名前付きの組み込みアクションにすると契約が明確になります。
v2.7.0 では、ファイル操作、成果物操作、データ変換、待機、git.checkout、duckdb.query、outputs.write、dag.enqueue などの組み込みアクションが追加されました。シェルが正しい場所ではシェルを使い、ワークフロー操作そのものを表したい場所では名前付きアクションを使えます。
本番ワークフローには本番向けの面が必要
シークレットはグローバルまたはワークスペース単位で管理できます。通知チャネルとルーティングルールは、メール、Webhook、Slack、Telegram へワークフローイベントを送れます。インシデントルーティングは PagerDuty や SolarWinds Incident Response などの接続とポリシーを扱えます。MCP サーバーは、AI ツールや他クライアントが Dagu ワークフローを構造的に読み取り、変更し、実行する方法を提供します。
ワークフローが明示的な実行単位になるほど、その周辺には運用面が必要になります。シークレットは YAML に置くべきではありません。失敗は正しい場所へ届くべきです。インシデントは見えるべきです。外部ツールは API レベルでワークフローを操作できるべきです。
互換性も重要
ワークフロー言語は、既存のワークフローを無視できません。v2.7.0 は command、script、type、call、従来のカスタム step_types を使う DAG を引き続き読み込みます。新しいスキーマは非推奨構文を警告できますが、一斉移行は求めません。
新しい構文は既存のランタイムモデルへ正規化されます。カスタムアクションは DAG 読み込み時に展開され、組み込みアクション名は必要に応じて executor 型へ対応します。アクションパッケージだけは、参照解決、マニフェスト検証、ワークスペースバンドル、出力検証という新しい責務を持ちます。
v2.7.0 が本当に目指すもの
Dagu は「ワークフローは YAML ファイル、コマンド群、それを実行する単一バイナリでよい」という単純な考えから始まりました。v2.7.0 はその考えを保ったまま、コマンドの周囲のレイヤーをより明示的にします。
- アクション参照はバージョン付き
- アクションパッケージは入力と出力を宣言
- アクションファイルは分散実行と一緒に移動
- ツールは DAG 内で固定
- 出力は構造化されて境界を越える
- 成果物で実行証跡を残しやすくなる
- シークレット、通知、インシデント、MCP が運用レイヤーを場当たり的でないものにする
だから v2.7.0 は、再利用性のリリースというより、再現性のリリースです。再利用性はうれしい副作用であり、再現しやすいワークフロー実行こそがこのリリースの意味です。
執筆者
Dagu を開発中。信頼性と移植性のある自動化のためのセルフホスト型ワークフローオーケストレーションエンジン。
Yota Hamada のその他の記事