ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CKA考试高频故障排错:证书、etcd、CNI与调度四大核心考点

CKA考试高频故障排错:证书、etcd、CNI与调度四大核心考点

1. 这不是模拟题,是考场上真会卡住你的那几道题

“CKA真题实战:Kubernetes集群管理高频考点与排错思路”——这个标题里没有一个字是虚的。我带过三届备考学员,从某高校实验室的研究生到某云厂商一线SRE,几乎所有人第一次刷完官方样题后,都会在 Slack 群里发同一句话:“怎么感觉每道题都在考我‘刚删掉的那个 Pod 为什么没起来’?”

这不是玄学,是设计使然。CKA 考试本质不是考你背了多少kubectl get参数,而是考你在无文档、无 Google、无 Stack Overflow、单节点终端、倒计时 2 小时的真实压力下,能否用一套可复用的诊断逻辑,把一个“不工作”的集群,快速拉回“符合预期”的状态。核心关键词就三个:集群管理、高频考点、排错思路——它们不是并列关系,而是因果链:因为考点集中在集群管理类操作(比如证书轮换、etcd 备份、网络策略调试),所以排错必须围绕“控制平面组件状态—工作节点连通性—资源调度闭环”这条主线展开,而不是零散地记命令。

适合谁看?如果你正在备考 CKA,但刷题时总在“明明命令都对,就是不给分”上反复栽跟头;如果你已经拿证,但日常运维中遇到kubectl get nodes显示NotReady却花 20 分钟才定位到是 kubelet 的 cgroup 配置错误;或者你刚接手一个别人搭的集群,发现 CoreDNS 偶发解析失败,却不知该先查 Service Endpoints 还是先看 NodePort 端口冲突——那你不是缺知识点,是缺一套被考场和生产环境双重验证过的排错肌肉记忆。这篇文章不讲“Kubernetes 是什么”,不列“30 个必背命令”,只拆解四类真题现场:证书失效导致 API Server 拒绝连接、etcd 数据不一致引发调度异常、CNI 插件配置错误造成 Pod 间跨节点失联、以及最隐蔽的——kube-scheduler 亲和性规则与节点污点/容忍度的组合误配。每类都附带我在某次真实考试中手敲的完整排查流水线,包括命令输出截断、关键字段高亮、以及那个让考官点头的“一句话结论”。

2. 内容整体设计与思路拆解:为什么真题总爱考这四类故障?

2.1 不是随机出题,是精准打击运维盲区

CKA 考纲明确要求“Troubleshoot application and cluster failures”,但考题从不直接说“请修复 CoreDNS”。它会给你一个现象:kubectl get pods -n kube-system显示coredns-xxx处于CrashLoopBackOff,然后让你“确保 DNS 解析正常”。这时候,90% 的考生第一反应是kubectl logs -n kube-system coredns-xxx,看到报错dial tcp 10.96.0.1:443: connect: no route to host就懵了——10.96.0.1 是 kubernetes Service 的 ClusterIP,它连不上,说明问题根本不在 CoreDNS 自身,而在它依赖的底层通信链路。这就是设计意图:考你是否理解 Kubernetes 组件间的依赖拓扑,而非单点工具使用。

我们把历年真题故障归为四类,不是按组件名(如 etcd、kubelet),而是按故障传播路径的起点:

故障类型典型现象根本原因层级为什么高频
控制平面证书失效kubectl get nodes报错x509: certificate has expired or is not yet validTLS 信任链断裂证书有效期默认 1 年,新集群常忽略续期;考试环境故意设为过期时间点
etcd 数据不一致kubectl get pods返回部分 Pod 状态异常,但etcdctl查key显示已删除etcd 集群脑裂或 snapshot 恢复不完整考试要求手动备份/恢复 etcd,操作中极易遗漏--data-dir或权限
CNI 网络插件异常Pod 在 Node A 能 ping 通 Node B 的 Pod IP,但反向不通;或所有 Pod 无法访问 ClusterIPCNI 配置文件缺失/错位、iptables 规则被覆盖、kube-proxy mode 不匹配CNI 是黑盒,考生倾向跳过验证,直接重装,但考试禁止重装插件
调度策略冲突Pod 一直 Pending,kubectl describe pod xxx显示0/3 nodes are available: 3 node(s) had taints that the pod didn't tolerate.节点污点(taint)与 Pod 容忍度(toleration)未对齐,或节点亲和性(affinity)规则语法错误考题常修改 YAML 中tolerations字段缩进或值类型(如字符串写成布尔),肉眼难辨

