本文へスキップ

CKA 概念ノート: ワークロード、リソース使用量、ネットワークのトラブルシューティング

  • kubernetes
  • cka
  • troubleshooting
  • logging
  • kubectl-top
  • service

基準: kubeadm クラスタ v1.35.9、metrics-server 0.9.0、Calico v3.33.0 で実際に壊して確認

kubectl describe、events、exec、kubectl debug で Pod を調べる基本の流れは CKAD 概念ノート: モニタリング、ログ、デバッグ にまとめた。ノードと control plane の故障は ノードとクラスタコンポーネントのトラブルシューティング を参照。

目次

リソース使用量

kubectl top は metrics-server がないと動かない (ないと error: Metrics API not available)。

kubectl top nodes --sort-by=cpu
kubectl top pods -A --sort-by=memory
kubectl top pod <pod> -n <ns> --containers
$ kubectl top nodes --sort-by=cpu
NAME      CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)
cka-cp1   214m         10%      1992Mi          70%
cka-w1    85m          4%       1156Mi          48%
cka-w2    69m          3%       743Mi           31%

$ kubectl top pods -A --sort-by=memory
NAMESPACE       NAME                     CPU(cores)   MEMORY(bytes)
kube-system     kube-apiserver-cka-cp1   55m          557Mi
calico-system   calico-node-lnw7f        32m          122Mi
  • --sort-by は cpu か memory
  • 値は収集周期ごとに更新される近似値。container が起動したばかりだと、まだ表示されないことがある
  • 「一番多く使っている Pod の名前をファイルに書け」のような問題は --sort-by と --no-headers | head -1 で

kubectl describe node の Allocated resources は実際の使用量ではなく request/limit の合計。2 つを混同しない。

container の出力

container が stdout、stderr に書いたものを container runtime がノードのファイルに保存し、kubectl logs はそのファイルを kubelet 経由で読む。

kubectl logs

container 2 つ (out: 2 秒ごとに stdout と stderr に 1 行ずつ、crash: 5 秒後に exit 3) の Pod で確認。

コマンド結果
kubectl logs loggerDefaulted container "out" out of: out, crash の後、最初の container のログ
kubectl logs logger -c out --tail=2out-19、err-19 (stdout と stderr が混ざって出る)
kubectl logs logger --all-containers --prefix --tail=1[pod/logger/out] err-19、[pod/logger/crash] boom
kubectl logs logger -c out --since=5s --timestamps先頭に RFC3339 の時刻
kubectl logs <pod> --previous再起動前の container のログ
kubectl logs -f追いかける
kubectl logs deploy/webDeployment の Pod 1 つ
kubectl logs -l app=weblabel で複数の Pod

--previous の確認: 25 秒後に exit 1 で終わる container が 1 回再起動した後

current:  run-at-02:35:59
previous: run-at-02:35:34

container が落ち続けて原因を見たいときは --previous。今の container は起動したばかりで何も出力していないことがある。終了コードは status に残る。

kubectl get pod logger -o jsonpath='{.status.containerStatuses[?(@.name=="crash")].lastState.terminated}'
# {"exitCode":3,"reason":"Error","startedAt":"...","finishedAt":"..."}

CrashLoopBackOff で待っている間は、--previous が unable to retrieve container logs for containerd://... で失敗することがあった。そのときは kubectl logs -c <container> (最後に終わった container) が同じ内容を見せてくれた。

ノードのログファイル

/var/log/pods/<namespace>_<pod>_<uid>/<container>/<restart 番号>.log   ← 実際のファイル
/var/log/containers/<pod>_<namespace>_<container>-<container id>.log   ← symlink
$ sudo sh -c 'ls /var/log/pods/app_logger_*/crash/'
2.log  3.log
$ sudo sh -c 'tail -n2 /var/log/pods/app_logger_*/out/0.log'
2026-10-14T02:35:21.78145528Z stdout F out-29
2026-10-14T02:35:21.781537313Z stderr F err-29
  • 1 行の形式: 時刻 stream タグ 内容。stream で stdout と stderr を区別できる。kubectl logs は 2 つを混ぜて見せる
  • ファイル名の番号が再起動の回数。最近のものだけが残っている
  • root 権限が必要で、glob は sudo sh -c '...' の中で使わないと展開されない
  • ログの rotation は kubelet 設定の containerLogMaxSize (デフォルト 10Mi)、containerLogMaxFiles (デフォルト 5)。このクラスタの config.yaml には指定がないのでデフォルト値
  • kubelet と containerd 自身のログはファイルではなく journalctl -u kubelet、journalctl -u containerd

