Kubernetes集群故障排查实战:从Pod CrashLoopBackOff到节点NotReady的诊断手册

Kubernetes故障排查的系统性方法论

Kubernetes集群的故障排查不同于传统服务器运维——一个Pod启动失败可能涉及镜像拉取、资源调度、存储挂载、网络策略、RBAC权限等十多个环节。靠经验拍脑袋定位问题效率极低,系统性的排查路径是:先看事件(Event)再看日志(Log),先查控制面再查数据面,先看状态再看配置。

这篇手册整理了生产环境最常见的6类Kubernetes故障,每类给出从现象到根因的完整诊断步骤。

Pod CrashLoopBackOff:最常见也最容易被误判

CrashLoopBackOff表示容器启动后反复崩溃退出的循环状态。直接看kubectl logs往往看不到有效信息——因为容器已退出,需要加–previous参数查看上一次的日志。

# 查看Pod状态
kubectl get pods -n production
# NAME                    READY   STATUS             RESTARTS   AGE
# api-server-5d7f8c6b9    0/1     CrashLoopBackOff   7          12m

# 查看上一次退出的日志
kubectl logs api-server-5d7f8c6b9 -n production --previous

# 查看Pod事件
kubectl describe pod api-server-5d7f8c6b9 -n production | grep -A20 Events

# 常见Exit Code含义
# Exit 1: 应用程序错误(配置错误、依赖缺失)
# Exit 137: OOMKilled(内存超限被杀)
# Exit 139: Segmentation Fault
# Exit 143: SIGTERM正常退出

OOMKilled场景的排查路径:

# 确认是否OOMKilled
kubectl describe pod api-server-5d7f8c6b9 -n production | grep -i oom
# Last State:  Terminated  Reason: OOMKilled  Exit Code: 137

# 解决方案1:增大资源限制
resources:
  limits:
    memory: '2Gi'  # 从1Gi增大到2Gi
  requests:
    memory: '1Gi'

# 解决方案2:JVM堆内存必须小于limit
# 常见错误:JVM -Xmx2g 但容器limit也是2Gi
# JVM实际内存 = 堆(2g) + 元空间 + 线程栈 + 堆外内存
# 容器limit应设为 -Xmx的1.5~2倍
env:
  - name: JAVA_OPTS
    value: '-Xmx1g -Xms1g -XX:+UseG1GC'

应用自身错误导致的CrashLoopBackOff,常见模式是启动时依赖的服务未就绪(数据库、Redis等):

# Init Container等待依赖就绪
initContainers:
  - name: wait-for-db
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z mysql-service 3306; do echo waiting; sleep 2; done']
  - name: wait-for-redis
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z redis-service 6379; do echo waiting; sleep 2; done']

ImagePullBackOff:镜像拉取失败的分层排查

镜像拉取失败在私有仓库场景下高频出现,排查步骤:

# 查看详细错误信息
kubectl describe pod <pod-name> | grep -A5 'Events'
# 常见错误类型:
# Failed to pull image: rpc error: NotFound
# Failed to pull image: rpc error: Unauthorized
# Failed to pull image: rpc error: Net/http: request timeout

# 1. 检查imagePullSecrets是否配置
kubectl get sa default -o yaml | grep imagePullSecrets

# 2. 创建registry secret
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=pull-user \
  --docker-password=<password> \
  --docker-email=ops@example.com

# 3. 在Pod/Deployment中引用
spec:
  imagePullSecrets:
    - name: regcred

网络层面的镜像拉取超时,在云环境中多见于跨区域拉取:

# 配置容器运行时镜像代理(containerd)
# /etc/containerd/config.toml
[plugins.'io.containerd.grpc.v1.cri'.registry.mirrors]
  [plugins.'io.containerd.grpc.v1.cri'.registry.mirrors.'docker.io']
    endpoint = ['https://mirror.gcr.io']

# 重启containerd
sudo systemctl restart containerd

