AWS/Terraform

Terraformのstate lock(ロック)を詳しく解説してみた

今回は、AWSでS3をバックエンドに設定したTerraformのstate lockについて詳しく解説していきます。

state lockとは

state lock(ステートロック)とは、複数の作業者が同時にTerraformを実行したとき、先にTerraformを実行した環境以外からのtfstateファイル(以下stateファイル)読み取り・書き込みを禁止する排他制御のことです。
参考:公式ドキュメント(State Locking)

stateファイルとは

stateファイルは、Terraformの動作設定・デプロイしたリソースの状態を管理するファイルです。
Terraformでデプロイを実行すると、デプロイ結果がstateファイルに書き込まれます。
参考:公式ドキュメント(State)

stateファイルのバックエンド設定とは

stateファイルのバックエンド設定とは、クラウド環境でstateファイルを管理する機能です。

Terraformのデフォルト設定では、stateファイルはローカル環境に作成されます。
作業者が複数存在する場合、stateファイルが作業者ごとに作成されるため、作業者がお互いにTerraformでデプロイしたリソースを参照・更新することができません。
Terraformのバックエンド設定を用いることで、1つのstateファイルをクラウド上で共用管理できます。
参考:公式ドキュメント(S3)

state lockのしくみ

以下の図に沿ってAWSにおけるstate lockのしくみ(流れ)をご説明します。

※Terraformの実行環境が2つ(A、B)の想定です。

  1. ローカル環境AでTerraformによるデプロイを実行したとします。
    バックエンドに設定されているDynamoDBに、stateファイルをロックする項目を生成します。
  2. ①でロック用の項目が生成されている間は、ローカル環境BからTerraformを実行できません。
    バックエンド設定によって、Terraformの実行には、ロック用の項目を生成する手順が必要になるためです。
  3. ローカル環境Aで実行されたTerraformは、バックエンドに設定したS3に保存されているstateファイル・ローカルのtfファイルの差分をチェックします。
  4. ③でチェックした差分に基づいて、デプロイを実行します。
    ※stateファイルで管理されているリソースに対してTerraform以外(例:マネジメントコンソールから手動)で変更が加えられている設定値についても、tfファイルとの差分として検出されます。
  5. ④のデプロイ結果(リソースの状態)をstateファイルに書き込みます。
  6. stateファイルへの書き込み完了後、①で生成したロック用の項目を削除します。
    これ以降は、ローカル環境BからTerraform実行は可能となります。

実際にロックさせてみた

今回は意図的に「state lockしたまま」の状態を再現します。
複数環境でTerraformを同時実行するのではなく、Terraformの実行を中断して、DynamoDBのロック用項目が削除されず「state lockしたまま」(DynamoDBのロック用項目が生成されたまま)の状態を再現します。
※Terraformの環境を壊しかねない検証ですので、お試しは自己責任でお願いします。

ロックの準備

state lockを再現する準備は以下です。

Terraformのバージョン
筆者が使用したTerraformのバージョン情報は以下です。
※ローカル環境。

Terraformのバージョン 1.0.0
AWSプロバイダのバージョン 4.2.0


tfファイル
以下のリポジトリで公開しています。
https://github.com/dogharasu/terraform-state-lock-s3-test

バックエンド用S3・DynamoDB
バックエンド用にS3バケット・DynamoDBテーブルを手動で作成します。
詳細な作成手順は以下の記事でご説明しています。

【Terraform】tfstateファイルをAWSのS3・DynamoDBで管理する今回は、Terraformのtfstateファイル(以下stateファイル)をAWSのS3・DynamoDBで管理する方法をご紹介します...

ロックしたままの状態を作る

ローカル環境にてリポジトリ内「terraform」ディレクトリで以下コマンドを実行してTerraformの自動初期設定を行います。
この時にバックエンド(S3、DynamoDB)の設定も行われます。

terraform init

以下コマンドを実行してデプロイを開始します。

terraform apply

Auroraのデプロイ中にターミナルごと「×」で閉じます。

※「Ctrl」+「C」でコマンドを中断した場合は、デプロイの中断前にDynamoDBのロック用項目が削除されるため、「state lockしたまま」の状態は再現できません。

ロックの動作確認

「state lock」したままとなっているか動作確認を行います。

ターミナル画面
terraform applyを実行してみると、ロック中のため実行できないエラーが出力されます。
terraform planのようなstateファイルを参照するコマンドもエラーとなります。

DynamoDBの画面
AWSマネジメントコンソールのDynamoDB画面にて、ロック用の項目として「LockID」および「Info」を属性とする項目が生成されていました。

属性「Info」にはロックIDが含まれています。

「LockID」および「Digest」を属性とする項目は、stateファイルが存在する限りデフォルトで存在しています。
Terraformによってstateファイルを更新したときに、stateファイルのMD5チェックサムを計算・格納して、stateファイルの整合性を担保する項目です。
stateファイルを手動で編集した場合やstateファイルが壊れてバージョンを戻した際には、stateファイルがロックされるため「Digest」の値をリセットする必要があるようです。
参考:個人ブログ※海外

ロックの解除

以下のコマンドを実行して、「state lockしたまま」の状態を強制的に解除します。

terraform force-unlock <ロックID>

ロックIDは、ターミナルのエラーメッセージもしくはDynamoDBの「Info」を参照します。

ロック解除コマンドはロックを引き起こした作業者以外からでも実行できます。
通常起こるロックは、単に別の作業者がTerraform実行中である可能性もありますので、ロック解除を行う場合はくれぐれもご注意ください。

ターミナル画面
terraform applyが問題なく実行できるようになりました。

DynamoDBの画面
ロック用に生成されていた項目が削除されていました。

マネジメントコンソールから直接項目を削除してもロック解除は可能ですが、公式の手順には無いためコマンド実行を推奨します。

まとめ

今回は、AWSにおけるTerraformのstate lockについて解説しました。
Terraform Cloudを利用せず複数の作業者でTerraformを管理する場合、stateファイルのバックエンド設定は必須でしょう。
バックエンド設定の知識を深めておくことで、state lock時に柔軟な判断・対応が可能となります。
この記事を通して、少しでもTerraformのstate lock理解のお役に立てれば幸いです。