调试失败的 Pod:describe、logs、exec 与 port-forward
一份可重复的清单,用来找出 Pod 为什么是 Pending、崩溃或没有响应。
迟早会有一个 Pod 无法启动、不断重启,或者在运行却没有响应。修复几乎总是通过少数几个 kubectl 命令、按正确的顺序使用就能看出来。本指南给你这个顺序:读取状态,询问事件,读取日志,进入容器,然后追踪网络路径。
先读取状态
从状态列开始:
kubectl get pods -o wide
最常见的状态,以及首先该看哪里:
- Pending。 Pod 没有被分配到节点。通常是没有足够的 CPU 或内存、配额已用尽,或者卷无法绑定。
describe会告诉你是哪个。 - ContainerCreating。 节点正在准备容器。如果一直停在这个状态,请检查是否缺少卷,或镜像拉取缓慢。
- ErrImagePull / ImagePullBackOff。 无法拉取镜像,最常见的原因是名称或标签拼写错误,或缺少私有镜像仓库的凭据。
- CrashLoopBackOff。 容器启动、退出,并以逐渐增长的延迟被反复重启。应用本身在失败,所以请读取它的日志。
- CreateContainerConfigError。 Pod 引用了不存在的 ConfigMap、Secret 或键。
- Running 但
READY 0/1。 容器已启动,但就绪探针失败。 - OOMKilled(在
describe中显示)。容器超过了它的内存 limit。
问 Kubernetes 为什么:describe 与事件
kubectl describe pod POD_NAME
滚动到底部的 Events 部分。它读起来像一条时间线:Scheduled、Pulling、Failed、BackOff。诸如 0/3 nodes are available: insufficient cpu 或 Failed to pull image "ngnix:1.25" 之类的消息,通常直接指出了问题。再往上,查看每个容器的 State 和 Last State,了解退出码和原因。要获得整个命名空间发生了什么的视图:
kubectl get events --sort-by=.metadata.creationTimestamp
读取容器的输出
kubectl logs POD_NAME
kubectl logs POD_NAME -c CONTAINER_NAME
kubectl logs POD_NAME --all-containers
kubectl logs POD_NAME --previous
kubectl logs POD_NAME -f --since=10m --tail=100
当 Pod 有多个容器时使用 -c,流式输出使用 -f,用 --since 和 --tail 限制输出,并用 --previous 读取上一个崩溃实例的日志,这正是崩溃循环所需要的。要试一试,创建一个让容器有意失败的 Deployment:
kubectl create deployment crashy --image=busybox:1.36 -- \
sh -c 'echo starting; echo "fatal: DB_URL is not set" >&2; exit 1'
kubectl get pods -l app=crashy
kubectl logs deployment/crashy --previous
状态会变成 CrashLoopBackOff,而日志显示了原因。用 kubectl delete deployment crashy 清理。
请记住,日志属于 Pod。当它被删除或替换时,它的日志会随之被清理,所以要保留历史,你需要像 Fluentd 或 Fluent Bit 这样的日志收集器,把日志发送到某个持久的地方。
进入内部:exec、port-forward 与 cp
kubectl exec -it POD_NAME -- sh
kubectl exec POD_NAME -- env
kubectl port-forward pod/POD_NAME 8080:80
curl http://localhost:8080
kubectl cp POD_NAME:/path/to/file ./file
exec 在容器中运行命令,port-forward 把本地端口连接到 Pod 中的端口(对 svc/NAME 同样有效),cp 把文件复制进来或出去。许多生产镜像都是最小化的,没有 shell 或 curl。在这种情况下,kubectl debug -it POD_NAME --image=busybox:1.36 --target=CONTAINER_NAME 会附加一个共享该 Pod 命名空间的临时调试容器。
你在运行中的容器内所做的任何更改,比如编辑配置文件,在 Pod 被替换的那一刻就会丢失。使用 exec 来调查,但要在镜像、清单或 ConfigMap 中做出修复。
沿请求路径向回推
如果 Pod 看起来健康,但应用无法访问,请从内向外追踪路径:
- Pod 在运行并且
READY吗?先修复 Pod。 - Service 的 selector 与 Pod 的标签匹配吗?
kubectl get pods --show-labels和kubectl describe service NAME应当一致。 - Service 有端点吗?
kubectl get endpointslices -l kubernetes.io/service-name=NAME应当列出 Pod IP。列表为空意味着 selector 不匹配,或没有就绪的 Pod。 - 端口匹配吗?Service 的
targetPort必须是容器实际监听的端口。 - 从另一个 Pod 访问能行吗?
kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://NAME。这会测试 DNS 和 Service,排除集群之外的任何东西。 - 会不会是 NetworkPolicy 在阻挡?用
kubectl get networkpolicy列出它们。 - 最后,检查它前面的 Ingress 或外部负载均衡器。
每一步都把问题缩小到一层,这比猜测要快。