Certified Kubernetes Administrator (CKA) #24 Troubleshooting 3: Control plane (apiserver/etcd/scheduler ダウン)、etcd 復旧

前の 2 つのトラブルシューティング記事は、クラスターが生きているという前提の上にありました。#22 の Pod 障害も、#23 のノード障害も kubectl が応答したからこそ、describelogs で症状を読めました。ところが今回の記事の舞台は違います。control plane 自体が崩れると kubectl が丸ごと止まります。 診断の取っ手だったコマンドが失われた状態で原因を絞り込まなければなりません。

幸い control plane には一貫した構造があります。kubeadm で立てたクラスターで apiserver、etcd、scheduler、controller-manager はすべて static Pod として動きます。この 1 つの事実が control plane トラブルシューティング全体を貫く鍵です。static Pod の動き方を理解すれば、apiserver が死んで kubectl が止まった状況でも何を見ればよいのかが明確になります。今回の記事ではその診断ツールと症状別の流れ、そして壊れた control plane をよみがえらせる方法を整理します。

診断の出発点: control plane は static Pod だ #

control plane トラブルシューティングを解くには、#2 で扱った 1 つの事実をもう一度取り出す必要があります。kubeadm クラスターで control plane コンポーネントは Deployment や DaemonSet ではなく static Pod として上がります。 static Pod は apiserver を経由せず、各ノードの kubelet がディスクのマニフェストディレクトリを直接監視して上げる Pod です。マニフェストは次の場所にあります。

ls /etc/kubernetes/manifests/
# etcd.yaml
# kube-apiserver.yaml
# kube-controller-manager.yaml
# kube-scheduler.yaml

この構造から 2 つの結論が出ます。第一に、kubelet がこのファイルを直接読むので ファイルを直せば kubelet が自動的に Pod を再生成します。 kubectl apply は不要です。第二に、static Pod は apiserver を経由しないので apiserver が死んでも kubelet はマニフェストを監視し続けます。 つまり apiserver が丸ごとダウンして kubectl が止まった状況でも、ノードに入ってマニフェストを直せば kubelet がコンポーネントを再び上げます。

そのため control plane トラブルシューティングはほぼ常に同じ場所へ降りていきます。control plane ノードに SSH で入り、マニフェストディレクトリと kubelet ログ、そしてコンテナランタイムを直接覗く作業です。

kubectl が止まったときの診断ツール #

apiserver が死ぬと kubectl getkubectl describe も応答しません。次のようなメッセージが典型的です。

The connection to the server 10.0.0.10:6443 was refused - did you specify the right host or port?

このときは kubectl を捨てて ノードの上のツール へ降ります。3 つが核心です。

crictl: コンテナを直接見る #

crictl は kubelet が使う CRI ランタイムに直接話しかけるコマンドです。apiserver を経由しないので apiserver が死んでも動きます。control plane コンテナが上がっているか、死んで再起動を繰り返しているかをここで見ます。

# すべてのコンテナ状態 (停止したものを含む)
crictl ps -a

# control plane コンテナだけ
crictl ps -a | grep -E 'apiserver|etcd|scheduler|controller'

crictl ps に apiserver コンテナが見えない、あるいは Exited 状態で再起動を繰り返すなら、apiserver が上がろうとして失敗しているという意味です。コンテナ ID でログを直接読みます。

# 死んだコンテナのログ (失敗原因がここに出る)
crictl logs <container-id>

journalctl: kubelet の目で見る #

static Pod を上げる主体は kubelet です。マニフェストにエラーがあれば kubelet がその事実をログに残します。

journalctl -u kubelet -f

マニフェストの YAML 文法エラー、誤ったフラグ、存在しないファイルパスといった問題は kubelet ログに直接現れます。

マニフェストファイル: 原因の 8 割はここにある #

control plane が上がらない問題は、ほとんどが マニフェスト自体の欠陥 から生じます。3 つを疑います。

  • YAML 文法の打ち間違い。インデントのずれ、コロンの欠落、引用符の不一致。kubelet がファイルをパースすらできなければ Pod がまったく上がりません。
  • 誤ったフラグや値。打ち間違えたフラグ、間違ったポート、存在しない証明書パス。コンテナは上がってすぐ死にます。
  • 誤った image タグ。存在しないバージョンを書くと ImagePull で止まります。

修正前に元のファイルをバックアップしておく習慣が安全です。

cp /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/kube-apiserver.yaml.bak

症状 1: apiserver ダウン (kubectl 応答なし) #

最も差し迫った症状です。kubectl が丸ごと止まるのでクラスターを操作できません。apiserver が上がらない原因は大きく 3 つです。

原因 A: マニフェストエラー #

/etc/kubernetes/manifests/kube-apiserver.yaml の YAML 打ち間違いや誤ったフラグが最も多いです。たとえば --etcd-servers の値を間違って書く、証明書パスに打ち間違いがある、インデントがずれている場合です。

# コンテナが上がっては死ぬのを繰り返すなら、死んだコンテナのログを見る
crictl ps -a | grep apiserver
crictl logs <apiserver-container-id>