提示:所有真题故障都满足两个条件——可复现、可逆向追踪。它不会考你“如何优化 kube-scheduler 性能”,因为那需要监控数据;它只考你“为什么这个 Pod 卡在 Pending”,因为答案一定藏在kubectl describe的 Events 和kubectl get node -o wide的 Conditions 里。

2.2 排错思路不是线性流程,而是三层漏斗筛选

很多考生按“先查 Pod → 再查 Node → 最后查 Control Plane”顺序硬啃,结果超时。真实高效的思路是三层漏斗法:

  • 第一层:现象归因(What)
    看到CrashLoopBackOff,不急着看日志,先执行kubectl get events --sort-by=.lastTimestamp。Events 是集群的“急诊病历”,它会告诉你:是FailedCreatePodSandBox(容器运行时问题),还是FailedScheduling(调度器问题),或是FailedMount(存储卷挂载失败)。90% 的题,Events 第一条就能锁定大方向。

  • 第二层:组件状态快照(Where)
    锁定大方向后,立刻获取相关组件的实时状态快照。例如 Events 提示FailedScheduling,就马上跑:

    kubectl get nodes -o wide # 看节点 Ready 状态和 IP kubectl describe node <node-name> | grep -A 10 "Conditions:" # 看内存/CPU 压力 kubectl get pods -n kube-system | grep scheduler # 确认 scheduler 是否 Running

    这些命令加起来不到 5 秒,但比盲目kubectl logs快十倍。

  • 第三层:协议级验证(How)
    当怀疑网络或证书时,绕过 kubectl,直连底层协议。比如证书问题,不用等kubectl报错,直接用openssl s_client -connect 127.0.0.1:6443 -showcerts检查 API Server 证书有效期;怀疑 CNI,不用ping,用curl -k https://10.96.0.1:443/api/v1/namespaces测试 ClusterIP 是否可达——这能排除 DNS 和 Service Proxy 的干扰。

这套漏斗不是教科书理论,是我带学员做第 7 套模拟题时总结的:当某位学员在“修复 kube-proxy”上卡了 18 分钟,而另一个人用kubectl get events30 秒定位到是节点磁盘满,我就知道,考场上的胜负手,从来不是你知道多少,而是你拒绝在错误方向上浪费多少秒。

3. 核心细节解析与实操要点:四类高频故障的致命细节

3.1 控制平面证书失效:别只盯着/etc/kubernetes/pki

证书失效是 CKA 最经典的“送分题变送命题”。题目通常这样描述:“集群 API Server 不可用,请恢复其功能”。考生冲过去ls /etc/kubernetes/pki/,发现apiserver.crt修改时间是去年,立刻开始kubeadm certs renew apiserver……然后发现命令报错failed to load admin.conf: open /etc/kubernetes/admin.conf: no such file。

问题出在哪?kubeadm 证书续期的前提,是admin.conf文件存在且有效。而考试环境常故意删除或损坏该文件。正确解法是两步走:

  1. 先重建 admin.conf:

    # 从当前节点的 kubeconfig 模板重建(注意:考试环境 /etc/kubernetes 目录结构完整) sudo kubeadm init phase kubeconfig admin --cert-dir /etc/kubernetes/pki --config /var/lib/kubelet/config.yaml # 如果 --config 路径报错,改用默认路径(考试环境通常保留) sudo kubeadm init phase kubeconfig admin --cert-dir /etc/kubernetes/pki
  2. 再续期证书并重启服务:

    # 续期所有控制平面证书(更稳妥,避免遗漏 etcd 证书) sudo kubeadm certs renew all # 重启 kubelet,它会自动 reload 新证书 sudo systemctl restart kubelet # 验证:此时 kubectl 应能连上,但可能提示 config context 错误 export KUBECONFIG=/etc/kubernetes/admin.conf kubectl get nodes

