2人が同時に同じインフラストラクチャの状態を変更したらどうなるか
次のシナリオを想像してみてください。開発者Aと開発者Bが、それぞれインフラストラクチャを更新する必要があります。2人は同じS3バケットから現在の状態をプルし、それぞれ変更を加え、そして両方とも新しい状態をS3にアップロードします。結果はどうなるでしょうか?片方の変更が、もう片方の変更を静かに上書きしてしまいます。クラウド上で動作しているインフラストラクチャは、状態ファイルが示す内容と一致しなくなります。どのリソースが実際に管理されているのか誰にもわからなくなり、チームは制御を失い始めます。
これは、2人が同じリソースを直接編集するという話ではありません。リソースの記録(状態ファイル)を2人が編集するという話です。そして、その解決策が「状態ロック(state locking)」というメカニズムです。
状態ロックの実際の動作
状態ロックの概念は単純です。誰かが状態の変更を開始する前に、システムがロックを試みます。ロックが成功すると、その人は状態を読み取り、変更を加え、新しいバージョンを保存できます。その間、同じ状態にアクセスしようとする他の人はブロックされます。ロックが解放されるまで待機します。変更が保存されると、ロックは自動的に解放されます。
次のシーケンス図は、2人の開発者が状態バックエンドとロックバックエンドとどのようにやり取りするかを示しています。
このメカニズムがなければ、同時変更によって状態が破損します。状態が破損すると、インフラストラクチャの信頼できる情報源(source of truth)を失うことになります。
バックエンドごとのロックの扱い方
すべての状態バックエンドが同じようにロックを処理するわけではありません。S3に状態を保存する場合、ロックを管理するためにDynamoDBのような追加のバックエンドが必要です。DynamoDBは、誰がロックを保持しているか、いつ開始されたか、どの状態がロックされているかを記録します。誰かが状態を変更しようとするたびに、システムは最初にDynamoDBをチェックします。ロックがアクティブな場合、操作は停止し、エラーが返されます。
以下は、S3とDynamoDBを使用して状態ロックを有効にする最小限のTerraformバックエンド設定です。
terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "prod/network/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
dynamodb_table属性は、ロック用にterraform-locksという名前のDynamoDBテーブルを使用するようTerraformに指示します。このテーブルはterraform initを実行する前に存在している必要があります。プライマリキーとしてLockID(型はString)が必要です。Terraformは状態操作中に、このテーブル内のロックエントリを自動的に作成および解放します。
ロックが組み込まれているバックエンドもあります。例えばConsulやetcdは分散システム向けに設計されているため、ロックは通常の操作の一部です。DynamoDBのような追加コンポーネントをセットアップする必要はありません。しかし、その便利さと引き換えに、Consulやetcdを自分で運用・保守する必要があります。
ロックが失敗するケース
ロックは、状態が動かなくなるような形で失敗することがあります。よくあるシナリオとして、誰かが変更を開始した後、操作の途中でインターネット接続が切断されるケースです。ロックは取得されましたが、プロセスが強制終了したため解放されませんでした。これにより状態がロックされ、誰も変更できなくなります。
このような状況では、強制アンロック(force unlock)が必要です。これは軽々しく行うべき操作ではありません。元のプロセスがまだどこかで実行されている間にロックを強制解放すると、状態が破損するリスクがあります。チームは、強制アンロックを行う前に、ハングしたプロセスが本当に停止していることを確認する必要があります。
Terraformのようなツールは、手動アンロックのためのコマンドを提供しています。しかし、強制アンロックは日常的な操作であるべきではありません。チームが頻繁にロックのスタック問題に直面する場合は、根本原因(ネットワークの安定性、タイムアウト設定、パイプラインの実行方法など)を調査してください。
ロックだけではない重要性
状態ロックは、より構造化された環境管理への架け橋です。同時変更から状態を保護できれば、コードを重複させることなく、単一の設定を複数の環境で使用する方法を検討できます。これがワークスペースの出番です。
しかし、ワークスペースに移行する前に、ロックを適切に設定してください。状態ロックを無視するチームは、いずれインフラストラクチャの変更が静かに消失し、リソースが孤立し、誰も状態ファイルを信頼しなくなるという状況に直面します。
状態ロックの実践的チェックリスト
- ロックをサポートするバックエンドを選択する。 S3にはDynamoDBが必要です。Consulとetcdは組み込みでサポートしています。使用するバックエンドの要件を把握してください。
- ロック失敗シナリオをテストする。 状態変更中にネットワーク切断をシミュレーションしてください。ロックはスタックしますか?チームはパニックにならずに復旧できますか?
- 強制アンロック手順を文書化する。 ハングしたプロセスが停止していることを確認する方法と、ロックを解放する方法を正確に記述してください。属人的な知識(tribal knowledge)に頼らないでください。
- ロック競合を監視する。 複数の人が頻繁にロック状態に遭遇する場合、チームはより良い調整、またはより小さく頻繁な変更が必要かもしれません。
- 適切なタイムアウトを設定する。 長時間実行される操作には、プロセスがハングした場合に自動的にロックを解放するタイムアウトを設定する必要があります。
具体的な結論
状態ロックはオプションではありません。これがないと、同時変更によってインフラストラクチャの状態が静かに破損し、本番環境で何かが壊れるまで気づきません。チームが1人を超える前にロックを設定し、強制アンロックは消火器のように扱ってください。つまり、どこにあるか、どう使うかを知っておくが、毎日使うつもりではいない、ということです。