Certified Kubernetes Administrator (CKA) #23 Troubleshooting 2: ノードと kubelet (NotReady、disk/memory pressure)
#22 Troubleshooting 1 では Pod とアプリレベルの障害を扱いました。Pending・CrashLoopBackOff・ImagePullBackOff・OOMKilled のように、1 つの Pod の状態を見るだけで原因が絞られる問題でした。今回の記事では 1 段下に降ります。ノード自体が NotReady に落ちた状況 です。
ノード障害は Pod 障害とは質が異なります。ノードが 1 つ NotReady になると、その上にあった複数の Pod が一度に影響を受け、kubectl だけでは原因が見えないことが多いです。結局 ノードに直接接続して kubelet とコンテナランタイムをシステムレベルで覗き込む 必要があります。CKA で Linux 運用の感覚が求められる代表的な領域であり、Troubleshooting ドメインの中でも配点が付きやすいタイプです。
ノード NotReady とは何か #
各ノードの kubelet は一定周期で control plane に自分の状態を報告します。この報告が正常ならノードは Ready であり、定められた時間内に報告が途切れたり kubelet が問題を知らせたりすると、ノードは NotReady と表示されます。最初に見る画面がノード一覧です。
k get nodesNAME STATUS ROLES AGE VERSION
master Ready control-plane 40d v1.31.0
node01 NotReady <none> 40d v1.31.0
node02 Ready <none> 40d v1.31.0node01 が NotReady です。ここですぐにノードへ接続したくなりますが、その前に control plane が受け取った情報から読むのが順序です。ノードに接続しなくても原因の半分は describe で明らかになります。
ステップ 1: k describe node で conditions を読む #
ノード診断の出発点はいつも describe です。ノードは複数の condition を持ち、各 condition の状態と最後の遷移時刻、そして人が読めるメッセージが一緒に出ます。
k describe node node01出力で最初に見る場所が Conditions ブロックです。
Conditions:
Type Status Reason Message
---- ------ ------ -------
MemoryPressure False KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False KubeletHasSufficientPID kubelet has sufficient PID available
Ready Unknown NodeStatusUnknown Kubelet stopped posting node status.ノード condition の意味 #
| Condition | 意味 | True のとき |
|---|---|---|
Ready | ノードが Pod を受け入れられる正常状態 | 正常。False/Unknown なら異常 |
MemoryPressure | ノードの利用可能メモリが閾値を下回る | メモリ不足。eviction が発生し得る |
DiskPressure | ノードのディスク容量または inode が閾値を下回る | ディスク不足。イメージ・Pod eviction が発生 |
PIDPressure | ノードの利用可能 PID が閾値を下回る | プロセス数が飽和 |
Ready だけ他の condition と方向が逆です。Ready=True が正常で、残りの pressure 系は False が正常です。上の例では Ready が Unknown でメッセージが Kubelet stopped posting node status. なので、kubelet が状態報告を止めた という意味です。pressure 系はすべて False なので資源不足ではありません。この場合、原因はほぼ確実に kubelet 自体にあります。
Ready の Status 別の解釈 #
Ready Status | 解釈 | 次の行動 |
|---|---|---|
True | 正常 | ノードの問題ではない。Pod レベルへ |
False | kubelet は生きているがノードが異常を報告 | メッセージの Reason を確認。ランタイム・ネットワーク・pressure を疑う |
Unknown | kubelet の報告が途切れた | ノードに接続。kubelet 停止・ノードダウン・ネットワーク断を疑う |
Unknown は control plane がノードとの連絡を失ったというシグナルです。kubelet が死んだか、ノードが落ちたか、ノードと apiserver の間のネットワークが塞がれたかの 3 つの枝です。ここからはノードに直接入る必要があります。
ステップ 2: ノードに接続して kubelet 状態を確認 #
試験では接続するノードのホスト名が問題に提示されます。SSH で入ったあと、権限が必要なら sudo で上がります。
ssh node01
sudo -iノードの上で最初に確認する対象は kubelet サービスです。kubelet はノードのすべての Pod を起動して管理するエージェントなので、これが止まるとノード全体が NotReady になります。
systemctl status kubelet● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; ...)
Active: inactive (dead) since Sat 2026-06-06 09:12:41 UTC; 3min agoActive: inactive (dead) なら kubelet が止まっています。active (running) なのにノードが NotReady なら、kubelet は起動しているが正常に動作できていない状況なので、ログを見る必要があります。
journalctl で kubelet ログを読む #
kubelet は systemd ユニットとして動くため、ログは journalctl で見ます。最近のログから逆にたどるのが速いです。
# kubelet ログ全体 (ページャ)
journalctl -u kubelet
# 最近 100 行だけ
journalctl -u kubelet -n 100 --no-pager
# リアルタイム追跡 (kubelet を再起動しながら一緒に見るとよい)
journalctl -u kubelet -fログで error、failed、unable to のようなキーワードを探すと、原因の 1 行がほぼ必ず出てきます。設定ファイルのパスエラー、証明書失効、ランタイムソケット接続失敗が代表的です。
よくある原因と解決 #
ノード NotReady の原因は大きく 5 つの枝です。一つずつ症状と解決を整理します。
原因 1: kubelet が停止した #
最も単純でありながら試験によく出るケースです。systemctl status kubelet が inactive (dead) なら kubelet を再び起動します。
systemctl start kubelet
# 起動時の自動開始もオンにしておく
systemctl enable kubelet
# 状態を再確認
systemctl status kubelet再び起動したのにすぐ死ぬなら、単純な停止ではなく設定の問題です。journalctl -u kubelet -n 50 --no-pager で死ぬ理由を確認し、次の項目に進みます。
原因 2: kubelet 設定が間違っている #
kubelet は複数の設定ファイルを読んで起動します。試験ではこのパスのうち 1 つがわざと壊されているケースがあります。
| パス | 役割 |
|---|---|
/var/lib/kubelet/config.yaml | kubelet の主設定。cgroup driver、eviction 閾値など |
/etc/kubernetes/kubelet.conf | apiserver 接続用の kubeconfig |
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf | systemd が kubelet を起動するときに使う引数 |
/var/lib/kubelet/kubeadm-flags.env | kubeadm が追加するランタイム引数 (例: ランタイムソケットのパス) |
ログに failed to load Kubelet config file または unable to load client CA file のような行が見えたら、上のファイルのパスと内容を点検します。よく出る罠の 1 つは kubeconfig が指す apiserver のポートやサーバアドレスが間違っているケース です。
# kubelet kubeconfig が指すサーバを確認
cat /etc/kubernetes/kubelet.conf | grep server設定ファイルを直したあとは、systemd に変更を読み直させてから kubelet を再起動します。
systemctl daemon-reload
systemctl restart kubelet原因 3: 証明書の問題 #
kubelet は apiserver と通信するときにクライアント証明書を使います。この証明書が失効すると、kubelet は起動していても apiserver に状態を報告できず、ノードが NotReady になります。ログに x509: certificate has expired or is not yet valid のような行が見えたら証明書失効です。
journalctl -u kubelet -n 50 --no-pager | grep -i x509証明書失効はそれ自体で扱う内容が多いため、#25 Troubleshooting 4 で kubelet 証明書の自動更新と手動更新を別に整理します。今回の記事では NotReady の原因候補に証明書失効がある という点だけ覚えておけば十分です。
原因 4: コンテナランタイムが停止した #
kubelet はコンテナを直接起動せず、CRI を通じてコンテナランタイム (通常 containerd) に委任します。ランタイムが死ぬと kubelet は Pod を起動できず、ノードが NotReady になります。ログに failed to get container runtime または connection refused が見えたらランタイムを確認します。
systemctl status containerd
# 止まっていれば再び起動
systemctl start containerd
systemctl enable containerd
# ランタイムが応答するか確認
crictl info
crictl psランタイムを再び起動したあとは kubelet も再起動しておきます。ランタイムソケットのパスが間違っているケースなら、/var/lib/kubelet/kubeadm-flags.env の --container-runtime-endpoint の値を確認します。
systemctl restart kubelet原因 5: ディスクフルとメモリ圧迫 #
資源枯渇は condition にそのまま現れるので、describe node ですでに手がかりが得られます。DiskPressure=True ならディスクが閾値を下回った状態であり、kubelet はノードを保護するために Pod を eviction (退避) します。
# ディスク使用量を確認
df -h
# inode 枯渇も一緒に確認 (容量は残っているのに inode が埋まるケース)
df -iディスクが埋まっているなら、最も安全な回収対象は 使っていないコンテナイメージ です。
# 未使用イメージの整理
crictl rmi --prune
# 終了したコンテナの整理
crictl rm $(crictl ps -a -q --state Exited)ログファイルが肥大化したケースも多いので、/var/log の下で容量が大きいファイルも一緒に点検します。ディスクを確保すると kubelet が再び DiskPressure=False を報告し、ノードが Ready に戻ります。
MemoryPressure=True はノードの利用可能メモリが eviction 閾値を下回った状態です。
# メモリ使用量
free -h
# メモリを多く使うプロセス
top -o %MEM特定の Pod がノードメモリを過度に使うケースなら、そのワークロードの requests/limits を調整するか (#15 リソース管理)、他のノードへ分散するのが根本解決です。試験では通常、原因が明確な 1 つに設計されるので、condition が指す資源を回収することに集中すればよいです。
症状別の診断表 #
ここまでの流れを一目で整理します。NotReady に出会ったら、この表に沿って絞っていけばよいです。
| 症状 / 手がかり | 疑う原因 | 確認コマンド | 解決 |
|---|---|---|---|
Ready=Unknown、“Kubelet stopped posting” | kubelet 停止 | systemctl status kubelet | systemctl start/restart kubelet |
| kubelet が起動してすぐ死ぬ | 設定ファイルエラー | journalctl -u kubelet | /var/lib/kubelet、kubelet.conf 修正後 daemon-reload |
ログに x509 ... expired | 証明書失効 | journalctl -u kubelet | grep x509 | 証明書更新 (#25) |
ログに runtime ... connection refused | ランタイム停止 | systemctl status containerd | systemctl start containerd 後に kubelet 再起動 |
DiskPressure=True | ディスクフル | df -h、df -i | crictl rmi --prune、ログ整理 |
MemoryPressure=True | メモリ圧迫 | free -h、top | ワークロード分散、requests/limits 調整 |
Ready=False、Reason がネットワーク系 | CNI プラグインの問題 | kubelet ログの NetworkPluginNotReady | CNI Pod 状態を確認 (#20) |
問題ノードを隔離する: cordon と drain #
ノードを直している間、新しい Pod がそのノードへスケジューリングされないように防いだり、上にある Pod を安全に空けたりする必要があるときがあります。このとき使うコマンドが cordon と drain です。
# 新しい Pod のスケジューリングだけ防ぐ (既存 Pod はそのまま)
k cordon node01
# 既存 Pod まで他のノードへ移す (cordon を含む)
k drain node01 --ignore-daemonsets --delete-emptydir-data
# 作業が終わったら再びスケジューリングを許可
k uncordon node01drain はノードを点検・アップグレードする前に、その上のワークロードを安全に他のノードへ再配置します。DaemonSet Pod はすべてのノードに起動する必要があるため --ignore-daemonsets でスキップし、emptyDir ボリュームを使う Pod があれば、データ損失を認識したという意味で --delete-emptydir-data を付けないと進みません。drain を実行すると、そのノードは自動的に cordon 状態になります。
cordon されたノードは k get nodes で SchedulingDisabled と表示されます。
NAME STATUS ROLES AGE VERSION
node01 Ready,SchedulingDisabled <none> 40d v1.31.0復旧が終わったら必ず uncordon でスケジューリングを再び開く必要があります。これを忘れるとノードは Ready なのに新しい Pod が上がってこず、あとで別の問題と誤認しやすくなります。
NotReady ノードの taint #
ノードが NotReady になると、control plane は自動で taint を付けて Pod スケジューリングを防ぎます。どの taint が付いているか見ると、ノード状態を素早く読めます。
k describe node node01 | grep -i taintTaints: node.kubernetes.io/not-ready:NoSchedule主なノード taint は次のとおりです。これらはノード状態に応じて Kubernetes が自動で付与します。
| Taint | 付与条件 |
|---|---|
node.kubernetes.io/not-ready | ノードの Ready=False |
node.kubernetes.io/unreachable | ノードの Ready=Unknown (連絡断) |
node.kubernetes.io/disk-pressure | DiskPressure=True |
node.kubernetes.io/memory-pressure | MemoryPressure=True |
node.kubernetes.io/unschedulable | cordon されたノード |
これらの taint はノードが正常に戻ると 自動で除去 されます。つまり kubelet を直してノードが再び Ready になると not-ready taint も消え、スケジューリングが正常化します。taint を手で剥がすのではなく、根本原因を直すこと が正解です。taint と toleration の動作は #14 Scheduling 2 で詳しく扱いました。
核心は kubectl レベル (describe) から始めてノードレベル (systemctl/journalctl) へ降りる という方向性です。k get nodes で対象を見つけ、k describe node で conditions を読んで方向を定め、ノードに接続して kubelet とランタイムを直したあと、設定を変えたなら systemctl daemon-reload 後に restart kubelet で締めます。この順序がほぼすべてのノード障害にそのまま適用されます。
試験ポイント #
describe nodeの conditions を先に読みます。Ready=Unknownなら kubelet 報告の断、pressure 系Trueなら資源枯渇です。ノードに接続する前に方向を定めると時間を節約できます。- kubelet は systemd ユニットです。
systemctl status kubeletとjournalctl -u kubeletが手に馴染んでいる必要があります。kubelet 停止はsystemctl enable --now kubeletで一度に起動できます。 - 設定を直したら
daemon-reloadを忘れないようにします。 systemd ユニットファイルや引数を変えたあとdaemon-reloadなしで再起動すると、変更が反映されません。 - ランタイムも候補です。
systemctl status containerdとcrictl infoでランタイム停止を素早く除外します。 - ディスクは
df -hとdf -iの両方を見ます。 容量は残っているのに inode が埋まって DiskPressure が出るケースを見落としやすいです。 - taint は手で剥がしません。 原因を直せば自動で消えます。taint を強制的に除去しても根本原因が残っていれば、ノードは再び NotReady になります。
まとめ #
この記事で押さえたこと:
- ノード NotReady の診断は describe から始めてノードのシステムレベルへ降ります。 conditions (
Ready/MemoryPressure/DiskPressure/PIDPressure) が方向を定めてくれます - kubelet はノードエージェントです。
systemctl status kubeletとjournalctl -u kubeletで停止・設定エラー・証明書・ランタイムの問題を切り分けます - よくある 5 つの原因。 kubelet 停止、設定エラー (
/var/lib/kubelet・/etc/kubernetes/kubelet.conf)、証明書失効、ランタイム停止 (containerd)、ディスクフルとメモリ圧迫です - cordon と drain で隔離します。 点検前にワークロードを安全に空け、復旧後
uncordonでスケジューリングを再び開きます - NotReady taint は自動です。
node.kubernetes.io/not-readyは原因を直せば自動で除去されます
次へ — Troubleshooting 3 #
ノードを直しました。ここからより危険な層、control plane 自体がダウンした状況 へ上がっていきます。
#24 Troubleshooting 3: Control plane (apiserver/etcd/scheduler ダウン)、etcd リカバリ では、kube-apiserver が応答しないときに static Pod マニフェスト (/etc/kubernetes/manifests) とコンテナランタイムを直接覗き込む方法、scheduler と controller-manager が死ぬとクラスターに何が起きるのか、そして etcd が壊れたときにスナップショットでリカバリする手順まで扱います。