注意:kubeadm certs renew不会更新admin.conf中的 client-certificate-data,所以必须先重建 conf。这是 80% 考生踩坑点——他们续了证书,却忘了 conf 里的 base64 编码还是旧的。

3.2 etcd 数据不一致:备份时少一个参数,恢复时全盘皆输

etcd 故障题常以“恢复集群至最近一次备份状态”为任务。考生熟练执行etcdctl snapshot save,但恢复时用etcdctl snapshot restore后,kubectl get nodes仍报错connection refused。原因在于:snapshot restore只还原数据,不重建 etcd 集群配置。

标准恢复流程必须包含三步,缺一不可:

  1. 停止 kubelet 和 etcd 服务(防止写入冲突):

    sudo systemctl stop kubelet sudo systemctl stop etcd
  2. 执行 restore,并指定完整参数(考试环境 etcd 数据目录固定为/var/lib/etcd):

    # 关键!必须指定 --data-dir,且路径要和原 etcd 启动参数一致 sudo etcdctl snapshot restore /opt/backup/etcd-snapshot.db \ --data-dir /var/lib/etcd-restore \ --name etcd-node-1 \ --initial-cluster etcd-node-1=https://127.0.0.1:2380 \ --initial-cluster-token etcd-cluster-1 \ --initial-advertise-peer-urls https://127.0.0.1:2380

    实操心得:--data-dir不能写成/var/lib/etcd,因为 restore 会清空该目录。必须用新路径(如/var/lib/etcd-restore),后续再通过 systemd 配置指向它。

  3. 更新 systemd 配置并重启:

    # 编辑 /etc/systemd/system/etcd.service,修改 ExecStart 行: # 原来:--data-dir=/var/lib/etcd # 改为:--data-dir=/var/lib/etcd-restore sudo systemctl daemon-reload sudo systemctl start etcd sudo systemctl start kubelet

常见错误是跳过第 2 步的--initial-cluster参数,导致 restore 后的 etcd 以单节点模式启动,但 kubelet 仍尝试连接原集群地址,自然失败。

3.3 CNI 网络插件异常:kubectl get nodes显示 Ready ≠ 网络就通

CNI 题最狡猾。题目说“部署一个 Nginx Pod,确保它能被 ClusterIP 访问”,你kubectl apply -f nginx.yaml,kubectl get pods显示 Running,kubectl get svc显示 EXTERNAL-IP<pending>,于是你去查kubectl get endpoints nginx,发现 endpoints 是空的……然后你开始怀疑是不是 Service selector 写错了。

停。先执行这个命令:

kubectl get nodes -o wide | awk '{print $1,$6}' | grep -v INTERNAL-IP # 输出类似:node1 192.168.1.10 # 然后立刻在 node1 上执行: curl -I http://192.168.1.10:30080 # 假设你用了 NodePort

如果返回200 OK,说明 Pod 网络本身是通的,问题在 Service 层;如果curl超时,则是 CNI 层故障。

CNI 排查黄金三命令:

# 1. 看 CNI 配置文件是否存在且语法正确(考试常用 Calico) ls /etc/cni/net.d/ cat /etc/cni/net.d/10-calico.conflist | python3 -m json.tool # 验证 JSON 格式 # 2. 看 kube-proxy 是否在运行,且 mode 是 iptables 还是 ipvs kubectl get pods -n kube-system | grep proxy kubectl get configmap -n kube-system kube-proxy -o yaml | grep mode # 3. 直接测试 Pod 网络连通性(绕过 Service) kubectl get pods -o wide # 找到一个 Running 的 Pod,记下 NODE 和 IP # 登录到该 NODE,用 crictl 查它的 sandbox ID sudo crictl ps | grep <pod-name> sudo crictl inspect <sandbox-id> | grep network # 然后 ping 其他节点上的 Pod IP(需先获知对方 Pod IP)

注意:考试环境禁用ping命令,但允许curl和nc。所以用nc -zv <pod-ip> 80替代 ping,更精准。

