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

Terraform実戦

コードから消したら稼働中のものが壊れかけた

TT Labで続きを見る

目標

リソースを壊さず、状態からだけ外すremovedブロックを使ってみて、このバージョンのremovedが何を受け取るかを、ツールに直接尋ねて記録し、movedで名前だけを変え、-replaceとtaintで1つだけ作り直したあと、モジュール1つをまるごと手放します。

なぜ重要なのか

インフラコードを長く運用していると、コードと実体の所有関係に手を入れる日が来ます。チームが分かれ、別のツールに移り、引き継いだものを返却します。ところが、設定からリソースブロックを削除することは、ツールには「壊せ」と読まれます。所有を手放すという意味ではありません。そのため、その意味を別に伝える宣言が必要で、コマンドではなく宣言で行う理由は、レビューと記録のためです。誰がいつ何を状態から外したのかが、コミットに残ります。ここに、もう1つ落とし穴があります。ドキュメントはいつも最新バージョンを説明しますが、使っているバージョンは、それより古いかもしれません。removedブロックが受け取る引数がバージョンごとに違うことを知り、ドキュメントではなく手元のツールに尋ねて確認する習慣が、このモジュールで学ぶ2つ目のことです。

ステップ

  1. /root/tfa-removed/keep.tfにrandom_pet.keep(length 2)と、その名前をout/keep.txtに書くlocal_file.keepを、/root/tfa-removed/legacy.tfに、out/legacy.txtにlegacy serviceを1行書くlocal_file.legacyを置いて、init・applyしてください。そのあと、状態ファイルを/root/tfa-removed/snapshots/before.tfstateに、1回だけコピーします。
  2. /root/tfa-removed/legacy.tfを削除して、/root/tfa-removed/removed.tfに、removedブロックでlocal_file.legacyを状態から外すと宣言してください。apply前に、プランの出力を/root/tfa-removed/removed-plan.txtに保存して、applyします。out/legacy.txtは、そのまま残っている必要があります。
  3. /root/tfa-removed/removed.tfのremovedブロックの中に、lifecycleブロック(destroy = false)を一時的に入れて、tofu planを回し、結果を/root/tfa-removed/lifecycle-probe.txtに保存したあと、元に戻してください。 そのあと、/root/tfa-removed/removed-facts.tsvに、3行をタブ区切りの2列で書きます。tofu_versionは、このPodのコアバージョン、removed_lifecycleは、supportedまたはunsupported、removed_effectは、forgetまたはdestroyです。
  4. /root/tfa-removed/keep.tfのrandom_pet.keepをrandom_pet.appに変更し(参照も一緒に)、/root/tfa-removed/moved.tfに、movedブロックで、古いアドレスから新しいアドレスに移すと宣言したあと、applyしてください。そのあと、/root/tfa-removed/moved.tsvに、before(ステップ1のコピーに書かれたrandom_pet.keepのid)とafter(いまの状態のrandom_pet.appのid)の2行を、タブ区切りの2列で書きます。2つの値は、同じである必要があります。
  5. /root/tfa-removed/legacy.tfを一時的によみがえらせて(ステップ2と同じ内容)tofu planを回し、出力を/root/tfa-removed/conflict.txtに保存したあと、そのファイルを再び削除してください。removedブロックは、そのままにします。最後に、プランはきれいである必要があります。
  6. 状態ファイルを/root/tfa-removed/snapshots/before-replace.tfstateにコピーしたあと、tofu apply -replace=random_pet.app -auto-approveを回してください。そのあと、/root/tfa-removed/replace.tsvに、before(コピーのid)とafter(いまのid)の2行を、タブ区切りの2列で書きます。2つの値は、異なる必要があります。
  7. tofu taint random_pet.appで印を付けたあと、状態に書かれたそのインスタンスのステータス値を、/root/tfa-removed/taint-status.txtに1行で保存し、tofu planの出力を/root/tfa-removed/taint-plan.txtに保存してください。そのあと、tofu untaintで元に戻して、プランがきれいな状態で終えます。
  8. /root/tfa-removed/modules/archive/main.tfに、out/archive-1.txt・out/archive-2.txtの2つのファイルを書くモジュールを作成し、/root/tfa-removed/archive.tfでmodule "archive"として呼び出したあと、applyしてください。そのあと、/root/tfa-removed/archive.tfを削除して、/root/tfa-removed/archive-removed.tfに、removedブロックでmodule.archiveをまるごと状態から外してください。プランの出力は/root/tfa-removed/module-plan.txtに保存し、2つのファイルは、ディスクに残っている必要があります。

参考

引き継いだものまで、1つの状態に入れる

/root/tfa-removed/keep.tfにrandom_pet.keep(length 2)と、その名前をout/keep.txtに書くlocal_file.keepを、/root/tfa-removed/legacy.tfに、out/legacy.txtにlegacy serviceを1行書くlocal_file.legacyを置いて、init・applyしてください。そのあと、状態ファイルを/root/tfa-removed/snapshots/before.tfstateに、1回だけコピーします。

あとのステップで、「このリソースが再作成されたのか、そのままなのか」を判断するには、いまの識別子をどこかに残しておく必要があります。コピーは、最初の1回だけ取ってください。あとで上書きすると、比較する原本が消えます。

