Appearance
排错体系
"服务不可用"在 K8s 里可能出在入口层、Service、Pod、节点、存储、DNS、NetworkPolicy、控制平面或应用自身。排查效率的差异通常不靠经验丰富,而靠先分层定位再看具体命令——每层的排查对象和工具是固定的,问题会跨层叠加,但入口不能混。
分层路径
用户请求进入集群后经过的每一层:
收到"服务不可用"时,第一组命令快速扫全局状态:
bash
kubectl get pods -A -o wide
kubectl get svc -A
kubectl get events -A --sort-by=.lastTimestamp
kubectl get nodes -o wide从这四条能看到:Pod 有没有大面积 CrashLoopBackOff、Service 有没有全部 NotReady、节点有没有大面积 NotReady、最近 Events 集中在哪个方向。大部分时候,看完这四条就知道问题大概落在哪一层。
Pod 层
Pod 是承载应用进程的地方,也是大部分排查的起点:
bash
kubectl describe pod <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace> --previous # Pod 重启后看上一次的Events 先看,日志后看。Events 说 Pod 卡在哪个阶段(仍在 ContainerCreating——还没到应用层面);日志说启动后的应用行为(报了数据库连接错误——应用层面有问题,平台层正常)。
| 状态 | 发生在哪个阶段 | 先检查 |
|---|---|---|
| Pending | 调度 | PVC 是否 Bound、节点资源、污点、亲和性 |
| ImagePullBackOff | 镜像拉取 | 仓库地址、imagePullSecrets、节点到仓库网络 |
| ContainerCreating | 容器和网络创建 | CNI 分配 IP、卷挂载、sandbox 创建 |
| CrashLoopBackOff | 容器运行 | logs --previous、exitCode、启动命令、OOM |
| OOMKilled | 容器运行 | memory limit 值、应用内存曲线 |
Service 层
Service 不通时分两步:DNS 能解析到 ClusterIP 吗?ClusterIP 能转到 Pod 吗?
bash
kubectl -n demo get svc web
kubectl -n demo get endpointslice -l kubernetes.io/service-name=web
kubectl -n demo get pod -l app=web -o wide临时 Pod 从集群内测试:
bash
kubectl run curl --rm -it --image=curlimages/curl --restart=Never -- sh
# Pod 内
curl -v http://web.demo.svc.cluster.local # 走 Service
curl -v http://<pod-ip>:8080 # 直连 Pod IP直连 Pod IP 通但 Service 不通——问题在 Service 端口映射、selector 和 Pod labels 不一致、EndpointSlice 为空或 kube-proxy/节点转发规则异常。
直连 Pod IP 也不通——问题在 CNI、NetworkPolicy 或应用没监听端口。
Gateway / Ingress 层
Gateway API:
bash
kubectl get gatewayclass
kubectl get gateway -A
kubectl get httproute -A
kubectl describe httproute <name> -n <namespace>HTTPRoute 的 status.conditions 是路由是否真正下发的直接信号:
| Conditions | 用户看到什么 | 排查 |
|---|---|---|
Accepted=False | 请求 404 或连接拒绝 | parentRefs 是否正确、namespace 权限、listener 是否匹配 |
ResolvedRefs=False | 请求 503 | 后端 Service 是否存在、Secret 名称和 namespace |
Programmed=False | 请求可能返回旧规则或 404 | 控制器状态、Gateway describe |
Ingress:
bash
kubectl get ingress -A
kubectl describe ingress <name> -n <namespace>
kubectl get ingressclass| 现象 | 方向 |
|---|---|
| 外部入口完全不通 | DNS、LB VIP、控制器 Pod 是否在运行 |
| 返回 404 | Host 或 Path 没有匹配的规则 |
| 502/503 | 后端 Service 不可用、Pod 不 Ready、端口写错 |
| TLS 报错 | Secret 不存在、证书过期、域名不匹配 |
存储层
PVC Bound 只说明 PV 和 PVC 绑定成功。挂载成功、权限正确、IO 正常——要分别验证:
bash
kubectl get pvc -A
kubectl describe pvc <pvc-name> -n <namespace>
kubectl get pv
kubectl describe pod <pod-name> -n <namespace> # Events 里有 mount 信息节点上:
bash
journalctl -u kubelet -n 100 --no-pager | grep -i mount
mount | grep kubeletNFS 场景在节点上确认客户端能到达服务器:
bash
showmount -e <nfs-server>节点层
bash
kubectl describe node <node-name> # Conditions 和 Events
systemctl status kubelet --no-pager
systemctl status containerd --no-pager
journalctl -u kubelet -n 200 --no-pager
df -h && free -h节点有 DiskPressure 或 MemoryPressure 时,Pod 被驱逐只是表面现象。要找到触发压力的根因——日志没轮转、镜像堆积、emptyDir 被写爆。只调大 Pod 的资源 limit 不解决节点磁盘满的问题。
控制平面层
bash
kubectl -n kube-system get pods -o wide | grep -E 'apiserver|scheduler|controller|etcd'
kubectl get --raw='/readyz?verbose'
kubectl get --raw='/livez?verbose'/readyz?verbose 能看到每个控制平面组件的健康检查结果,出问题时能定位具体是哪个组件不健康。
| 现象 | 可能方向 |
|---|---|
| kubectl 操作卡顿、超时 | apiserver 延迟、etcd 磁盘慢 |
| 资源创建失败 | etcd 写入异常、准入 webhook、认证 |
| Pod 一直不被调度 | scheduler 状态、节点资源、污点 |
| 副本异常不自动恢复 | controller-manager |
故障现场保留
一次故障至少保留这些:
| 信息 | 获取方式 |
|---|---|
| 相关资源 YAML | Deployment、Service、HTTPRoute、PVC |
| Pod Events 和状态 | kubectl describe pod |
| 应用日志 | kubectl logs(当前和 --previous) |
| 节点状态 | Node Conditions、kubelet/containerd 日志 |
| 入口指标 | Gateway/Ingress 的 QPS、状态码、延迟 |
| 变更记录 | 镜像 tag、Helm revision、Git commit、kubectl rollout history |
复盘时这些信息比一句"Pod 不稳定"有分析价值。