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

Infrastructure as Code

desired / actual / planの三分割を作る

TT Labで続きを見る

このラボは本物のVM上で動きます

このボックスはPodではなく、KubeVirtが起動した仮想マシンです。Linuxカーネルが別に動き、systemdが実際にサービスを管理し、dockerは模倣ではなく本物のDockerエンジンです。docker runで起動したコンテナは実際にプロセスになり、docker execもdocker logsもそのまま動作します。

以前は、このラボはPodの中で動いていました。カーネルの権限をすべて手放したボックスだったため、コンテナを起動するステップがふさがれていて、そのためイメージのアーカイブを直接展開してみるという回り道で学んでいました。今は回り道は不要です。

知っておくことが2つあります。

目標

望ましい状態(desired)、現在の状態(actual)、両者の差(plan)という3分割の構造を自分で作り、ドリフトがどう検知され、どう収束するかを確認します。

なぜ重要なのか

IaCツールが画面に出力する+ create、- destroy、~ updateは魔法ではなく、2つの状態を比較した結果です。この構造を手で1回作ってみると、planを読む目が変わります。特に、自動化では結果を終了コードで受け取る必要があるので、0は変更なし、1はエラー、2は変更ありという慣例に、そのまま従います。3軸モデルでいえば、コードはDesired、状態ファイルはLast Known、実際のインフラはActualで、3つのうちどれか2つでも食い違えばドリフトです。GitOpsの自己修復も、結局はこの比較を繰り返し回すことです。

ステップ

  1. /root/iac1/desired.yamlに、望ましい状態を宣言してください。最上位にcontainers:の行を1つ置き、その下に項目を2つ置きます。名前はそれぞれiac-webとiac-cacheで、項目ごとにimage:を1行ずつ書きます(ファイル全体でimage:はちょうど2行)。イメージは、Podに事前にあるalpine:3.20、busybox:1.36、python:3.12-alpine、nginx:1.27-alpineの中からだけ選んでください。
  2. /root/iac1/parse.shを作成して、そのYAMLを/root/iac1/desired.jsonに変換してください。JSONの形は{"containers":[{"name":"...","image":"..."}, ...]}で、要素は2個、imageの値が空であってはいけません。
  3. /root/iac1/actual.shを作成してください。現在実際に起動している管理対象のコンテナを、同じ形のJSONで標準出力に出力します。各要素にはnameとimageの両方が必要です。管理対象は、名前がiac-で始まるコンテナだけで、イメージの文字列は、localhost/やdocker.io/library/の接頭辞を取り除いて、desiredと同じ表記に揃えてください。
  4. /root/iac1/plan.sh <desired.json> <actual.json>を作成してください。desiredにだけあれば+ create <이름>、actualにだけあれば- destroy <이름>、両方にあってimageが違えば~ update <이름>を、1行ずつ出力します(プレースホルダーはコンテナ名です)。差が1つもなければno changesを出力して、終了コード0を返します。差があれば、終了コード2を返します(0=変更なし、1=エラー、2=変更あり)。
  5. /root/iac1/apply.shを作成して、実際の状態を宣言に合わせてください。終わったら、desiredに書かれた2つのコンテナが、どちらもrunningでなければなりません。
  6. 何も変えないまま、planをもう一度実行して、/root/iac1/plan2.txtに保存してください。このファイルにはno changesがあり、+、-、~で始まる行が1つもあってはいけません。
  7. コードの外から状態を揺さぶってください。docker rm -f iac-cacheでコンテナを1つ削除してから、planを実行して/root/iac1/drift.txtに保存します。変更の行はちょうど1つで、その行は+ create iac-cacheでなければなりません。
  8. もう一度apply.shを実行して収束させ、そのあとのplanを/root/iac1/plan3.txtに保存してください。no changesが出て、2つのコンテナがどちらもrunningでなければなりません。

参考

望ましい状態を宣言する

/root/iac1/desired.yamlに、望ましい状態を宣言してください。最上位にcontainers:の行を1つ置き、その下に項目を2つ置きます。名前はそれぞれiac-webとiac-cacheで、項目ごとにimage:を1行ずつ書きます(ファイル全体でimage:はちょうど2行)。イメージは、Podに事前にあるalpine:3.20、busybox:1.36、python:3.12-alpine、nginx:1.27-alpineの中からだけ選んでください。

/root/iac1/desired.yamlの最上位にcontainers:の行を1つ置き、その下に項目を2つ置きます。名前は正確にiac-webとiac-cache、項目ごとにimage:を1行ずつ(合計2行)です。オフラインなので、イメージは事前にあるものだけを使います。

宣言をマシンが読める形式にする

/root/iac1/parse.shを作成して、そのYAMLを/root/iac1/desired.jsonに変換してください。JSONの形は{"containers":[{"name":"...","image":"..."}, ...]}で、要素は2個、imageの値が空であってはいけません。

YAMLパーサーがなくてもかまいません。grep/sed/awkでnameとimageの値を取り出して、jq -nで{containers:[...]}を組み立ててください。/root/iac1/desired.jsonの要素は2個で、imageが空であってはいけません。

現在の状態を読み取る

/root/iac1/actual.shを作成してください。現在実際に起動している管理対象のコンテナを、同じ形のJSONで標準出力に出力します。各要素にはnameとimageの両方が必要です。管理対象は、名前がiac-で始まるコンテナだけで、イメージの文字列は、localhost/やdocker.io/library/の接頭辞を取り除いて、desiredと同じ表記に揃えてください。

actual.shは、標準出力でdesiredと同じ形のJSONを出力します。管理対象は、名前がiac-で始まるコンテナだけです。podmanが付けるlocalhost/、docker.io/library/の接頭辞は取り除いて、desiredと表記を揃えてください。

差を計算する

/root/iac1/plan.sh <desired.json> <actual.json>を作成してください。desiredにだけあれば+ create <이름>、actualにだけあれば- destroy <이름>、両方にあってimageが違えば~ update <이름>を、1行ずつ出力します(プレースホルダーはコンテナ名です)。差が1つもなければno changesを出力して、終了コード0を返します。差があれば、終了コード2を返します(0=変更なし、1=エラー、2=変更あり)。

plan.sh <desired.json> <actual.json>は、desiredにだけあれば+ create <이름>、actualにだけあれば- destroy <이름>、イメージが違えば~ update <이름>を出力します(プレースホルダーはコンテナ名です)。記号と語の間は空白です。差がなければ、no changesと終了コード0です。

宣言した状態を作る

/root/iac1/apply.shを作成して、実際の状態を宣言に合わせてください。終わったら、desiredに書かれた2つのコンテナが、どちらもrunningでなければなりません。

apply.shは、planが言ったことだけを実行すればよいです。alpine:3.20のようにすぐ終了するイメージは、tail -f /dev/nullのようなコマンドを与えないと、runningのまま残りません。

再適用しても変化がないことを確認する

何も変えないまま、planをもう一度実行して、/root/iac1/plan2.txtに保存してください。このファイルにはno changesがあり、+、-、~で始まる行が1つもあってはいけません。

何も変えていない状態で、planをもう一度実行して、/root/iac1/plan2.txtに保存してください。no changesだけがあり、+、-、~で始まる行が1つもあってはいけません。採点ツールは、desired.jsonとactual.shの結果を再び比較するので、イメージの表記が1文字違うだけでも失敗します。

外で起きた変更を検出する

コードの外から状態を揺さぶってください。docker rm -f iac-cacheでコンテナを1つ削除してから、planを実行して/root/iac1/drift.txtに保存します。変更の行はちょうど1つで、その行は+ create iac-cacheでなければなりません。

docker rm -f iac-cacheでコードの外から状態を揺さぶったあと、planを/root/iac1/drift.txtに保存します。変更の行はちょうど1つ、+ create iac-cacheでなければなりません。1つだけ削除したのに複数行が出るなら、actual.shが管理対象を誤って選んでいるという意味です。

自動で収束させる

もう一度apply.shを実行して収束させ、そのあとのplanを/root/iac1/plan3.txtに保存してください。no changesが出て、2つのコンテナがどちらもrunningでなければなりません。

もう一度applyしたあとのplanを、/root/iac1/plan3.txtに保存します。no changesが出て、2つのコンテナがどちらもrunningなら、GitOpsコントローラーが行う自己修復を、手で再現したことになります。