K8s 运维系列 | 第 25 天:故障排查——常见集群故障定位与处理

引言

在生产环境中,Kubernetes 集群不可能永远保持健康状态。节点宕机、镜像拉取失败、Pod 反复重启、Service 无法访问、网络策略误配——这些故障一旦发生,直接冲击业务的可用性与响应时延。与单节点应用不同,K8s 引入了控制平面、节点、Pod、Service、Ingress 等多层抽象,一个表现层的问题(例如「页面打不开」)根因可能藏在调度器、CNI、CSI、内核参数等任何一环。

掌握系统化的故障排查能力,是 K8s 运维人员区别于「会敲命令」与「能救火」的分水岭。本篇将围绕常见集群故障,梳理一套可复用的排查方法论,覆盖核心组件异常、调度失败、镜像问题、网络连通性、存储挂载等高频场景,并给出每个场景的定位命令与处置动作。

K8s 运维 第25天

故障排查的整体方法论

K8s 故障排查遵循「从控制平面到数据平面、从全局到局部、从对象到组件」的递进思路。面对一个现象,运维人员应当先确认是「控制面问题」还是「数据面问题」,再逐步缩小范围。

控制面问题通常表现为:kubectl 命令无响应、API Server 连接超时、etcd 不可写、控制器(Deployment/PV/Job 控制器)状态停滞。数据面问题则表现为:Pod 已 Running 但进程无响应、容器内网络不通、挂载卷读写异常、节点内核资源耗尽。

定位时遵循三条原则:

  1. 先描述(describe)后查看(logs)kubectl describe 会输出 Events 列表,Events 是 K8s 内置的故障线索库,往往直接给出根因(如 FailedSchedulingFailedPullImageMountVolume.SetUp failed)。
  2. 先看调度器日志再改配置:调度失败类问题(Pending、节点不足)务必先读 kube-scheduler 日志或 kubectl describe node 中的 Taints/Allocatable,再考虑修改容忍度或污点。
  3. 优先使用官方诊断工具kubectl get events --sort-by=.metadata.creationTimestampkubectl debug node/<node>kubectl topkubectl run -it --rm --image=busybox 临时调试容器,是定位网络与镜像问题的利器。

核心故障场景与处置

场景一:Pod 处于 Pending 状态

Pod 长时间 Pending 通常意味着调度器无法找到合适节点。原因可分为资源不足、污点未容忍、亲和性冲突、PVC 未绑定四类。

定位命令:

# 查看 Pod 事件,重点关注 FailedScheduling 关键字
kubectl describe pod <pod-name> -n <namespace>

# 查看节点可分配资源与污点
kubectl describe node <node-name> | grep -A 5 -E "Allocatable|Taints|Conditions"

# 集群整体资源概览
kubectl top nodes

常见原因与处置:

现象关键字 根因 处置动作
0/{N} nodes are available: insufficient cpu/memory 资源配额不足 缩减 request、扩容节点、删除低优先级 Pod
node(s) had untolerated taint 污点未容忍 为 Pod 添加对应 toleration,或移除节点污点
node(s) didn't match Pod's node affinity 亲和性冲突 检查 nodeSelector / affinity 配置
persistentvolumeclaim not found PVC 未绑定 检查 StorageClass 与 PV 状态

资源不足示例处置:

# 调低 request 或提高 limit(针对 Deployment)
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
        - name: app
          resources:
            requests:
              cpu: "250m"   # 从 1000m 降至 250m
              memory: "512Mi"
            limits:
              cpu: "1"
              memory: "1Gi"

场景二:镜像拉取失败(ImagePullBackOff / ErrImagePull)

ImagePullBackOff 是最常见也最易被忽略的故障。其根因包括镜像名/tag 拼写错误、私有仓库未配置镜像拉取密钥(imagePullSecret)、节点到仓库网络不通、仓库侧限流。

定位命令:

# 查看 Pod 事件中的 ImagePull 错误信息
kubectl describe pod <pod-name> -n <namespace>

# 直接在节点上手动拉取,复现错误
docker pull registry.example.com/app:v1.0
# 或 crictl pull (containerd 环境)
crictl pull registry.example.com/app:v1.0

处置动作:

私有仓库需先创建拉取密钥并绑定到 ServiceAccount:

