コードから乖離するインフラストラクチャ

Terraformでインフラ全体を定義しているとします。すべてのセキュリティグループ、インスタンスサイズ、データベースパラメータがコード化され、パイプラインを通じてデプロイされています。リポジトリの内容と本番環境で動いているものが一致していると確信しているでしょう。ところがある日、planを実行すると、まったく予期していない変更が表示されます。触っていないリソースが変更されようとしています。コードと現実の間に何か違いが生じているのです。

その違いには名前があります。ドリフトです。

ドリフトの正体

ドリフトとは、インフラストラクチャコードが「あるべき姿」として定義している状態と、クラウドプロバイダやサーバーに実際に存在する状態との間のギャップです。TerraformやPulumiのファイルは望ましい状態(Desired State)を定義します。AWS、Google Cloud、Azure上で動作しているリソースは実際の状態(Actual State)を表します。この2つが一致しないとき、ドリフトが発生しています。

単純に聞こえますが、その影響は深刻です。Infrastructure as Code(IaC)は、「コードがすべての設定の唯一の情報源である」という重要な前提に基づいています。この前提が成り立つのは、インフラへのすべての変更がパイプラインを通じて行われる場合だけです。パイプラインの外で何かが変更された瞬間、その前提は崩れます。

ドリフトが忍び寄る3つの経路

ドリフトは誰かがミスをしたから発生するわけではありません。インフラを管理するのは、現実のプレッシャーにさらされた生身の人間だからです。

以下の図は、各経路が意図されたIaCパイプラインを迂回し、どのようにドリフトに至るかを示しています。

flowchart TD A[意図された経路: IaCパイプライン] --> B[コード上の望ましい状態] C[手動によるコンソール変更] --> D[直接変更] D --> E[ドリフト] F[インシデント対応] --> G[緊急変更] G --> E H[外部ツール / オートスケーラー] --> I[パイプライン外の自動変更] I --> E B -.->|ドリフトなし| J[実際の状態がコードと一致] E --> K[実際の状態 != コード]

手動変更

誰かがクラウドコンソールにログインして、直接変更を加えます。新しいオフィスIPからサーバーにアクセスできるようにセキュリティグループのルールを追加するかもしれません。デモが近づいてきてキャパシティが必要になったので、インスタンスをリサイズするかもしれません。コンソールでの変更は30秒で終わります。Terraformコードを更新し、プルリクエストを作成し、レビューを待ち、パイプラインを実行するのは、はるかに時間がかかります。だから、その手順をスキップしてしまうのです。

これは怠慢ではありません。小さな変更に大きなコストがかかるシステムに対する、合理的な反応です。しかし、コンソールでの直接変更は、コードと現実の間にギャップを生み出します。

インシデント対応

本番環境がダウンしているとき、誰も最初にプルリクエストを開いたりしません。チームはコンソールやCLIに飛び込み、サービスを復旧させるために必要な変更を何でも行います。インスタンスのキャパシティを増やし、データベースの接続制限を変更し、クラウドダッシュボードからフィーチャーフラグを無効にします。

これらの緊急変更は、運用上は正しい対応です。優先順位はサービスを復旧させることであり、インフラの純粋性を維持することではありません。しかし、インシデントが終わった後、IaCコードがその変更を反映するように更新されることはほとんどありません。チームは次の問題に移り、ドリフトは残ったままになります。

外部ツールとプロセス

すべてのドリフトが人間の操作によるものとは限りません。オートスケーラーは負荷に応じてインスタンスを追加・削除します。セキュリティツールは別のメカニズムを通じてポリシーを適用します。シークレット管理システムは自動的に認証情報をローテーションします。構成管理ツールはIaCパイプラインの外部でパラメータを更新します。

これらは正当で自動化されたプロセスであり、インフラの稼働とセキュリティを維持します。しかし同時に、Terraformコードが定義する状態と実際に動作している状態との間にギャップを生み出します。

ドリフトが重要な理由

少量のドリフトはすぐに問題を引き起こさないかもしれません。アプリケーションは動き続け、ユーザーは何も気づきません。しかし、ドリフトは蓄積され、その蓄積が現実のリスクを生み出します。

最も直接的な問題は信頼です。Terraformのplanを実行するとき、意図した変更だけが表示されることを期待します。しかし、ドリフトが存在すると、予期しない変更が計画に表示されます。触っていないリソースがコードで定義された状態に戻されようとしています。誰かが手動で追加したセキュリティルールは、次のapplyで削除されます。インシデント対応で行われたインスタンスのリサイズは、ロールバックされます。

これは危険です。なぜなら、何を壊すことになるのか分からないからです。変更するつもりのなかったリソースへの変更がplanに表示された場合、applyを実行するのを止めるべきです。しかし実際には、プレッシャーのかかったチームは、変更は無害だろうと想定して、そのまま承認してしまうことがあります。無害な場合もあれば、そうでない場合もあります。

より深い問題は、ドリフトによってインフラが予測不可能になることです。現在の状態が実際にどうなっているのか分からないため、自信を持って変更を加えることができません。すべてのデプロイがギャンブルになります。パイプラインは正しく動作するのか?インシデント中に行われた重要な変更を元に戻してしまわないか?何ヶ月も問題なく動いていたものを壊してしまわないか?

ドリフトは失敗の証ではない

ドリフトは、無頓着なチームの証拠ではないことを理解することが重要です。複数のメンバー、競合する優先事項、様々な時間的プレッシャーの中でインフラを管理することの自然な結果です。本番インフラを運用するすべてのチームがドリフトを経験します。うまく対処できるチームと苦労するチームの違いは、ドリフトが存在するかどうかではありません。その存在を認識し、検出する方法を持っているかどうかです。

ドリフトを無視するチームは、やがてデプロイパイプライン全体への信頼を失います。planを信頼しなくなり、自動化を信用しないため、さらに手動変更を行うようになります。インフラは誰も触りたがらないブラックボックスと化します。

ドリフトを認識するチームは、検出をワークフローに組み込みます。定期的にドリフトチェックを実行し、コードと現実を調整するプロセスを持ちます。ドリフトを隠すべき失敗ではなく、インフラ管理の正常な一部として扱います。

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

IaCでインフラを管理しているなら、ドリフトに対処するための短いチェックリストを以下に示します。

  • 何もデプロイしていないときでも、少なくとも週に1回は本番環境に対してplanを実行し、予期しない変更がないか出力を確認する。
  • 自動化されたドリフト検出を設定する。ほとんどのIaCツールには、実際の状態が望ましい状態と異なる場合に警告を発する機能や統合機能がある。
  • インシデントの後は必ず、緊急変更を反映するようにIaCコードを更新する時間を確保する。
  • パイプライン外でインフラを変更する外部ツールやプロセスを文書化する。何が、なぜ自動的に変更されるのかを把握する。
  • planでドリフトを確認したら、applyする前に調査する。違いの原因を理解し、それを元に戻しても安全かどうかを判断する。

次に来るもの

ドリフトは運用上の頭痛の種を生み出すだけではありません。IaCの計画ツールの出力を信頼できないものにします。ドリフトしたインフラに対してplanを実行すると、結果が誤解を招く可能性があります。安全に見えるが、実際には重要な設定を元に戻してしまう変更が表示されるかもしれません。あるいは、行うべきだった変更が、期待通りの表示にならないために見落とされるかもしれません。

本当の危険は、ドリフトがInfrastructure as Codeの価値の基盤である信頼を侵食することです。パイプラインへの信頼がなければ、変更を迅速かつ安全に行う自信を失います。そして、その自信こそがインフラを自動化するすべての意味なのです。