Terraformのステートファイルが消えたとき:実際に機能する復旧戦略
terraform planを実行したら、いつもの出力ではなくエラーが返ってきた。ステートファイルがない。破損している。あるいは数時間前に死んだプロセスがロックを保持したままになっている。最初はパニックになりそうになるが、知っておくべきことはこれだ:インフラストラクチャはおそらくまだ正常に動作している。サーバー、データベース、ロードバランサーはまだそこにある。壊れているのは、Terraformが存在すると認識しているものの記録だけだ。
この状況は稀だが、発生するとチームは動けなくなる。ステートが復旧するまで、コードを通じて変更を加えることはできない。幸いなことに復旧は可能であり、計画を立てておくかどうかで、迅速な修正と数時間に及ぶ危機の分かれ目となる。
なぜステートファイルは壊れるのか
ステートファイルはいくつかの予測可能な方法で故障する。最も一般的なのは誤削除だ。誰かがS3バケットをクリーンアップする際に、ステートファイルがそこにあることに気づかない。別のシナリオとしては、書き込み処理が途中で中断されて発生する破損がある。ネットワーク障害、電源障害、プロセスの強制終了などにより、ステートファイルが中途半端に書き込まれて読み取り不能になることがある。
そしてロック問題もある。Terraformは複数のプロセスが同時に書き込むのを防ぐためにステートファイルをロックする。プロセスがロックを解放せずに死んだ場合、そのロックはそのまま残る。後続のすべてのplanやapplyは、Terraformが他の誰かがまだ作業中だと判断するため失敗する。
深刻度は異なるが、核心的な問題は同じだ:コードと実行中のインフラストラクチャとの間の接続を失ったのだ。
まず最初に:すべてを再構築しない
最も重要なルールは、すべてを破壊して最初からやり直そうという衝動を抑えることだ。インフラストラクチャはまだ稼働している。クラウドプロバイダーは、作成したすべてのリソースをまだ認識している。失われたのは、Terraformがそれらのリソースをローカルに記録したものだけだ。
terraform destroyを実行してすべてを再作成すると、不必要なダウンタイムを引き起こし、データ損失のリスクが生じる。データベースは消去される。永続ボリュームは削除される。DNSレコードは変更される。復旧の道筋は、インフラストラクチャを再構築することではなく、ステートを復元することにある。
深刻度別の復旧手順
ロックされたステート:簡単な修正
以下は、ステートファイルの問題の種類に基づいて復旧パスを迅速に特定するための判断ツリーです:
ステートファイルは存在するがロックされている場合、これが最も簡単なシナリオだ。どのプロセスがロックを保持しているかを特定する。そのプロセスがまだ実行中であれば、完了するまで待つ。予期せず終了した場合は、force-unlockコマンドを使用する:
terraform force-unlock <lock_id>
ただし注意が必要だ。Force-unlockは、他のプロセスがステートに書き込みを行っていないことが絶対に確かな場合にのみ安全である。2つのプロセスが同時に書き込むと、単純なロックよりも修正が困難な破損したステートになってしまう。
削除されたステートとバックアップがある場合:理想的なシナリオ
ステートファイルが削除されたがバックアップがある場合、状況は良好だ。復旧は簡単で、バックアップを元の場所に復元するだけである。
これが、ステートバックエンドでのバージョニングが重要である理由だ。S3を使用している場合は、バケットのバージョニングを有効にする。ファイルが削除された場合、S3コンソールまたはCLIから直接以前のバージョンを復元できる。バックアップファイルの管理は不要だ。
バージョニングがない場合でも、別の場所への手動バックアップで対応できる。重要なのは、プライマリのステート保存場所とは別の場所にコピーを保管しておくことだ。
バックアップなしで破損または消失したステート:困難な道筋
ここからが厄介な状況だ。バックアップがなく、ステートファイルは読み取り不能か消えている。しかしインフラストラクチャはまだ稼働している。解決策は、リソースを1つずつインポートしてステートを再構築することだ。
Terraformにはimportコマンドがあり、既存のインフラストラクチャを読み取ってステートファイルに追加する。コードで定義された各リソースに対して、次のように実行する:
例えば、設定でaws_instance.webとして定義されているEC2インスタンスをインポートするには、次のように実行する:
terraform import aws_instance.web i-1234567890abcdef0
リソースアドレス(aws_instance.web)は、.tfファイルにあるものと完全に一致している必要がある。リソースがモジュール内にある場合は、module.my_module.aws_instance.webのようにモジュールパスを使用する。インポート後、terraform planを実行してステートが設定と一致していることを確認する。
terraform import <リソースタイプ>.<リソース名> <リソースID>
リソースIDの形式はプロバイダーによって異なる。AWSの場合はインスタンスIDやARN、GCPの場合は通常リソース名または完全なURLとなる。
このプロセスは大規模なインフラストラクチャでは面倒だ。数十のEC2インスタンス、RDSデータベース、ロードバランサー、セキュリティグループがある場合、しばらくの間インポートコマンドを実行し続けることになる。しかし、何も破壊せずにコードと現実を一致させる唯一の方法である。
復旧手段のない完全な消失:最終手段
ステートが消え、バックアップもなく、インフラストラクチャが複雑すぎるか、ドキュメントが不十分で1つずつインポートできない場合がある。この場合、選択肢は1つだけだ:スクラッチから再構築する。
これは、既存のすべてのリソースを破壊し、Terraformコードから再作成することを意味する。特に本番環境では軽々しく下せる判断ではない。しかし、インポートによる復旧が実行不可能な場合、再構築は多くの場合、数百ものリソースのステートを手動で再構築しようとするよりも速い。
この方法を取る前に、以下を確認しておくこと:
- 実行中のものと一致する完全なTerraformコード
- データベースと永続ストレージのデータバックアップ
- ステークホルダーの承認を得たメンテナンスウィンドウ
- 再構築が失敗した場合のロールバック計画
ステート復旧のための実践的チェックリスト
ステートが壊れた場合、このチェックリストを順に実行する:
- 被害を確認する - ステートはロックされているか、削除されているか、破損しているか?エラーメッセージを注意深く確認する。
- バックアップを確認する - バックエンドのバージョニングを確認する。手動バックアップの場所を確認する。
- 可能なら復元する - バックアップがあれば復元し、
terraform planで確認する。 - ロックされていれば強制ロック解除 - 他のプロセスがアクティブでないことが確かな場合のみ。
- リソースをインポートする - バックアップがない場合、リソースを1つずつインポートし始める。
- 再構築を検討する - インポートが非現実的で、完全なコードカバレッジがある場合のみ。
予防は復旧に勝る
ステート障害に備える最善の時期は、それが発生する前だ。3つのプラクティスが復旧をはるかに容易にする:
まず、ステートバックエンドでバージョニングを有効にする。S3、Azure Storage、GCSのいずれであっても、バージョニングは誤削除や破損に対するセーフティネットとなる。
次に、バックアップを自動化する。バージョニングがあっても、ステートファイルのコピーを別の場所に保存する。ステートファイルを別のバケットやストレージアカウントにコピーする単純なcronジョブやパイプラインステップは、設定に5分もかからない。
第三に、インフラストラクチャを文書化する。リソースをインポートする必要がある場合、何が存在し、どのようなIDを持っているかを知る必要がある。READMEに記載するか、クラウドプロバイダーのAPIから生成した最新のリソース一覧があれば、復旧時の時間を大幅に節約できる。
その後にすべきこと
ステートが復旧し、terraform planが再び実行できるようになったら、作業は終わりではない。このインシデントをきっかけに、ステート管理の方法を見直すべきだ。バックアップ戦略にギャップはないか?誤削除を防ぐためにアクセス制御を追加すべきか?ステート復旧のためのランブックが必要か?
ステート復旧とは、ミスを避けることではない。ミスが発生したときの計画を立てることだ。ステート障害に備えているチームは数分で復旧する。備えていないチームは、何時間もパニックに陥り、その後何日もかけて失ったものを手動で再構築することになる。
あなたが構築したインフラストラクチャはまだそこにある。ステートは単なる地図に過ぎない。地図を失ったからといって、街を焼き払ってはいけない。新しい地図を描けばいいのだ。