Kubernetes 集群故障排查实战:从 Pod CrashLoopBackOff 到节点驱逐的完整诊断链路

Pod CrashLoopBackOff 诊断与修复

CrashLoopBackOff 是 Kubernetes 运维中最常见也最让人头疼的状态之一。Pod 启动后容器进程退出,kubelet 反复尝试重启,每次重启间隔指数退避。定位这个问题的核心是看容器的退出码和上一个容器的日志。

# 查看 Pod 状态和重启次数
kubectl get pods -n production -o wide

# 查看事件,定位重启原因
kubectl describe pod <pod-name> -n production

# 关键字段:Last State 里的 Exit Code
# Exit Code 0: 进程正常退出(通常是应用逻辑问题)
# Exit Code 1: 应用未捕获异常
# Exit Code 137: OOMKilled(内存超限被杀)
# Exit Code 139: Segfault(段错误)
# Exit Code 143: SIGTERM 正常终止

查看上一个崩溃容器的日志:

# --previous 参数查看上次容器的 stdout/stderr
kubectl logs <pod-name> -n production --previous

# 如果日志滚动太快,重定向到文件
kubectl logs <pod-name> -n production --previous > /tmp/crash.log 2>&1

OOMKilled 是 CrashLoopBackOff 的高频原因。处理方式:

# 查看当前资源限制
kubectl get pod <pod-name> -n production -o jsonpath='{.spec.containers[*].resources}'

# 调整资源限制
resources:
  requests:
    memory: "512Mi"
    cpu: "250m"
  limits:
    memory: "1Gi"      # 原来可能是 512Mi,适当调大
    cpu: "500m"

# 查看 cgroup 层面的内存使用
kubectl exec <pod-name> -- cat /sys/fs/cgroup/memory/memory.usage_in_bytes

调整 limits 不是唯一解法。内存泄漏导致的 OOM 需要从应用层面修复。可以用 pprof 抓取堆内存快照进行分析。

ImagePullBackOff 与镜像拉取问题

私有镜像仓库的认证失败是 ImagePullBackOff 最常见的原因:

# 创建 docker-registry 类型的 Secret
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=robot \
  --docker-password=<token> \
  -n production

# 在 Pod 的 imagePullSecrets 中引用
spec:
  imagePullSecrets:
    - name: regcred
  containers:
    - image: registry.example.com/app:v2.1.0

网络层面的排查路径:

# 在节点上手动拉取测试
docker pull registry.example.com/app:v2.1.0

# 检查 DNS 解析
kubectl run dns-test --image=busybox --rm -it -- nslookup registry.example.com

# 检查节点到仓库的网络连通性
kubectl run net-test --image=busybox --rm -it -- wget -O- https://registry.example.com/v2/

Service 与 Endpoints 连通性问题

Service 能找到 Pod 依赖于 Endpoints 的正确映射。Service selector 和 Pod label 不匹配是连通性问题的头号元凶:

# 检查 Endpoints 是否有后端 Pod
kubectl get endpoints <svc-name> -n production

# 空 Endpoints 说明 selector 没匹配到 Pod
# 对比 Service selector 和 Pod labels
kubectl get svc <svc-name> -n production -o yaml | grep -A5 selector
kubectl get pods -n production --show-labels

# 常见错误:selector 里用 camelCase,label 里用 kebab-case
# selector: { app: myApp }  vs  labels: { app: my-app }

如果 Endpoints 正常但访问不通,排查 kube-proxy:

# 查看 kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy

# 检查 iptables 规则(kube-proxy iptables 模式)
iptables -t nat -L KUBE-SERVICES | grep <svc-cluster-ip>

# 如果使用 IPVS 模式
ipvsadm -Ln | grep <svc-cluster-ip>

节点 NotReady 与驱逐处理

节点变为 NotReady 通常是因为 kubelet 与 API Server 的通信中断,或节点资源耗尽导致 kubelet 无法正常上报状态:

# 查看节点状况
kubectl describe node <node-name>

# 关注 Conditions 部分
# Ready=False: kubelet 失联
# MemoryPressure=True: 节点内存不足
# DiskPressure=True: 节点磁盘不足
# PIDPressure=True: 进程数过多

# 查看 kubelet 日志
journalctl -u kubelet --since "10 minutes ago" -f

# 查看节点资源使用
kubectl top node <node-name>

节点 NotReady 超过 pod-eviction-timeout(默认 5 分钟)后,Pod 会被标记为驱逐状态。但驱逐是异步的,如果节点只是短暂失联,Pod 可能会出现”双活”问题(旧节点上 Pod 还在运行,新节点上已调度了新 Pod)。处理方法:

# 确认节点确实无法恢复后,手动标记不可调度并驱逐
kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

# 如果节点已经彻底失联,drain 会卡住,加 --force 并跳过等待
kubectl drain <node-name> --ignore-daemonsets --force --grace-period=0

核心组件故障处理

etcd 是 Kubernetes 的核心存储,etcd 不可用意味着整个集群瘫痪:

# 检查 etcd 健康状态
kubectl get cs
# 或直接访问 etcd 端点
ETCDCTL_API=3 etcdctl endpoint health \
  --endpoints=https://127.0.0.1:2379 \
  --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 --write-out=table \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 清理历史修订版本释放空间
ETCDCTL_API=3 etcdctl compact $(etcdctl endpoint status --write-out=json | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['Status']['header']['revision'])")
ETCDCTL_API=3 etcdctl defrag

可观测性建设

故障排查的效率取决于可观测性建设的完善程度。推荐最小化可观测性栈:

# Prometheus + Grafana + Alertmanager
# 关键告警规则
- alert: PodCrashLooping
  expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
  for: 5m

- alert: NodeNotReady
  expr: kube_node_status_condition{condition="Ready",status="unknown"} == 1
  for: 5m

- alert: HighMemoryUsage
  expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.9
  for: 5m

- alert: EtcdInsufficientMembers
  expr: count(up{job="etcd"} == 1) < floor(count(up{job="etcd"}) / 2) + 1
  for: 3m

告警渠道配置 PagerDuty 或企业微信 Webhook,确保值班人员第一时间收到通知。故障排查能力是日积月累的结果,每处理一次故障都建议复盘并补充到运维知识库中。

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

(0)
小编小编
上一篇 2026年7月29日
下一篇 2026年7月29日

相关推荐

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)
小编小编
上一篇 2026年7月28日
下一篇 2026年7月28日

相关推荐

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)
小编小编
上一篇 2026年7月28日
下一篇 2026年7月28日

相关推荐