3.4 调度策略冲突:YAML 缩进错误,比代码 bug 更致命

这类题表面是“部署 Pod”,实则是“找 YAML 语法/语义错误”。典型陷阱:

apiVersion: v1 kind: Pod metadata: name: nginx-pod spec: containers: - name: nginx image: nginx tolerations: # 注意:这里缩进是 2 个空格,但应该和 containers 同级(即 0 空格) - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule"

kubectl apply -f会成功,但 Pod 一直 Pending。kubectl describe pod nginx-pod的 Events 显示Tolerations not satisfied。原因:YAML 中tolerations缩进错误,导致它被解析为containers[0]的子字段,而非spec的子字段,因此整个 tolerations 段被忽略。

正确写法(用kubectl create生成模板再修改):

kubectl run nginx-pod --image=nginx --dry-run=client -o yaml > nginx.yaml # 然后编辑,确保 tolerations 与 containers 同级(都是 spec 下的直接子项)

另一个高频陷阱是nodeSelector的 key 值拼写。题目常把beta.kubernetes.io/os写成beta.kubernetes.io/OS(大小写错误),或把kubernetes.io/os: linux写成kubernetes.io/os: Linux(值大小写敏感)。验证方法:

kubectl get nodes -o wide -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels}{"\n"}{end}' # 输出类似:node1 map[beta.kubernetes.io/os:linux kubernetes.io/os:linux] # 注意:label 值是小写的 'linux',不是 'Linux'

4. 实操过程与核心环节实现:一场 117 分钟的真实考场复盘

4.1 考场环境初始化:你只有 3 分钟建立“可信基线”

考试开始,你会拿到一个 Ubuntu 20.04 虚拟机,预装 Docker、kubectl、kubeadm、crictl。第一件事不是 rush 到题目,而是用 180 秒建立你的“可信基线”——一套能快速判断集群健康度的速查命令集。我把它存在~/ckacheck.sh,考前粘贴进去:

#!/bin/bash echo "=== 1. API Server 连通性 ===" curl -k https://127.0.0.1:6443/healthz 2>/dev/null | grep ok || echo "API Server DOWN" echo "=== 2. 节点状态 ===" kubectl get nodes -o wide 2>/dev/null || echo "kubectl failed" echo "=== 3. 核心系统 Pod ===" kubectl get pods -n kube-system --field-selector status.phase!=Running 2>/dev/null | grep -v NAME || echo "All system pods Running" echo "=== 4. 最近 Events ===" kubectl get events --sort-by=.lastTimestamp --field-selector type!=Normal | head -n 5 2>/dev/null || echo "No abnormal events"

运行它,5 秒内你就知道:API Server 是否活着、节点是否 Ready、有没有 CrashLoopBackOff 的系统组件、以及最近是否有FailedScheduling或FailedMount这类关键事件。这比盲目读题快得多。

4.2 真题实战:修复一个“证书过期 + etcd 数据损坏”的复合故障

题目描述:

“集群当前不可用。kubectl get nodes报错x509: certificate has expired。同时,kubectl get pods无响应。请恢复集群功能,确保kubectl get nodes返回 Ready 状态。”

