第 11 章 排错:用证据缩小问题范围
一条通用路径
遇到问题,先确认目标,再看状态,再看事件,最后看应用输出。不要在不了解原因时连续删除资源;删除可能让关键现场消失。
kubectl config current-context
kubectl get pods -n k8s-book -o wide
kubectl get deployment,service -n k8s-book
kubectl get events -n k8s-book --sort-by=.metadata.creationTimestamp接着选择具体 Pod,替换名称后检查:
kubectl describe pod <实际的Pod名称> -n k8s-book
kubectl logs <实际的Pod名称> -n k8s-book --tail=100
kubectl logs <实际的Pod名称> -n k8s-book --previous--previous 查看同一 Pod 内上一次退出的容器实例日志,并非任意历史 Pod 的日志;如果没有上一次实例,报错是正常的。多容器 Pod 应增加 -c 容器名。应用没有把信息写到标准输出时,logs 不一定有你需要的内容。
常见状态与第一步
| 现象 | 常见方向 | 优先证据 |
|---|---|---|
| Pending | 资源不足、调度约束、PVC 未绑定 | describe 中的调度事件 |
| ContainerCreating | 拉镜像、挂载、网络初始化 | Pod 事件 |
| ImagePullBackOff | 镜像名错误、权限、网络 | 拉取镜像错误原文 |
| CrashLoopBackOff | 启动后反复退出 | 当前与 previous 日志、退出码 |
| Running 但未就绪 | 探针失败或依赖未准备好 | 探针事件、应用日志 |
| OOMKilled | 容器内存不足等 | 容器终止状态、内存限制 |
| Service 访问失败 | 选择器、端口、后端、DNS、网络策略 | EndpointSlice 与实际 Pod 标签 |
这些字符串并非都属于 Pod 的正式 phase 字段。kubectl get pods 的 STATUS 栏综合展示多种信息;例如 CrashLoopBackOff 表示容器处于重启退避状态,而不是一种独立的 Pod phase。
一次可恢复的网络故障实验
保留正常 service.yaml,只在实时对象中把选择器临时改错:
kubectl patch service hello-web -n k8s-book \
--type=merge -p '{"spec":{"selector":{"app":"wrong-app"}}}'
kubectl get endpointslices -n k8s-book -l kubernetes.io/service-name=hello-web此时后端会消失或没有可用地址,集群内部对该服务的请求会失败。空后端可能表现为没有可用切片、endpoints: [] 或 endpoints: null;判断时应检查可用地址与实际请求,不能假设 API 永远返回非空数组。不要用已经连上的旧端口转发来判断,因为它可能仍指向之前选中的 Pod。
观察后恢复:
kubectl apply -n k8s-book -f service.yaml
kubectl get pods -n k8s-book --show-labels
kubectl get endpointslices -n k8s-book -l kubernetes.io/service-name=hello-web这次实验让你把“服务不通”具体化为“Service 选择器没有选中正确的 Pod”。恢复后,重新运行第 6 章的临时客户端验证访问。
看懂四种常见报错
connection refused 可能表示目标端口没有进程监听,也可能出现在错误的 API 地址上,要先看它连接的是哪里。NotFound 先查名字、类型和命名空间。Forbidden 表示请求已被识别但权限不足,不应通过借用管理员凭据来草率绕过。no matches for kind 可能表示 API 版本或类型错误,也可能表示所需自定义资源尚未安装。
本章自测:镜像拉取失败时,先改探针有用吗?为什么排查 Service 要同时看选择器与 Pod 标签?