インフラストラクチャの状態が現実と一致しないとき

コードとしてのインフラストラクチャを構築したとしよう。Terraform、Pulumi、あるいは選んだツールは何でもいい。すべてが追跡され、バージョン管理され、再現可能だ。ステートファイルには本番サーバーのCPUが4コア、RAMが16GBと記録されている。順調そのものだ。

ところがある日、誰かがクラウドコンソールにログインして、そのサーバーを手動でリサイズしてしまう。パフォーマンス問題が発生し、サポートチームが迅速に対応する必要があったのかもしれない。コードベースにないスクリプトを誰かが実行したのかもしれない。理由はどうあれ、ステートファイルは依然としてCPU 4コア、RAM 16GBを示しているが、実際のサーバーはCPU 8コア、RAM 32GBで動作している。

ステートが示す内容と実際に存在するものとの間のこのギャップをドリフトと呼ぶ。そしてこれは、ほとんどのチームが認識しているよりもはるかに大きな問題である。

なぜドリフトが発生するのか

ドリフトは珍しいことではない。想像以上に頻繁に発生し、その理由はたいてい理解できるものである:

  • インシデント対応中に誰かがクラウドコンソールで素早く変更を行った
  • 別のチームが、あなたのリソースに影響を与える独自の自動化を実行した
  • 監視ツールがステートを更新せずに自動スケーリングを行った
  • 開発者がテスト目的でリソースを直接変更し、元に戻すのを忘れた

意図が悪意であることはほとんどない。しかし結果は同じだ:ステートが信頼できなくなる。そしてステートが信頼できないと、その後のすべてのデプロイはギャンブルになる。設定と一致しなくなったリソースを更新しようとするかもしれない。あるいは、存在すると思っていたリソースが変更または削除されていることに気づくかもしれない。次にパイプラインを実行したとき、予測可能な結果ではなく、驚きを得ることになる。

ドリフトの本当のコスト

ドリフトは自動化を壊すだけではない。それは、デリバリープロセス全体への信頼を損なう。チームが「コードとしてのインフラストラクチャ」が実際の状態を反映していると信頼できなくなると、再び手動変更を行い始める。そして手動変更はさらなるドリフトを生む。それは悪循環だ。

本番環境では、ドリフトは特に危険である。プロセス外で変更されたリソースは、負荷がかかると異なる動作をする可能性がある。手動で変更されたセキュリティグループは、隙を生むかもしれない。ステートを更新せずにリサイズされたデータベースインスタンスは、予期しないコストやパフォーマンス問題を引き起こす可能性がある。そして何か問題が発生したとき、実際に何が変更されたかの信頼できる記録は存在しない。

ドリフト検出:シンプルな方法

ドリフト検出の最も基本的なアプローチは手動比較である。Terraformの場合、terraform planを実行すると、コード、ステート、実際のインフラストラクチャの間の差分が表示される。コード外で変更されたリソースは、予期しない変更として表示される。

実行するコマンドと確認すべき点は以下の通り:

# コード変更なしでterraform planを実行し、ドリフトを検出
terraform plan

# ドリフトを示す出力例(コード変更は行っていない)
# Terraform will perform the following actions:
#
#   # aws_instance.web_server will be updated in-place
#   ~ resource "aws_instance" "web_server" {
#       ~ instance_type = "t3.large" -> "t3.medium"
#         id            = "i-0abcd1234efgh5678"
#         tags          = {}
#         # (12 unchanged attributes hidden)
#     }
#
# Plan: 0 to add, 1 to change, 0 to destroy.

# コードを変更していないのに変更が表示された場合 = ドリフト

これは時折のチェックには有効だ。しかし手動検出には根本的な問題がある:ドリフトは、探しに行ったときにしか見つからない。週に一度しかチェックしなければ、ドリフトは何日も気づかれずに存在し続ける。そしてその間に、実際の問題を引き起こす可能性がある。

ドリフト検出の自動化

一貫した制御が必要な環境では、ドリフト検出は自動的に実行されるべきだ。多くのチームは、terraform planまたは同等のコマンドを定期的に実行するスケジュールパイプラインを設定している。ドリフトが検出されると、パイプラインはチームに通知を送信する。