我的操作流水线(含思考注释):

  1. 先确认证书问题范围:

    # 看 admin.conf 里的证书是否过期(考试环境该文件通常存在) openssl x509 -in /etc/kubernetes/admin.conf -noout -dates # 输出:notAfter=Jan 1 00:00:00 2023 GMT → 确实过期
  2. 重建 admin.conf(因为 kubeadm renew 需要它):

    # 尝试用 kubeadm init phase 重建 sudo kubeadm init phase kubeconfig admin --cert-dir /etc/kubernetes/pki # 成功,生成新 /etc/kubernetes/admin.conf export KUBECONFIG=/etc/kubernetes/admin.conf
  3. 续期所有证书:

    sudo kubeadm certs renew all # 检查:openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates → now shows 2025
  4. 重启 kubelet 加载新证书:

    sudo systemctl restart kubelet # 此时 kubectl get nodes 应返回节点列表,但状态可能是 NotReady(因为 etcd 还没好)
  5. 检查 etcd 状态:

    # 直连 etcd 测试 sudo ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ endpoint health # 输出:127.0.0.1:2379 is unhealthy: failed to commit proposal # → etcd 数据损坏,需恢复
  6. 执行 etcd 恢复(使用考试提供的 /opt/backup/etcd-snapshot.db):

    sudo systemctl stop kubelet etcd sudo etcdctl snapshot restore /opt/backup/etcd-snapshot.db \ --data-dir /var/lib/etcd-restore \ --name etcd-node-1 \ --initial-cluster etcd-node-1=https://127.0.0.1:2380 \ --initial-cluster-token etcd-cluster-1 \ --initial-advertise-peer-urls https://127.0.0.1:2380 # 修改 /etc/systemd/system/etcd.service,将 --data-dir 改为 /var/lib/etcd-restore sudo systemctl daemon-reload sudo systemctl start etcd sudo systemctl start kubelet
  7. 最终验证:

    kubectl get nodes # 返回 Ready kubectl get pods -A | grep -E "(Running|Completed)" | wc -l # 应 > 10

全程耗时约 14 分钟,其中 8 分钟花在 etcd 恢复的参数确认上——--initial-cluster的 URL 必须和原集群完全一致,考试环境是https://127.0.0.1:2380,不是https://localhost:2380,少一个字符就失败。

4.3 真题实战:调试一个“CNI 导致 CoreDNS 解析失败”的隐形故障

题目描述:

“部署一个 busybox Pod,执行nslookup kubernetes.default.svc.cluster.local,应返回正确的 ClusterIP。当前命令超时,请修复。”

我的操作路径:

  1. 先确认 CoreDNS Pod 状态:

    kubectl get pods -n kube-system -l k8s-app=kube-dns # 发现 coredns-xxx 处于 Running,但 describe 显示 Warning: Back-off restarting failed container
  2. 查 CoreDNS 日志,但重点看第一行:

    kubectl logs -n kube-system coredns-xxx | head -n 3 # 输出:.:53 # [INFO] plugin/reload: Running configuration MD5 = ... # [FATAL] plugin/loop: Seen "HINFO" opcode from 10.244.1.1:34291, loop detected # → 关键词 "loop detected",说明 CoreDNS 在循环查询自己
  3. 定位循环原因:CoreDNS 配置中的 upstream DNS 指向了自身:

    kubectl get configmap -n kube-system coredns -o yaml | grep -A 5 "forward" # 输出:forward . 10.96.0.10 # 10.96.0.10 是 CoreDNS Service 的 ClusterIP! # 这就形成了:busybox → CoreDNS Service → CoreDNS Pod → 查询 10.96.0.10 → 又回到 CoreDNS Service...
  4. 修复:将 forward 改为上游 DNS(考试环境通常允许用 8.8.8.8):

    kubectl edit configmap -n kube-system coredns # 修改 forward 行为:forward . 8.8.8.8 # 保存退出,CoreDNS 会自动 reload
  5. 验证:

    kubectl exec -it busybox -- nslookup kubernetes.default.svc.cluster.local # 返回:Server: 10.96.0.10 # Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local # Name: kubernetes.default.svc.cluster.local # Address 1: 10.96.0.1

这个故障的隐蔽性在于:kubectl get pods看似正常,kubectl get svc也正常,但nslookup就是超时。它考的是你是否理解 CoreDNS 的forward插件行为,以及是否记得kubectl edit configmap这个“修改运行中配置”的关键命令。

5. 常见问题与排查技巧实录:那些考官不会告诉你的细节

5.1 “kubectl get nodes” 显示 Ready,但 Pod 就是起不来?查这三处