ログにどのフラグやパスが問題かが出ます。マニフェストを直して保存すれば kubelet が新しい引数で apiserver を再生成します。反映が遅ければ、マニフェストをディレクトリの外へ一時的に移してから戻し、強制的に再ロードできます。

mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
# しばらく後で戻す
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/

原因 B: etcd に接続できない #

apiserver は etcd が生きていてはじめて上がります。etcd が死んでいるか、apiserver の --etcd-servers アドレスが間違っていると、apiserver は etcd に付けず起動中に死にます。ログに connection refused や etcd 関連のエラーが見えるなら、問題の根は apiserver ではなく etcd かもしれません。このときは下の症状 2 の etcd 診断へ進みます。

原因 C: 証明書の問題 #

apiserver は etcd と通信するとき、そしてクライアントを認証するときに証明書を使います。証明書パスが間違っているか証明書が失効すると apiserver が上がれません。マニフェストの --client-ca-file--etcd-cafile--tls-cert-file といったフラグが指すファイルが実際に存在するかを確認します。証明書失効そのものは #25 で別に扱います。

症状 2: etcd ダウン #

etcd が死ぬとクラスターの状態ストアが消えます。apiserver は etcd に付けず一緒に死に、結果として kubectl も止まります。つまり apiserver ダウンに見える症状の本当の原因が etcd であるケース がよくあります。そのため apiserver ログに etcd 接続エラーが見えたら etcd から見ます。

# etcd コンテナの状態とログ
crictl ps -a | grep etcd
crictl logs <etcd-container-id>

etcd が上がらない原因は 2 つです。

  • データディレクトリの問題--data-dir が指すディレクトリ (通常は /var/lib/etcd) が壊れているか、マニフェストの hostPath が間違ったパスを指している場合です。ディスクが満杯で etcd が書き込めない場合もあります。
  • 証明書の問題。etcd も自身の証明書で通信します。/etc/kubernetes/pki/etcd/ の証明書パスが間違っているかファイルがないと etcd が上がれません。

マニフェストエラーで etcd が死んだなら /etc/kubernetes/manifests/etcd.yaml を直してよみがえらせます。データ自体が壊れて etcd が生き返れないなら、このときが #7 で身につけた スナップショット復旧 が必要な瞬間です。

etcd データ破損時の復旧 #

スナップショットがあれば復旧の流れは #7 と同じです。取っておいたスナップショットを新しい data-dir に展開した後、etcd マニフェストの hostPath をそのディレクトリに変えて、kubelet が新しいデータで etcd を再起動するようにします。

# 1) スナップショットを新しいディレクトリに復元 (オフライン作業、証明書不要)
ETCDCTL_API=3 etcdctl snapshot restore /opt/snapshot.db \
  --data-dir=/var/lib/etcd-restore

# 2) /etc/kubernetes/manifests/etcd.yaml の hostPath を
#    /var/lib/etcd から /var/lib/etcd-restore に変更

# 3) 保存すると kubelet が etcd を再生成する
crictl ps | grep etcd

復旧手順の落とし穴 (restore に証明書を入れないこと、復元後の hostPath 反映を漏らさないこと) は #7 etcd バックアップと復旧 に整理してあるので、実技前にその記事で手に覚えさせることをおすすめします。

症状 3: scheduler / controller-manager ダウン #

apiserver と etcd が死ぬとクラスターが丸ごと止まるので症状が劇的です。一方 scheduler と controller-manager が死ぬと クラスターは応答するが一部の動作だけが静かに止まります。 kubectl が正常に動くため、むしろ気づきにくいのです。

  • kube-scheduler ダウン。新しい Pod をどのノードに置くかを決めるコンポーネントです。死ぬと 新しく作った Pod がずっと Pending にとどまります。 すでに上がっている Pod は無事です。kubectl get pods で Pending が解けず、describe のイベントにスケジュール関連のメッセージがなければ scheduler を疑います。
  • kube-controller-manager ダウン。reconciliation loop を回して望む状態へ収束させるコンポーネントです。死ぬと 自己修復が止まります。 Deployment の replica を増やしても新しい Pod が生まれず、死んだ Pod が作り直されず、ノードが抜けても Pod が再配置されません。

診断と復旧は apiserver と同じです。どちらも static Pod なので、該当するマニフェストを見てコンテナログを読み、原因を探します。

# scheduler / controller-manager コンテナの状態
crictl ps -a | grep -E 'scheduler|controller'
crictl logs <container-id>

# マニフェスト
cat /etc/kubernetes/manifests/kube-scheduler.yaml
cat /etc/kubernetes/manifests/kube-controller-manager.yaml

この 2 つは死んでもクラスターが即座に止まらないので相対的にそれほど差し迫りませんが、「Pod が Pending から解けない」とか「Deployment をスケールしても反応がない」という症状の根がここにある可能性があることを覚えておく必要があります。

static Pod を直す方法: マニフェスト修正 → kubelet 自動再起動 #

