· 638 words · 3 min read

7. オーケストレーション#

ネットワークチームはすべてを正しく行っていた。堅固な信頼できる情報源(SoT)があり、すべての操作に対してテスト済みのPlaybookがあり、それらを実行するための明確なランブックがあった。書類上では、新しいVLANサービスのデプロイは完全に自動化されていた。実際には、半日と特定のエンジニア一人が必要だった。

そのエンジニアはシーケンスを知っていた。まず、SoTデータが完全であることを検証する。次に事前チェックPlaybookを実行する。次に出力を確認し、失敗したデバイスを探し、続行するかどうかを決める。次にデプロイメントPlaybookをトリガーする。次に待つ。次に検証Playbookを実行する。次にServiceNowチケットを手動で更新する。途中でデバイスが失敗した場合は、他のデバイスが気づく前にロールバックする。彼女はそれをすべて、誰も完全に内面化していない共有ドキュメントのランブックにステップごとに持っていた。

彼女が休暇に入ると、デプロイメントが止まった。ジュニアエンジニアがシーケンスを試みて順序を間違えると、ネットワークは6時間部分的な状態のままだった。自動化は存在していた。調整は依然として手動だった。

それがこの章が解決する問題だ。オーケストレーションは、自動化ツールのコレクションを一体として動作するシステムに変えるビルディングブロックだ。他のブロックを調整し、いつ実行するかを決定し、障害を優雅に処理し、すべての決定を追跡し、エンジニアが手でフローを管理しながら中間に立つことなくこれらすべてを行う。

7.1. 基礎#

7.1.1. コンテキスト#

これまでカバーしてきたすべてのビルディングブロックはそれぞれ1つのことを得意とする。SoTはインテントを保持する。Executorはそれを適用する。コレクターは状態を取得する。オブザーバビリティはその状態を理解する。それぞれは専門家であり、専門家にはコネクターが必要だ: 各ブロックがいつ行動すべきかを決め、結果をどう処理するかを決め、何かがうまくいかないときにどう回復するかを決めるもの。

そのコネクターがOrchestratorだ。

第3章では、他のすべてを調整するブロックとしてNAFフレームワークの中心に置いた。第5章はExecutorが設定変更をどう適用するかを示した。第6章はオブザーバビリティが結果をどう検証するかを示した。本章ではオーケストレーターが両方をどう順序付けし、その間で起こりうるすべての問題をどう処理するかを示す。

オーケストレーションなしには、自動化を実行していない。誰かが正しい順序で、正しいタイミングで、正しい方法で解釈して呼び出さなければならないスクリプトを実行しているだけだ。それは名ばかりの自動化だ。

7.1.2. ゴール#

オーケストレーターは5つのゴールを達成する必要がある:

  1. マルチブロックワークフローをエンドツーエンドで調整する。 デプロイメントは単一のアクションではなく、シーケンスだ。SoTのインテントを検証し、事前チェックを実行し、設定を実行し、結果を検証し、ステークホルダーに通知する。オーケストレーターがフルシーケンスをまとめて保持する。

  2. 人間の開始の有無にかかわらず、自動的にイベントに反応する。 ServiceNowの承認、オブザーバビリティのアラート、スケジュールされたコンプライアンスのスキャン: これらはすべて人間が手動で開始しなくてもワークフローをトリガーできる必要がある。

  3. 耐障害性とスケーラブルな実行。 800のスイッチを並列で操作するワークフローは確実に完了しなければならない。再起動を生き延びなければならない。成功したデバイスの作業を失うことなく部分的な障害を処理しなければならない。

  4. 改ざん防止の可視性を提供する。 オペレーターは今何が実行されているかを見る必要がある。監査員は先月何が実行されたか、誰がトリガーしたか、どのバージョンのワークフローが実行されたか、すべてのステップが何を生成したかを見る必要がある。両方のニーズが同じシステムから満たされなければならない。

  5. ワークフロー定義を本番ソフトウェアとして管理する。 800のスイッチにデプロイするワークフローは不用意に変更できない。ロジック自体が、本番で動作する他のコードに適用されるのと同じ規律で永続化、バージョン管理、テスト、本番へのプロモーションが必要だ。

7.1.3. 柱#

5つの柱がこれらのゴールを支える:

  1. ワークフローエンジン: 多段階プロセスの定義、実行、追跡
  2. トリガーレイヤー: 手動、スケジュール、イベント駆動、Webhook
  3. 耐障害性実行: ワークフローが再起動を生き延びる。数百のデバイスにわたってデバイスごとのボトルネックなしにリトライ、ロールバック、並列操作をサポートする
  4. 監査とオブザーバビリティ: すべてのステップをログに記録し、すべての決定を追跡可能にする
  5. パイプライン管理: ワークフロー定義を本番で安全に永続化、バージョン管理、プロモーションする

7.1.4. スコープ#

オーケストレーターは調整する。行動しない。

スコープ内:

  • 他のビルディングブロックの調整
  • ワークフローロジックとステップ依存関係の定義
  • 複数のソースからのトリガー処理
  • 実行状態の追跡と監査証跡の生成
  • ステップが失敗したときのロールバック決定の管理