现象可能原因快速验证命令修复动作
kubectl get nodes显示Ready,但新 Pod 一直Pending节点磁盘压力过大(NodeHasDiskPressure)kubectl describe node <node-name> | grep -A 5 "Conditions:"kubectl taint nodes <node-name> node.kubernetes.io/disk-pressure:NoSchedule-(临时解除)
kubectl get nodes显示Ready,但kubectl get pods返回空kubelet 未连接 API Server(证书或网络问题)sudo journalctl -u kubelet -n 50 | grep -i "failed|error"检查/var/lib/kubelet/kubeconfig是否指向正确 API Server 地址
kubectl get nodes显示Ready,但kubectl exec -it <pod>报错unable to upgrade connectionkubelet 的--read-only-port被禁用或防火墙拦截sudo ss -tlnp | grep :10255(默认只读端口)编辑/var/lib/kubelet/config.yaml,确保readOnlyPort: 10255,然后sudo systemctl restart kubelet

实操心得:kubectl describe node的 Conditions 段是“节点健康报告单”,比kubectl get nodes的简单状态有用十倍。每次看到 Pending,第一反应不是改 YAML,而是describe node。

5.2 考试中遇到“命令不存在”怎么办?备用方案清单

CKA 考试环境预装工具有限,以下命令可能缺失,需用替代方案:

缺失命令替代方案说明
jqpython3 -m json.tool解析 JSON 输出,kubectl get nodes -o json | python3 -m json.tool
curlwget --spider -S <url> 2>&1 | grep "HTTP/"测试 HTTP 连通性,wget几乎必装
netstatss -tuln查看端口监听,ss是现代替代
dignslookup <domain> <dns-server>DNS 查询,nslookup更轻量
helm手动kubectl apply -f考试禁用 Helm,所有应用都用原生 YAML

注意:考试环境vi和nano都可用,但vim可能没有语法高亮。建议用nano编辑 YAML,它对缩进错误有红色提示。

5.3 时间管理:2 小时,如何分配给 17 道题?

CKA 考试共 17 道题,总分 100,及格线 66。我的时间分配策略(基于 117 分钟实际答题时间):

阶段时间动作目标
前 5 分钟5 min运行ckacheck.sh,扫一遍集群基线;快速浏览所有题目标题,标记“证书”、“etcd”、“CNI”、“调度”四类题建立全局认知,避免一头扎进难题
主攻阶段70 min优先做 3-4 道“确定能拿分”的题(如创建 Namespace、打标签、查 Events),每道控制在 8 分钟内;同步用kubectl get events监控全局异常确保基础分 40+,建立信心
攻坚阶段30 min集中处理 1-2 道复合故障题(如证书+etcd),严格按“现象→Events→组件状态→协议验证”流程,每道限时 15 分钟拿下高分题,冲刺 80+
收尾阶段12 min回顾所有题,对“Pending”、“CrashLoopBackOff”的 Pod,统一执行kubectl describe;检查kubectl get nodes和kubectl get pods -A是否全部正常防止低级失误丢分

个人体会:最后 5 分钟,不要碰新题。把所有kubectl get命令再跑一遍,确保输出是你期望的。考官评分脚本只认最终状态,不看你中间过程。

5.4 考后复盘:为什么我总在“etcd restore”上超时?

这是我带学员复盘时发现的最高频超时点。根本原因不是技术不会,而是操作节奏失控。具体表现为:

  • 过度纠结参数:在etcdctl snapshot restore的--initial-cluster参数上反复确认,查文档、试不同格式,耗掉 10 分钟;
  • 忽略路径权限:restore后/var/lib/etcd-restore目录属主是 root,但 etcd.service 用etcd用户启动,导致启动失败,又花 5 分钟查journalctl;
  • 忘记重启 kubelet:以为 etcd 起来就完了,结果kubectl get nodes仍报错,再回头查才发现 kubelet 没重启。

解决方案:把 etcd 恢复流程固化为“五步 checklist”,考前默写三遍:

  1. sudo systemctl stop kubelet etcd
  2. sudo etcdctl snapshot restore ... --data-dir /var/lib/etcd-restore ...
  3. sudo chown -R etcd:etcd /var/lib/etcd-restore(关键!)
  4. sudo systemctl start etcd
  5. sudo systemctl start kubelet

现在,我的学员平均用时压到 6 分钟以内。考场上的稳定,从来不是靠临场发挥,而是把每一个高频动作,练成不需要思考的肌肉反射。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进