0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

パイプラインアーキテクチャにおいて必須な設計原則

Posted at

前置き

IMG_20251020_185424.jpg

今回は、CI/CDやデータパイプラインを「単なるスクリプトの連なり」から、

「信頼性と保守性の高い、堅牢なソフトウェアアーキテクチャ」

へと昇華させるため、必要な設計思想を書いていきます。

すべてを基礎として有機的につなげ、パイプラインの保守性や可観測性を向上させていきましょう。

契約による設計 × パイプライン

ソフトウェア工学におけるDbCは、ソフトウェアのコンポーネント(この場合はパイプラインの各ステップ)が、お互いに 「契約」 を結んで動作することを前提とします。

この「契約」は、主に以下の3つの要素で構成されます。

1. 事前条件 (Preconditions)

パイプライン中のステップ2の事前条件として、ステップ1のチェックが全て通っていること

意味

ステップ2(消費者)が、ステップ1(生産者)に対して

「この処理を実行する前に、あなたは最低限この品質を保証してください」

と要求する契約です。

・ステップ1が生成したファイルが存在する。

・ステップ1が生成したデータのスキーマが正しい。

・ステップ1の適応度関数(例:テストカバレッジ)が閾値を満たしている。

これらが、次のステップ2を開始できる事前条件です。

2. 不変条件 (Invariants)

ステップ2が実行中常に満たすべき条件。

意味

そのステップが実行されている間、常に真でなければならないシステムの「健康状態」に関する契約です。

・処理時間が、設定されたタイムアウト(例:10分)を超えていない。

・メモリやCPUの使用率が、設定された閾値を超えていない。

・(ストリーム処理の場合)処理の遅延(ラグ)が、SLA(例:30秒)を超えていない。

3. 事後条件 (Postconditions)

意味

ステップ2が、後続のステップ(あるいは最終的な利用者)に対して

「私の処理が正常に完了したら、この品質を保証します」

と約束する契約です。

・ステップ2が生成した出力データが、定義されたスキーマを満たしている。

・出力データに、NULLやマイナス値などの不正な値が含まれていない。

・機密情報がマスキングされている。

なぜこれがパイプラインの保守性を劇的に向上させるのか

DbCをパイプラインに適用すると、保守性が飛躍的に向上します。

1. 障害検知と原因特定が瞬時に行える

これが最大のメリットです。

パイプラインが失敗したとき、「誰が契約を破ったか」 が即座に分かります。

「事前条件」で失敗→ステップ1(生産者)が悪い。

「不変条件」or「事後条件」で失敗→ステップ2(消費者)の内部ロジックが悪い。

これにより、パイプラインのデバッグと修復時間(MTTR)が劇的に短縮されます。

2. 変更が安全になる (OCP/ECRSの実践)

パイプラインの各ステップが「契約」によってカプセル化されます。

ステップ2をリファクタリング(ECRS)したい場合、

開発者は、「事前条件を満たし、事後条件を守る」 ことだけに集中すればよくなります。

内部の実装を(例えばPythonからGoに)書き換えても、この契約さえ守れば後続のステップに影響を与えません。


さらに、新しいステップ3を追加(OCP)したい場合、ステップ2の「事後条件」が、ステップ3の「事前条件」を満たしているかを確認するだけで済みます。

3. 責務が明確になる

「このステップは何を保証し、何を期待するのか」がコード(YAMLや設定)として明文化されます。

これにより、「なんとなく動いている」という属人化したパイプラインから、誰でも保守・改善できるアーキテクチャへと進化します。

ステートマシン図 × パイプライン

パイプラインで処理されるリリース対象(成果物、Artifact)の状態をステートマシン図として明確に定義し、各ステップで想定される状態遷移を検証・強制することは、パイプラインの安全性と信頼性を確保する上で極めて重要です。

これは、上記の「契約による設計(DbC)」を、成果物のライフサイクルという時間軸に適用したものです。

なぜステートマシンが重要なのか? (工場のアナロジー 🏭)

CI/CDパイプラインは、製品(ソフトウェア)を作る自動化された工場です。

成果物 (Artifact)

工場で組み立てられている製品(例: 車)

パイプラインのステップ

各工程(例: 溶接、塗装、検査)

ステートマシン

製品が各工程を経て「どの状態にあるべきか」を定義した品質管理基準(例: 溶接済, 塗装済, 検査合格)

メカニズムのアナロジー

もし、「塗装」工程に「溶接済」ではない部品(想定しない状態変化)が流れてきたら、
工場はそのラインを即座に停止させますよね?
そうしないと、不良品が出荷されてしまうからです。

CI/CDパイプラインもまったくメカニズムは同じです。

「セキュリティスキャン」のステップに、「テスト合格済」ではない成果物が渡されたら、何かが根本的に間違っています。

よって、そのままデプロイに進むのは極めて危険です。

パイプラインにおけるステートマシンの実装

これをパイプラインアーキテクチャで実現するには、以下の要素が必要です。

1. 状態の定義

まず、成果物が取りうる「状態」を明確に定義します。

例として、
SOURCE_CHECKED_OUT, BUILT, UNIT_TESTED, STATIC_ANALYZED, READY_FOR_INTEGRATION, INTEGRATION_TESTED, SECURITY_SCANNED, READY_FOR_STAGING, DEPLOYED_TO_STAGING, STAGING_TESTED, READY_FOR_PRODUCTION, DEPLOYED_TO_PRODUCTION, OBSERVED_STABLE