Service の接続の問題

nginx の Deployment (app=web、replicas 2) と busybox のクライアントで、1 つずつ壊して wget の結果を比べた。

故障EndpointSliceクライアントの結果
正常Pod IP 2 つ、port 80Welcome to nginx!
selector の不一致<unset> (endpoint なし)Connection refused
targetPort の誤り (8080)Pod IP 2 つ、port 8080Connection refused
readiness に失敗する Podその Pod だけ ready=false残りの Pod から正常に応答
CoreDNS の replicas 0kube-dns の endpoint なし名前は bad address、ClusterIP なら正常
NetworkPolicy の default deny ingress正常download timed out

selector の不一致

kubectl create svc clusterip web-bad-sel --tcp=80:80 は selector を app: web-bad-sel で作る。Pod の label は app=web。

kubectl get svc web-bad-sel -o jsonpath='{.spec.selector}'    # {"app":"web-bad-sel"}
kubectl get pods -l app=web --show-labels                       # app=web,pod-template-hash=...
kubectl get endpointslice -l kubernetes.io/service-name=web-bad-sel
# web-bad-sel-rcjwq   IPv4   <unset>   <unset>

endpoint が空なら、selector と Pod の label から比べる。

targetPort の誤り

endpoint はあるが、port が container の listen していない 8080。EndpointSlice の PORTS 列ですぐ分かる。refused はその Pod IP:port で誰も listen していないということ。

readiness の失敗

readinessProbe が存在しないパス (/nope) を見るように変えた新しい Pod は 0/1 Running で、EndpointSlice に ready=false で残る。rolling update なので既存の Pod 2 つはそのまま Ready で、リクエストもそちらに行く。新しい Pod はトラフィックを受けない。

DNS

CoreDNS を 0 に減らすと名前解決だけが失敗し、ClusterIP への直接接続は通る。「Service が動かない」という問題で名前と IP を別々に試すと、DNS の問題かどうかがすぐ分かれる。replicas を 2 に戻した直後は少しの間失敗が続き、すぐに正常に戻った。

kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl -n kube-system get endpointslice -l kubernetes.io/service-name=kube-dns
kubectl run t --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default.svc.cluster.local

NetworkPolicy

podSelector: {} + policyTypes: [Ingress] の default deny があると、接続は拒否されず、応答なしで timeout になる。refused (すぐ失敗) と timeout (待ってから失敗) の違いが手がかり。

確認の順番

1. kubectl get svc <svc> -o wide            selector、port、targetPort
2. kubectl get endpointslice -l kubernetes.io/service-name=<svc>
   ├─ 空 → selector と Pod の label、Pod が Ready か
   └─ ある → port が container の port と合っているか
3. Pod IP:port に直接 → 通れば Service/kube-proxy 側、通らなければ Pod/NetworkPolicy 側
4. ClusterIP に → 通れば DNS の問題
5. FQDN で nslookup
6. kubectl get networkpolicy -A
7. kube-proxy、CNI の Pod の状態 (kubectl -n kube-system get pods、calico-system など)

紛らわしいものの比較

比較AB
kubectl top node vs describe node の Allocated実際の使用量request/limit の合計
logs vs logs --previous現在の (または最後の) containerその前の container
-c <name> vs --all-containers1 つの container全部。--prefix で区別
/var/log/pods vs /var/log/containers実際のファイルsymlink
Connection refused vs timed outそのアドレスで listen しているものがない (endpoint なし、port の誤り)パケットが止められている (NetworkPolicy など)
bad address名前解決の失敗DNS (CoreDNS、dnsPolicy) を確認
endpoint なし vs ready=falseselector の不一致か Pod がないPod はあるが readiness に失敗

試験のポイント

すぐ確認するコマンド

kubectl top pods -A --sort-by=cpu --no-headers | head -1
kubectl logs <pod> -c <container> --previous
kubectl logs <pod> --all-containers --prefix
kubectl logs <pod> > /path/to/file          # ログをファイルに保存する問題
kubectl get endpointslice -l kubernetes.io/service-name=<svc>
kubectl get pods -l <selector> --show-labels
kubectl run t --image=busybox:1.36 --rm -it --restart=Never -- wget -qO- -T3 http://<svc>.<ns>.svc.cluster.local
  • 結果をファイルに書けという問題は、パスと形式 (名前だけか、1 行か) を問題のとおりに
  • busybox の wget、nslookup には -T3 のような timeout を付けて、長く待たないように

kubectl explain

kubectl explain endpointslice.endpoints.conditions
kubectl explain pod.status.containerStatuses.lastState.terminated

参考ドキュメント