Certified Kubernetes Administrator (CKA) #25 Troubleshooting 4: Networking、DNS、RBAC、証明書の期限切れ

#24 Troubleshooting 3 では control plane コンポーネントが死んだときの診断と etcd の復旧まで扱いました。ノードも生きていて control plane も正常なのに、通信ができない、または権限が塞がれる ケースが残っています。トラブルシューティング最後の記事となる今回の #25 では、ネットワークと権限の領域、つまりサービス通信の失敗、DNS 解決の失敗、RBAC 拒否、証明書の期限切れを順に診断します。

この領域の共通点は、エラーメッセージが原因を直接指し示さない という点です。Pod は Running なのに curl がタイムアウトし、クラスターは正常なのに kubectl が突然拒否されます。だからこそ、症状から原因まで絞り込んでいく 診断順序 を手に覚えさせるのが核心です。

サービス通信ができないとき #

最もよく出る症状です。クライアント Pod からサービス名や ClusterIP で接続したのに応答がありません。サービス通信は 名前解決 → サービス → Endpoints → 対象 Pod → ネットワークポリシー の段階で流れるので、この流れに沿って一段ずつ切ってみると原因が絞られます。

1 段階: 本当にネットワークの問題かを切り分ける #

まずクライアント Pod の中で直接呼び出してみます。サービス名で失敗するなら DNS の問題かもしれないので、ClusterIP でも一度呼び出して両方のケースを分けておきます。

# クライアント Pod の中からサービス名で
k exec -it client -- curl -sS http://my-svc:80

# 同じ呼び出しを ClusterIP で (名前解決を飛ばす)
k get svc my-svc          # CLUSTER-IP を確認
k exec -it client -- curl -sS http://10.96.0.42:80

サービス名は失敗するのに ClusterIP は通るなら DNS の問題 なので、後ろの DNS 節へ進みます。ClusterIP もダメならサービスまたは Endpoints、ネットワークポリシーの側を見ます。

2 段階: Endpoints が空かを見る #

サービストラブルシューティングの半分はここで終わります。サービスがトラフィックを送る対象 Pod のリストが Endpoints (または EndpointSlice) ですが、これが空だとサービスは ClusterIP だけがあって受け取る先がありません。

# Endpoints の確認 (空なら <none>)
k get endpoints my-svc
k get endpointslices -l kubernetes.io/service-name=my-svc

Endpoints が空なら原因は三つのうちの一つです。

原因確認対処
selector の不一致サービスの selector と Pod の labels を比較ラベルまたは selector を合わせる
対象 Pod が Ready でないk get pod -l app=... の READY 列readinessProbe またはアプリ自体を直す
targetPort の不一致サービスの targetPort とコンテナの containerPortポートを合わせる

selector の不一致が最もよくあります。サービスの selector と Pod のラベルを直接比較します。

# サービスの selector
k get svc my-svc -o jsonpath='{.spec.selector}'

# その selector で実際に Pod が捕まるか
k get pod --selector app=web

selector で Pod が一つも捕まらないならラベルがずれています。もう一つよくある落とし穴は、Pod は Running なのに Ready でない ケースです。Endpoints には Ready 状態の Pod だけが載るので、readinessProbe が失敗すると Running でも Endpoints から外れます。

3 段階: kube-proxy を確認する #

Endpoints は埋まっているのに ClusterIP で接続できないなら、ClusterIP を実際の Pod へ転送する kube-proxy を疑います。kube-proxy はたいてい各ノードに DaemonSet として立っていて、iptables または IPVS のルールをノードに敷きます。

# kube-proxy DaemonSet の状態
k -n kube-system get pods -l k8s-app=kube-proxy -o wide

# 特定のノードの kube-proxy ログ
k -n kube-system logs ds/kube-proxy

kube-proxy Pod が一部のノードで死んでいると、そのノードに立ったクライアントだけ通信できないという、位置によって変わる症状が出ます。CNI プラグイン (Calico、Cilium など) の Pod も同じ方法で状態を確認します。

