Kubernetes 故障排查实战:从诊断到修复
Kubernetes 故障排查方法论
Kubernetes 集群故障排查不是"瞎猜",而是一套分层递进的诊断体系。从应用层到基础设施层,逐层排查才能高效定位根因。
分层排查模型
| 层级 | 范围 | 关键命令 |
|---|---|---|
| L1 应用层 | Pod 状态、容器日志 | kubectl logs, kubectl describe pod |
| L2 服务层 | Service、Endpoint、DNS | kubectl get endpoints, nslookup |
| L3 调度层 | Deployment、ReplicaSet、HPA | kubectl rollout status, kubectl get hpa |
| L4 存储层 | PVC、PV、StorageClass | kubectl describe pvc, kubectl get pv |
| L5 节点层 | Node 状态、资源压力 | kubectl describe node, kubectl top nodes |
| L6 基础设施层 | 网络插件、API Server、etcd | systemctl status kubelet, crictl ps |
诊断黄金流程
# 第一步:确认集群整体健康
kubectl get nodes
kubectl get componentstatuses
# 第二步:定位问题命名空间
kubectl get all -n <namespace>
# 第三步:聚焦异常资源
kubectl describe <resource> <name> -n <namespace>
# 第四步:查看事件和日志
kubectl get events -n <namespace> --sort-by='.lastTimestamp'
kubectl logs <pod-name> -n <namespace> --previous
# 第五步:进入容器排查
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh
Pod 故障排查
Pod 是 Kubernetes 最小调度单元,也是故障最密集的资源。掌握每种异常状态的诊断方法至关重要。
CrashLoopBackOff
容器启动后立即退出,Kubelet 反复重启导致循环崩溃。
# 查看当前日志
kubectl logs <pod-name> -n <namespace>
# 查看上一次崩溃的日志(关键!)
kubectl logs <pod-name> -n <namespace> --previous
# 查看容器退出码
kubectl describe pod <pod-name> -n <namespace> | grep -A5 "Last State"
常见原因与修复:
| 退出码 | 含义 | 修复方案 |
|---|---|---|
| 0 | 正常退出但未设置长运行模式 | 检查入口命令,确保前台运行 |
| 1 | 应用错误(未捕获异常) | 检查应用日志,修复代码逻辑 |
| 137 | OOMKilled(被 SIGKILL) | 增大 resources.limits.memory |
| 139 | Segmentation Fault | 检查依赖库兼容性 |
| 143 | SIGTERM 优雅终止 | 检查 preStop hook 或终止信号处理 |
# 修复示例:添加启动探针避免慢启动被杀
spec:
containers:
- name: app
image: my-app:v1
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 15
failureThreshold: 3
ImagePullBackOff
镜像拉取失败,通常由镜像地址、认证或网络问题引起。
# 查看详细错误信息
kubectl describe pod <pod-name> | grep -A10 "Events"
# 常见错误信息
# ErrImagePull: registry.k8s.io/pause:3.9 - 连接超时
# ImagePullBackOff: unauthorized: authentication required
排查清单:
- 镜像地址错误:检查 image 字段拼写,确认 tag 存在
- 私有仓库认证:确认 imagePullSecrets 已配置
- 网络不通:节点无法访问镜像仓库(防火墙/代理)
- 镜像不存在:tag 拼写错误或镜像未推送
# 修复:配置私有仓库认证
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: app
image: harbor.company.com/project/app:v1.2.3
# 创建 imagePullSecret
kubectl create secret docker-registry registry-credentials \
--docker-server=harbor.company.com \
--docker-username=admin \
--docker-password=Harbor12345 \
-n <namespace>
OOMKilled
容器内存超限被内核 OOM Killer 杀死。
# 确认 OOMKilled
kubectl describe pod <pod-name> | grep -A5 "Last State"
# Last State: Terminated Reason: OOMKilled Exit Code: 137
# 查看容器内存使用趋势
kubectl top pod <pod-name> -n <namespace>
# 查看节点 cgroup 日志
dmesg | grep -i oom
修复策略:
# 策略一:调大内存限制
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "1Gi" # 从 512Mi 增大到 1Gi
cpu: "500m"
# 策略二:优化应用内存使用(JVM 示例)
env:
- name: JAVA_OPTS
value: "-Xms256m -Xmx512m -XX:+UseG1GC"
# 策略三:设置 QoS 为 Guaranteed(requests == limits)
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "500m"
Pod Pending
Pod 无法被调度到任何节点,通常因为资源不足或约束不满足。
# 查看调度失败原因
kubectl describe pod <pod-name> | grep -A20 "Events"
# 常见信息:
# 0/3 nodes are available: 3 Insufficient cpu.
# 0/3 nodes are available: 1 node(s) had taints that the pod didn't tolerate.
常见原因与解决方案:
| 原因 | 事件信息 | 解决方案 |
|---|---|---|
| CPU 不足 | Insufficient cpu | 降低 requests 或扩容节点 |
| 内存不足 | Insufficient memory | 降低 requests 或扩容节点 |
| PVC 未绑定 | pod has unbound immediate PersistentVolumeClaims | 先排查 PVC 状态 |
| 节点选择器不匹配 | node(s) didn't match node selector | 检查 nodeSelector / nodeAffinity |
| 污点容忍 | node(s) had taints that the pod didn't tolerate | 添加 tolerations |
| PodDisruptionBudget | Cannot evict pod | 检查 PDB 配置 |
Service 与网络问题排查
DNS 解析失败
# 部署 DNS 调试 Pod
kubectl run dnsutils --image=registry.k8s.io/e2e-test-images/agnhost:2.39 \
--restart=Never -- sleep infinity
# 测试集群内 DNS 解析
kubectl exec -it dnsutils -- nslookup kubernetes.default.svc.cluster.local
kubectl exec -it dnsutils -- nslookup <service-name>.<namespace>.svc.cluster.local
# 检查 CoreDNS 状态
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns
CoreDNS 常见问题:
- CoreDNS Pod 未运行:检查 kube-system 中 coredns 状态
- ConfigMap 配置错误:检查
corednsConfigMap 的 plugins 配置 - ndots 导致超时:
/etc/resolv.conf中 ndots=5 导致多次查询
# 优化 Pod DNS 配置
spec:
dnsConfig:
options:
- name: ndots
value: "2"
- name: single-request-reopen
- name: timeout
value: "3"
dnsPolicy: ClusterFirst
Endpoint 为空
Service 的 Endpoint 列表为空,流量无法路由到任何 Pod。
# 检查 Endpoint
kubectl get endpoints <service-name> -n <namespace>
# 常见原因排查
# 1. label selector 不匹配
kubectl get pods -n <namespace> --show-labels
kubectl get svc <service-name> -n <namespace> -o yaml | grep -A5 selector
# 2. Pod 未 Ready
kubectl get pods -n <namespace> # 查看 READY 列是否 1/1
# 3. 端口不匹配
kubectl describe svc <service-name> -n <namespace>
# 确保 Service selector 与 Pod labels 匹配
# Service
spec:
selector:
app: my-app # 必须与 Pod label 一致
ports:
- port: 80
targetPort: 8080 # 必须与容器端口一致
# Pod
metadata:
labels:
app: my-app # 与 Service selector 对应
spec:
containers:
- name: app
ports:
- containerPort: 8080
连接被拒绝
# 从调试 Pod 测试连通性
kubectl run debug --image=busybox --rm -it --restart=Never -- sh
# 在调试 Pod 内
wget -qO- http://<service-name>.<namespace>:<port>/healthz
curl -v telnet://<service-name>.<namespace>:<port>
# 检查网络策略是否阻断
kubectl get networkpolicy -n <namespace>
# 检查 kube-proxy 模式和 iptables/ipvs 规则
kubectl logs -n kube-system -l k8s-app=kube-proxy
iptables-save | grep <service-cluster-ip>
Deployment 问题排查
回滚操作
# 查看 rollout 历史
kubectl rollout history deployment/<name> -n <namespace>
# 回滚到上一版本
kubectl rollout undo deployment/<name> -n <namespace>
# 回滚到指定版本
kubectl rollout undo deployment/<name> -n <namespace> --to-revision=2
# 暂停和恢复 rollout(用于金丝雀发布)
kubectl rollout pause deployment/<name> -n <namespace>
kubectl set image deployment/<name> app=my-app:v2 -n <namespace>
kubectl rollout resume deployment/<name> -n <namespace>
扩缩容失败
# 手动扩容
kubectl scale deployment/<name> --replicas=5 -n <namespace>
# 检查 HPA 状态
kubectl get hpa -n <namespace>
kubectl describe hpa <hpa-name> -n <namespace>
# HPA 常见失败原因
# 1. metrics-server 未部署
kubectl get pods -n kube-system -l k8s-app=metrics-server
# 2. 资源 requests 未设置(HPA 无法计算利用率)
kubectl describe pod <pod-name> | grep -A5 "Requests"
# 3. 扩容达到最大副本数限制
kubectl get hpa <hpa-name> -o yaml | grep maxReplicas
# 正确的 HPA 配置
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
PVC/PV 存储问题排查
PVC Pending
# 查看 PVC 状态和事件
kubectl describe pvc <pvc-name> -n <namespace>
# 常见原因
# 1. 没有匹配的 PV(静态 provisioning)
kubectl get pv | grep <storage-class>
# 2. StorageClass 不存在(动态 provisioning)
kubectl get storageclass
# 3. 动态 provisioner 未运行
kubectl get pods -n <namespace> | grep provisioner
PV Lost
# 查看PV状态
kubectl get pv
# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM
# pv-data 50Gi RWO Delete Lost default/pvc-data
# 修复:确认后端存储是否恢复
# 如果存储已恢复,手动清除 Lost 状态
kubectl patch pv <pv-name> -p '{"spec":{"claimRef": null}}'
# 重新绑定 PVC
AccessMode 不匹配
# PVC 请求 ReadWriteMany 但 PV 只支持 ReadWriteOnce
# PVC
spec:
accessModes:
- ReadWriteMany # 需要 RWX
resources:
requests:
storage: 10Gi
storageClassName: nfs
# 确认 StorageClass 支持的 accessMode
kubectl describe storageclass nfs
| AccessMode | 缩写 | 说明 | 典型后端 |
|---|---|---|---|
| ReadWriteOnce | RWO | 单节点读写 | AWS EBS, Ceph RBD |
| ReadOnlyMany | ROX | 多节点只读 | NFS |
| ReadWriteMany | RWX | 多节点读写 | NFS, CephFS |
| ReadWriteOncePod | RWOP | 单 Pod 独占读写 | CSI 支持 |
Node 节点问题排查
NotReady 状态
# 查看节点状态
kubectl get nodes
kubectl describe node <node-name>
# 检查 kubelet 服务
systemctl status kubelet
journalctl -u kubelet -n 100 --no-pager
# 检查容器运行时
crictl ps
systemctl status containerd
# 常见原因
# 1. kubelet 证书过期
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
# 2. 磁盘空间不足
df -h /var/lib/kubelet
# 3. 网络插件异常
kubectl get pods -n kube-system -o wide | grep <node-name>
资源压力条件
# 查看节点条件
kubectl describe node <node-name> | grep -A20 "Conditions"
# DiskPressure:节点磁盘使用率超过阈值
# MemoryPressure:节点可用内存不足
# PIDPressure:进程数过多
# 调整 kubelet 驱逐阈值(不推荐生产调低)
kind: KubeletConfiguration
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
imagefs.available: "15%"
evictionSoft:
memory.available: "200Mi"
nodefs.available: "20%"
evictionSoftGracePeriod:
memory.available: "1m30s"
nodefs.available: "2m"
kubectl 调试命令速查表
资源查看
| 命令 | 用途 |
|---|---|
kubectl get all -n <ns> |
查看命名空间所有资源 |
kubectl get pods -o wide |
查看 Pod 含节点信息 |
kubectl get pods --show-labels |
查看 Pod 标签 |
kubectl top pods --sort-by=memory |
按内存排序 |
kubectl api-resources |
查看所有 API 资源类型 |
调试排障
| 命令 | 用途 |
|---|---|
kubectl logs <pod> -c <container> --previous |
上一次容器日志 |
kubectl logs <pod> --since=1h |
最近1小时日志 |
kubectl logs <pod> -f --tail=100 |
实时跟踪最近100行 |
kubectl exec -it <pod> -- /bin/sh |
进入容器终端 |
kubectl debug <pod> -it --image=busybox |
临时调试容器 |
kubectl port-forward svc/<svc> 8080:80 |
端口转发本地调试 |
集群管理
| 命令 | 用途 |
|---|---|
kubectl cordon <node> |
标记节点不可调度 |
kubectl drain <node> --ignore-daemonsets |
驱逐节点所有 Pod |
kubectl uncordon <node> |
恢复节点可调度 |
kubectl taint nodes <node> key=value:NoSchedule |
添加污点 |
kubectl label node <node> key=value |
添加标签 |
日志与事件分析
集中日志收集
# 使用 kubectl 日志聚合(多容器 Pod)
kubectl logs <pod-name> --all-containers=true
# 使用 label selector 批量查看
kubectl logs -l app=my-app -n <namespace> --since=5m
# 导出日志到文件分析
kubectl logs <pod-name> -n <namespace> > pod-debug.log
Event 事件分析
# 查看命名空间所有事件(按时间排序)
kubectl get events -n <namespace> --sort-by='.lastTimestamp'
# 按类型过滤
kubectl get events -n <namespace> --field-selector type=Warning
# 持续监控事件
kubectl get events -n <namespace> --watch
# 查看特定资源的事件
kubectl events --for pod/<pod-name> -n <namespace>
日志级别与格式
# API Server 审计日志
cat /var/log/kubernetes/audit.log | jq '. | select(.responseStatus.code >= 400)'
# kubelet 日志
journalctl -u kubelet -f
# etcd 日志
kubectl logs -n kube-system etcd-<node-name> --since=10m
资源限制与请求最佳实践
QoS 等级
| QoS 等级 | 条件 | 驱逐优先级 | 适用场景 |
|---|---|---|---|
| Guaranteed | requests == limits(CPU+内存) | 最低(最后被杀) | 核心服务 |
| Burstable | requests < limits | 中等 | 一般服务 |
| BestEffort | 未设置 requests/limits | 最高(最先被杀) | 批处理任务 |
# 生产环境推荐配置模板
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# 使用 LimitRange 设置命名空间默认值
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: production
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
max:
cpu: "2"
memory: "4Gi"
常见资源问题
# 查看节点资源使用
kubectl top nodes
kubectl describe node <node-name> | grep -A10 "Allocated resources"
# 查看命名空间资源配额
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota -n <namespace>
NetworkPolicy 调试
策略阻断排查
# 查看命名空间所有 NetworkPolicy
kubectl get networkpolicy -n <namespace>
# 查看策略详情
kubectl describe networkpolicy <policy-name> -n <namespace>
# 常见 NetworkPolicy 配置错误
# 错误:忘记允许 DNS 流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
网络连通性测试
# 部署网络调试工具
kubectl run netshoot --image=nicolaka/netshoot --rm -it --restart=Never
# 在调试 Pod 中测试
# 同命名空间
curl http://<service-name>:<port>/healthz
# 跨命名空间
curl http://<service-name>.<namespace>.svc.cluster.local:<port>/healthz
# 测试外部连通性
curl -I https://external-api.example.com
Helm Chart 常见问题
Release 安装失败
# 查看 Release 状态
helm status <release-name> -n <namespace>
helm history <release-name> -n <namespace>
# 查看渲染后的 YAML(不安装)
helm template <chart> --debug
# 查看失败 Release 的值
helm get values <release-name> -n <namespace>
# 回滚 Helm Release
helm rollback <release-name> <revision> -n <namespace>
常见 Helm 错误
| 错误 | 原因 | 修复 |
|---|---|---|
rendered manifests contain a resource that already exists |
资源冲突 | 使用 --replace 或清理旧资源 |
Release "xxx" has not been installed yet |
Release 不存在 | 检查 namespace 和 release name |
timed out waiting for the condition |
Pod 未 Ready | 检查 --timeout 值和 Pod 状态 |
YAML parse error |
values.yaml 语法错误 | 使用 /encode/yaml 验证 |
# 清理失败的 Release
helm uninstall <release-name> -n <namespace>
# 强制安装(开发环境)
helm install <release-name> <chart> --replace -n <namespace>
Prometheus 监控告警
关键告警规则
groups:
- name: kubernetes-alerts
rules:
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[5m]) > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping"
- alert: NodeNotReady
expr: kube_node_status_condition{condition="Ready",status="unknown"} == 1
for: 5m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.node }} is not ready"
- alert: PVCAlmostFull
expr: kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} is >85% full"
- alert: DeploymentReplicasMismatch
expr: kube_deployment_spec_replicas != kube_deployment_status_available_replicas
for: 10m
labels:
severity: warning
常用 PromQL 查询
# Pod 内存使用率
container_memory_working_set_bytes / container_spec_memory_limit_bytes * 100
# 节点 CPU 利用率
rate(node_cpu_seconds_total{mode!="idle"}[5m]) * 100
# 请求错误率
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100
# PVC 使用率
kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes
应急响应手册
P0:集群不可用
# 1. 检查 API Server
kubectl get componentstatuses
curl -k https://<api-server>:6443/healthz
# 2. 检查 etcd
kubectl logs -n kube-system etcd-<node> --tail=50
ETCDCTL_API=3 etcdctl endpoint health --cluster
# 3. 检查 kubelet
systemctl status kubelet
journalctl -u kubelet --since "10 minutes ago"
# 4. 紧急恢复:重启控制面组件
systemctl restart kubelet
systemctl restart containerd
P1:服务全面不可用
# 1. 快速回滚
kubectl rollout undo deployment/<name> -n <namespace>
# 2. 紧急扩容
kubectl scale deployment/<name> --replicas=10 -n <namespace>
# 3. 节点紧急维护
kubectl cordon <node>
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
P2:存储故障
# 1. 检查 PV 状态
kubectl get pv | grep -v Bound
# 2. 检查 CSI Driver
kubectl get pods -n <csi-namespace>
# 3. 紧急处理:强制删除卡住的 Pod
kubectl delete pod <pod-name> --force --grace-period=0 -n <namespace>
FAQ
Q: Pod 一直处于 ContainerCreating 状态怎么办?
A: 通常是镜像拉取慢或 StorageClass 挂载失败。使用 kubectl describe pod 查看 Events,重点关注镜像拉取和卷挂载事件。
Q: 如何查看已删除 Pod 的日志?
A: 如果配置了日志聚合(EFK/PLG),可通过日志平台查询。否则已删除 Pod 的日志无法恢复,建议生产环境必须部署日志收集方案。
Q: kubectl 命令超时怎么排查?
A: 通常是 API Server 负载过高或网络问题。检查 ~/.kube/config 中 server 地址,使用 kubectl get --request-timeout=5s nodes 测试连通性。
Q: 如何快速定位哪个节点资源不足?
A: 使用 kubectl top nodes 查看实时资源,或 kubectl describe node | grep -A5 "Allocated resources" 查看已分配量。
Q: Service ClusterIP 无法访问怎么排查?
A: 依次检查:① kube-proxy 是否正常运行 ② iptables/ipvs 规则是否正确 ③ Endpoint 是否为空 ④ NetworkPolicy 是否阻断 ⑤ CNI 插件状态。
Q: 如何防止 Pod 被意外驱逐?
A: 设置 PodDisruptionBudget,为关键服务配置 PDB 保证最小可用副本数。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: my-app
Q: Helm upgrade 失败但 Release 状态卡住怎么办?
A: 使用 helm rollback 回滚,或 helm history 查看历史版本。如果 Release 锁定,可使用 helm rollback <release> <last-good-revision> 恢复。
本站提供浏览器本地工具,免注册即可试用 →