パイプライン外でインフラが変更されたとき:ドリフト検出の演習

Terraform構成でセキュリティグループを定義したとします。名前も正しく、インバウンドルールも適切で、タグも正しく設定されています。パイプラインは正常に実行され、リソースは作成され、ステートファイルもクリーンです。すべてが順調に見えます。

ところが、誰かがクラウドコンソールにログインして、小さな変更を加えます。名前がわかりにくかったからとリネームするかもしれません。何かを素早くテストするためにインバウンドルールを追加するかもしれません。不要に見えたタグを削除するかもしれません。コードの変更はありません。パイプラインの実行もありません。単なるコンソールでの手動操作です。

あなたのインフラは、コードが定義するべき状態とは異なるものになりました。この差異をドリフトと呼びます。そして、その発生に気づかなければ、次回のデプロイで予期せぬ形で問題が発生する可能性があります。

ドリフトの実際の姿

ドリフトは、インフラの実際の状態がコードで定義された望ましい状態から乖離したときに発生します。これは理論上の問題ではありません。実際のチームで常に発生しています。

  • パイプラインでは時間がかかりすぎるため、誰かが本番環境で直接緊急の問題を修正する。
  • クラウドプロバイダーが証明書を自動ローテーションしたり、デフォルト設定を変更したりする。
  • チームメンバーが他のものをクリーンアップしているときに、誤ってリソースを削除する。
  • パイプライン外の自動化ポリシーが、コンプライアンス上の理由でリソースを変更する。

問題はドリフトが存在すること自体ではありません。問題は、何かが壊れるまでその存在に気づかないことです。

ドリフトを実際に体験する簡単な演習

最小限のセットアップで、自分の環境でドリフトをシミュレートできます。無料枠のリソースがあるクラウドアカウントか、LocalStackのようなローカルシミュレーターが必要です。学習目的であれば、モックのステートファイルでも構いません。

まず、Terraformで1つのリソースを作成します。AWSのセキュリティグループや、任意のクラウドプロバイダーのストレージバケットが適しています。リソースが作成され、ステートファイルが保存されるまでパイプラインを実行します。クラウドコンソールでリソースを確認できるようにしてください。

以下は、この演習で使用できる最小限のTerraform構成です。

# main.tf
provider "aws" {
  region = "us-east-1"
}

resource "aws_security_group" "web_sg" {
  name        = "web-server-sg"
  description = "Allow HTTP and SSH traffic"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "web-server-sg"
    Env  = "test"
  }
}

terraform initterraform apply を実行してセキュリティグループを作成します。次に、AWSコンソールで、セキュリティグループの名前を手動で web-server-sg-manual に変更し、Env タグを削除します。最後に、terraform plan を実行してドリフトを確認します。

$ terraform plan
aws_security_group.web_sg: Refreshing state... [id=sg-0123456789abcdef0]