节点NotReady:从kubelet到网络的完整排查

节点NotReady意味着kubelet停止向API Server汇报状态,超过node-monitor-grace-period(默认40秒)后节点被标记为NotReady。

# 查看节点条件和事件
kubectl describe node <node-name>
# 关注Conditions部分:
# Ready            Unknown   → kubelet失联
# MemoryPressure   True      → 节点内存紧张
# DiskPressure     True      → 节点磁盘满
# NetworkUnavailable True    → 网络插件问题

# SSH到节点检查kubelet状态
sudo systemctl status kubelet
sudo journalctl -u kubelet --since '10 minutes ago'

# 常见原因1:磁盘满导致kubelet无法写文件
df -h /var/lib/kubelet
df -h /var/lib/containerd
# 清理废弃镜像和容器
crictl rmp --all  # 删除停止的容器
ctr -n k8s.io image rm $(ctr -n k8s.io image ls -q | head -50)

# 常见原因2:kubelet证书过期
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
# 如果已经过期,需要重新签发
sudo kubeadm certs renew all
sudo systemctl restart kubelet

PVC Pending:存储挂载失败的排查路径

PVC一直处于Pending状态,常见原因包括:

# 查看PVC状态和事件
kubectl get pvc -n production
kubectl describe pvc data-pvc -n production

# 原因1:没有可用的PV满足PVC要求
# 检查StorageClass是否有可用PV
kubectl get storageclass
kubectl get pv | grep Released  # Released的PV可回收

# 原因2:StorageClass的provisioner异常
kubectl get pods -n kube-system | grep csi
kubectl logs csi-driver-controller -n kube-system

# 原因3:PVC的accessModes与PV不匹配
# RWO (ReadWriteOnce) → 只能被单节点挂载
# RWX (ReadWriteMany) → NFS/CEPH等共享存储
# 如果PVC请求RWX但只有RWO的PV,会一直Pending

动态供给失败的具体排查:

# 检查CSI驱动日志
kubectl logs -n kube-system csi-rbdplugin-controller-0
# 常见错误:
# 'failed to create volume' → Ceph集群连接问题
# 'pool not found' → StorageClass配置的pool不存在
# 'insufficient quota' → Ceph配额不足

Service无法访问:网络策略和DNS的双面排查

Service不可达的排查需要区分ClusterIP、NodePort和LoadBalancer三个入口层级:

# 1. 集群内DNS解析验证
kubectl run tmp --image=busybox:1.36 --rm -it --restart=Never -- \
  nslookup api-service.production.svc.cluster.local

# 2. ClusterIP直连测试
kubectl run tmp --image=busybox:1.36 --rm -it --restart=Never -- \
  wget -qO- http://api-service.production:8080/health

# 3. Endpoints检查(最容易被忽略)
kubectl get endpoints api-service -n production
# 如果Endpoints为 → selector匹配不到Pod
# 检查selector标签是否与Pod标签一致

# 4. NetworkPolicy阻断
kubectl get networkpolicy -n production
# 检查是否有deny-all策略或未放行特定端口

CoreDNS故障会导致全集群DNS解析失败:

# 检查CoreDNS状态
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns

# CoreDNS配置检查
kubectl get configmap coredns -n kube-system -o yaml
# 常见问题:stubdomain配置错误、forward规则冲突

# 强制重启CoreDNS
kubectl rollout restart deployment coredns -n kube-system

构建系统化的故障排查SOP

生产环境Kubernetes集群的故障排查效率,取决于SOP的系统化程度。核心原则:

1. 现象归类优先于逐条排查——CrashLoopBackOff、NotReady、Pending三类覆盖80%以上的故障
2. Event和Log是黄金信息源——kubectl describe和kubectl logs –previous是前两步必做动作
3. 控制面和数据面分离排查——先确认API Server/etcd/scheduler健康,再排查节点和Pod
4. 变更溯源——90%的故障发生在变更后1小时内,先查最近一次Deployment/ConfigMap变更

