インフラが壊れる前に知っておくべきStateとEnvironment管理の重要性

あなたとチームメイトが同じサーバを管理していると想像してください。あなたはファイアウォールの設定を変更してポート443を開放します。チームメイトはそのことを知らずに、同じ設定をポート80を開放するように変更します。二人は数分のうちにそれぞれの変更を適用します。するとサーバには矛盾したルールが設定されます。どちらの変更が有効になっているのでしょうか?誰にもわかりません。

これはコードでインフラを管理するときに最初に直面する問題です。最後に適用した人が勝つ、という問題です。しかし、勝つことが正しい変更を意味するわけではありません。もしかするとチームメイトの変更が正しかったかもしれませんが、それが数秒遅れて完了しただけかもしれません。あるいはあなたの変更が正しかったのに上書きされたかもしれません。いずれにせよ、サーバの最終状態は誰も意図していないものになります。

この問題には名前があります:State競合です。Stateとは、インフラが現在どのような状態かを記録したものです。サーバを作成するコードを書くと、Stateファイルにはそのサーバが存在すること、サイズ、接続先のネットワークなどが記録されます。Stateがなければ、ツールはサーバがすでに存在するのか、新しく作成する必要があるのかを判断できません。また、前回コードを実行したときから何が変わったのかもわかりません。

Environment混在問題

次に別のシナリオを考えてみましょう。あなたはノートPCで新機能を開発しています。テスト用のサーバが必要なので、クラウドアカウントにサーバを立ち上げ、機能を試した後、削除するのを忘れました。そのサーバは動き続け、課金が発生し続け、誰もその存在を知りません。数週間後、本番チームが見慣れないサーバを同じクラウドアカウント内で発見します。意図的に作成されたものなのか?何のためにあるのか?セキュリティは大丈夫か?誰も答えられません。

これら2つの問題(State競合とEnvironment混在)は、並べて見ると理解しやすくなります。

flowchart TD subgraph StateConflict["State競合"] A["開発者A: ファイアウォールをポート443に更新"] --> B["適用実行"] C["開発者B: ファイアウォールをポート80に更新"] --> D["後から適用実行"] B --> E["最後に書き込んだ方が勝つ"] D --> E E --> F["意図しない最終状態"] end subgraph EnvMixing["Environment混在"] G["開発者が共有アカウントにサーバを作成"] --> H["削除し忘れ"] H --> I["不明なリソースが残る"] I --> J["コストとセキュリティリスク"] end

これがEnvironment混在問題です。Environmentとは、アプリケーションやインフラが動作する場所のことです。理想的には、開発、ステージング、本番のEnvironmentは明確に分離されているべきです。しかし、誰もが同じクラウドアカウント内でルールなしにリソースを作成できると、Environmentは混ざり合います。開発サーバが誤って本番ネットワークに接続されたり、本番データベースが誤ったコンテキストで開発スクリプトを実行して消去されたりする可能性があります。

手動管理がこれらの問題を隠す理由

StateとEnvironmentの問題は、インフラを手動で管理しているときには表面化しません。サーバにログインして設定を一つずつ変更するとき、何が起こっているかを正確に把握できます。どのサーバにいるか、何を変更したかがわかります。隠れたStateファイルが破損することもありません。

しかし、コードでインフラを管理する場合、サーバ上で直接作業するのではなく、Stateファイルを操作することになります。コードを変更すると、ツールがStateを読み取り、現実と比較し、変更を加えます。このプロセスにより、すべてが再現可能で監査可能になります。しかし同時に、新たな障害モードも導入されます。

Stateファイルが破損する可能性があります。二人が同時に同じStateファイルを変更する可能性があります。ツールの外部で手動変更が行われた場合、Stateが現実から乖離する可能性があります。同じコードが明確な境界なしにあらゆる場所に適用されるため、Environmentが混在する可能性があります。

理解すべき中核概念

Stateはインフラの信頼できる情報源です。プロビジョニングツールに、何がすでに存在し、何を変更する必要があるかを伝えます。Terraform、Pulumi、AWS CDKなどの一般的なツールはすべてStateファイルに依存しています。正確なStateがなければ、これらのツールは何を作成、更新、削除すべきかを判断できません。