状態からだけ外して、実体は残す

/root/tfa-removed/legacy.tfを削除して、/root/tfa-removed/removed.tfに、removedブロックでlocal_file.legacyを状態から外すと宣言してください。apply前に、プランの出力を/root/tfa-removed/removed-plan.txtに保存して、applyします。out/legacy.txtは、そのまま残っている必要があります。

設定からリソースブロックだけを削除すると、ツールは、それを壊せという意味に解釈します。removedブロックは、「このアドレスを状態から忘れろ」を別に伝える宣言です。プランの出力で、この動作を指す語がdestroyではない点を確認してください。

このバージョンのremovedが何を受け取るかを、ツールに尋ねる

/root/tfa-removed/removed.tfのremovedブロックの中に、lifecycleブロック(destroy = false)を一時的に入れて、tofu planを回し、結果を/root/tfa-removed/lifecycle-probe.txtに保存したあと、元に戻してください。 そのあと、/root/tfa-removed/removed-facts.tsvに、3行をタブ区切りの2列で書きます。tofu_versionは、このPodのコアバージョン、removed_lifecycleは、supportedまたはunsupported、removed_effectは、forgetまたはdestroyです。

ドキュメントは、いつも最新バージョンを説明します。いま手元にあるバージョンが何を受け取るかは、ツールに尋ねて初めてわかり、その答えを書き留めておくことが、チームの時間を節約します。効果は、ステップ2のプランの出力が、すでに語っています。

名前だけを変える。同じものを作り直さずに

/root/tfa-removed/keep.tfのrandom_pet.keepをrandom_pet.appに変更し(参照も一緒に)、/root/tfa-removed/moved.tfに、movedブロックで、古いアドレスから新しいアドレスに移すと宣言したあと、applyしてください。そのあと、/root/tfa-removed/moved.tsvに、before(ステップ1のコピーに書かれたrandom_pet.keepのid)とafter(いまの状態のrandom_pet.appのid)の2行を、タブ区切りの2列で書きます。2つの値は、同じである必要があります。

movedブロックがないと、ツールは、「古い名前が消えて、新しい名前ができた」と解釈して、壊して作り直します。movedは、同じオブジェクトのアドレスだけが変わったと伝える宣言なので、識別子がそのまま維持されます。それが本当にそうなのかを、ステップ1のコピーと比べて確認してください。

忘れろと言っておいて、再び宣言すると

/root/tfa-removed/legacy.tfを一時的によみがえらせて(ステップ2と同じ内容)tofu planを回し、出力を/root/tfa-removed/conflict.txtに保存したあと、そのファイルを再び削除してください。removedブロックは、そのままにします。最後に、プランはきれいである必要があります。

忘れろという宣言と、作れという宣言が、同じアドレスに同時にあると、ツールは何をすべきかがわかりません。このエラーは、tofu validateは通過して、planでだけ出ます。なぜそうなのかを考えてみてください(アドレスの解決は、設定の検証ではなく、プランの段階の仕事です)。

1つだけ作り直す

状態ファイルを/root/tfa-removed/snapshots/before-replace.tfstateにコピーしたあと、tofu apply -replace=random_pet.app -auto-approveを回してください。そのあと、/root/tfa-removed/replace.tsvに、before(コピーのid)とafter(いまのid)の2行を、タブ区切りの2列で書きます。2つの値は、異なる必要があります。

-replaceは、設定を変えずに、「これだけを壊して作り直せ」をプランに入れます。プロバイダーの調子が悪くなったときや、設定に現れない起動状態を初期化したいときに使います。このリソースに依存するものが、一緒に再作成されるかも、プランで見てください。

手で印を付けて、元に戻す

tofu taint random_pet.appで印を付けたあと、状態に書かれたそのインスタンスのステータス値を、/root/tfa-removed/taint-status.txtに1行で保存し、tofu planの出力を/root/tfa-removed/taint-plan.txtに保存してください。そのあと、tofu untaintで元に戻して、プランがきれいな状態で終えます。

印は、インフラには触れず、状態ファイルにだけ書かれます。次のプランがそれを読んで、置き換えを入れます。jqで、そのインスタンスのstatusを見てください。今日は、同じことを-replaceで、より安全に行えますが(状態を事前に変えないため)、このコマンドが何をするかを知っておかないと、他人が印を付けておいたものに出会ったときに慌てます。

モジュール1つをまるごと手放す

/root/tfa-removed/modules/archive/main.tfに、out/archive-1.txt・out/archive-2.txtの2つのファイルを書くモジュールを作成し、/root/tfa-removed/archive.tfでmodule "archive"として呼び出したあと、applyしてください。そのあと、/root/tfa-removed/archive.tfを削除して、/root/tfa-removed/archive-removed.tfに、removedブロックでmodule.archiveをまるごと状態から外してください。プランの出力は/root/tfa-removed/module-plan.txtに保存し、2つのファイルは、ディスクに残っている必要があります。

removedのfromには、リソースアドレスだけでなく、モジュールアドレスも指定でき、その場合、そのモジュール内のものが、一度に状態から外れます。組織を分けるとき、1つのチームの持ち分をまるごと引き渡す作業が、この形です。モジュール内でファイルパスを指定するときは、ルート基準のパスを使うほうが、混乱しません。