Readyと動いているは別の命題だ
一言でいうと
トラブルシューティングは、CKAの配点の30%で、5つのドメインの中で最も大きいです。そしてコツは1つです。状態を上から下へ1層ずつ下りながら確認し、どの層で話が途切れるのかを見つけること。Podの状態が、診断の出発点です。
なぜ必要なのか
症状だけを見て原因を当てようとすると、外れます。同じ「接続できない」が、セレクターのタイプミスかもしれず、スケジュールの失敗かもしれず、PVCの未バインドかもしれません。そのため、順序を決めておきます。
| Podの状態 | どこで詰まったか | まず見るもの |
|---|---|---|
| Pending | スケジューラーのfilter段階 | describeのEvents、リソース要求、テイント、nodeSelector、PVCのバインド |
| ContainerCreating | kubeletのボリューム/ネットワークの準備 | ボリュームのマウント、CNI、イメージ |
| ImagePullBackOff | イメージの取得 | タグのタイプミス、レジストリの認証 |
| CrashLoopBackOff | コンテナが死に続けている | ログ、設定、プローブ |
| Runningなのにトラフィックがない | サービス層 | セレクター、Ready、ポート |
この表で最もよく出てくるのがPendingで、その中で最もよく出てくるのが、リソース要求の単位のミスです。cpu: 500は、500ミリコアではなく、500コアです。500mと書く必要があります。memory: 2000Giも、2000Miのタイプミスであることが多いです。スケジューラーのイベントは、次のように語ります。
0/3 nodes are available: 3 Insufficient cpu.
どう動くのか
etcdのバックアップは、コントロールプレーンのDRの最後の保険です。
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d%H%M).db --endpoints=https://127.0.0.1:2379 --cacert=... --cert=... --key=...
etcdctl snapshot status /backup/etcd-*.db --write-out=table
復旧の原則は3つです。
- 既存のdata-dirを上書きしてはいけません。
--data-dirで新しいパスに展開し、設定をそのパスに変えて起動します。 - 復旧中はapiserverを停止します。生きているapiserverが書き込みを続けると、復旧したものとずれます。
--initial-clusterとpeer URL(2380)を、正確に合わせます。クォーラムを失った状況なら、まず単一ノードで復旧してから、メンバーを1つずつ追加し直します。
そして、必ず指摘しておくべきことがあります。クォーラムは障害への備えであり、ミスへの備えではありません。誤った削除は、すぐにすべてのメンバーに複製されます。特定の時点に復旧できるのは、スナップショットだけです。また、etcdのスナップショットには、PV内のデータがありません。アプリケーションのデータには、Veleroのような別の手段が必要です。
現場での姿
事例1: すべてReadyなのに動きません。ホームラボにKubeVirtを導入したとき、コンポーネントの状態はすべてAllComponentsReadyだったのに、VMは起動しませんでした。virt-launcherのPodのマニフェストを詳しく調べると、initコンテナが実行するバイナリを含むボリュームマウントが抜けていました。筆者の表現をそのまま借りれば、「状態がReady」と「実際に動作する」は別の命題だということを、そのクラスターだけで3回目に確認した瞬間でした。上位のstatusフィールドは、そのコントローラーが知っている範囲のことしか語りません。
事例2: バイナリがあることと、設定されていることは別物です。GPU Operatorをインストールするときに、ノードにnvidia-ctkバイナリがあるのを見て、toolkitのインストールを無効にしました。結果は、次のとおりでした。
Failed to create pod sandbox: rpc error: code = Unknown
desc = failed to get sandbox runtime: no runtime for "nvidia" is configured
Podは、起動すらできませんでした。コンテナの中で何かが失敗したのではなく、サンドボックスを作る段階で詰まったのです。確認してみると、kubectl get runtimeclassにはnvidiaがありますが、ノードでは、grep -c nvidia /etc/containerd/config.tomlが0でした。RuntimeClassはhandler: nvidiaという名前を指すラベルにすぎず、実体はノードのランタイム設定になければならないのに、再構築でランタイムをcri-dockerdからcontainerdに変えたときに、その設定が消えていたのです。ホストのバイナリは、過去の名残でした。
修正したあとも、config.tomlは依然として0でした。設定は、/etc/containerd/conf.d/99-nvidia.tomlというdrop-inファイルに入っていました。grepした場所がすべてだと信じたことが、2つ目の落とし穴でした。
事例3: クォーラムがあるからといって、バックアップが不要なわけではありません。コントロールプレーンを3台に増やしてetcdメンバー3つを確保したあとも、残りの課題リストの2行目は、これでした。「etcdの定期スナップショット: クォーラムは障害への備えであり、ミス(誤削除)への備えではない」。データが3つ複製されていても、誤ったkubectl deleteを一度実行すれば、3つすべてに即座に反映されます。
次のラボですること
最初のラボで、etcdのスナップショットを実際に取得し、バックアップ前後のリソースを照合して、スナップショットに含まれないものが何かを目で確認し、復旧計画をドキュメントとして残します。2つ目のラボでは、5種類の障害を自分で作って、自分で直します。誤ったイメージタグ、リソース要求の単位のミス、トレラレーションの欠落、ラベルの不一致、ResourceQuotaの超過、PDBで塞がれたdrainです。直す前に症状をファイルに残すことが、採点の対象です。