Environmentはアプリケーションが動作するコンテキストです。最低限、ほとんどのチームには3つのEnvironmentが必要です。

  • 開発(Development): 実験して壊す場所。安価で、迅速に再作成でき、他のすべてから隔離されているべきです。
  • ステージング(Staging): 本番環境に変更を適用する前に検証する場所。同じリスクを負わずに、可能な限り本番環境を模倣する必要があります。
  • 本番(Production): 実際のユーザーがアプリケーションとやり取りする場所。最も高い安定性要件があります。

StateとEnvironment管理を無視するとどうなるか

この基盤をスキップしたチームは、予測可能な問題パターンに直面します。

  • 本番アカウントに謎のサーバが出現する。誰がなぜ作成したのか誰も知らない。セキュリティチームはパニックに陥る。
  • Stateファイルがロックされてデプロイが失敗する。ある開発者のパイプラインがStateロックを保持し、他の全員をブロックする。
  • ステージングと本番が乖離する。ステージングでテストした変更は問題なく動作するが、Environmentが同一でなくなったため本番では異なる動作をする。
  • 誤って本番に変更を加えてしまう。開発者が開発用のスクリプトを実行しようとしたが、ターミナルセッションが本番Environmentを指していた。単純な設定変更でサイトがダウンする。
  • ロールバックが不可能になる。Stateファイルが現実と一致しなくなり、ツールが既知の正常状態に戻せなくなる。

始めるための実践的アプローチ

これらの問題を解決するために複雑なプラットフォームチームは必要ありません。基本から始めましょう。

  1. Environmentごとにクラウドアカウントまたはプロジェクトを分離する。 AWSを使用している場合は、開発、ステージング、本番用に個別のアカウントを作成します。GCPの場合は個別のプロジェクトを使用します。これが最も強力な分離方法です。

  2. ロック機能付きのリモートStateストレージを使用する。 StateファイルをS3、GCS、Azure Blob Storageなどの共有ロケーションに保存します。Stateロックを有効にして、2つのパイプラインが同時に同じStateを変更できないようにします。

  3. リソース名を一貫して命名する。 すべてのリソース名またはタグにEnvironment名を含めます。これにより、サーバのリストを見たときの混乱を防ぎます。

  4. Environment作成を自動化する。 オンデマンドでEnvironmentを作成および破棄するスクリプトまたはパイプラインを作成します。Environmentを数分でゼロから再作成できれば、Stateドリフトのリスクを減らせます。

  5. 本番への変更適用を制限する。 本番デプロイには承認ゲートまたは個別のサービスアカウントを使用します。全員に本番アクセス権が必要なわけではありません。

現在のセットアップを評価するためのクイックチェックリスト

  • 現在の本番Environmentにあるすべてのサーバまたはリソースを一覧表示できますか?
  • 各リソースを誰がなぜ作成したかわかりますか?
  • ステージングEnvironmentを1時間以内にゼロから再作成できますか?
  • Stateファイルはリモートに保存され、ロックが有効になっていますか?
  • 開発、ステージング、本番のEnvironmentは別々のクラウドアカウントまたはプロジェクトにありますか?
  • 開発者が自分のノートPCから誤って本番に変更を適用できますか?

これらの質問のいずれかに「いいえ」と答えた場合、やるべきことがあります。

まとめ

StateとEnvironment管理は、インフラがすでに稼働している後に対処する高度なトピックではありません。他のすべてを可能にする基盤です。明確なStateがなければ、ツールは正しく動作しません。明確なEnvironmentがなければ、変更は境界を越えて漏れ出し、予測不能な障害を引き起こします。

管理可能な最もシンプルな分離から始めましょう。各Environmentに個別のクラウドアカウントまたはプロジェクト、ロック付きのリモートState、一貫した命名規則です。これだけで、コードでインフラを管理するチームを悩ませる一般的な問題のほとんどを防げます。謎のサーバが出現する前に行ってください。出現した後では遅すぎます。