Dev Study
← 宣言的に動かす — Kubernetes と Terraform に共通する仕組み
レッスン 12 / 12·約 7 分·第26期 宣言的に動かす

戻すのも、新しい宣言を1つ出すこと

アプリと DB の下に何が積まれているか(左が下、右が上)
  1. 物理機リージョン/AZ
  2. ハイパーバイザVM exit で捕まえる
  3. VMゲストOS今ここ
  4. コンテナnamespace / cgroup今ここ
  5. プロセスアプリ / DB

この期の最後に、「元に戻す」という操作も宣言と比較の外には出られないことを確かめます。

まず結論

「戻す」は過去へ移動する操作ではありません。過去の中身を持った新しい宣言を、もう1つ出す操作です。

Kubernetes の rollout undo は新しい revision を作り、Terraform で消したコードを戻して apply すると、同じ名前の別の実体ができます。そして宣言の写しが複数あれば、戻したのは1つだけかもしれません。

? 読む前に予測

Deployment に v1 を apply し、次に v2 を apply してから `kubectl rollout undo` を実行しました。その後の `kubectl rollout history` に並ぶ REVISION はどれでしょうか(k3s v1.37.0 の実演)。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

戻す操作も、前へ進む

Deployment は、Pod(コンテナをまとめて動かす最小単位)の雛形と台数を宣言するオブジェクトです。雛形が変わるたびに ReplicaSet(同じ雛形の Pod を指定の数だけそろえるオブジェクト)を用意し、コントローラが新旧の台数を入れ替えます。

雛形の版には番号が付きます。公式文書はこう書きます。

Each new ReplicaSet updates the revision of the Deployment.

Rollback to an earlier Deployment revision if the current state of the Deployment is not stable. Each rollback updates the revision of the Deployment.

2文目の後半が要点です。rollback も revision を更新します。 戻す操作は番号を巻き戻さず、次の番号を作ります。中身が過去と同じでも、宣言としては新しい1件です。

rollout undo は、過去の雛形を新しい番号で出し直す(実演の流れ)
v1 を apply1.36 の雛形 → ReplicaSet A が revision 1
v2 を apply
ReplicaSet B が revision 21.37 の雛形。A は 0 台に
rollout undo
ReplicaSet A が revision 3中身は 1.36。表示から 1 が消える
  • ・A と B は説明用の呼び名。実演の ReplicaSet 名は雛形のハッシュ付き
  • ・この実演では ReplicaSet が2つなので、history の表示も2行のまま
具体実物

番号は 1 に戻らず、3 が現れる

k3s v1.37.0(kubectl v1.37.0+k3s1)で、busybox:1.36 の v1 と 1.37 の v2 を順に apply し、undo しました。

実演ログ(抜粋。Waiting・rolled out・exit・区切り・空行・history 見出し・undo の警告の各行を省略)コマンド出力
deployment.apps/web created
REVISION CHANGE-CAUSE
1 <none>
deployment.apps/web configured
REVISION CHANGE-CAUSE
1 <none>
2 <none>
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
web-658b7cffc8 0 0 0 3s c busybox:1.36 app=web,pod-template-hash=658b7cffc8
web-7579fc6c9 2 2 2 2s c busybox:1.37 app=web,pod-template-hash=7579fc6c9
deployment.apps/web rolled back
REVISION CHANGE-CAUSE
2 <none>
3 <none>
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
web-658b7cffc8 2 2 2 5s c busybox:1.36 app=web,pod-template-hash=658b7cffc8
web-7579fc6c9 0 0 0 4s c busybox:1.37 app=web,pod-template-hash=7579fc6c9
live image=busybox:1.36 revision=3

undo の後、history は 2 と 3 です。ReplicaSet は前後とも同じ2つで、1.36 のほうが 2 台に戻り、今度は 3 番を名乗っています。最終行は Deployment 自身の image と revision で、1.36 と 3 です。

中身は v1 と同じでも、番号は 3。**「1 に戻った」のではなく、「1 と同じ中身の 3 を出した」**のです。2回実行して、ReplicaSet 名まで同じ結果でした。

なお文書は、戻るのは Pod の雛形の部分だけだとも書いています。台数や DB の中身がどうなるかは第24期で扱います。

✓ ここまでの確認

rollout undo の直後の Deployment について、実演のログから言えることはどれですか。

抽象原理

宣言の写しが複数あれば、戻したのは1つだけ

新しい宣言を出したなら、次に問うべきはその宣言がどこに書かれたかです。この実演では、宣言の写しが3か所にありました。手元の v2 ファイル、apply が Deployment に残す last-applied-configuration 注釈、そして Deployment の spec そのものです。

undo が書き換えたのは spec だけでした。kubectl は undo のときにこう警告します(下は実演の出力で、文面は kubectl のソース rollout_undo.go の書式文字列に資源名などを埋めたものと一致します。文書のページには載っていません)。

Warning: resource deployments/web was previously managed with 'kubectl apply'. Rolling back will not update the kubectl.kubernetes.io/last-applied-configuration annotation, which may cause unexpected behavior on future 'kubectl apply' operations. Consider using 'kubectl apply' with your previous configuration file instead.