などなど。

2. 各ステップの「契約」としての状態遷移

パイプラインの各ステップ(ジョブ)は、自身の 事前条件として「期待する入力状態」を、
事後条件として「保証する出力状態」を定義します。

例 (security_scan ジョブ)

事前条件: 成果物の状態が INTEGRATION_TESTED であること。

実行:スキャンを実行する。

事後条件:成果物の状態を SECURITY_SCANNED に遷移させる。

3. 検証と停止メカニズム

各ステップの開始時に、渡された成果物の現在の状態が、そのステップの事前条件と一致するかを検証します。

一致しない場合には、即座にパイプラインを失敗させ、デプロイを停止します。

これは、パイプラインのロジック自体にバグがあるか、手動で不正な操作が行われた可能性を示す重大なアラートだからです。

結論

パイプラインにおける成果物の状態をステートマシンとしてモデル化し、各ステップでその状態遷移を厳格に検証することは、以下のメリットをもたらします。

安全性の向上

テストやスキャンなどの重要な品質ゲートが意図せずスキップされることを防止します。

可読性と保守性の向上

パイプラインが「何をしているのか」「何を保証するのか」が、状態遷移として明確に文書化されます。

デバッグの容易化

パイプラインが停止した際、「どのステップ」で「どの状態遷移」に失敗したのかが即座に分かり、原因特定が容易になります。

これは、単にプロセスを自動化するだけでなく、そのプロセス自体の正当性と安全性を保証するための、極めて重要な設計プラクティスです。

イベントソーシング × パイプライン

扱う成果物が、まだシンプルなモノリシックアーキテクチャのものなら、以下のような
一本のエンドツーエンドのパイプラインで済むのでいいんです。

IMG_20251020_185440.jpg

問題は、モジュラーモノリスとかにしないといけなくなってきてからです。

モジュラーモノリス構造の際には、以下のような「ファンインパイプライン」構造を取り、
各モジュールは別々にパイプラインを通っていき、集約されるスタイルを取ります。

IMG_20251020_185424.jpg

このファンインパイプラインのように、複雑さが増す(=複数のプロセスが集約される)アーキテクチャにおいてこそ、各ステップの状態変化を記録するイベントソーシングの考え方は、トラブルシューティングを劇的に容易にするための最も強力な武器となります。

なぜイベントソーシングが「最強の武器」になるのか

各ステップでの状態変化を(例: Pipe-A: BUILT, Pipe-A: TEST_PASSED)のようにイベントとして記録し続けることは、パイプライン上の 「フライトデータレコーダー(航跡記録器)」 を持つことと等価です。

この「何が起きたか」の 不変のログ が存在することにより、問題が発生した際の原因調査が非常に行いやすくなります。

1. シナリオA:「デプロイできない」(事前)

問題

ファンインパイプラインの最後の「集約デプロイ」ステップが失敗した。

原因

10本のパイプラインのうち、どれがデプロイの「事前条件」を満たしていないのかが分からない。

イベントソーシングによる解決

イベントログ(状態DB)をクエリするだけで、即座に原因が特定できます。

image.png

メリット

推測や関係者へのヒアリング、その際の調整コストは不要です。

「Pipe-Cがセキュリティスキャンに失敗したため、集約デプロイが契約違反(事前条件の不履行)として停止した」という事実が即座に判明するからです。

2. シナリオB:「問題がある状態でデプロイ」(事後)

問題

本番環境で「注文ができない」という障害が発生。

原因

どのデプロイがこの問題を引き起こしたのか、いつから発生したのかが分からない。

イベントソーシングによる解決

イベントログは、完璧な監査証跡(Audit Trail)およびトレーサビリティを提供します。

①. まず、「DEPLOYED_TO_PRODUCTION」イベントを時系列で確認します。そこで「障害発生(14:00)の直前にデプロイされたのは、13:55のPipe-A (v1.5.0)Pipe-B (v3.1.0)だ」と特定します。

②. 次に、そのデプロイに関連する 「状態遷移の“全履歴”」 をドリルダウンします。

③. Pipe-A (v1.5.0)の履歴を見ると、STATE: INTEGRATION_TEST_PASSEDが記録されている。

④. Pipe-B (v3.1.0)の履歴を見ると、STATE: INTEGRATION_TEST_SKIPPED (!!!) という異常な状態遷移が記録されているのを発見します。

メリット

なぜ問題が起きたのか(=Pipe-Bがテストをスキップしたままデプロイされたから)が定量的な証拠をもって判明します。

これにより、

サービス復旧時間(MTTR)が劇的に短縮される

だけでなく、

「なぜテストがスキップされたのか?」 という、パイプラインアーキテクチャ自体の根本原因の改善

にも繋がります。

結論

ファンインパイプラインは、その集約性ゆえに「何が起きているか」がブラックボックス化しやすい危険性をはらんでいます。

ゆえに、「各ステップの状態変化をイベントソーシングとして記録する」アプローチは、
そのブラックボックスに 完璧な「可観測性(Observability)」 を与え、
トラブルシューティングを運用者の「推測ゲーム」から「事実の確認作業」へと変える、
最も効果的な設計といえるでしょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?