# 创建拉取密钥
kubectl create secret docker-registry regcred 
  --docker-server=registry.example.com 
  --docker-username=admin 
  --docker-password='P@ssw0rd' 
  --docker-email=ops@company.com 
  -n default

# 绑定到默认 ServiceAccount(或指定 SA)
kubectl patch serviceaccount default -p 
  '{"imagePullSecrets": [{"name": "regcred"}]}'

部署清单中显式引用:

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  imagePullSecrets:
    - name: regcred
  containers:
    - name: app
      image: registry.example.com/app:v1.0

注意:镜像 tag 不可使用 latest 之外的占位。生产环境务必锁定语义化版本(如 v1.2.3),避免拉取到不可预期的镜像。

场景三:容器反复重启(CrashLoopBackOff)

CrashLoopBackOff 表明容器进程启动后又退出。根因包括应用启动命令错误、健康检查失败、配置缺失、OOMKill。

定位命令:

# 查看上一次退出日志(关键!不要只看当前日志)
kubectl logs <pod-name> -n <namespace> --previous

# 查看容器退出码与原因
kubectl describe pod <pod-name> -n <namespace> | grep -E "State|Reason|Exit Code|Restart Count"

# 实时跟踪新日志
kubectl logs -f <pod-name> -n <namespace> --tail=100

退出码速查:

退出码 含义 排查方向
0 正常退出 配置错误导致进程主动退出,检查启动参数
1 通用错误 应用代码异常,看 --previous 日志
137 SIGKILL(OOM 或强杀) 检查内存 limit 是否过低,kubectl top pod 验证
143 SIGTERM 优雅关闭信号,通常非故障

OOMKill 验证:

# 查看节点内核日志中的 OOM 记录
kubectl exec -n default -- sh -c 'dmesg | grep -i oom | tail -20'

# 或在节点上直接查看(需 SSH)
journalctl -k | grep -i oom-kill

处置思路:先确认应用真实内存需求,再适度上调 limit;同时检查应用是否存在内存泄漏。

场景四:Service 无法访问

Service 是 K8s 服务发现的核心。访问不通的常见根因包括:Selector 与 Pod Label 不匹配、Endpoint 为空、端口映射错误、iptables/ipvs 规则异常、后端容器未监听该端口。

定位命令:

# 1. 确认 Endpoint 是否存在
kubectl get endpoints <svc-name> -n <namespace>

# 2. 检查 Selector 与 Pod Label 是否匹配
kubectl get pod -l app=web -n <namespace>
kubectl get svc <svc-name> -n <namespace> -o yaml | grep -A 3 selector

# 3. 集群内 Pod 间连通性测试
kubectl run -it --rm --image=busybox:1.36 --restart=Never test-pod -- sh
> ping <svc-name>
> wget -qO- http://<svc-name>:<port>/healthz
> nc -zv <svc-name> <port>

Selector 不匹配修复示例:

# Pod 标签
metadata:
  labels:
    app: web     # 注意:是 app=web 而不是 component=web
    version: v1

# Service 必须使用一致的选择器
spec:
  selector:
    app: web      # 匹配 Pod label,才能注册到 Endpoints

调试 Service 连通性时,临时 Pod(kubectl run --rm)配合 busybox 是最快路径。务必指定 --restart=Never 避免留下垃圾 Pod。

场景五:节点 NotReady

节点 NotReady 是控制平面与数据平面交互的中枢级故障。其根因可能是 kubelet 进程异常、节点网络中断、磁盘压力、资源耗尽、内核死锁。

定位命令:

# 查看节点状态与原因
kubectl describe node <node-name>

# 查看 kubelet 日志(节点上)
journalctl -u kubelet --no-pager | tail -50

# 检查节点资源
top -bn1 | head -10
df -h /var/lib/containerd

# 查看节点上 Pod 状态
kubectl get pod -A --field-selector spec.nodeName=<node-name>

处置动作:

# 重启 kubelet(节点上)
systemctl restart kubelet

# 若节点彻底失联,先 cordon 隔离,再 drain 迁移 Pod
kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

# 恢复节点后解隔离
kubectl uncordon <node-name>

若磁盘压力(DiskPressure)导致 NotReady,需清理节点上未使用的镜像与容器:

# 清理悬空镜像与停止容器
crictl rmi --prune
crictl rm -f $(crictl ps -a -q --state exited)