4 段階: NetworkPolicy が塞いだかを見る #

上がすべて正常なのに塞がれるなら、NetworkPolicy がトラフィックを遮断した可能性が高いです。ネットワークポリシーは一つでも Pod に適用されると、明示的に許可していないトラフィックを既定で拒否に変えます。

# 対象ネームスペースのポリシー一覧
k -n prod get networkpolicy

# 特定ポリシーの詳細 (どの ingress/egress を許可するか)
k -n prod describe networkpolicy default-deny

describe で見るべき箇所は二つです。podSelector がどの Pod に適用されるか、そして Ingress/Egress ルールがどの送信元・宛先を許可するかです。default-deny ポリシーだけがあって許可ルールがなければ、そのネームスペースの通信が全部塞がれます。さらによく抜ける落とし穴は、egress と ingress を両方とも開ける 必要があるという点です。クライアントの egress が許可されても、サーバーの ingress が塞がれていれば通信は失敗します。DNS を塞いでしまうミスも多く、egress ポリシーを掛けるときに kube-system の CoreDNS へ向かう UDP/TCP 53 番を抜かすと、名前解決から壊れます。ポリシーの文法と動作は #20 Networking 3 で詳しく扱いました。

DNS 解決が失敗するとき #

サービス名は失敗し ClusterIP は成功するケース、または外部ドメインの解決ができないケースです。クラスター DNS は CoreDNS が担うので、ここを中心に絞ります。

まず CoreDNS Pod の状態から #

# CoreDNS Pod とサービス
k -n kube-system get pods -l k8s-app=kube-dns -o wide
k -n kube-system get svc kube-dns

# CoreDNS ログ (エラーが出ているか)
k -n kube-system logs -l k8s-app=kube-dns

CoreDNS Pod が CrashLoop だったり 0 個で立っていたりすると、クラスター全体の名前解決が壊れます。ログに plugin/errors のようなメッセージが見えたら、ConfigMap の Corefile 設定を確認します。

k -n kube-system get configmap coredns -o yaml

nslookup で直接テストする #

診断用 Pod を立てて、クラスターの中から直接名前を解決してみます。外部イメージが塞がれているなら、既存の Pod の中で nslookupgetent hosts を使っても構いません。

# 使い捨ての診断 Pod
k run dnsutils --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 \
  --rm -it --restart=Never -- bash

# クラスター内部サービスの解決
nslookup my-svc.default.svc.cluster.local

# kubernetes 既定サービス (これがダメなら DNS 自体が死んでいる)
nslookup kubernetes.default

# 外部ドメイン (これだけダメなら upstream/forward の問題)
nslookup example.com

解決結果で原因が分かれます。kubernetes.default すらダメなら CoreDNS または kube-dns サービス自体の問題で、内部は通るのに外部だけダメなら CoreDNS の forward 設定かノードの upstream DNS の問題です。

/etc/resolv.conf を見る #

Pod の DNS 設定は /etc/resolv.conf に入ります。nameserver が kube-dns サービスの ClusterIP を指し、search ドメインがクラスターのサフィックスを含んでいてこそ、短い名前の解決が動作します。

# Pod の中の resolv.conf
k exec -it client -- cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

nameserver の IP が k -n kube-system get svc kube-dns の ClusterIP と違えば、DNS が見当違いの場所を指しています。search 行が空なら my-svc のような短い名前が解けず、FQDN でしか解けません。ノード自体の DNS が疑わしいなら、ノードの /etc/resolv.conf と kubelet の --resolv-conf 設定まで確認します。

DNS 診断順序の整理 #

症状疑う箇所確認コマンド
すべての名前解決が失敗CoreDNS Pod のダウンk -n kube-system get pods -l k8s-app=kube-dns
内部は通るが外部が失敗CoreDNS forward / upstreamlogsconfigmap coredns
短い名前だけ失敗resolv.conf の searchk exec ... cat /etc/resolv.conf
一つの Pod だけ失敗その Pod の dnsPolicy/resolv.confPod spec の dnsPolicy