4 つのコンポーネントすべて復旧方式が同じです。マニフェストを直せば kubelet が自分で再起動します。 流れをもう一度整理するとこうです。

  1. control plane ノードに入り /etc/kubernetes/manifests/ の該当ファイルをバックアップする。
  2. crictl logsjournalctl -u kubelet で何が問題かを読む。
  3. マニフェストの打ち間違い・フラグ・パス・image を直して保存する。
  4. kubelet が変更を検知して Pod を再生成する。kubectl apply は不要。
  5. 反映が遅ければマニフェストをディレクトリの外へ一時的に移してから戻し、強制的に再ロードする。
# 強制再ロードが必要なとき
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
# しばらく後で
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/

ここでよくある間違いが 2 つあります。1 つはマニフェストを直してから kubectl applysystemctl restart を探すことです。static Pod はファイル保存だけで反映されるので、どちらも不要です。もう 1 つは修正前にバックアップを取らず、もう一度間違えたときに元の値へ戻せないことです。直す前に cp でバックアップを取っておく 1 行が時間を守ります。

症状別診断表 #

control plane トラブルシューティングを症状から原因へ絞り込む地図です。

症状一次的に疑うもの診断コマンドよくある原因
kubectl が応答なし (connection refused)apiservercrictl ps -a | grep apiservercrictl logsマニフェストの打ち間違い、etcd に接続不可、証明書パスエラー
apiserver ログに etcd connection refusedetcdcrictl ps -a | grep etcdcrictl logsetcd マニフェストエラー、data-dir 破損、証明書
etcd が生き返れない (データ破損)etcd データcrictl logs <etcd>data-dir 破損。スナップショット復旧が必要 (#7)
新しい Pod がずっと Pendingschedulercrictl logs <scheduler>scheduler マニフェストエラーでダウン
スケール・自己修復が止まるcontroller-managercrictl logs <controller>controller-manager ダウン
マニフェストを直しても反映されないkubeletjournalctl -u kubelet -fパース失敗、kubelet 自体ダウン (#23)

表を読む順序がそのまま診断の順序です。kubectl が止まったら apiserver を見て、apiserver ログが etcd を指したら etcd へ降り、etcd データが壊れていたらスナップショット復旧へ進みます。kubectl が生きているのに Pod だけ Pending か自己修復が止まっていたら scheduler と controller-manager を疑います。

試験ポイント #

  • control plane の 4 コンポーネントはすべて static Pod です。診断も復旧も /etc/kubernetes/manifests/ のマニフェストを中心に回ります。
  • apiserver が死ぬと kubectl が止まります。このときはノードに入り crictl と journalctl でコンテナと kubelet ログを直接見ます。
  • apiserver ダウンのよくある原因はマニフェストの打ち間違い、etcd に接続不可、証明書パスエラーです。ログがどちらかを教えてくれます。
  • apiserver ダウンに見えても 本当の原因が etcd であるケースが多いです。apiserver ログに etcd 接続エラーが見えたら etcd から見ます。
  • etcd データが壊れていたら #7 のスナップショット復旧でよみがえらせます。restore には証明書が不要で、復元後の hostPath 反映を漏らさないことが核心です。
  • scheduler が死ぬと 新しい Pod が Pending にとどまり、controller-manager が死ぬと 自己修復が止まります。 kubectl は正常なので気づきにくいです。
  • static Pod は マニフェスト保存だけで kubelet が再起動します。 kubectl applysystemctl restart は不要です。修正前のバックアップは必須です。

まとめ #

この記事で押さえたこと:

  • control plane は static Pod として動く という 1 つの事実が診断全体を貫きます。kubectl が止まってもノードでマニフェストを直せば kubelet がよみがえらせます。
  • 診断ツール: apiserver が死ぬと kubectl の代わりに crictl ps -a/crictl logs でコンテナを、journalctl -u kubelet で kubelet を、マニフェストで打ち間違い・フラグ・パスを見ます。
  • 症状別の原因: apiserver ダウン (マニフェスト・etcd・証明書)、etcd ダウン (data-dir・証明書)、scheduler ダウン (Pod Pending)、controller-manager ダウン (自己修復停止)。
  • 復旧: マニフェストを直して保存すれば kubelet が再起動します。etcd データ破損は #7 のスナップショット復旧が答えです。

control plane を復旧させる感覚は、#2 クラスターアーキテクチャ で固めた「どのコンポーネントが何をして、停止すると何が止まるのか」という全体像の上に立っています。コンポーネントの役割を正確に把握しておけば、症状を見ただけでどのマニフェストへ降りればよいかがすぐに浮かびます。

次へ: Troubleshooting 4 #

control plane までよみがえらせたら、クラスターの骨格は再び立ちました。最後に残るのは 接続と権限 の層です。#25 Troubleshooting 4: Networking、DNS、RBAC、証明書失効 では、Service と Pod の間の通信が切れる問題、CoreDNS 障害で名前解決ができない問題、RBAC 権限不足でリクエストが拒否される問題、そして証明書失効で apiserver と etcd が互いを信用できず通信が切れる問題を、症状別に絞り込みながら整理します。トラブルシューティングドメインの最後のピースです。

X