# 或重启容器运行时
systemctl restart containerd

实用诊断工具与命令速查

kubectl debug:节点级调试

kubectl debug 是 K8s 1.18 引入的节点调试利器,可在不 SSH 登录节点的情况下,对节点运行一个特权调试容器,检查内核、网络、挂载等数据平面细节。

# 在节点上启动一个 ubuntu 调试容器(hostPID/hostNetwork 共享)
kubectl debug node/<node-name> -it --image=ubuntu:20.04 -- chroot /host bash

# 进入节点宿主机上下文后,可执行:
> ip addr               # 查看节点网卡
> ss -lntp              # 查看监听端口
> cat /var/log/messages # 查看系统日志

临时调试 Pod 排查网络

排查 Service、Ingress、NetworkPolicy 等问题时,部署一个临时的 busybox Pod 是最有效的手段:

# 创建临时调试 Pod
kubectl run -it --rm --image=busybox:1.36 --restart=Never -- ns-debug -- sh

# 进入 Pod 后可执行:
> nsenter -t 1 -n ping 10.96.0.1     # 测试到 Kube-apiserver 的连通
> wget -qO- http://kubernetes.default.svc.cluster.local/healthz

事件聚合查询

Events 是 K8s 的故障线索库,建议养成查询习惯:

# 按时间排序查看集群事件,过滤 Warning
kubectl get events -A --sort-by=.metadata.creationTimestamp | tail -30
kubectl get events -A --field-selector type=Warning

# 查询特定对象的最近事件
kubectl get events -A --watch

常见问题 FAQ

Q1:kubectl 命令一直超时怎么办?

先看是否能连通 API Server:curl -k https://<api-server>:6443/healthz。若超时,检查 etcd 集群是否健康(etcdctl endpoint health)、API Server 是否有重启、集群网络分区。常见原因是 etcd 单点不可用或网络分区导致 quorum 丢失。

Q2:Pod Running 但应用未响应,怎么排查?

优先检查三个方向:容器进程是否存活(kubectl exec -- ps aux)、容器内服务是否监听正确端口(ss -lnt)、健康检查探针配置是否合理(livenessProbe 路径/端口匹配)。如果探针配置错误,K8s 会反复重启容器,造成「Running 但不稳定」的假象。

Q3:如何快速判断是控制面问题还是数据面问题?

执行 kubectl get nodeskubectl get pods -A。若命令本身超时或返回大量 Unknown 节点,是控制面问题;若命令正常返回但 Pod 状态异常(如 CrashLoopBackOff、ImagePullBackOff),是数据面问题。区分后排查路径完全不同。

Q4:生产环境允许直接删 Pod 排查吗?

不建议。删除 Pod 会让 Deployment 控制器重新调度,丢失现场。正确做法是:先 kubectl cp 导出日志、kubectl debug 进入调试容器、kubectl describe 收集 Events,再决定是否需要重启或删除。删除动作应作为最后手段,并保留完整的事件与日志证据。

Q5:如何建立日常巡检机制,预防故障?

结合第 14 天的 Prometheus+Grafana 监控,建立节点状态、Pod 重启次数、镜像拉取失败次数、Endpoint 空值率、etcd 延迟五大看板;结合第 21 天的安全加固,定期运行 kubectl get pods -A --field-selector=status.phase!=Running 巡检脚本,并在 CI 中加入静态校验(kubeval、kube-score)。

总结

K8s 故障排查的精髓在于建立「分层、循证、可复现」的思维习惯。本篇覆盖了 Pending、ImagePullBackOff、CrashLoopBackOff、Service 不通、节点 NotReady 五大高频场景,每一场景都给出了从「定位命令」到「处置动作」的完整链路,并配套 kubectl debug、临时调试 Pod、Events 聚合三大实用工具。

运维人员在面对未知故障时,不要急于重启节点或改配置——先用 kubectl describe 拿到 Events 线索,再按控制面/数据面分层缩小范围,最后通过临时调试容器复现问题。这套方法论可以覆盖 80% 以上的生产故障场景。

下一篇将进入 CI/CD 领域,探讨如何在 K8s 上构建云原生发布流水线,实现从代码提交到集群部署的全自动闭环。

下期预告

K8s 运维系列 | 第 26 天:云原生 CI/CD——Jenkins 与 GitLab 镜像构建发布

微信二维码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容