この警告は apply で管理していた場合に出るものです。undo 後の実体と注釈、そしてファイルをもう一度 apply した結果です。

undo の後(抜粋。exit 行と diff の行、history の見出し行と空行を省略。2行目は注釈から image を取り出したもの)コマンド出力
live image=busybox:1.36 revision=3
"image":"busybox:1.37"
=== re-apply the v2 file (busybox:1.37) after undo
deployment.apps/web configured
live image=busybox:1.37 revision=4
=== re-apply the v1 file (busybox:1.36) after undo
deployment.apps/web configured
live image=busybox:1.36 revision=5
REVISION CHANGE-CAUSE
4 <none>
5 <none>

実体は 1.36、注釈は 1.37。同じ Deployment の中で宣言の写しがずれています。 そこへ 1.37 のファイルを apply すると configured で実体は 1.37、revision は 4。undo は、次の宣言にそのまま上書きされました。

警告の言う「予期しない振る舞い」の具体例は、この単純な例では再現していません。実測で言えるのは、注釈が更新されず、ファイルを流し直すと undo が消えた、までです。

具体実物

Terraform には undo が無い

Terraform には「戻す」コマンドがありません。戻すなら、コードを戻して apply するだけです。Terraform 1.16.4 と kreuzwerker/docker v4.6.0 でコンテナ1つを作り、resource ブロックを消して apply し、書き戻して apply しました(この実演は docker.sock を渡しており、ホストの Docker を操作する権限を Terraform に与えています)。

実演ログ(run 1 の抜粋。id は先頭12桁で切った)コマンド出力
# docker_container.web will be created
docker_container.web: Creation complete after 1s [id=238745042ec2…]
### remove the resource block from the code and apply
# docker_container.web will be destroyed
# (because docker_container.web is not in configuration)
Plan: 0 to add, 0 to change, 1 to destroy.
state list: []
### put the resource block back (as a git revert would) and apply
# docker_container.web will be created
Plan: 1 to add, 0 to change, 0 to destroy.
docker_container.web: Creation complete after 1s [id=1fb6041f2407…]
DIFFERENT ID

消すと not in configuration を理由に destroy、state も空。書き戻すと Terraform には消えたものを作るとしか見えず、will be created です。できたコンテナは名前も設定も同じなのに id が違います。run 2 でもう一度消して戻すと、また別の id でした。

これは terraform destroy コマンドとは別の道筋で、構成から消すだけで実体は消えます。lifecycle の文書は、この場合 prevent_destroy でも止まらないと書いています。

✓ ここまでの確認

Terraform で resource ブロックを消して apply し、そのブロックを元どおり書き戻してもう一度 apply しました。実演で起きたことはどれですか。

⇄ 2つを見比べる
具体例 A

Kubernetes の rollout undo

以前の雛形を内容とする revision 3 ができた。コントローラはそれに合わせて旧い ReplicaSet を 2 台に戻す。ただし last-applied 注釈と手元のファイルは 1.37 のまま残った。

具体例 B

Terraform でコードを戻して apply

Terraform に戻す操作は無く、書き戻したブロックは「作るべきもの」として plan に載った。できたコンテナは同じ名前・同じ設定だが、別の id だった。

片方には undo コマンドがあり、片方には無い。それでも同じ構造だと言えるのはなぜでしょうか。

操作の後に「比べる者」が見ている宣言は、過去のものか、いま出したものか。

この章のまとめ

  • rollout undo は revision を進めます。実演では history が 1,2 から 2,3 になり、1.36 の ReplicaSet が 3 番を名乗りました。
  • undo は last-applied-configuration 注釈を更新しないと kubectl 自身が警告します。実測では注釈 1.37、実体 1.36 でした。
  • ファイルを apply し直すと undo は上書きされます(revision 4)。戻すなら、ファイルという写しも戻すことになります。
  • Terraform に undo はありません。コードを戻すと will be created で、同じ設定の別の id ができます。構成から消せば prevent_destroy があっても destroy されると文書は書きます。

この期の芯は、宣言は望む状態の記録にすぎず、世界がそれに近づくのは誰かが世界と比べて差を埋めたときだけ、でした。「戻す」もその外には出られません。戻すとは、昔と同じ中身の記録を新しく1つ出し、比べる者に埋めさせることです。だから、戻した後に確かめるのは、どの写しがどの宣言を持っていて、次に誰がどれと比べるか、です。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

apply で管理していた Deployment に `kubectl rollout undo` を実行すると、kubectl は警告を出しました。何が更新されないと言っていますか。

用語集(3語)
revision(Deployment)
Deployment の雛形(Pod template)の版に付く番号。雛形が変わったときと rollback のときに新しくなる。台数の変更では作られない。
kubectl rollout undo
Deployment などを以前の rollout に戻すコマンド。実演では、以前の雛形を新しい revision として出し直した。
last-applied-configuration
kubectl apply が、適用した設定ファイルの内容をオブジェクトに書き込む注釈。rollout undo はこれを更新しない。