TT Lab
はじめる
学ぶ 学習パス コース

Terraform実戦

countからfor_eachへ、破壊せずに移す

TT Labで続きを見る

目標

countとfor_eachの違いを状態アドレスで確認し、すでに適用されたリソースを、破棄なしでfor_eachに移行させる手順を身につけます。

なぜ重要なのか

countとfor_eachは、文法の選択のように見えますが、実際には、各インスタンスの識別子を何にするかを決める判断です。countは順序を、for_eachは名前を、識別子として使います。リストの真ん中の項目を1つ削除してみると、違いが現れます。countは、後ろが1つずつ繰り上がって、残っているべきリソースまで再作成され、for_eachは、該当のキーだけがなくなります。実際のクラウドだったなら、この違いは、「設定を1行消しただけなのに、サーバー2台が再作成された」として現れます。すでに適用されたリソースのアドレスを変える必要があるときは、movedブロックを使ってください。movedは、状態だけを移して実体には触れず、コードにリファクタリングの履歴が残るため、ほかの人がplanを回すだけでも自動的に追従します。移行が正しくできたかを判定する基準は、1つです。プランに破棄が1件もないことです。

ステップ

  1. /root/tf/foreach/main.tfにlocal_fileリソースnodeをcount = 3で宣言し、tofu initのあとに適用してください。そのあと、アドレスを移す前に、tofu state listの結果を/root/tf/foreach/out/count-state.txtに保存してください。このファイルには、local_file.node[0]、local_file.node[1]、local_file.node[2]の3行が入っている必要があり(文字列キーの形が入っていてはいけません)、ステップ7の移行前のスナップショットの役割を果たします。
  2. nodeのファイル名を/root/tf/foreach/out/node-${count.index}.txt、内容をnode-${count.index}にしてください。名前を3回手で書かず、必ず繰り返しインデックスで作る必要があります。outの下のnode-*.txtは、ちょうど3つで、各ファイルの最初の行は、node-0、node-1、node-2である必要があります。
  3. local_fileリソースzoneをfor_each = toset(["kr", "us", "eu"])で作成し、ファイルは/root/tf/foreach/out/zone-<키>.txtが3つできるようにしてください(プレースホルダーはキーです)。状態のキーは、eu、kr、usである必要があります。
  4. local_fileリソースserviceを、マップで回してください。キーはapi、web、workerで、各値にはポートが入ります(webのポートは必ず8080)。/root/tf/foreach/out/service-web.txtの中には、name=webとport=8080が、それぞれ1行で入っている必要があります。
  5. for式で、サービス名をキー、ポートを値とする出力service_portsを定義し、tofu output -jsonの結果を/root/tf/foreach/out/outputs.jsonに保存してください。項目は3つで、webは8080である必要があります。
  6. variable "enable_debug"(bool、デフォルト値はfalse)を作成し、local_fileリソースdebugにcount = var.enable_debug ? 1 : 0を使ってください。ファイルのパスは/root/tf/foreach/out/debug.txtで、内容は自由です(スイッチはインスタンスが最大1つなのでインデックスがずれることがない、という点が、countを使い続けてよい理由です)。スイッチをオンにしたプラン(tofu plan -var enable_debug=true)の出力を/root/tf/foreach/out/toggle.txtに保存してください(1 to addが表示されている必要があります)。ただし、適用はしないでください。最終状態には、debugインスタンスが0個で、/root/tf/foreach/out/debug.txtもない必要があります。
  7. nodeをfor_each = { n0 = 0, n1 = 1, n2 = 2 }に変更してください。マップのキーは新しいアドレス用で、値は古いインデックスなので、ファイル名と内容は、each.keyではなく、each.valueで作って、node-0.txt・node-1.txt・node-2.txtとその内容が、ステップ1–2と完全に同じになるよう維持する必要があります(名前を変えると、移行ではなく再作成になり、プランに破棄が表示されます)。そして、movedブロック3つで、local_file.node[0]をlocal_file.node["n0"]に移す形で、古いアドレスと新しいアドレスをつなげてください。適用したあと、tofu planの出力を/root/tf/foreach/out/after-plan.txtに保存すると、No changesで、破棄が1件もない必要があります。
  8. /root/tf/foreach/out/addresses.jsonを作成してください。addressesキーに、tofu state listのアドレスを配列として入れ(node 3 + zone 3 + service 3 = 9個以上)、数値のインデックスが付いたアドレスは1つもあってはいけません。numeric_index_countキーには、数値インデックスのアドレスの個数を書き、移行が終わっていれば0です。

参考

countで3つ作り、移す前のアドレスを残す

