K8s排障实战#
一句话答案#
K8s 排障的核心思路是「描述→日志→事件→指标→网络」五步法:先 describe 看状态和事件,再 logs 看应用日志,然后检查资源指标和网络连通性,90% 的问题在前三步就能定位。
核心要点
一、Pod OOMKilled 排查
现象:Pod 状态 OOMKilled,Last State 显示 exit code 137
排查步骤:
# 1. 确认OOM
kubectl describe pod <name> | grep -A5 "Last State"
# → reason: OOMKilled, exit code: 137
# 2. 查看资源配置
kubectl get pod <name> -o jsonpath='{.spec.containers[0].resources}'
# → 对比 limits.memory 与实际使用
# 3. 查看内存趋势
kubectl top pod <name> --containers
# → 观察是否持续增长(泄漏)还是瞬时飙高(大请求)
# 4. JVM 应用特有:堆内 vs 堆外
# 容器 limit 512Mi,JVM -Xmx 设 512m → 堆外内存(Metaspace/DirectBuffer/线程栈)会超限
# 正确做法:limit 512Mi 时 -Xmx 设 350m~400m,留 20-30% 给堆外bash常见原因及解决:
| 原因 | 特征 | 解决 |
|---|---|---|
| JVM堆内存溢出 | 堆内存持续增长 | jmap分析 + 修复泄漏 + 调大-Xmx |
| 堆外内存(DirectBuffer) | 堆内正常但RSS持续涨 | 加 -XX:MaxDirectMemorySize 限制 |
| JVM limit设置不当 | limit=Xmx,无堆外余量 | limit 比 Xmx 多 20-30% |
| 非JVM内存泄漏 | C库/Native泄漏 | pmap/jemalloc profiling |
| 容器 OOM Killer | dmesg 有 oom-kill 记录 | 调大 limits 或优化内存使用 |
二、CrashLoopBackOff 排查
现象:Pod 反复重启,状态 CrashLoopBackOff,重启间隔指数退避
# 1. 看退出码
kubectl describe pod <name> | grep "Exit Code"
# exit 1: 应用异常 exit 137: OOM/SIGKILL exit 143: SIGTERM
# 2. 看当前和上一次日志
kubectl logs <name> # 当前(可能为空,刚重启)
kubectl logs <name> --previous # 上一次崩溃的日志(关键)
# 3. 看事件
kubectl get events --field-selector involvedObject.name=<name> --sort-by='.lastTimestamp'
# 4. 如果日志不够,exec进去排查
kubectl exec -it <name> -- sh # 如果Pod还活着
kubectl debug <name> -it --image=busybox # 如果进不去bash常见原因:
- 配置错误:ConfigMap/Secret 引用不存在或内容格式错
- 依赖不可用:DB/Redis/MQ 连不上,启动失败
- 端口冲突:多个容器用同一端口
- 健康检查过严:livenessProbe 超时导致反复杀重启
- 权限问题:文件系统只读、SecurityContext 限制
三、探针抖动排查
现象:Pod 运行一段时间后被 liveness 探针杀掉重启
# 1. 查看探针配置
kubectl get pod <name> -o jsonpath='{.spec.containers[0].livenessProbe}'
# 关注:timeoutSeconds, periodSeconds, failureThreshold
# 2. 检查探针端点响应时间
kubectl exec <name> -- curl -w "%{time_total}" -o /dev/null -s http://localhost:8080/health
# 3. 常见原因
# a) GC 停顿导致探针超时 → 加大 timeoutSeconds 或优化 GC
# b) 线程池耗尽,探针请求排队 → 探针用独立线程池或管理端口
# c) 初始化慢 → 使用 startupProbe 替代加大 initialDelaySecondsbash探针配置最佳实践:
startupProbe: # 启动探针:允许慢启动
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30次 × 10s = 允许5分钟启动
periodSeconds: 10
livenessProbe: # 存活探针:检测死锁/假死
httpGet:
path: /health
port: 8080
timeoutSeconds: 5 # 给够超时时间
periodSeconds: 10
failureThreshold: 3 # 连续3次失败才杀
readinessProbe: # 就绪探针:控制是否接收流量
httpGet:
path: /ready
port: 8080
timeoutSeconds: 3
periodSeconds: 5
failureThreshold: 3yaml四、Service/Ingress 不通排查
# 排查路径:Client → Ingress → Service → Endpoints → Pod
# 1. 检查 Pod 是否 Ready
kubectl get pods -l app=myapp
# → 确认 READY 列是 1/1
# 2. 检查 Endpoints 是否有后端
kubectl get endpoints myapp-svc
# → 如果为空,说明 selector 不匹配或 Pod 未 Ready
# 3. 检查 Service 是否正确
kubectl describe svc myapp-svc
# → 确认 selector、port、targetPort
# 4. 从 Pod 内部测连通性
kubectl exec -it <client-pod> -- curl http://myapp-svc:8080/health
# → 排除网络策略和 DNS 问题
# 5. 检查 NetworkPolicy
kubectl get networkpolicy -A
# → 是否有策略阻止了流量
# 6. DNS 排查
kubectl exec -it <pod> -- nslookup myapp-svc.default.svc.cluster.localbash五、HPA 与资源配额问题
| 问题 | 原因 | 解决 |
|---|---|---|
| HPA 不扩容 | 未安装 metrics-server | 部署 metrics-server |
| HPA 扩容太慢 | 默认冷却期 5min | 调整 --horizontal-pod-autoscaler-downscale-stabilization |
| 扩容后不缩容 | 缩容冷却期+指标延迟 | 属于正常保护机制 |
| 新Pod调度失败 | 资源不足(Insufficient cpu/memory) | 检查 ResourceQuota、node 可用资源 |
| Pod Pending | 无节点满足亲和性/污点要求 | kubectl describe pod 看调度失败原因 |
六、kubectl 排障命令速查表
| 场景 | 命令 |
|---|---|
| 看Pod状态和事件 | kubectl describe pod <name> |
| 看上次崩溃日志 | kubectl logs <name> --previous |
| 实时跟踪日志 | kubectl logs -f <name> |
| 看资源使用 | kubectl top pod/node |
| 进入Pod | kubectl exec -it <name> -- sh |
| 看集群事件 | kubectl get events --sort-by='.lastTimestamp' |
| 调试不可进入的Pod | kubectl debug <name> -it --image=busybox |
| 端口转发本地调试 | kubectl port-forward pod/<name> 8080:8080 |
| 看yaml定义 | kubectl get pod <name> -o yaml |
| 批量看Pod资源 | kubectl top pods --sort-by=memory |
面试回答(2分钟版)
K8s排障我有一套标准流程。首先kubectl describe pod看状态和事件,这能发现80%的问题:OOMKilled说明内存超限,CrashLoopBackOff要看exit code区分是应用异常还是被杀。然后kubectl logs —previous看上次崩溃日志找具体报错。最常见的几个场景:OOMKilled通常是JVM的Xmx设得和容器limit一样大,没给堆外留余量,解决方案是limit比Xmx多20-30%。CrashLoopBackOff常见原因是配置错误或依赖服务不可用。探针抖动通常是liveness超时设太短,GC停顿或线程池满导致健康检查超时被杀,解决方案是用startupProbe处理慢启动、liveness加大超时。Service不通从后往前查:先看endpoints有没有后端Pod,再看selector是否匹配,最后查NetworkPolicy和DNS。遇到问题我会优先看describe的事件和日志,90%的情况前两步就能定位。
追问与易错
追问方向:
- “OOM 是容器层还是 JVM 层怎么区分?”→ 容器层 OOM 看 dmesg 有 oom-kill 记录且 exit code=137;JVM 层 OOM 看 GC log 有 OutOfMemoryError 且进程可能还活着,只是无法分配内存
- “如何预防 OOM?”→ 正确设 limits/requests(limit 比 Xmx 多 20-30%)+ 配置 JVM 参数(-Xmx/-XX:MaxDirectMemorySize/-XX:MaxMetaspaceSize)+ 上线前做内存压测
- “livenessProbe 和 readinessProbe 应该查什么?”→ liveness 检查进程是否假死(死锁/线程池满),失败则重启容器;readiness 检查依赖是否就绪(DB/Redis 连接正常),失败则从 Service 摘除不再接流量
- “Pod 调度失败 Pending 怎么排查?”→ kubectl describe pod 看 Events 中的调度失败原因,常见:Insufficient cpu/memory(资源不足)、node affinity 不匹配、所有节点有 Taint 但 Pod 无对应 Toleration
- “如何做到发布零停机?”→ readinessProbe 确保新 Pod 就绪才接流量 + preStop hook 等 Service 摘除旧 Pod + 应用优雅关闭处理完进行中请求 + maxUnavailable=0 保证始终有可用实例
易错点:
- ❌ “OOM就调大内存”——先确认是泄漏还是正常需求,泄漏调大只是延缓
- ❌ “去掉liveness解决重启”——等于放弃自愈能力,应该调整探针参数
- ❌ “limit和request设一样”——QoS变成Guaranteed,不灵活;但生产环境建议这样做保证稳定性
- ✅ 排障记住五步法:describe → logs —previous → events → top → network