第 12 / 18 篇

第 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 标签?