インフラストラクチャドリフトがTerraform Planを無意味にするとき

新しいアプリケーションバージョンをデプロイするためにパイプラインを実行したとしよう。Terraform planが実行され、その出力は本番データベースインスタンスのリサイズを提案している。チームの誰もデータベースに触れるつもりはなかった。プルリクエストのレビュアーは困惑しながらplanを眺める。これは新しいバージョンの副作用だろうか?見逃していたセキュリティ要件だろうか?リリースをブロックしないために誰かが承認し、ピークトラフィック時にデータベースがより小さなハードウェアで動作することになる。

このシナリオは仮定の話ではない。これは、インフラストラクチャがコードの定義から乖離(ドリフト)したときに発生する。そして、一度ドリフトが定着すると、安全なインフラ変更のための最も信頼できるツールが信頼できなくなる。

ドリフトの本当の意味

インフラストラクチャドリフトとは、コードが定義するインフラの理想状態と、クラウド環境に実際に存在する状態との間のギャップである。あなたはTerraform、Pulumi、またはCloudFormationのコードでリソースを定義した。誰かがクラウドコンソールにログインし、インスタンスタイプを変更したり、セキュリティグループルールを追加したり、データベースパラメータを微調整したりする。その変更はコードリポジトリには決して反映されない。パイプラインも通過しない。ただ発生するだけだ。

インフラストラクチャはまだ動作する。アプリケーションも実行され続ける。しかし、あなたのコードはもはや現実を記述していない。それは現実がかつてそうだった状態を記述しているに過ぎない。

あなたを欺くPlan

TerraformのようなInfrastructure as Codeツールは、コード定義とステートファイル(クラウド上の実際の状態を追跡する)の2つを比較することで動作する。両者が一致している場合、得られるplanは正確である。最新のコードコミットに基づいて、何が変更されるかを正確に示す。

次のフローチャートは、ドリフトがコード、ステート、実際のインフラストラクチャの間に不一致を生み出し、不正確なplanにつながる仕組みを示している:

flowchart TD A[コード定義] --> B[Terraform Plan] C[ステートファイル] --> B D[実際のインフラ] -.->|ドリフト| C D -.->|同期不全| B B --> E[意図しない変更を示すPlan] style D fill:#f9f,stroke:#333,stroke-width:2px style E fill:#f96,stroke:#333,stroke-width:2px

ドリフトはこの比較を壊す。実際のインフラストラクチャがパイプライン外で変更されたため、ステートファイルは古くなる。Terraformがplanを実行すると、古いステートを読み取り、それをコードと比較する。その結果、Terraformが変更を加えようとしているように見えるが、実際にはそれらの変更はインフラをコードの状態に戻すための修正である。あなたが意図した変更ではない。

これがplanドリフトである:コードと現実の間の既存の差異を反映したplanであり、実際にデプロイしたい変更ではない。

ドリフトがパイプラインに与える3つの損害

予期しない破壊

これが最も危険な結果である。セキュリティチームが機密性の高いワークロードを分離するために手動で作成したネットワークセキュリティグループを想像してほしい。そのリソースはIaCコードに存在しない。パイプラインがそのリソースを定義していないコードを使ってterraform applyを実行すると、Terraformはそれを存在すべきでないものと見なす。そして破壊する。セキュリティ設定は静かに消え去り、何かが壊れるまで誰も気づかない。

レビュー時間の浪費

プルリクエストのレビュアーは、デプロイする機能とは無関係のリソースへの変更を示すplanを目にする。それらが偶発的な副作用なのか、必要な更新なのか、あるいは何か怪しいものなのかを判断できない。実際の作業の一部ではない変更を調査するために時間が費やされる。レビューサイクルは長引く。チームは実際のコード変更に集中する代わりに、「また誰かが本番を触ったのか?」といった質問をし始める。

自動化への信頼の喪失

planが予期しない変更を表示し続けると、チームはパイプラインを信頼しなくなる。デプロイ前に手動チェックを実行する。パイプラインが信頼できないと感じるため、コンソールで直接変更を行い始める。皮肉な結果だ:パイプライン外での変更が増えれば増えるほど、ドリフトは悪化する。パイプラインの信頼性が低下するため、人々はそれを迂回するようになり、さらに信頼性が低下する。

実際のチームでドリフトが発生する理由

ドリフトはエンジニアの能力不足が原因ではない。実際の業務では、パイプラインが最速の経路ではない状況が生まれるからだ:

  • インシデントにより即時の変更が必要になる。オンコールエンジニアは、コードを書き、コミットし、パイプラインを待つには時間がかかりすぎるため、コンソールで修正する。
  • データベース管理者がIaCツールを日常的に使っていないため、クラウドコンソールで直接パラメータを調整する。
  • セキュリティチームが監査中に一時的なファイアウォールルールを追加し、文書化するのを忘れる。
  • 開発者が何かを素早くテストする必要があり、手動でリソースを作成し、「後でコードに追加する」つもりでいる。

これらの行動はそれぞれ単独では理にかなっている。しかし、それらが積み重なることで、コードと現実の間にギャップが生まれ、パイプラインが信頼できなくなるまで拡大する。

被害が出る前にドリフトを検出する

解決策はコンソールアクセスを禁止することではない。緊急時や正当な運用上のニーズは常に厳格なルールを迂回するため、そのアプローチは失敗する。解決策は、ドリフトを自動的に検出し、誰かがplanを実行する前に表面化させることだ。

ほとんどのIaCツールはドリフト検出機能を提供している。Terraform CloudとEnterpriseには、スケジュールに従ってplanを実行し、実際のインフラストラクチャがステートと異なる場合にアラートを出すドリフト検出機能がある。TerraformのオープンソースフォークであるOpenTofuも同様の機能を含んでいる。また、スケジュールされたパイプライン実行を使用して、ステートを実際のクラウドリソースと比較する独自の検出機能を構築することもできる。

重要なのは、ドリフトを可視化することだ。チームメンバーがコンソールで何かを変更した場合、次のスケジュールされたドリフトチェックでそれをフラグ付けする必要がある。チームはその後、コードを変更に合わせて更新するか、変更をコードの定義に戻すかを決定できる。どちらの選択も有効であり、意図的で追跡可能である限り問題ない。

実践的なドリフト検出チェックリスト

IaCでインフラストラクチャを管理している場合、次のチェックをルーチンに追加することを検討してほしい:

  • 本番環境に対して週次のドリフト検出実行をスケジュールする
  • ドリフトが障害を引き起こしたときだけでなく、見つかったときにチームにアラートを出す
  • 定期的なチーム同期でドリフトレポートをレビューする
  • ドリフトを調整するための明確なプロセスを文書化する:コードを更新するか、変更を元に戻すか
  • 手動によるコンソール変更は恒久的な解決策ではなく、一時的な回避策として扱う

ドリフトを無視することの本当のコスト

ドリフトはインフラストラクチャを即座に壊すわけではない。それはパイプラインの信頼性をゆっくりと侵食する。検出されないドリフトが1つあるごとに、次のplanの信頼性は低下する。チームを驚かせるplanが1つあるごとに、彼らは自動化を敬遠するようになる。最終的に、インフラストラクチャはブラックボックスと化し、誰も実際に何が動いているのかを知らず、コードリポジトリは真実の源ではなく願望を記した文書と化す。

目標はドリフトを完全に排除することではない。複雑なシステムではある程度のドリフトは避けられない。目標は、それを迅速に検出し、可視化し、チームにそれを調整するための明確な道筋を提供することだ。あなたのplanが意図した変更のみを示すとき、あなたは再びパイプラインを信頼できるようになる。