スコープ外:

  • デバイス変更の実行(それはExecutor
  • ネットワーク運用状態の保存(それはObservability
  • ネットワークインテントの保持(それはSoT)

よくある誤りは、隣のブロックの実行またはストレージの責任を複製するオーケストレーターを構築することだ。ブロック間のインターフェースはクリーンに保たなければならない。

認証情報の保存、デバイスインベントリの管理、または独自の設定エンジンの実行も行うオーケストレーターは別のものに成長している。オーケストレーションツールが他のブロックから責任を吸収し始めたら、後でほどくのにコストがかかるアーキテクチャ上の結合問題が生じている。

7.2. 機能#

5つのゴールと柱は5つのコア機能を通じて実現される。それぞれが1つのゴールとその支持する柱に直接対応している:

  1. ワークフローエンジン: ワークフローがどう構造化され、ステップがどう調整されるか
  2. トリガー: ワークフローがいつ、どのように開始するか、および呼び出し元が何を体験するか
  3. レジリエンスとスケール: 障害下および大量のデバイスに対してワークフローが確実に完了するか
  4. ステートと追跡可能性: 実行状態の追跡と改ざん防止の監査記録の生成
  5. パイプライン管理: ワークフロー定義を本番で安全に永続化および管理する
graph LR

    subgraph ゴール
        direction TB
        A1[マルチブロックワークフローをエンドツーエンドで調整する]
        A2[自動的にイベントに反応する]
        A3[耐障害性とスケーラブルな実行]
        A4[改ざん防止の可視性と監査]
        A5[本番パイプラインへの安全な変更]
    end

    subgraph 柱
        direction TB
        B1[ワークフローエンジン: 定義、実行、追跡]
        B2[トリガーレイヤー: 手動、スケジュール、イベント駆動]
        B3[耐障害性実行: 耐久性、リトライ、スケールでのロールバック]
        B4[監査とオブザーバビリティ: すべてのステップをログに記録]
        B5[パイプライン管理: 安全に永続化、バージョン管理、プロモーション]
    end

    subgraph 機能
        direction TB
        C1[ワークフローエンジン]
        C2[トリガー]
        C3[レジリエンスとスケール]
        C4[ステートと追跡可能性]
        C5[パイプライン管理]
    end

    A1 --> B1 --> C1
    A2 --> B2 --> C2
    A3 --> B3 --> C3
    A4 --> B4 --> C4
    A5 --> B5 --> C5

    classDef row1 fill:#eef7ff,stroke:#4a90e2,stroke-width:1px;
    classDef row2 fill:#ddeeff,stroke:#4a90e2,stroke-width:1px;
    classDef row3 fill:#cce5ff,stroke:#4a90e2,stroke-width:1px;
    classDef row4 fill:#b3d8ff,stroke:#4a90e2,stroke-width:1px;
    classDef row5 fill:#99ccff,stroke:#4a90e2,stroke-width:1px;

    class A1,B1,C1 row1;
    class A2,B2,C2 row2;
    class A3,B3,C3 row3;
    class A4,B4,C4 row4;
    class A5,B5,C5 row5;

7.2.1. ワークフローエンジン#

ワークフローエンジンはオーケストレーターのコアだ。多段階プロセスを定義し、実行し、状態を追跡し、ステップ間の関係を処理する。

パターンに入る前に、4つの基本的な調整アプローチに名前を付けることが価値ある。この選択がそれ以外のすべてを形作るからだ。

7.2.1.1. 調整アプローチ#

  • モノリス(命令型プログラム): 単一のPythonスクリプトが各ステップを順番に呼び出し、各結果を待ち、次に何をするかを決める。書くのはシンプルで、多くのチームはここから始める。問題は耐久性だ: スクリプトが10ステップ中の5ステップでクラッシュすると、最初からやり直す。実行間に永続的な状態はない。スレッディングロジックを自分で書かなければ、ステップを並列化することもできない。小さく、高速な操作には機能するが、負荷と信頼性の低いインフラには壊れる。実際、これはExecutorブロックがすでに提供しているものだ: オペレーターが呼び出すスクリプト。オーケストレーションと呼ぶのは寛大だ。

  • ワークフロー(DAGベース): ここで真のオーケストレーションが始まる。ステップは有向非巡回グラフとして定義され、各ノードはタスクでエッジは依存関係を表現する。エンジンはノードごとに状態を追跡する。クラッシュ後に再起動すると、失敗したまたは不完全なステップだけが再実行される。並列性は組み込まれており、独立したブランチが同時に実行される。これは本番オーケストレーションの支配的なアプローチであり、ネットワーク自動化において最も実証されている。このカテゴリーのツールにはPrefect、Temporal、AWXワークフローテンプレートが含まれる。

  • コレオグラフィー(イベント駆動、中央コーディネーターなし): オーケストレーターがない。各コンポーネントが他のコンポーネントが発行したイベントに反応する。Executorが「デプロイメント完了」を発行し、オブザーバビリティシステムがそれを消費して検証を実行し、通知システムが検証結果を消費する。コンポーネント間の結合は緩く、新しいコンシューマーを追加しやすい。欠点は、フルワークフロー状態を理解できる単一の場所がないことだ。サービス間の障害のデバッグには複数のシステムからのイベントの相関が必要だ。このアプローチはシンプルなリアクティブパターンには機能するが、複雑さとともにスケールが難しくなる。

  • エージェンティック(感知-ロジック-行動ループ): Large Language Model (LLM)またはAIシステムがロジックレイヤーとして機能する。エージェントはオブザーバビリティまたはSoTから現在の状態を観察し、どのアクションがギャップを埋めるかを推論し、Executorを呼び出す。事前定義されたワークフローグラフに従うのではなく、実行時に動的に決定を行う。これは決定論を柔軟性と引き換えにするアプローチであり、自律ネットワークのアーキテクチャ的基盤を形成する。セクション7.2.7で深く扱い、第17章では完全な自律性に拡張する。

ほとんどのチームはモノリスアプローチから始め、自動化が成熟するにつれてワークフロー(DAGベース)に移行する。監査と追跡可能性の要件が中央コーディネーターを価値あるものにするため、コレオグラフィーはネットワーク運用には正しい選択になることがほとんどない。エージェンティックアプローチは実在するが、本番ネットワーク運用ではまだデフォルトではない。

7.2.1.2. ワークフローパターン#

DAGベースのアプローチ内では、4つのパターンが現実のネットワーク自動化ワークフローのほとんどをカバーする:

  • シーケンシャル: ステップが次々に実行され、各ステップは前のステップが正常に完了することに依存する。厳密な順序が必要なときに適している: インテントを検証し、事前チェックを実行し、実行し、検証する。

  • 並列(ファンアウト/ファンイン): 複数の独立したステップが同時に実行される。ファンアウトステップは多くの並列タスクを起動し(デバイスごと、または建物ごと)、ファンインステップはすべての完了を待ってから進む。数百のデバイスにわたる操作に不可欠だ: ファンアウトなしに、800デバイスにわたるデバイスごとに10秒の操作は順次2時間以上かかる。

  • 条件分岐: 取るパスは実行時の状態に依存する。事前チェックは通過したか?実行にブランチ。失敗したか?中止して通知にブランチ。ワークフロー定義には結果を評価して次のステップを選択する決定ノードが含まれる。

  • Sagaパターン(Saga Pattern): 長時間実行されるワークフローのために、Sagaパターンは各ステップに補償トランザクションを追加する。ワークフローがステップNまで成功してから失敗した場合、ステップN-1から1の補償アクションを逆順に実行し、システムを既知の正常な状態に戻す。これがオーケストレーションレベルでのロールバックの仕組みだ: ワークフロー全体を再実行するのではなく、成功した各ステップの逆を実行する。

flowchart TD
    subgraph シーケンシャル
        S1[ステップA] --> S2[ステップB] --> S3[ステップC]
    end

    subgraph FanOut["ファンアウトとファンイン"]
        F0[開始] --> F1[デバイス1]
        F0 --> F2[デバイス2]
        F0 --> F3[デバイスN]
        F1 --> FJ[ファンイン]
        F2 --> FJ
        F3 --> FJ
    end

    subgraph Saga["Saga - 失敗時ロールバック"]
        P1[ステップ1] --> P2[ステップ2] --> P3[ステップ3]
        P3 -- 失敗 --> R3[ロールバック3]
        R3 --> R2[ロールバック2]
        R2 --> R1[ロールバック1]
    end

SagaパターンはプログレッシブロールアウトのStructural的な基盤でもある: ネットワーク変更を一度にすべてではなくウェーブでデプロイする。ウェーブ1は小さなテスト対象をカバーし、成功すればワークフローはウェーブ2、次にウェーブNにファンアウトする。いずれかのウェーブが失敗した場合、補償はそのウェーブのみをロールバックする。これはカナリアデプロイメントのネットワーク版だ: ソフトウェアチームがコードリリースに適用するのと同じ制御されたプロモーション規律をネットワーク変更に適用する。

7.2.1.3. 依存関係管理#

ワークフローのステップが単独で存在することはほとんどない。エンジンは3種類の依存関係を表現して強制する必要がある:

  • データ依存関係: タスクBはタスクAが完了してBが消費する結果を生成するまで開始できない。エンジンはステップ間で出力を構造化データとして渡す。実際には、事前チェックステップが到達可能なデバイスのリストを実行ステップに渡す(最初からクエリを再実行するのではなく)。

  • マルチ入力依存関係(ファンイン): ステップは進む前に複数の並列ブランチを待つ。エンジンは各ブランチの状態を保持し、設定された完了ポリシーが満たされたときのみジョインステップをトリガーする: すべて完了、またはMのうちN、または最初の成功。

  • 外部依存関係: システムの外で何かが起こるまでステップが進めない場合がある。変更ウィンドウが開く。人間が承認する。デバイスが再起動後に到達可能になる。エンジンは、待機が解決されない場合の設定可能なタイムアウトと定義されたエスカレーションパスを持つ待機状態をサポートしなければならない。

7.2.2. トリガー#

ワークフローは何らかの方法で開始しなければならない。トリガーは2つの問いに答える: 何がワークフローを開始させるか、そして呼び出し元はその後何を体験するか。

第5章とのトリガーの違い: 第5章では、トリガーはオペレーターまたはシステムが実行エンジンを直接呼び出す方法を指した: AWXへのAPIコール、Command Line Interface (CLI)からのテンプレート起動。それは実行レイヤーのトリガーだ。ここでは、トリガーはオーケストレーターがワークフローを開始させる原因を説明しており、それがいくつかのステップの1つとしてExecutorを呼び出す場合がある。オーケストレーターはトリガーを受け取り、実行する完全なワークフローを決定する。Executorはオーケストレーターが指示することを実行するだけだ。

7.2.2.1. トリガーモード#

4つのモードが人間から完全自動化までの全スペクトルをカバーする:

  • APIコール: オーケストレーターがHTTPエンドポイントを公開する。オペレーターが手動で呼び出し、UIボタンが呼び出し、または外部システム(ServiceNow、Nautobot、CI/CDパイプライン)がイベントが発生したときに呼び出す。これらは同じメカニズムだ: 異なるのは誰がコールを開始するか、そして構造化されたイベントデータを持っているかどうかだ。開始者が人間かシステムかにかかわらず、今すぐ開始する必要がある変更に適している。

  • スケジュール: cronのようなスケジュールが設定された時間にワークフローを開始する。夜間のコンプライアンスのスキャン、週次のファームウェア監査、月次のキャパシティレポート。ほとんどのオーケストレーターはこれをネイティブにサポートし、一部のチームは外部スケジューラーを使ってオーケストレーターAPIを呼び出す。

  • メッセージキュー: オーケストレーターがキュー(Kafka、NATS、RabbitMQ)からメッセージを消費し、メッセージごとにワークフローを開始する。これによりイベントプロデューサーとオーケストレーターが分離される: プロデューサーはパブリッシュして先に進み、オーケストレーターは自分のペースで処理する。直接APIコールからの最大1回配信保証が不十分な高ボリュームイベントストリームに適している。

プッシュ(APIコール)とプル(メッセージキュー、スケジュール)の選択はボリュームと結合許容度に依存する。APIコールはシンプルだがセンダーがオーケストレーターのエンドポイントを知る必要がある。メッセージキューは高ボリュームストリームに対してより良くスケールするが、運用するインフラが追加される。

7.2.2.2. レスポンスコントラクト#

ワークフローが開始したら、呼び出し元は何を期待するかを知る必要がある:

  • 同期: 呼び出し元はワークフローが完了するまで待ち、結果を直接受け取る。呼び出し元が進むために結果を必要とする短い操作(30秒未満)に適している。ほとんどのインタラクティブなユースケースはここから始まり、10分のデプロイメントを実行しようとしてAPIクライアントがタイムアウトしたときに限界を超えていることに気づく。

  • 非同期: 呼び出し元はすぐにワークフロー実行IDを受け取り、ステータスをポーリングするか、完了したときに結果を受け取るコールバックURLを登録する。一握り以上のデバイスに触れるワークフローには必須だ。これはシステムがステータスをどう表示するかに直接影響する: プレゼンテーションレイヤー(第8章)はステータスエンドポイントとプッシュ通知を公開しなければならない。なぜなら、ユーザーがどこかからワークフローをトリガーし、オープンなHTTP接続を保持せずにそれを追跡する方法が必要だからだ。

  • ハイブリッド: ワークフローは非同期で開始するが、呼び出し元は必要に応じてオプションでsync-waitエンドポイントでブロックできる。呼び出し元に事前選択を強制しないコンビニエンスパターンだ。

イベント駆動ワークフローのべき等性: オブザーバビリティがアラートを発火するかSoT変更イベントが発火すると、複数のシステムが同時に反応する可能性がある。同じアラートが2回発火する可能性があり、メッセージキューは同じメッセージを2回以上配信する場合がある。すべてのイベント駆動ワークフローはべき等でなければならない: 同じ入力に対して2回実行しても1回実行した場合と同じ結果を生成しなければならない。オーケストレーションレベルでは、通常これには重複排除キーが必要だ: オーケストレーターが新しい実行を開始する前にチェックする一意の識別子。そのキーを持つ実行がすでに存在してまだ実行中であれば、重複を拒否またはキューに入れる。シンプルに聞こえるが、実際には驚くほど間違えやすい。

クローズドループパターン(Closed-Loop Pattern): オブザーバビリティがドリフト状態を検出する(デバイス設定がインテントから乖離した)。上記のトリガーメカニズムを通じてオーケストレーターにアラートを発火する。オーケストレーターが修復ワークフローを開始する: SoTに期待される状態をクエリし、デバイスを修正するためにExecutorを呼び出し、修正を確認するためにオブザーバビリティチェックを再実行する。チェックが通過したら、ループが閉じる。失敗したら、オーケストレーターがエスカレーションする。このパターンは自己修復自動化の基盤であり、第15章で詳しく扱う。ループが機能するのは、オーケストレーター、オブザーバビリティ、Executorが分離されていて、それぞれが独立して役割を果たせる場合のみだ。

7.2.3. レジリエンスとスケール#

これがプロトタイプで使うオーケストレーターと本番の午前3時に信頼するオーケストレーターを区別するものだ。理想的な条件下で確実に実行されるワークフローは、コーディネーター実行中の再起動を生き延びるか、800のうち40のデバイスが事前チェックに失敗した場合に処理できるか、または依存関係が解決されない場合に優雅に劣化するかについては何も教えない。次にオーケストレーターがテストされるとき、デモではないだろう。

第5章でカバーされた実行レイヤーのレジリエンスは、個々のデバイスレベルのべき等性とリトライを扱う: Playbookが1つのデバイスに失敗した場合、リトライする。第5章が扱わないのはワークフローレベルの耐久性だ: オーケストレーター自身が実行中に再起動したとき、800のうち40のデバイスが事前チェックに失敗したとき、または依存関係が解決されずワークフローがハングしたときに何が起こるか。オーケストレーター自身のスケーリング(高可用性、水平ワーカー、同時実行中のデータベース負荷を含む)は、第11章で扱うインフラの関心事だ。

7.2.3.1. 耐久性のある状態#

実行状態をメモリに保存するワークフローエンジンは再起動時にすべてを失う。これはスクリプトには許容可能だが、本番のオーケストレーションには許容できない。耐久性のある状態とは、ワークフローの進行がオーケストレーターの障害を生き延びることを意味する: どのステップが完了したか、何を生成したか、どれがまだ保留中か。オーケストレーターがオンラインに戻ると、中断したところから再開する。

Temporalはこの保証を中心に完全に構築されている: 再起動時にイベント履歴からワークフロー関数を再生し、実行中の状態をクラッシュ耐性にする。AWXはジョブ状態をデータベースに保存する。スクリプトは何も保存しない。

7.2.3.2. リトライ戦略#

すべての障害が同等ではない。ファームウェア再起動中に一時的に到達不可能だったデバイスはリトライすべきだ。恒久的な認証エラーを返したデバイスはリトライすべきではない: リトライは役に立たず、ロックアウトポリシーをトリガーする可能性がある。

リトライ設定は以下を区別すべきだ:

  • 一時的な障害: ネットワークの瞬断、APIタイムアウト、一時的なリソース競合。指数バックオフでリトライする。
  • 恒久的な障害: 不正な認証情報、メンテナンスモードのデバイス、このデバイスタイプに適用されない設定。中止してエスカレーションする。

ワークフロー定義はステップごとのリトライポリシーを指定すべきだ。事前チェックステップと設定プッシュステップは異なる障害セマンティクスを持ち、同じリトライ設定を共有すべきではない。

7.2.3.3. ロールバックと補償#

デプロイメントワークフローが途中で失敗すると、3つの選択肢がある: 部分的な状態のままにする、手動で修正する、または自動的にロールバックする。ほとんどのネットワーク操作では、部分的な状態は危険だ: 新しい設定を受け取ったデバイスはそうでないデバイスとは異なる動作をする。

Sagaパターンは各ステップの補償トランザクションを定義することでこれを解決する。ワークフローがステップNまで成功してから失敗した場合、ステップN-1から1の補償アクションを逆順に実行する。VLANデプロイメントでは: 設定プッシュが30のデバイスで成功し31番目で失敗した場合、Sagaは失敗を報告する前に30の成功したプッシュをロールバックする。

Sagaパターンは補償トランザクションをワークフロー設計時に事前に定義することを必要とする。これは事前の追加作業だ。代替手段は、どのランブックも説明しない状態にネットワークがある午前2時に発見することだ。

7.2.3.4. 並列実行制御#

800のデバイスに対する同時ファンアウトはほとんどの実行レイヤーを圧倒する。オーケストレーションレベルでの並列実行制御は以下を意味する:

  • バッチング: N台のデバイスを並列で実行し、バッチが完了するのを待ち、次のNを実行する。これにより爆発半径を制御する: 不正な設定が問題を明らかにした場合、まだ800台すべてのデバイスには触れていない。
  • レート制限: 実行レイヤーには制限がある。オーケストレーターはそれを尊重しなければならない。AWXキューを800の同時ジョブで埋めるな。
  • 部分的成功しきい値: スケールでの成功が何を意味するかを定義する。800のうち798のデバイスが正しく設定されていれば、進めるのに十分かもしれない。800のうち600は、ワークフローを停止してエスカレーションすべきだ。

7.2.3.5. タイムアウトとサーキットブレーカー#

永遠に待つワークフローは信頼性の問題だ。ブロックできるすべてのステップには設定されたタイムアウトが必要だ。タイムアウトが切れたとき、ワークフローには定義されたアクションが必要だ: エスカレーション、スキップ、または中止。

Circuit Breakerパターンはこれを繰り返し障害に拡張する: ステップがウィンドウ内でN回以上失敗した場合、試行を停止してアラートを発生させる。これにより、到達不可能な単一のデバイスがワークフローを何時間も開いたままにすることを防ぐ。

7.2.3.6. ブロック境界を越えたレジリエンス#

上記の5つのパターンはオーケストレーター自身の実行内の障害を扱う: 耐久性のある状態、リトライ、ロールバック、並列実行、タイムアウト。しかしワークフローはブロック間の境界でも失敗し、それらの障害には異なる処理が必要だ。

オーケストレーターがSoT APIを呼び出してタイムアウトを受け取った場合、適切な応答はExecutorがデバイス障害を返した場合とは異なる。SoTタイムアウトはオーケストレーターがデプロイしようとしているインテントが最新であることを確認できないことを意味する。キャッシュされたデータで進めることは古い設定を適用するリスクがある。正しい応答は通常、不確かさのまま進めるのではなく中止してリトライすることだ。到着しないプレゼンテーションレイヤーのイベント(ServiceNowからの承認Webhook)は、リクエストが拒否されたか、統合が壊れているか、またはメッセージが失われたことを意味する可能性がある。これらには異なる回復パスが必要だ: 欠落した承認は無期限にリトライすべき一時的な障害ではない。

ブロック境界の障害はワークフロー定義で明示的に分類すべきだ:

  • 依存関係が利用不可(SoT、オブザーバビリティ): ワークフローは安全に進めない。クリーンに失敗し、診断イベントを発行し、古いデータでリトライするな。
  • 依存関係が劣化(部分的なSoT読み取り、オブザーバビリティの遅延): ワークフローは信頼度が低下した状態で動作していることを明示的に認識して進む場合がある。劣化状態をログに記録し、サイレントに進むな。
  • 下流ブロックが失敗(Executorがエラーを返した、コレクターがデータを返さなかった): 上記のリトライとロールバックパターンを適用するが、アラートと監査記録が正しいブロックを指定するよう障害ソースを正確に分類する。

すべての障害をリトライ可能なデバイスエラーとして扱うワークフローは、壊れたSoT統合をリトライバジェットを使い果たすまでリトライし、間違った診断でオンコールエンジニアにページを送る。ブロック境界での障害の分類は、高速で失敗するワークフローと混乱して失敗するワークフローの違いだ。

7.2.4. ステートと追跡可能性#

午前3時に何かがうまくいかない場合、2つの問いへの即座の答えが必要だ: ワークフローの現在の状態は何か、そして誰がそれをトリガーしたか?

3ヶ月後に監査チームが質問をするとき、同じ記録が答えなければならない: 何が実行されたか、どのバージョンのワークフロー定義が実行されたか、各ステップが入力として何を受け取ったか、出力として何を生成したか、そして最終的な結果は何だったか。

これらは異なる緊急性を持つ異なる問いだが、同じソースから来る: ワークフローの実行状態とその監査記録だ。

追跡可能性は自動化プラットフォーム全体の横断的な関心事だ: SoTはインテントへのすべての変更を記録し、オブザーバビリティはネットワーク状態へのすべての変更を記録する。しかし、それらの記録のどちらも誰がワークフローを開始したか、どのステップが順番に実行されたか、何が結果を引き起こしたかを教えない。オーケストレーターの監査記録だけがそのギャップを埋める。オーケストレーターが他のすべてのブロックを調整するため、そのトレースが特定のイベントに対してフル自動化システム全体で起こったすべてのことの権威あるビューだ。

7.2.4.1. ステートはネットワーク状態ではない#

この区別は見逃しやすい。第6章では、「状態」はネットワークの運用状態を意味した: インターフェースカウンター、BGPセッション、デバイスCPU。第5章では、「状態」はSoTに保持される目的の設定インテントを意味した。ここでは、状態はワークフロー自身の実行状態を意味する: どのステップが実行されたか、どれが実行中か、どれが失敗したか、そして何を生成したか。

2つは関連している(ワークフローステップはオブザーバビリティが検証するネットワーク状態変化を生成する)が、異なる方法で保存され、異なる方法でクエリされ、異なる目的を果たす。それらを混同するな。

7.2.4.2. ワークフロー状態マシン#

すべてのワークフロー実行は定義された状態マシンを通じて移動する:

  • ペンディング: 受信されたが未開始
  • 実行中: アクティブにステップを実行中
  • 成功: すべての必要なステップが正常に完了した
  • 失敗: ステップが失敗し、ワークフローが続行できなかった
  • キャンセル済み: 人間または外部シグナルによって停止された

ワークフロー内の各ステップは独自の状態を持つ。部分的に成功したファンアウト実行は、どのデバイスが成功し、どれが失敗し、どれがスキップされたかを正確に示さなければならない。「ワークフローが失敗した」はデバイスごとの内訳なしには役に立たない。

7.2.4.3. 監査ログの要件#

すべてのワークフロー実行記録には最低限以下が含まれなければならない:

  • 実行をトリガーしたもの: 人間のアイデンティティ、システム名、トリガーイベントID
  • 開始時刻と完了時刻
  • 実行されたワークフロー定義のバージョン
  • ワークフローが受け取った完全な入力
  • すべてのステップの出力
  • 最終的な結果

この記録は改ざん防止でなければならない: 事後に編集できない。規制された環境では、オーケストレーターの監査ログが変更管理記録だ。この記録が変更可能であれば、変更管理システムは信頼できない。

ほとんどのオーケストレーションツールは自分が所有するデータベースに監査ログを書き込む。それで十分かどうかはコンプライアンス要件に依存する。一部の組織は、オーケストレーター自身のデータベースに対してクエリを実行できる人物を含む、データベースアクセスを持つ誰かによる改ざんを防ぐために、オーケストレーション監査ログを追記専用の外部システム(SIEM、書き込み一度のログストア)にエクスポートする。

7.2.5. パイプライン管理#

800の本番スイッチを設定するワークフローはアーキテクチャ的に本番ソフトウェアだ。中心的な課題はバージョン管理だけではない: ワークフロー定義をどう保存、検証、プロモーションするかで、ロジック自体がそれが管理するインフラと同じくらい信頼できるようにする。

ワークフロー定義は決定をエンコードする: どの事前チェックを実行するか、デバイスが到達不可能な場合に何をするか、何が成功を構成するか。不用意に変更することは、そのワークフローが次に実行するすべてのデバイスに何が起こるかを変える。

チームがオーケストレーションツールのUIでワークフロー定義を直接編集してサイレントに本番ワークフローを破損するのを見てきた。変更されたステップが正しく処理しないデバイスタイプに次の実行が触れるまで、誰も気づかなかった。UI編集にはレビューも、テストも、ロールバックパスもなかった。

7.2.5.1. 戦略#

  • Gitに裏付けられた定義: ワークフロー定義をバージョン管理に保存する。オーケストレーションツールはローカルのUI編集からではなく、デプロイ時にGitからプルする。これにより履歴、レビュープロセス、および以前のバージョンにロールバックする能力が得られる。

  • ブルー/グリーンパイプラインバージョン: 本番に2つのバージョンのワークフローを維持する: すべてのアクティブなワークフローを処理する現在の安定バージョンと、検証期間後に新しい実行を受け取る新バージョン。新しいバージョンが安定していることが証明されたときのみトラフィックがシフトする。

  • カナリアロールアウト: 新しいワークフローバージョンが最初に少数の実行を処理する。ファームウェアアップグレードワークフローは完全なフリートに昇格する前に10台のデバイスに新しいバージョンを適用するかもしれない。問題は800台ではなく10台で明らかになる。

7.2.5.2. オーケストレーション変更のテスト#

ワークフロー定義は本番前にテストできる:

  • ドライランモード: 実際の入力に対してワークフローを実行するが、ネットワークを変更するステップをスキップする。ロジックが期待されるアクションのシーケンスを生成することを確認する。
  • ステージング環境: 本番に対して実行する前にワークフロー変更をテストするための専用デバイスのサブセット。
  • シャドウ実行: 現在のバージョンと並行して新バージョンを実行するが、その結果は無視する。カットオーバー前に分岐を検出するために出力を比較する。

2つの異なるロールバック: ワークフロー実行のロールバックは特定の実行によって行われたネットワーク変更を元に戻すことを意味する(セクション7.2.3のSagaパターンによって処理される)。ワークフロー定義のロールバックはワークフローロジックを以前のバージョンに戻すことを意味する: Git参照を切り替え、定義を再デプロイし、次の実行が以前のバージョンを使用する。これらは直交する操作だ。それらを混同することは、チームが間違ったものをロールバックする原因になる。

7.2.6. ソリューションランドスケープ#

注記: ここにリストされているツールは説明目的の例であり、推薦ではない。それぞれが異なるアーキテクチャとトレードオフプロフィールを持つ。チームの能力、既存のツーリング、および運用上の制約に対して評価すること。

オーケストレーションツール市場は、ネットワーク固有のプラットフォームから汎用ワークフローエンジンまで、幅広いモデルをカバーする。問いはほとんどの場合「どれが最善か」ではなく、「どれが我々の運用方法に合っているか」だ。

ツール実行モデルアーキテクチャ的に異なる点ネットワーク自動化への適合性
AWX / Ansible AAPYAMLワークフローテンプレート、UI優先オーケストレーターとExecutorが同じシステム: Ansibleジョブがファーストクラスで翻訳レイヤーが不要。RBAC、認証情報、インベントリが統合されている。実行にAnsibleをすでに使用しているチーム。Ansibleがすでに実行レイヤーである場合の簡単なパス
Itentialローコード/ノーコードビジュアルビルダー、ネットワーク固有ネットワーク運用のために特別に構築: ITSM、IPAM、マルチベンダーデバイス用のビルド済みアダプター。開発者以外がアクセスできるワークフロービルダー。カスタムコードなしでマルチベンダー、マルチシステム統合が必要なエンタープライズネットワークチーム
PrefectPythonのコードとしてのDAG、開発者優先ワークフローはデコレーターを持つPython関数。パイプラインはソフトウェアだ: アプリケーションコードのようにテスト、バージョン管理、観測される。パイプライン自体の強力なネイティブオブザーバビリティ。ソフトウェアエンジニアリングの規律でオーケストレーションを扱いたいPythonに慣れたチーム
Temporal耐久性のある実行エンジン、コード定義ワークフロー途中のクラッシュを生き延びる: すべてのステップが最後のチェックポイントから再生される。SagaとCompensationはボルトオンではなくファーストクラスの構造体。部分的な実行とロールバックが確実でなければならない長時間実行ワークフロー(ファームウェアアップグレード、大規模なロールアウト)
Windmillスクリプト優先、軽量、オープンソースワークフロー内の各ノードは独立したスクリプト(任意の言語)。低い運用オーバーヘッド; エンタープライズプラットフォームの複雑さなしに自己ホストとカスタマイズが容易。プラットフォームの重みなしに柔軟なカスタムロジックが欲しい小さなチームまたは組織

これらすべてにわたって1つのアーキテクチャ上の問いが切る: オーケストレーションツールが実行エンジンとしても機能するか、それとも分離しているか?AWX/AAPは両方を1つに組み合わせる。Prefect、Temporal、Windmillは別の実行ツール(Ansible、カスタムスクリプト、API)を呼び出すオーケストレーターだ。組み合わせたモデルは運用がシンプルで、分離したモデルは実行エンジンを独立して交換する柔軟性が高い。第3章では最小限の結合のもとでこのトレードオフを紹介した。

上の表のAWX/AAPの行は、それを主要プラットフォームとして検討するチームへの明確化注記に値する。AWXワークフローテンプレートがオーケストレーターで、個々のAnsibleジョブテンプレートがExecutorだ。2つの間のアーキテクチャ上の境界は、同じプラットフォーム内で実行していても存在する。すべてのロジックをジョブテンプレート(Executor)に入れてワークフローテンプレートをそれらを連鎖させるためだけに使用するチームは、実行プリミティブからオーケストレーターを効果的に構築しており、可視性、リトライ設定、ロールバックオプションが制限される。逆に、ワークフローテンプレートにビジネスロジックを入れる(外部データに基づく条件分岐、動的インベントリ選択)チームはAWXを真のオーケストレーターとして使用している。AWXの監査証跡、承認ゲート、通知フックはワークフローレベルの機能なので、この区別は重要だ。すべてのロジックがジョブテンプレートにある場合、再構造化なしにそれらの機能を使用できない。これはAWXの制限ではなく、設計でオーケストレーションと実行を区別しないことの結果だ。

これらのツールは大幅に重なっている: Pythonは PrefectとWindmillの両方で動作し、PrefectとTemporalの両方がAnsibleを呼び出せる。大まかな出発点: チームがAnsible優先で新しいコンポーネントを最小限にしたい場合、AWXワークフローテンプレートが自然な適合; テスト可能なコードファーストのパイプラインをソフトウェアエンジニアリングの規律で望む場合、PrefectとWindmillはどちらも異なる運用モデルで機能する; 部分的な障害下での耐久性が主要な制約であれば、Temporalの再生モデルはそれのために特別に構築されている。これを最終的な答えではなく、評価のための足がかりとして扱う。

7.2.7. エージェンティックオーケストレーター#

7.2.1にリストされたエージェンティック調整アプローチは異なる実行モデルを導入する: 事前定義されたDAGに従う代わりに、Large Language Model (LLM)がゴールを受け取り、実行時にアクションのシーケンスを決定する。エージェントはオブザーバビリティとSoTブロックから現在の状態を観察し、どのアクションがギャップを埋めるかを推論し、Executorを呼び出し、結果を確認するために再観察する。期待どおりでなければ、また推論する。決定ロジックはワークフロー定義にエンコードされていない; モデルの推論の中にある。

フルオートメーションプラットフォーム全体でこれを可能にするのはModel Context Protocol (MCP)(Model Context Protocol)だ。各ビルディングブロックはMCPサーバーを公開する: エージェントが推論ループの一部として呼び出せるツールの定義されたセット。SoTサーバーはデバイスクエリとインテントルックアップを公開し、オブザーバビリティサーバーは状態クエリとコンプライアンスチェックを公開し、Executorサーバーはジョブトリガーとステータスポーリングを公開する。エージェントは、ワークフロー作成者がすべての可能な組み合わせを事前にコード化せずに、状況が必要とするどのシーケンスでもこれらのツールを呼び出す。

アーキテクチャ上の意味は、ブロックが互いについて、またエージェントについて知る必要がないことだ。MCPインターフェースがコントラクトだ。今日DAGベースのワークフローからRESTコールに答える同じSoTが、変更なしにAIエージェントからのMCPツールコールに答えるだろう。クリーンなブロック境界は、密結合が決して報われない方法でここで報われる。

DAGベースのワークフローとエージェンティックオーケストレーションは相互排他的ではない。実際には、DAGベースのワークフローはルーティン、よく定義された操作に適している: VLANデプロイメント、コンプライアンスのスキャン、ファームウェアアップグレード。これらは高頻度で、予測可能な監査証跡が必要で、LLM推論に依存すべきではない。エージェンティックレイヤーは新規のものを処理する: インシデント駆動の修復、異常の調査、現在の状態が理解されるまで正しいアクションのシーケンスが決定できない状況。2つのアプローチは同じプラットフォームで共存でき、状況が既知のパターンに一致しない場合、DAGがエージェンティックワークフローにルーティングする。

このセクションはパターンを紹介する。エージェンティックオーケストレーションがネットワーク全体での継続的な自律運用にどうスケールするかを含む完全なアーキテクチャ的扱いは、第17章にある。

7.3. 実装例#

7.3.1. キャンパスVLANサービスライフサイクルのオーケストレーション#

パート2を通じて同じキャンパスネットワークを追跡してきた。第4章では、NautobotにVLANサービスリクエストを保存した: VLAN ID、サブネット、ターゲットスイッチ、ベンダーごとの設定テンプレート。第5章では、AWX経由のAnsibleを使ってキャンパススイッチにその設定をプッシュした。第6章では、デプロイメントを検証してサービスの監視を開始した。

まだ対処していないのは、それらすべてを誰が調整するかだ。各章には暗黙の仮定があった: エンジニアが各ステップを手動で実行した。ここではそれを明示的にし、エンジニアをワークフローに置き換える。

シナリオ

アプリケーションチームが新しいアプリケーションセグメントのリクエストを提出した: VLAN app-payments、サブネット10.22.14.0/24、CiscoとAristaスタック全体でbuilding-bのすべてのアクセススイッチにデプロイ。リクエストはServiceNowを通じて提出され、最終承認を受けたばかりだ。

その承認がWebhookを発火する。オーケストレーターがそれを受け取り、VLANサービスデプロイメントワークフローを開始する。

ワークフロー

すでに整っている実行レイヤーと一致して、AWXワークフローテンプレートとしてこれを実装する。AWXは条件分岐と承認ゲートを持つジョブテンプレートを連鎖させるワークフローテンプレートをサポートしており、このシナリオには十分だ。

flowchart TD
    A[ServiceNow Webhook\nVLAN申請が承認された] --> B[ステップ1: SoT検証\nNautobotでデバイスレコードと\nVLAN定義の完全性を確認]
    B --> C{SoTデータは完全か?}
    C -- いいえ --> Z1[中止: エンジニアに通知\nServiceNowチケットを更新]
    C -- はい --> D[ステップ2: ファンアウト事前チェック\n全ターゲットスイッチで\n到達性とVLAN状態を確認]
    D --> E{事前チェック通過?}
    E -- 失敗あり --> Z2[中止: デバイスごとの\n失敗をServiceNowに報告]
    E -- 通過 --> F[ステップ3: 承認ゲート\n本番実行前の任意の\n人間によるサインオフ]
    F --> G[ステップ4: デプロイメント実行\nAWX経由のAnsible Playbook]
    G --> H[ステップ5: ファンアウト検証\nスイッチごとのオブザーバビリティチェック]
    H --> I{全デバイスが検証済みか?}
    I -- 完全成功 --> J[ステップ6: 通知\nServiceNowチケットを更新\nSlackに投稿]
    I -- 部分的な失敗 --> K[ステップ6a: Saga補償\n失敗したスコープのみロールバック]
    K --> J

ステップ1: SoT検証

オーケストレーターはNautobotにクエリして、ネットワークに何も触れる前にデバイスレコードとVLAN定義が完全であることを確認する。このガードステップはExecutorではなくオーケストレーターの責任だ: Executorは有効な入力を受け取るべきであり、プッシュの途中で不正なデータを発見すべきではない。何かのチェックが失敗した場合、ワークフローは中止してServiceNowチケットを特定のギャップで更新する。(SoT検証の構造化方法は第4章でカバーされている。)

ステップ2: ターゲットスイッチ全体の事前チェック(ファンアウト)

ワークフローはすべてのターゲットスイッチに並列でファンアウトする。各ブランチは軽量な事前チェックを実行する: 到達性、VLANテーブルの状態、利用可能なキャパシティ。ファンインステップは結果を分類してブランチし、失敗しきい値は設定可能だ。オーケストレーション的に重要なのは、これが順次ポーリングではなく条件分岐を持つファンアウト/ファンインパターンだということだ。(事前チェック実行パターンは第5章にある。)

ステップ3: 承認ゲート

本番プッシュ前の任意の人間によるサインオフ。エンジニアは承認または拒否のために30分ある。タイムアウトは自動的に進行しログに記録される。オーケストレーターは承認が届くかウィンドウが切れるまで、外部依存関係を持つ待機状態にワークフローを保持する。

承認ゲートはアーキテクチャ上の決定ではなく、ポリシー上の決定だ。高成熟度チームは多くの場合それを削除し、事前チェック結果から導出された自動信頼しきい値で人間の承認を置き換える: 事前チェックのカバレッジが95%を超えて重大な障害が見つからなければ、自動的に進む。信頼を構築しているチームはゲートを保持する。どちらの選択も第1章で議論した自動化成熟度スペクトラムの異なる地点で有効だ。

ステップ4: デプロイメント実行

オーケストレーターは第5章のAWXジョブテンプレートをトリガーし、SoT検証ステップからのパラメーターを渡し、結果を待つ。Executorが残りを処理する。これが実際の関心事の分離だ: オーケストレーターが行動することを決定し、Executorが行動する。

ステップ5: オブザーバビリティ検証(ファンアウト)

デプロイメント後、2番目のファンアウトがターゲットスイッチごとに検証ジョブを実行する: VLANがVLANテーブルに表示されるか、インターフェースが期待される状態にあるか?ファンインは結果を分類する: 完全成功、部分成功、または失敗。(オブザーバビリティレイヤーが何を検証してどうするかは第6章でカバーされている。)

ステップ6: 結果ルーティング

完全成功はワークフローをクローズする: ServiceNowチケットが更新されてSlackにサマリーが投稿される。部分的な失敗はSaga補償をトリガーする: ロールバックPlaybookは検証に失敗したデバイスのみに実行され、成功したデバイスは設定を保持する。ServiceNowチケットはデバイスごとの結果を記録する。

冒頭のエンジニアに戻って

このワークフローは第3章のNAFフレームワークからの7つすべてのビルディングブロックが共に機能することを示す: Nautobot(SoT)がインテントを提供し、AWX(Executor)がそれを適用し、Prometheus(オブザーバビリティ)が結果を検証し、AWXワークフローテンプレート(オーケストレーター)がフルシーケンスを調整し、ServiceNow Webhook(プレゼンテーション、第8章でカバー)がアプリケーションチームのリクエストからフロー全体をトリガーした。

冒頭の話のエンジニアはまだここにいる。彼女は承認ゲートが発火したときにレビューする人物だ。Sagaパターンがフラグを立てたが完全に解決できなかった部分的な失敗を調査する人物だ。彼女はまだエキスパートだ。オーケストレーターが彼女から取ったのはエキスパート性ではなく、シーケンスを頭の中でまとめてステップごとに再実行し、できる唯一の人物であるという負担だ。それが自動化なしの調整が実際にコストするものだ。

7.4. サマリー#

本章は、Orchestratorが一連の機能する自動化ツールを一体として動作するシステムに変えるビルディングブロックであることを確立した。それなしには、調整は手動のままで、障害には人間の介入が必要で、ネットワークのスケールが実際に可能な自動化の量の制限になる。

核心では、オーケストレーションは多段階プロセスがどう構造化され実行されるかを定義するワークフローエンジンに依存する。モノリス、DAG、コレオグラフィー、エージェンティック調整の選択はツーリングの好みではなく、アーキテクチャ上の決定だ。その名前付きパターン(シーケンシャル、ファンアウト、条件分岐、Saga)を持つDAGモデルはネットワーク自動化の本番標準だ。インターフェースレイヤーとしてModel Context Protocol (MCP)を持つセクション7.2.7で導入されたエージェンティックパターンは異なるトレードオフを表し、第17章でその完全な扱いを受ける。

トリガーは外部世界がオーケストレーターに到達する方法を定義する。実行レイヤートリガー(第5章)とオーケストレーションレイヤートリガーの区別はアーキテクチャ的に重要だ: 一方は単一のデバイス変更を駆動し、他方は調整された多システムワークフローを駆動する。イベント駆動自動化とクローズドループパターンは、自動トリガーが人間のレビューなしに発火するため、べき等性と重複排除の交渉不可能な特性を継承する。

レジリエンスとスケールは、デモで機能する自動化と午前3時に機能する自動化を区別する。耐久性のある状態、一時的な障害と恒久的な障害を区別するリトライ戦略、部分的な障害補償のためのSagaパターン、大きなデバイス群のための並列実行制御は後で追加するオプション機能ではない。条件が理想的でないときにオーケストレーターを信頼できるかどうかを定義する。

ステートと追跡可能性は何が実行されたか、誰がトリガーしたか、すべてのステップが何を生成したかの記録を提供する。この記録はネットワーク運用状態(第6章)とネットワークインテント(第4章)から区別される。改ざん防止の監査ログはコンプライアンス要件であり、後付けではない。パイプライン管理はループを閉じる: ワークフロー定義は本番ソフトウェアであり、自動化プラットフォームで動作する他のコードと同じ規律でバージョン管理、テスト、プロモーションされなければならない。

キャンパスVLANサービスシナリオはこれらの5つの機能をまとめた: ServiceNow WebhookによってトリガーされたSoT検証、事前チェック、実行、オブザーバビリティ検証、Saga補償を調整する10ステップのワークフローで、調整ループにエンジニアはいない。自然な次の問いは誰がこのワークフローが実行されているのを見て、それをトリガーしたアプリケーションチームがその進行をどう追跡するかだ。それが第8章でカバーするプレゼンテーションレイヤーだ。

💬 Found something to improve? Send feedback for this chapter