RBAC で拒否されるとき #

kubectl や ServiceAccount が Forbidden エラーで塞がれるケースです。クラスターは正常なので、ネットワークではなく 権限 の問題です。

Forbidden エラーを読む #

RBAC のエラーメッセージは形式が決まっているので、そのメッセージ自体が診断の出発点になります。

Error from server (Forbidden): pods is forbidden:
User "dev" cannot list resource "pods" in API group "" in the namespace "prod"

この一行に必要な情報がすべて入っています。主体User "dev"動詞listリソースpodsAPI グループ"" (core)、ネームスペースprod です。つまり dev というユーザーに prod ネームスペースの pods を list する権限がないという意味なので、この五つを満たす Role と Binding が存在するかを確認します。

auth can-i で権限を確認する #

権限の有無は推測せず、auth can-i で直接問います。

# 自分の権限
k auth can-i list pods -n prod

# 特定のユーザー/ServiceAccount の権限 (管理者が代わりに確認)
k auth can-i list pods -n prod --as=dev
k auth can-i create deployments -n prod \
  --as=system:serviceaccount:prod:builder

# 持っている権限を全列挙
k auth can-i --list -n prod --as=dev

--as で別の主体になりすまして問うのが診断の核心です。--list はその主体が持つすべての権限を表で見せてくれるので、何が抜けているのかが一目で分かります。

Role/Binding が欠けているかを見る #

no が出たら、権限の鎖のどこが切れているかを突き止めます。権限は 主体 → Binding → Role → ルール へとつながるので、Binding がない、Role がない、Role のルールが足りないの三つのケースです。

# ネームスペースの Role と RoleBinding
k -n prod get role,rolebinding
k -n prod describe rolebinding dev-binding

# クラスター範囲なら
k get clusterrole,clusterrolebinding | grep -i dev

describe rolebinding で二つを突き合わせます。Subjects に問題のユーザーや ServiceAccount が正確な名前・種類で入っているか、そして Role が指す対象が実際に存在し、必要な動詞・リソースを含んでいるかです。よく出る落とし穴は次のとおりです。

  • RoleBinding はあるのに指す Role がない。roleRef のタイプミスや削除された Role を指すと権限が 0 になります
  • Subject の種類の混同UserGroupServiceAccount は別物です。ServiceAccount は kind: ServiceAccount でネームスペースまで合っている必要があります
  • 範囲の不一致。ネームスペース権限が必要なのに ClusterRole だけがある、またはその逆のケースです。クラスター範囲の権限は RoleBinding ではなく ClusterRoleBinding で束ねてこそ全ネームスペースに適用されます

RBAC のオブジェクトモデルと ServiceAccount トークンまでは #9 RBAC で扱ったので、ここでは塞がれたときに絞り込んでいく流れに集中します。

証明書が期限切れのとき #

最も戸惑う症状は、ある日 kubectl が丸ごと拒否されることです。クラスターは回っているのに認証が通らないなら、証明書の期限切れ を真っ先に疑います。kubeadm クラスターの control plane 証明書は既定の有効期間が 1 年なので、アップグレードなしに長く放置したクラスターでよく弾けます。

症状: kubectl 認証の失敗 #

Unable to connect to the server: x509: certificate has expired or is not yet valid

この x509 ... expired メッセージが見えればほぼ確定です。ネットワークエラー (connection refused) とは別のメッセージなので、混同しません。

check-expiration で確認する #

control plane ノードで kubeadm を使い、すべての証明書の期限を一度に見ます。

# control plane ノードの中で
kubeadm certs check-expiration
CERTIFICATE                EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
admin.conf                 Jun 01, 2026 09:00 UTC   <invalid>       no
apiserver                  Jun 01, 2026 09:00 UTC   <invalid>       no
apiserver-etcd-client      Jun 01, 2026 09:00 UTC   <invalid>       no
...