/root/tf/foreach/main.tfにlocal_fileリソースnodeをcount = 3で宣言し、tofu initのあとに適用してください。そのあと、アドレスを移す前に、tofu state listの結果を/root/tf/foreach/out/count-state.txtに保存してください。このファイルには、local_file.node[0]、local_file.node[1]、local_file.node[2]の3行が入っている必要があり(文字列キーの形が入っていてはいけません)、ステップ7の移行前のスナップショットの役割を果たします。

リソースブロックにcount = 3を入れると、インスタンスが3つできます。あとでアドレスを移すので、いまのアドレスの一覧をファイルに残しておいてください。移す前の記録がないと、何がどのように移ったかを証明できません。

count.indexでインスタンスを区別する

nodeのファイル名を/root/tf/foreach/out/node-${count.index}.txt、内容をnode-${count.index}にしてください。名前を3回手で書かず、必ず繰り返しインデックスで作る必要があります。outの下のnode-*.txtは、ちょうど3つで、各ファイルの最初の行は、node-0、node-1、node-2である必要があります。

インスタンスごとにファイル名が違う必要があるため、インデックスを名前に埋め込みます。内容も、同じ方法で区別してください。

セットを回すfor_eachを使う

local_fileリソースzoneをfor_each = toset(["kr", "us", "eu"])で作成し、ファイルは/root/tf/foreach/out/zone-<키>.txtが3つできるようにしてください(プレースホルダーはキーです)。状態のキーは、eu、kr、usである必要があります。

リストはfor_eachに直接渡せません。セットに変換する必要があり、そのとき、キーは要素の値そのものになります。

マップを回すfor_eachとeach.value

local_fileリソースserviceを、マップで回してください。キーはapi、web、workerで、各値にはポートが入ります(webのポートは必ず8080)。/root/tf/foreach/out/service-web.txtの中には、name=webとport=8080が、それぞれ1行で入っている必要があります。

マップを渡すと、each.keyはマップのキー、each.valueはその値です。値がオブジェクトなら、フィールドに掘り下げられます。

for式で出力マップを作る

for式で、サービス名をキー、ポートを値とする出力service_portsを定義し、tofu output -jsonの結果を/root/tf/foreach/out/outputs.jsonに保存してください。項目は3つで、webは8080である必要があります。

for_eachはリソースを増やすメタ引数で、forは値を別の形に変換する式です。{ for k, v in ... : k => ... }の形を使ってください。

条件付きcountで機能スイッチを作る

variable "enable_debug"(bool、デフォルト値はfalse)を作成し、local_fileリソースdebugにcount = var.enable_debug ? 1 : 0を使ってください。ファイルのパスは/root/tf/foreach/out/debug.txtで、内容は自由です(スイッチはインスタンスが最大1つなのでインデックスがずれることがない、という点が、countを使い続けてよい理由です)。スイッチをオンにしたプラン(tofu plan -var enable_debug=true)の出力を/root/tf/foreach/out/toggle.txtに保存してください(1 to addが表示されている必要があります)。ただし、適用はしないでください。最終状態には、debugインスタンスが0個で、/root/tf/foreach/out/debug.txtもない必要があります。

있으면 1, 없으면 0(韓国語の文は「あれば1、なければ0」という意味です)は、オン・オフの慣用句です。オンの状態は、プランでだけ確認し、実際には適用しないでください。

movedブロックでcountをfor_eachに移行する

nodeをfor_each = { n0 = 0, n1 = 1, n2 = 2 }に変更してください。マップのキーは新しいアドレス用で、値は古いインデックスなので、ファイル名と内容は、each.keyではなく、each.valueで作って、node-0.txt・node-1.txt・node-2.txtとその内容が、ステップ1–2と完全に同じになるよう維持する必要があります(名前を変えると、移行ではなく再作成になり、プランに破棄が表示されます)。そして、movedブロック3つで、local_file.node[0]をlocal_file.node["n0"]に移す形で、古いアドレスと新しいアドレスをつなげてください。適用したあと、tofu planの出力を/root/tf/foreach/out/after-plan.txtに保存すると、No changesで、破棄が1件もない必要があります。

アドレスが変わると、ツールは破棄と作成として解釈します。movedで古いアドレスと新しいアドレスをつなぎ、ファイル名・内容はそのまま維持すると、再作成が起きません。

全アドレスの一覧と移行完了レポートを作る

/root/tf/foreach/out/addresses.jsonを作成してください。addressesキーに、tofu state listのアドレスを配列として入れ(node 3 + zone 3 + service 3 = 9個以上)、数値のインデックスが付いたアドレスは1つもあってはいけません。numeric_index_countキーには、数値インデックスのアドレスの個数を書き、移行が終わっていれば0です。

状態の一覧を、JSON配列に変換して入れます。数値インデックスのアドレスが1つも残っていないかを、自分で数えてみてください。