一部のツールにはこれが組み込まれている。Terraform CloudとAtlantisはどちらも自動ドリフト検出を提供する。Pulumiにも同様の機能がある。しかし、これらのツールがなくても、インフラストラクチャの検証を実行し、不一致があった場合にアラートを送信するシンプルなcronジョブやスケジュールCIパイプラインを設定できる。

自動検出は本番環境では特に重要だ。本番環境でのドリフトは、あなたの週次チェックを待ってはくれない。すぐにユーザーに影響を与える。

ドリフトを見つけたらどうするか

検出は問題の半分に過ぎない。ドリフトが存在することを知ったら、それに対して何をするかを決定する必要がある。主な選択肢は2つある:

以下のフローチャートは、ドリフト処理の2つの主要な経路をまとめたものである:

flowchart TD A[ドリフト検出] --> B{変更は意図的か?} B -->|いいえ| C[コードに合わせて調整] B -->|はい| D{新しい標準とすべきか?} D -->|いいえ| C D -->|はい| E[ステートを現実に合わせて更新] C --> F[applyを実行し、望ましい状態に戻す] E --> G[リソースをインポートし、コードを更新] F --> H[残存ドリフトがないことを確認] G --> H H --> I[変更を文書化]

選択肢1: コードに合わせて調整する。 設定を再度実行し、インフラストラクチャを望ましい状態に戻す。これは本番環境にとって最も安全な選択肢である。コードが信頼できる唯一の情報源(シングルソースオブトゥルース)であることを強化し、手動変更は永続しないことを明確にする。

選択肢2: ステートを現実に合わせて更新する。 現在のインフラストラクチャをステートにインポートし、次にコードを更新して一致させる。これは、手動変更が意図的であり、新しい標準とすべき場合に意味を持つ。ただし、注意が必要だ:ドリフトをステートに受け入れることは、定義されたプロセスの外部でインフラストラクチャが変更されることを受け入れることを意味する。

成熟したチームのほとんどは、本番環境では選択肢1を選ぶ。インフラストラクチャを望ましい状態に調整する。このプラクティスは**調整(レコンシリエーション)**と呼ばれ、KubernetesオペレーターやGitOpsワークフローの核となる考え方である。システムは継続的に現実が望ましい状態と一致しているかをチェックし、見つかったドリフトを自動的に修正する。

ドリフト検出プラクティスの構築

初めてドリフト検出を設定する場合は、シンプルに始めよう。最も重要な環境に対してスケジュールされたplanを実行する。結果をチームが確認できるチャットチャンネルに送信する。対応を自動化しようとする前に、まずドリフトを見える化する。

チームがドリフト通知に慣れてきたら、非本番環境の対応を自動化し始める。ステージング環境と開発環境はパイプラインが自動的に調整するようにしよう。本番環境については、自動化に自信が持てるまでは人間をループに残しておく。

そして常にこれを心に留めておいてほしい:ドリフト検出は単にミスをキャッチするためではない。それは「コードとしてのインフラストラクチャ」への信頼を維持するためである。チームがステートが正確であると知っていれば、自信を持って変更を行える。そうでなければ、すべてが遅くなる。

実践的なチェックリスト

  • 本番環境では、terraform planまたは同等のコマンドをスケジュール実行する
  • ドリフト通知をチームチャンネルに送信する
  • 明確なポリシーを定義する:コードに調整するか、ステートを更新するか
  • まず非本番環境の調整を自動化する
  • 意図的な手動変更の処理方法を文書化する
  • ドリフトパターンを月次でレビューし、プロセスのギャップを特定する

まとめ

ドリフトはツールの失敗ではない。それはプロセスにギャップがあるというシグナルである。誰かが変更を行う必要があり、定義されたプロセスがその人にとって機能しなかったのだ。プロセスが遅すぎたのかもしれない。アクセス権がなかったのかもしれない。プロセスの存在自体を知らなかったのかもしれない。

ドリフトを見つけたら、インフラストラクチャを修正するだけでは不十分だ。ドリフトの発生を許したプロセスそのものを修正しよう。人々が正しい経路を通じて変更を行うことを、迂回するよりも容易にすること。それこそが、絶え間ない監視なしに一貫性を保つシステムを構築する方法である。