将以上排查路径整理为运维Runbook,配合Prometheus告警自动关联诊断脚本,能把平均故障定位时间(MTTI)从30分钟压缩到5分钟以内。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/kubernetes-ji-qun-gu-zhang-pai-cha-shi-zhan-cong/

(0)
小编小编
上一篇 4小时前
下一篇 4小时前

相关推荐

Kubernetes集群故障排查实战:从Pod CrashLoopBackOff到根因定位全流程

Kubernetes故障排查的系统性方法论

Kubernetes集群出问题时,最怕的不是某个Pod挂掉,而是排障过程像无头苍蝇——日志看了一堆,命令跑了一通,却没定位到根因。系统化排障的核心是缩小范围:从集群→节点→Pod→容器→应用,逐层定位。

排查的第一步永远是确认故障的层级。集群级别的问题(API Server不可达、节点NotReady)和Pod级别的问题(CrashLoopBackOff、OOMKilled)的排查路径完全不同。

Pod CrashLoopBackOff:最常见的在线故障

CrashLoopBackOff意味着容器启动后反复崩溃,Kubelet不断重试。排查流程:

# 查看Pod状态和事件
kubectl get pod myapp-7f9b8c6d4-x2k1j -o wide
kubectl describe pod myapp-7f9b8c6d4-x2k1j

# 关键信息在Events部分和Last State中
# Last State: Terminated  Reason: Error  Exit Code: 137  → OOMKilled
# Last State: Terminated  Reason: Error  Exit Code: 1   → 应用异常退出
# Last State: Terminated  Reason: Completed  Exit Code: 0 → 主进程正常退出但非长期运行

不同Exit Code的根因方向:

Exit Code 137 (OOMKilled):容器内存超限。查看资源限制和实际使用:

kubectl describe pod myapp-7f9b8c6d4-x2k1j | grep -A5 "Limits"
# 解决方案:调大resources.limits.memory,或优化应用内存使用
kubectl patch deployment myapp -p '{"spec":{"template":{"spec":{"containers":[{"name":"myapp","resources":{"limits":{"memory":"2Gi"}}}]}}}}'

Exit Code 1:应用自身报错。查看容器日志:

# 当前容器日志
kubectl logs myapp-7f9b8c6d4-x2k1j

# 上一次崩溃的容器日志(关键!)
kubectl logs myapp-7f9b8c6d4-x2k1j --previous

Exit Code 0:命令执行完就退出了。常见于Job类型的镜像被Deployments使用,或entrypoint脚本执行完毕。检查Dockerfile的ENTRYPOINT和CMD是否正确。

Pod一直Pending:调度失败诊断

Pod长时间Pending意味着调度器无法为其找到合适节点:

kubectl get pod myapp-7f9b8c6d4-x2k1j -o jsonpath='{.status.conditions[0].message}'

常见原因及对应方案:

Insufficient cpu/memory:集群资源不足。检查节点实际可用量:

kubectl top nodes
kubectl describe node node1 | grep -A5 "Allocated resources"

node(s) had volume node affinity conflict:PVC绑定的PV有节点亲和性限制,导致Pod只能调度到特定节点但该节点资源已满。这种情况需要检查StorageClass的volumeBindingMode是否为WaitForFirstConsumer:

kubectl get storageclass standard -o yaml | grep volumeBindingMode
# WaitForFirstConsumer: 延迟绑定,Pod调度后再创建PV
# Immediate: 立即绑定,可能导致亲和性冲突

MatchNodeSelector / NodeAffinity:标签选择器过于严格。列出节点标签确认:

kubectl get nodes --show-labels
kubectl get pod myapp-7f9b8c6d4-x2k1j -o yaml | grep -A10 affinity

Service无法访问:网络层排障

Pod正常但Service访问不通,从底层往上排查:

# 1. Pod自身是否健康
kubectl exec -it myapp-7f9b8c6d4-x2k1j -- curl -s http://localhost:8080/healthz

