ステートフルなオーケストレーションの隠れたコスト
SchemaBridge Team · 2026-02-02 · Distributed Systems, Immutable Infrastructure, Orchestration, Anti-Patterns, Reliability
「シンプルな」社内ツールが最大の信頼性リスクになり得る理由と、イミュータブルインフラのパターンをアプリケーションロジックに適用して身を守る方法。
マイクロサービスの世界では、私たちは疎結合に執着します。キューを使い、イベント駆動アーキテクチャを採用し、モノリスを分割します。それなのに、オーケストレーションエンジンの内部では、分散システムの大罪をしばしば犯しています。ステートフルなループを書いてしまう のです。
SchemaBridge では最近、コアとなるシステムワークフロー、つまり私たち自身の段階的ロールアウトを駆動するワークフローを全面的に見直しました。その過程で、多くのエンジニアリング組織を悩ませているのと同じアンチパターンに直面しました。これは単なるリリースノートの話ではなく、イミュータブルインフラストラクチャ の原則をアプリケーション層に適用する試みです。
「ループ」というアンチパターン
古典的な「ポーラー」を考えてみましょう:
while not deployment.is_ready():
sleep(60)
check_status()
このコードは安定した世界を前提にしています。ポーリングするプロセスが永遠に生き続けると仮定しているのです。実際には、これは 分散モノリス の産物です。長い同期的な待機のあいだ状態をメモリに保持するプロセスは脆弱です。ホストが再起動したり呼び出しがハングしたりすると、その状態を失うか、誰にも監視されない「ゾンビ」として残り続け、障害モードは予測不能になります。
解決策:再帰的チェイニング
私たちは内部の while ループから、関数型プログラミングで使われる 継続渡しスタイル(Continuation Passing Style) に似た 再帰的実行モデル へと移行しました。
- パターン: 各ラウンドは監視用の子ワークフローを生成し、その子ワークフローが
sleep()ではなく永続的なDELAYステップで待機します。その後SPAWNERステップが次のラウンドを 子ワークフロー として起動します。つまり、ロールアウトワークフローが自分自身の別の実行を生成します。 - 結果: 親は依然として子からの報告を待ちますが、その待機は 永続的 です。待機はスレッドやインメモリのループではなく、PostgreSQL の行(子のコールバックを待つ
WAIT_ALLマージ)です。ロールアウトの途中でワーカーが落ちても何も失われず、データベースの状態が引き続き信頼できる唯一の情報源であり続けます。
イミュータブルなロジック:正規化ハッシュによるバージョニング
バージョニングは難しいことで有名です。ワークフロー定義を更新したとき、すでに実行中のものはどうなるのでしょうか。
- アンチパターン: 「インプレース更新」。新しいコードをデプロイすると、既存の実行が突然これまでと違う振る舞いを始めます。
- 業界標準: イミュータブルインフラストラクチャ。サーバーに SSH で入ってパッチを当てることはせず(置き換える)のと同じように、実行中のワークフロー定義にもパッチを当てるべきではありません。
SchemaBridge のワークフローバージョンはイミュータブルです。デプロイは新しいバージョンを作成し、実行は開始時のバージョンに紐づけられます。私たちが追加したのは 正規化 DSL ハッシュ です。インポートされたすべてのワークフロー定義は正規化された JSON 形式にシリアライズされてハッシュ化され、そのハッシュがワークフローとともに保存されます(dslHash)。
このハッシュは、以前は意外なほど答えにくかった問い、つまり ロジックは本当に変わったのか? に答えてくれます。設定のインポートや私たち自身のシステムワークフローのブートストラップが、すでに本番稼働中のものとハッシュが一致する定義に出会うと、再デプロイではなく no-op を計画します。また、ソース定義から乖離した本番ワークフローは、ハッシュを比較することで検出できます。
「ゴッドモード」を封じる
最後に、社内ツールのセキュリティに取り組みました。社内の「クリーンアップ」スクリプトに root 権限を与えるのはよくあることですが、これは 最小権限の原則 に反します。
私たちの社内「Gradual Rollout」ツールは、操作対象のワークフローをハードコードされた論理 ID で特定していたため、誤ったワークフローを指定して上書きしてしまいやすい状態でした。これを2つの方法で修正しました:
- 名前ではなく役割で解決する:ロールアウトは、ハードコードされた論理 ID による検索ではなく、一意性が検証された明示的なタグを通じて対象のエントリーポイントを見つけるようになりました。
- 最小権限のクレデンシャル:ロールアウトワークフロー内の Gateway ステップは、専用の名前付き API キーで認証します。このキーには、システムテナントの設定を書き換える権限である
config:manageが意図的に付与されていません。最も信頼度の低い場所から実行される自動化は、もはや自らが従うルールを変更できません。
結論
オーケストレーションが sleep() に依存しているなら、あなたはクラウドと戦っていることになります。
再帰、イミュータビリティ、最小権限 を取り入れることで、「希望頼み」のオーケストレーションから、状態を検査でき、影響範囲を限定できるオーケストレーションへと移行できます。
参考資料: