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

FDE総合演習:倉庫に同じ注文が3回届いた

閉域網クラスタで ImagePullBackOff

TT Labで続きを見る

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

VMの中にk3s 1台(v1.33.3+k3s1)が起動していて、containerd・kubeletが、実際にイメージをプルします。最初に起動するのに数分かかります。VMは、最初はインターネット(80/443)に届きます。ステップ1が接続された持ち込み準備マシンの役割で、ステップ2で、あなた自身が外への送信を塞いで、顧客のエアギャップ環境に変えます。採点は、外からVMの8899ポートに入ってくるエージェントが行うので、すでに確立した接続とループバックを塞ぐと、採点が途切れます。

目標

接続された場所で、イメージをアーカイブとして取得してハッシュを記録し、送信が塞がれたk3sで、ImagePullBackOffを証拠として残したあと、k3s ctr images importと、k3sのイメージのディレクトリの2つの経路で持ち込み、持ち込んでも失敗するプルポリシーまで確認して、持ち込みの記録を、ランタイムと照合して残します。

なぜ重要なのか

エアギャップ環境の顧客先で、「Podが起動しません」の半分は、イメージです。レジストリに届かないクラスターは、イメージを自分では入手できず、誰かがランタイムのイメージストアに入れてあげる必要があります。しかし、入れたら終わりではありません。運ぶ間に変わったファイルを入れると、何が動いているのか誰にもわからなくなり、マニフェストがimagePullPolicy: Alwaysであったり、タグがlatestであったりすると、入れておいたイメージがあるのに、レジストリに問い合わせに出ていって失敗します。持ち込みは、「ファイルをコピーした」ではなく、「ハッシュが合うイメージをランタイムが持っていて、Podがそれを使っている」ことを証明する仕事です。

想定所要時間は60分です。セッションが終わるとVMが消えるので、残したいファイルは、終了の前に別に保管してください。

ステップ

  1. 2つのイメージregistry.k8s.io/e2e-test-images/busybox:1.36.1-1とregistry.k8s.io/e2e-test-images/nginx:1.15-4を、イメージアーカイブとして取得して配置し(配置先: /root/airgap/bundle/busybox.tar、/root/airgap/bundle/nginx.tar)、sha256 파일이름 이미지を1行ずつ記録します(プレースホルダーはファイル名とイメージ名です。ファイル: /root/airgap/manifest.txt)。アーカイブの中のイメージ名は、上の名前のままである必要があります。
  2. 何度動かしても、ルールが一式だけ残るスクリプトを書いて、外への送信を塞ぎます(ファイル: /root/airgap/egress.sh)。iptablesのチェーン名はAIRGAP-EGRESSで、OUTPUTとFORWARDから、1回ずつそこへ送ります。ループバック・すでに確立した接続・k3sのPodの帯域(10.42.0.0/16)・Serviceの帯域(10.43.0.0/16)は生かし、残りは塞ぎます。塞ぐ前と塞いだあとに、curl -s -o /dev/null -w '%{http_code}' https://registry.k8s.io/v2/で測った値を、before=・after=として記録します(ファイル: /root/airgap/egress-proof.txt)。
  3. ネームスペースairgapに、busyboxのイメージでsleep 3600を実行するPodshop-apiを作ります(マニフェスト: /root/airgap/shop-api.yaml)。プルに失敗したら、uid=<파드 UID>(プレースホルダーはPodのUIDです)と、状態・イベントを保存します(ファイル: /root/airgap/pull-failure.txt)。
  4. ハッシュを照合したあと、busybox.tarをk3s ctr images importで持ち込み、出力を保存します(ファイル: /root/airgap/import-busybox.txt)。shop-apiをRunningにします。
  5. nginx.tarを、k3sのエージェントのイメージのディレクトリ(/var/lib/rancher/k3s/agent/images/)に入れて持ち込み、そのイメージでPodshop-webをRunningにします(マニフェスト: /root/airgap/shop-web.yaml)。
  6. 持ち込んでおいたbusyboxのイメージで、imagePullPolicy: AlwaysのPodshop-alwaysを作って(マニフェスト: /root/airgap/shop-always.yaml)、失敗を見て、uid=・policy=と状態を保存します(ファイル: /root/airgap/always.txt)。
  7. 送信の遮断を維持したまま、shop-alwaysをimagePullPolicy: IfNotPresentで作り直して、Runningにします。
  8. 持ち込みの記録を残します(ファイル: /root/airgap/import-record.json)。images配列の項目ごとに、ref・tar・tar_sha256・imported_via(ctrまたはimages-dir)・runtime_image_id(k3s crictl inspectiが報告するid)・pods(いま、そのイメージでRunningのairgapのPod名の一覧)を書き、最上位に、egress_blockedをtrue/falseで書きます。

参考

接続された側で、持ち込む物を作る

2つのイメージを、イメージアーカイブとして取得して配置し(配置先: /root/airgap/bundle/busybox.tar、/root/airgap/bundle/nginx.tar)、sha256 파일이름 이미지を1行ずつ記録してください(プレースホルダーはファイル名とイメージ名です。ファイル: /root/airgap/manifest.txt)。

skopeo copyの宛先に、docker-archive:パス:名前を渡すと、レジストリなしで、ファイル1つとして取得できます。名前を付けておかないと、持ち込みのあとで、Podが同じ名前で探せません。ハッシュは、運ぶ前に測っておかないと、受け取った側で照合できません。

外への送信を塞いで、エアギャップ環境を作る

何度動かしても、ルールが一式だけ残るスクリプトで、外への送信を塞ぎ(ファイル: /root/airgap/egress.sh)、塞ぐ前後のHTTPステータスコードを、before=・after=として記録してください(ファイル: /root/airgap/egress-proof.txt)。

新しいチェーンを1つ作って、OUTPUTとFORWARDの先頭からそこへ送れば、空にしてから埋め直す方式で、何度動かしても同じ形になります。生かすべきものから、RETURNで書き、最後に塞いでください。このVMの採点は、外から8899で入ってくる接続なので、すでに確立した接続を塞ぐと、採点が途切れます。

塞がれたクラスターでのImagePullBackOff

ネームスペースairgapに、busyboxのイメージでPodshop-apiを作って、プルの失敗を見て、uid=<파드 UID>(プレースホルダーはPodのUIDです)と、状態・イベントを保存してください(ファイル: /root/airgap/pull-failure.txt)。

タグを固定したイメージは、imagePullPolicyを書かなければ、IfNotPresentです。ランタイムにないので、プルしに出ていって塞がれます。Podはすぐに作り直されるので、UIDとイベントを、いま残しておいてください。kubeletの記録は、k3sのログにも残ります。

ctrでランタイムに直接持ち込む

busybox.tarをk3s ctr images importで持ち込んで、出力を保存し(ファイル: /root/airgap/import-busybox.txt)、shop-apiをRunningにしてください。

Kubernetesが使うイメージは、containerdのk8s.ioネームスペースにあってはじめて見えます。持ち込んでも、kubeletは、失敗が重なるほど、次の試行までを長く待つので(最大5分)、待ちたくなければ、Podを削除して、同じマニフェストで作り直してください。

k3sのイメージのディレクトリで持ち込む

nginx.tarを、k3sのエージェントのイメージのディレクトリに入れて持ち込み、そのイメージでPodshop-webをRunningにしてください。

k3sは、agentのimagesディレクトリに置かれたtarを、containerdに取り込みます。ドキュメントのバージョンの表で、実行中に入れたファイルも取り込むかを確認してください。ランタイムに見えるまで、少し待ってから、Podを作ります。

持ち込んだのに失敗するPod

持ち込んでおいたbusyboxのイメージで、imagePullPolicy: AlwaysのPodshop-alwaysを作って、失敗を見て、uid=・policy=と状態を保存してください(ファイル: /root/airgap/always.txt)。

Alwaysは、イメージがローカルにあっても、毎回レジストリに問い合わせて、タグをダイジェストに解決しようとします。レジストリに届かなければ、その段階で失敗します。タグがlatestであるか、ないとき、デフォルトのポリシーがAlwaysになることも、思い出してみてください。

遮断はそのままで、ポリシーを直す

送信の遮断を維持したまま、shop-alwaysをimagePullPolicy: IfNotPresentで作り直して、Runningにしてください。

PodのコンテナのimagePullPolicyは、作ったあとでは変更できないフィールドです。遮断を解いて通すことは、顧客のエアギャップ環境ではできない解決なので、マニフェストを直して作り直してください。

持ち込みの記録を、ランタイムと照合して残す

持ち込んだ2つのイメージの記録と、egress_blockedを残してください(ファイル: /root/airgap/import-record.json)。

記録の値は、手で書き写さず、コマンドの出力から取り出してください。ランタイムのイメージIDは、crictl inspectiが教えてくれ、そのイメージを使うRunningのPodは、kubectlのJSONから集めます。持ち込みの経路は、あなたが実際に使った方法です。