RESIDUAL TIME<invalid> だったりマイナスだったりすれば、すでに期限切れです。個別の証明書ファイルを直接確認するなら、openssl でも期限を読めます。

openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -enddate

更新して kubeconfig を取り直す #

kubeadm で期限切れの証明書を更新します。更新後は control plane コンポーネント (static Pod) を新しい証明書で立て直し、管理者用の kubeconfig も新たにコピーする必要があります。

# すべての証明書を更新
kubeadm certs renew all

# static Pod の再起動 (manifests を一時的に外して戻し、強制的に再起動)
# または kubelet の再起動で新しい証明書を反映
systemctl restart kubelet

# 更新された admin.conf を再び使う
cp /etc/kubernetes/admin.conf ~/.kube/config

# 再び正常に動作するか
kubeadm certs check-expiration
k get nodes

更新直後でも kubectl が同じエラーを出すなら、手元のユーザー kubeconfig が 古い証明書をそのまま抱えている ケースです。更新は /etc/kubernetes/admin.conf を新しく書くので、そのファイルをユーザー kubeconfig の場所へ再びコピーしてこそ反映されます。PKI 構造と更新手順の背景は #8 証明書管理 で扱いました。

症状別の診断順序を一目で #

ネットワーク・権限領域のトラブルシューティングを症状から出発して整理すると次のとおりです。試験ではこの表の最初の枠 (症状) だけが与えられるので、次の二つの枠を頭の中ですぐ思い浮かべられてこそ時間を節約できます。

症状まず見る場所核心コマンド
サービス名・IP どちらも応答なしEndpoints が空か → selectork get endpointsk get svc -o jsonpath
名前だけ失敗、ClusterIP は成功CoreDNS → resolv.confnslookupk -n kube-system logs -l k8s-app=kube-dns
Endpoints は正常なのに塞がれるkube-proxy → NetworkPolicyk -n kube-system get pods -l k8s-app=kube-proxyk get netpol
Forbidden エラーエラーの 4 要素 → Role/Bindingk auth can-i --as=k get role,rolebinding
x509 certificate has expired証明書の期限切れkubeadm certs check-expiration
kubectl connection refusedapiserver ダウン (#24)crictl ps、static Pod ログ

試験ポイント: トラブルシューティング総まとめ #

#22 から続けてきたトラブルシューティング 4 編を試験の観点でまとめます。

  • 症状から始めて一段ずつ切る。サービス通信は Endpoints、DNS は CoreDNS と resolv.conf、権限は Forbidden の 4 要素、認証は x509 メッセージが最初の分岐点です
  • Endpoints が空かをまず見る。サービストラブルシューティングで最も早く点が取れる確認であり、原因はほぼ selector の不一致か Pod が Ready でないケースです
  • auth can-i --as で推測をなくす。権限の問題は問わずに直接なりすまして問えば片付きます。--list で抜けた権限を一目で見ます
  • 証明書は check-expiration 一行。更新後に ~/.kube/config の再コピーを忘れません
  • 直したあとは必ず再現して確認する。curl が再び通るか、k get nodes が再び回るかを結果で検証してこそ採点されます
  • context を先に合わせる。トラブルシューティング問題は指定されたクラスター・ノードで解いてこそ採点されるので、use-context を先に実行します

トラブルシューティングは比重 30% で単一ドメインの中で最も大きく、他のドメインの知識を前提とします。ここまで付いてきたなら、アーキテクチャ・ワークロード・ネットワーキング・ストレージの知識が診断の流れへとつながったはずです。

次へ: 試験のコツ #

トラブルシューティングまで全ドメインを一周しました。残るのは 2 時間をどう運用するか です。

#26 試験のコツと時間管理、よく間違えるパターン では、作業の順序を決める方法、部分点を狙う戦略、詰まったときに飛ばす基準、そして selector の不一致・context の抜け・kubeconfig の未コピーのように合格ラインを削るおなじみのミスをまとめて整理します。

X