Terraform will perform the following actions:

  # aws_security_group.web_sg will be updated in-place
  ~ resource "aws_security_group" "web_sg" {
        id          = "sg-0123456789abcdef0"
      ~ name        = "web-server-sg-manual" -> "web-server-sg"
        tags        = {
          - "Env"  = "test" -> null
            "Name" = "web-server-sg"
        }
        # (6 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

このプランは、Terraformが名前を元に戻し、欠落したタグを追加し直すことを示しています。これが実際のドリフトです。

ここで、Terraformコードには一切触れずに、クラウドコンソールを開いて手動で変更を加えてみてください。以下のような変更を試せます。

  • セキュリティグループの名前を変更する。
  • コードに存在しないインバウンドルールを追加する。
  • コードで定義されているタグを削除する。
  • バケットのパブリックアクセス設定を変更する。

目標は、実際のリソースがコードと一致しなくなる状況を作り出すことです。これがドリフトです。

以下のフローチャートは、このドリフト検出演習における一連のイベントを示しています。

flowchart TD A[望ましい状態: Terraform構成] --> B[パイプラインがリソースを作成] B --> C[実際の状態が構成と一致] C --> D[コンソールで手動変更] D --> E[実際の状態が構成からドリフト] E --> F[terraform plan を実行] F --> G{プランに変更が表示されるか?} G -- はい --> H[ドリフト検出] G -- いいえ --> I[ドリフトなし] H --> J{是正判断} J -- 変更を受け入れる --> K[Terraformコードを更新] J -- 構成を復元する --> L[terraform apply を実行] K --> M[パイプライン実行、状態が一致] L --> M

ドリフト後の terraform plan の実行

手動で変更を加えたら、ターミナルで terraform plan を実行します。出力を注意深く見てください。Terraformはステートファイルと実際のリソースを比較し、apply を実行した場合に何が変更されるかを表示します。

重要な点に注意してください。プランには予期しない変更が表示される可能性があります。セキュリティグループの名前を元の名前に戻そうとするかもしれません。誰かが追加したルールを削除しようとするかもしれません。しかし、変更したリソースに依存する他のリソースへの変更も表示される可能性があります。単純な名前の変更が、そのセキュリティグループを名前で参照しているロードバランサー、ターゲットグループ、またはIAMポリシーに影響を与える可能性があります。

これが、ドリフトが発生した後にプランを盲目的に信頼できない理由です。プラン自体は正しいかもしれませんが、提案された各変更を検証する必要があります。一見きれいに見えるプランでも、インフラの他の部分を壊す連鎖的な影響が隠れている可能性があります。

明示的なドリフト検出

Terraformには terraform refresh というコマンドがあり、リソースの実際の状態でステートファイルを更新します。これを実行してから、再度 terraform plan を実行してください。同じドリフトが表示されますが、今度はステートファイルが現実を反映しています。これは何が変更されたかを理解するのに役立ちますが、ドリフトを修正するわけではありません。単にそれを認識するだけです。

SpaceliftやTerragruntなどの一部のプラットフォームには、スケジュールに従って実行される組み込みのドリフト検出機能があります。ドリフトが検出されたときに通知したり、自動的な是正をトリガーしたりできるものもあります。しかし、この演習では、メカニズムを理解するために手動での検出で十分です。

どのリソースがドリフトし、何が変更されたかを書き留めてください。この記録は、次のステップを考えるのに役立ちます。

是正判断を行う

ここで選択肢があります。インフラがドリフトしていること、何が変更されたかを把握しました。どう対処しますか?

自問してみてください。

  • 手動での変更は意図的でしたか?緊急の問題を修正するなど、正当な理由で誰かが行ったものですか?
  • その変更はまだ必要ですか?おそらく緊急事態は終了し、リソースは元の状態に戻すべきです。
  • 変更を元に戻しても安全ですか?元に戻すことで、新しい構成に依存している何かが壊れる可能性があります。
  • チーム内で、なぜその変更が行われたのか知っている人はいますか?チケットやチャットログに記録はありますか?

是正することを決定した場合は、terraform apply を実行します。リソースはコードで定義された状態に戻るはずです。すべてが期待通りに動作することを確認してください。

変更を採用することを決定した場合は、実際の状態に一致するようにTerraformコードを更新します。その後、通常どおりパイプラインを実行します。コードとインフラが再び一致するため、ドリフトは解決されます。

試すべきバリエーション

基本的なシナリオを理解したら、より複雑なバリエーションを試してみてください。

  • トラフィック急増時にインスタンス容量を増やすなど、一時的な変更を行います。急増が終わった後に、ドリフト検出がどのようにそれをキャッチするかを確認します。
  • 依存関係を持つリソースを変更します。たとえば、ターゲットグループに接続するロードバランサーの構成を変更します。プランが連鎖的な影響をどのように表示するかを観察します。
  • 元に戻すのが難しい変更、たとえば他のリソースが依存しているリソースを削除します。Terraformが依存関係チェーンをどのように処理するかを確認します。

それぞれのバリエーションは、実際のシステムでドリフトがどのように動作するかについて何かを教えてくれます。シナリオが複雑になればなるほど、ドリフトの検出と是正には自動化だけでなく慎重な検討が必要であることが明確になります。

ドリフト管理のための実践的なチェックリスト

先に進む前に、自分の環境に適用するための短いチェックリストを以下に示します。

  • 重要なインフラリソースに対して、自動化されたドリフト検出を設定する。
  • ドリフト処理の明確なプロセスを定義する:誰が通知を受けるか、どのように決定が行われるか、いつ是正がトリガーされるか。
  • 一時的な変更も含めて手動変更の記録を保持し、チームがなぜドリフトが存在するのかを知ることができるようにする。
  • 本番環境に適用する前に、非本番環境で是正プロセスをテストする。
  • 何かが壊れたときだけでなく、定期的にドリフトレポートをレビューする。

この演習が教えてくれること

ドリフトは理論上の概念ではありません。これは、コードを通じてインフラを管理するすべてのチームが直面する現実の運用上の問題です。この演習は、ドリフトが静かに発生し得ること、変更されたリソースだけでなくそれ以上に影響を及ぼす可能性があること、そして是正の判断は常に状況に依存することを示しています。

すべてのドリフトを防ぐことはできません。人は手動で変更を行います。クラウドプロバイダーはリソースを変更します。緊急事態は発生します。できることは、ドリフトを早期に検出し、その影響を理解し、変更を元に戻すか採用するかについて情報に基づいた決定を下すことです。

次に誰かが「コンソールでちょっと修正しただけだよ」と言ったとき、それがあなたのインフラにとって何を意味するのか、正確に理解できるでしょう。そして、それに対処するためのプロセスが準備できているはずです。