# 2. Service ClusterIP是否可达(从集群内Pod测试)
kubectl run tmp --image=busybox --rm -it --restart=Never --   wget -qO- http://myapp-svc:8080/healthz

# 3. 检查Endpoints是否正常关联
kubectl get endpoints myapp-svc
# 如果ENDPOINTS为none,说明selector匹配不到任何Pod
kubectl describe svc myapp-svc | grep Selector
kubectl get pods -l app=myapp  # 确认Pod标签

iptables/IPVS规则检查

# iptables模式
iptables -t nat -L KUBE-SERVICES | grep myapp-svc

# IPVS模式
ipvsadm -Ln | grep 10.96.0.100  # 替换为Service ClusterIP

DNS解析问题

# 从Pod内测试DNS
kubectl run tmp --image=busybox --rm -it --restart=Never --   nslookup myapp-svc.default.svc.cluster.local

# CoreDNS日志
kubectl logs -n kube-system coredns-6d4b75cb7d-xxxx
# 如果DNS查询超时,检查CoreDNS的ready探针和上游DNS配置

节点NotReady:Kubelet问题诊断

节点状态变为NotReady时,SSH登录该节点排查:

# 检查kubelet状态
systemctl status kubelet
journalctl -u kubelet --since "10 min ago"

# 常见原因1: PLEG超时(Pod Lifecycle Event Generator)
# 日志特征: "PLEG is not healthy"
# 原因: Docker/containerd运行时卡死,或节点I/O负载过高
# 临时解决: 重启容器运行时
systemctl restart containerd

# 常见原因2: 磁盘压力
df -h /var/lib/kubelet
# kubelet默认磁盘使用率>85%触发DiskPressure,停止调度新Pod
# 清理不用的镜像和已退出容器
crictl rmp -a  # 清理已退出容器
crictl rmi -a  # 清理不用的镜像

etcd集群健康检查

etcd是Kubernetes的数据存储,etcd异常会导致整个集群不可用:

# 检查etcd成员健康
ETCDCTL_API=3 etcdctl endpoint health   --cacert=/etc/kubernetes/pki/etcd/ca.crt   --cert=/etc/kubernetes/pki/etcd/server.crt   --key=/etc/kubernetes/pki/etcd/server.key

# 检查etcd性能指标
ETCDCTL_API=3 etcdctl endpoint status -w table   --cacert=/etc/kubernetes/pki/etcd/ca.crt   --cert=/etc/kubernetes/pki/etcd/server.crt   --key=/etc/kubernetes/pki/etcd/server.key

# 重点关注:DB SIZE是否超过2GB警告线,是否有member处于learner状态

etcd慢查询告警(committed index too far behind)通常意味着某个成员磁盘I/O慢。SSD是etcd节点的硬性要求,机械盘环境必须迁移。

排障工具箱与快捷命令

# 快速查看集群整体状态
kubectl get --raw='/readyz?verbose'
kubectl get events --sort-by='.metadata.creationTimestamp' -A

# 资源配额和使用率一览
kubectl resource-capacity  # 需安装resource-capacity插件

# 网络连通性快速测试
kubectl run netshoot --image=nicolaka/netshoot --rm -it --restart=Never -- bash

# 一行命令收集所有Pod的最近错误日志
kubectl get pods -A --field-selector=status.phase=Failed -o name |   xargs -I{} kubectl logs {} --tail=50

Kubernetes排障的关键是层级定位:先确认是控制面还是数据面的问题,再逐层下钻。盲目进容器看日志只会浪费时间。建立从集群→节点→Pod→容器→应用的排查顺序,配合describe、logs、events三板斧,绝大多数故障都能在15分钟内定位到根因。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/kubernetes-ji-qun-gu-zhang-pai-cha-shi-zhan-cong/

(0)
小编小编
上一篇 5小时前
下一篇 5小时前

相关推荐