故障排查
故障排查
故障排查方法论
排查流程
1. 问题识别
↓
2. 收集信息(日志、事件、指标)
↓
3. 分析根因
↓
4. 制定方案
↓
5. 实施修复
↓
6. 验证结果
↓
7. 总结复盘
信息收集工具箱
# 基础信息
kubectl get pods -o wide
kubectl get nodes
kubectl get events --sort-by='.lastTimestamp'
# 详细信息
kubectl describe pod <pod-name>
kubectl describe node <node-name>
# 日志
kubectl logs <pod-name> --tail=100
kubectl logs <pod-name> --previous # 上一个容器
kubectl logs <pod-name> -c <container> # 多容器
# 资源使用
kubectl top nodes
kubectl top pods
# 进入容器调试
kubectl exec -it <pod-name> -- /bin/sh
Pod 问题排查
1. Pod 一直处于 Pending
症状:Pod 创建后一直是 Pending 状态
排查步骤:
# 查看 Pod 详情
kubectl describe pod <pod-name>
# 常见原因及解决方案
原因 1:资源不足
# 查看事件
Events:
Warning FailedScheduling insufficient cpu
# 解决方案
# 1. 降低资源请求
kubectl edit deployment <name>
# 修改 resources.requests
# 2. 扩容节点
kubectl get nodes
# 添加新节点或扩容现有节点
# 3. 清理不需要的 Pod
kubectl delete pod <unused-pod>
原因 2:镜像拉取失败
# 查看事件
Events:
Warning Failed Failed to pull image "myapp:latest": rpc error
# 检查镜像是否存在
docker pull myapp:latest
# 检查 imagePullSecrets
kubectl get pod <pod-name> -o yaml | grep imagePullSecrets
# 解决方案
# 1. 修正镜像名称
kubectl set image deployment/myapp myapp=correct-image:tag
# 2. 创建镜像拉取凭证
kubectl create secret docker-registry regcred \
--docker-server=<registry> \
--docker-username=<username> \
--docker-password=<password>
# 3. 在 Pod 中引用
kubectl patch deployment myapp -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}}}'
原因 3:节点选择器不匹配
# 查看 Pod 的节点选择器
kubectl get pod <pod-name> -o yaml | grep -A 5 nodeSelector
# 查看节点标签
kubectl get nodes --show-labels
# 解决方案:移除或修改 nodeSelector
kubectl edit deployment <name>
原因 4:PVC 未绑定
# 查看 PVC 状态
kubectl get pvc
NAME STATUS VOLUME CAPACITY
mypvc Pending
# 检查 StorageClass
kubectl get storageclass
# 解决方案
# 1. 创建 PV(如果使用静态供应)
# 2. 检查 StorageClass 是否正确
# 3. 查看 PVC 事件
kubectl describe pvc <pvc-name>
2. Pod 频繁重启(CrashLoopBackOff)
症状:Pod 反复重启,状态显示 CrashLoopBackOff
排查步骤:
# 查看重启次数
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp 0/1 CrashLoopBackOff 5 3m
# 查看日志
kubectl logs <pod-name> --previous
# 查看事件
kubectl describe pod <pod-name>
原因 1:应用启动失败
# 查看应用日志
kubectl logs <pod-name> --previous
# 常见错误
# - 配置文件错误
# - 依赖服务不可用
# - 端口冲突
# - 权限问题
# 解决方案:修复应用代码或配置
原因 2:健康检查失败
# 查看探针配置
kubectl get pod <pod-name> -o yaml | grep -A 10 livenessProbe
# 解决方案
# 1. 调整探针时间
kubectl edit deployment <name>
# 增加 initialDelaySeconds 和 periodSeconds
# 2. 修复健康检查端点
# 确保 /health 或 /readiness 端点正常
# 3. 暂时禁用探针(调试用)
kubectl patch deployment <name> --type=json \
-p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'
原因 3:OOMKilled(内存不足)
# 查看终止原因
kubectl describe pod <pod-name> | grep -A 5 "Last State"
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
# 查看内存使用
kubectl top pod <pod-name>
# 解决方案
# 1. 增加内存限制
kubectl set resources deployment <name> \
--limits=memory=2Gi \
--requests=memory=1Gi
# 2. 优化应用内存使用
# - 检查内存泄漏
# - 优化缓存策略
# - 减少并发处理
原因 4:应用崩溃
# 查看退出码
kubectl describe pod <pod-name> | grep "Exit Code"
# 常见退出码
# 0: 正常退出
# 1: 应用错误
# 137: SIGKILL (OOMKilled)
# 143: SIGTERM (正常终止)
# 调试方法
# 1. 修改命令为 sleep,保持容器运行
kubectl run debug --image=myapp --command -- sleep 3600
# 2. 进入容器手动启动应用
kubectl exec -it debug -- /bin/sh
# 手动运行启动命令,观察错误
3. Pod 处于 ImagePullBackOff
症状:无法拉取镜像
# 完整排查流程
kubectl describe pod <pod-name>
Events:
Warning Failed Failed to pull image "myapp:v1.0": rpc error: code = Unknown
Warning BackOff Back-off pulling image "myapp:v1.0"
# 1. 检查镜像名称是否正确
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].image}'
# 2. 手动拉取镜像测试
docker pull myapp:v1.0
# 3. 检查私有仓库凭证
kubectl get secret
kubectl describe secret <pull-secret>
# 4. 检查网络连接
kubectl run test --image=busybox -it --rm -- wget -O- <registry-url>
# 解决方案
# 方案 1: 修正镜像名称
kubectl set image deployment/myapp myapp=correct-repo/myapp:v1.0
# 方案 2: 使用公共镜像(临时)
kubectl set image deployment/myapp myapp=nginx:latest
# 方案 3: 配置镜像加速
# 在 Docker daemon.json 中配置镜像加速器
4. Pod 状态正常但服务不可访问
排查步骤:
# 1. 检查 Pod 是否 Ready
kubectl get pods
NAME READY STATUS RESTARTS
myapp 1/1 Running 0 # ✓ Pod 正常
# 2. 检查容器端口
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].ports}'
# 3. 测试 Pod IP 直接访问
kubectl get pod <pod-name> -o wide
NAME IP NODE
myapp 10.244.0.5 node-1
# 进入测试 Pod
kubectl run test --image=busybox -it --rm -- sh
wget -O- http://10.244.0.5:8080
# 4. 检查 Service
kubectl get svc <service-name>
kubectl describe svc <service-name>
# 5. 检查 Endpoints
kubectl get endpoints <service-name>
# 如果 Endpoints 为空,检查标签匹配
kubectl get pod --show-labels
kubectl get svc <service-name> -o yaml | grep selector
# 6. 测试 Service
kubectl run test --image=busybox -it --rm -- sh
wget -O- http://<service-name>:80
# 7. 检查 Ingress
kubectl get ingress
kubectl describe ingress <ingress-name>
网络问题排查
DNS 解析问题
# 测试 DNS
kubectl run test --image=busybox -it --rm -- sh
nslookup kubernetes.default
nslookup <service-name>
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
# 检查 DNS 配置
kubectl get configmap coredns -n kube-system -o yaml
# 测试解析
kubectl run dnsutils --image=tutum/dnsutils -it --rm -- sh
dig @10.96.0.10 kubernetes.default.svc.cluster.local
网络连接问题
# 测试 Pod 间网络
kubectl exec <pod-a> -- ping <pod-b-ip>
# 测试跨节点网络
kubectl exec <pod-a> -- ping <pod-on-other-node-ip>
# 检查 NetworkPolicy
kubectl get networkpolicy -A
kubectl describe networkpolicy <name>
# 检查 CNI 插件
kubectl get pods -n kube-system | grep -E 'calico|flannel|weave'
# 查看 iptables 规则(节点上)
sudo iptables-save | grep <service-name>
Service 访问不通
# 完整排查流程
# 1. Service 是否存在
kubectl get svc <service-name>
# 2. Endpoints 是否正常
kubectl get endpoints <service-name>
# 应该有 Pod IP 列表
# 3. Pod 标签是否匹配
kubectl get pods -l <service-selector>
# 4. 测试 ClusterIP
kubectl run test --image=busybox -it --rm -- sh
wget -O- http://<cluster-ip>:<port>
# 5. 检查端口映射
kubectl get svc <service-name> -o yaml
# 查看 port 和 targetPort
# 6. 查看 kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy
性能问题排查
CPU 问题
# 查看 CPU 使用
kubectl top nodes
kubectl top pods --sort-by=cpu
# 查看是否被限流
kubectl describe pod <pod-name> | grep -i cpu
# 检查 CPU 限制
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].resources}'
# 进入容器查看进程
kubectl exec <pod-name> -- top
kubectl exec <pod-name> -- ps aux | sort -rn -k 3 | head -5
# 解决方案
# 1. 增加 CPU 限制
kubectl set resources deployment <name> --limits=cpu=2
# 2. 启用 HPA
kubectl autoscale deployment <name> --cpu-percent=80 --min=2 --max=10
# 3. 优化应用代码
内存问题
# 查看内存使用
kubectl top pods --sort-by=memory
# 查看内存限制
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].resources.limits.memory}'
# 检查是否 OOM
kubectl describe pod <pod-name> | grep OOM
# 进入容器查看内存
kubectl exec <pod-name> -- free -h
kubectl exec <pod-name> -- cat /proc/meminfo
# 分析内存使用(Java 应用)
kubectl exec <pod-name> -- jmap -heap 1
# 解决方案
# 1. 增加内存限制
kubectl set resources deployment <name> --limits=memory=2Gi
# 2. 调整 JVM 参数(Java)
# -Xmx 设置为 limits 的 75%
# 3. 启用内存分析
# 添加 HeapDump 选项
磁盘问题
# 查看节点磁盘使用
kubectl get nodes
kubectl describe node <node-name> | grep -A 5 "Allocated resources"
# 查看 PVC 使用
kubectl get pvc
kubectl exec <pod-name> -- df -h
# 清理镜像(节点上)
ssh node-1
docker system prune -af
# 清理日志(节点上)
find /var/log/containers -name "*.log" -mtime +7 -delete
# 扩容 PVC(如果支持)
kubectl edit pvc <pvc-name>
# 修改 storage 大小
集群级问题
节点 NotReady
# 查看节点状态
kubectl get nodes
NAME STATUS ROLES AGE
node-1 NotReady worker 10d
# 查看节点详情
kubectl describe node node-1
# 常见原因
# 1. kubelet 未运行
ssh node-1
systemctl status kubelet
systemctl restart kubelet
# 2. 资源耗尽
df -h
free -h
# 3. CNI 插件问题
kubectl logs -n kube-system -l k8s-app=calico-node
# 4. 网络问题
ping <api-server-ip>
API Server 不可访问
# 检查 API Server
kubectl cluster-info
# 检查证书
kubeconfig view
openssl x509 -in ~/.kube/config -text -noout
# 检查 API Server Pod(如果可访问节点)
ssh master-node
docker ps | grep kube-apiserver
docker logs <apiserver-container>
# 查看 API Server 日志
journalctl -u kube-apiserver -f
etcd 问题
# 检查 etcd 状态
ETCDCTL_API=3 etcdctl \
--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 \
endpoint health
# 查看 etcd 成员
ETCDCTL_API=3 etcdctl member list
# 备份 etcd
ETCDCTL_API=3 etcdctl snapshot save backup.db
实战案例
案例 1:应用突然无法访问
现象:用户反馈网站无法访问
排查过程:
# 1. 检查 Ingress
kubectl get ingress
kubectl describe ingress myapp
# 2. 检查 Service
kubectl get svc myapp
kubectl get endpoints myapp
# 发现 Endpoints 为空!
# 3. 检查 Pod
kubectl get pods -l app=myapp
# 所有 Pod 都是 CrashLoopBackOff
# 4. 查看日志
kubectl logs <pod-name> --previous
# 发现:Error: Cannot connect to database
# 5. 检查数据库
kubectl get pods -l app=mysql
# MySQL Pod 正常
kubectl exec -it <mysql-pod> -- mysql -u root -p
# 无法连接!发现 MySQL 密码被修改
# 6. 根因:Secret 被误修改
kubectl get secret db-secret -o yaml
# 解决:恢复正确的密码
kubectl apply -f backup/db-secret.yaml
kubectl rollout restart deployment myapp
案例 2:Pod 内存持续增长
现象:Pod 定期被 OOMKilled
排查过程:
# 1. 确认问题
kubectl get pods
kubectl describe pod <pod-name> | grep OOM
# 2. 查看内存趋势
# 在 Grafana 中查看内存使用曲线
# 发现持续增长,典型的内存泄漏
# 3. 采集堆转储(Java 应用)
kubectl exec <pod-name> -- jmap -dump:live,format=b,file=/tmp/heap.hprof 1
kubectl cp <pod-name>:/tmp/heap.hprof ./heap.hprof
# 4. 分析堆转储
# 使用 Eclipse MAT 或 VisualVM 分析
# 发现:大量未关闭的数据库连接
# 5. 代码修复
# 修复连接池配置,确保连接正确关闭
# 6. 临时缓解
kubectl set resources deployment myapp --limits=memory=4Gi
kubectl autoscale deployment myapp --cpu-percent=50 --min=3 --max=10
案例 3:网络间歇性超时
现象:服务间调用偶尔超时
排查过程:
# 1. 查看监控
# Grafana 显示 P99 延迟偶尔飙升
# 2. 检查 Pod 分布
kubectl get pods -o wide
# 发现所有 Pod 都在同一个节点
# 3. 检查节点资源
kubectl top node node-1
# CPU 和内存使用率很高
# 4. 检查其他工作负载
kubectl get pods --all-namespaces -o wide | grep node-1
# 发现有个批处理任务占用大量资源
# 5. 解决方案
# 方案 1: 添加 Pod 反亲和性
kubectl edit deployment myapp
# 添加 podAntiAffinity
# 方案 2: 为批处理任务设置资源限制
kubectl set resources deployment batch-job --limits=cpu=2,memory=4Gi
# 方案 3: 扩容集群
kubectl scale deployment myapp --replicas=6
常用调试工具
kubectl 插件
# 安装 krew(kubectl 插件管理器)
(
set -x; cd "$(mktemp -d)" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew-linux_amd64.tar.gz" &&
tar zxvf krew-linux_amd64.tar.gz &&
./krew-linux_amd64 install krew
)
# 有用的插件
kubectl krew install debug # 调试工具
kubectl krew install tail # 多 Pod 日志聚合
kubectl krew install tree # 资源树状视图
kubectl krew install sniff # 网络抓包
调试容器
# 启动临时调试容器(Ephemeral Container)
kubectl debug -it <pod-name> --image=busybox --target=<container-name>
# 调试节点
kubectl debug node/<node-name> -it --image=ubuntu
# 复制 Pod 进行调试
kubectl debug <pod-name> --copy-to=debug-pod --container=app -- sh
网络抓包
# 使用 ksniff 插件
kubectl sniff <pod-name> -n <namespace>
# 或手动抓包
kubectl exec <pod-name> -- tcpdump -i any -w /tmp/capture.pcap
kubectl cp <pod-name>:/tmp/capture.pcap ./capture.pcap
# 使用 Wireshark 分析
最佳实践
1. 日志规范
- 使用结构化日志(JSON)
- 包含关键字段:timestamp、level、message、trace_id
- 设置合适的日志级别
- 定期轮转和清理
2. 监控告警
- 设置关键指标监控
- 配置合理的告警阈值
- 建立告警响应流程
- 定期回顾和优化
3. 故障预防
- 资源限制必须设置
- 健康检查必须配置
- 使用 PDB 防止批量删除
- 定期备份关键数据
4. 文档记录
- 记录常见问题和解决方案
- 建立故障知识库
- 定期进行故障演练
- 复盘和改进流程
常用命令速查
# 快速诊断
kubectl get pods --field-selector=status.phase!=Running
kubectl get pods --field-selector=status.phase=Failed
kubectl get events --sort-by='.lastTimestamp' | tail -20
# 资源使用
kubectl top nodes --sort-by=cpu
kubectl top pods --all-namespaces --sort-by=memory
# 批量操作
kubectl delete pods --field-selector=status.phase=Failed
kubectl get pods -o json | jq '.items[] | select(.status.phase != "Running") | .metadata.name'
# 强制删除
kubectl delete pod <pod-name> --grace-period=0 --force
# 查看完整配置
kubectl get pod <pod-name> -o yaml
kubectl get pod <pod-name> -o json | jq
小结
故障排查是 Kubernetes 运维的核心技能:
排查方法:
- 系统化的排查流程
- 充分收集信息
- 分析根本原因
- 验证解决方案
常见问题:
- Pod 问题:Pending、CrashLoop、ImagePull
- 网络问题:DNS、Service、Ingress
- 性能问题:CPU、内存、磁盘
- 集群问题:节点、API Server、etcd
核心工具:
- kubectl describe / logs / exec
- kubectl top / events
- kubectl debug
- 监控和日志系统
关键原则:
- 先看日志和事件
- 从症状到根因
- 记录和分享经验
- 预防胜于治疗
下一章我们将学习安全最佳实践,构建安全的 Kubernetes 环境。