
K8s Pod 一直重启真正该看的不是 describe而是这几个点K8s 里最常见的问题之一就是 Pod 一直重启。很多人第一反应是kubectl describe pod xxx这个命令当然要看。但我想说一句Pod 一直重启真正该看的不只是 describe。因为 describe 很多时候只能告诉你“发生了什么”。但它不一定能直接告诉你“为什么发生”。Back-off restarting failed container这句话的意思只是 容器启动失败了K8s 正在退避重启。 但它没有告诉你根因。 根因可能是程序启动报错 端口没起来 配置文件挂载错了 环境变量不对 健康检查太激进 内存被打爆 依赖服务连不上 镜像里的启动命令有问题所以排查 Pod 重启不能只盯着 describe。第一步先看 Pod 的状态和重启次数先看整体状态kubectl get pod -n 命名空间 你可能会看到: NAME READY STATUS RESTARTS app-xxx 0/1 CrashLoopBackOff 12 这里最重要的是两个字段 STATUS RESTARTS 如果 RESTARTS 一直增加说明容器确实在反复挂。 如果状态是 1 CrashLoopBackOff 一般说明容器启动后很快退出。 如果状态是 1 Running 但 READY 一直是 0/1那可能是 readinessProbe 没过。 这两种情况不一样。 一个是“容器自己挂了”。 一个是“容器还活着但 K8s 认为它没准备好”。第二步看上一次崩溃前的日志这是很多人会漏掉的地方。 如果容器一直重启直接看日志可能只看到当前这一轮。 比如kubectl logs app-xxx -n prod但如果容器刚重启过你真正需要看的是上一次退出前的日志1 kubectl logs app-xxx -n prod --previous 这个命令很重要。这个命令很重要。 很多问题只有 --previous 才能看到比如 Error: connect ECONNREFUSED mysql:3306 或者 java.lang.OutOfMemoryError 或者 Config file not found 或者 Permission denied这些才是根因。 所以Pod 重启时我一般先执行1 kubectl logs pod名称 -n 命名空间 --previous第三步看 Last State 和 Exit Code 然后再看 describe。kubectl describe pod app-xxx -n prod 重点不是从头看到尾。 重点看这个位置 Last State: Terminated: Reason: Error Exit Code: 1常见情况有几种。Exit Code 1一般是程序主动退出。比如配置错、启动命令错、依赖连接失败。Exit Code 137 这个很常见。很多时候代表容器被杀了常见原因是内存不够也就是 OOMKilled。你可能会看到Reason: OOMKilled Exit Code: 137这时候不要只盯应用日志要看内存限制resources: limits: memory: 512Mi如果 Java 服务只给 512Mi但 JVM 参数没控制好很容易被杀。Exit Code 143 这个通常表示进程收到了终止信号。可能是正常被 K8s 停掉也可能是滚动发布、探针失败后被重启。第四步看是不是探针把服务杀了 很多 Pod 重启不是程序自己想退出。而是 livenessProbe 配得太急把程序杀了。 比如livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 1 failureThreshold: 3这个配置看起来没问题。 但如果你的 Java 应用启动比较慢或者启动时要加载配置、连数据库、初始化缓存那么 10 秒可能根本不够。 结果就是应用还没启动好 探针开始检查 检查失败 K8s 认为容器不健康 直接重启容器 然后又重复这个过程 最后 Pod 就一直重启。所以我一般建议启动慢的服务一定要加 startupProbe。例如startupProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 failureThreshold: 30 livenessProbe: httpGet: path: /health port: 8080 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 5 failureThreshold: 3简单理解startupProbe给应用启动时间livenessProbe判断应用是不是死了readinessProbe判断应用能不能接流量不要一上来就用 livenessProbe 盯死应用。很多服务不是挂了是还没启动完。第五步看资源是不是太小如果 describe 里看到 OOMKilled 那就要看资源配置 kubectl describe pod app-xxx -n prod 或者看 YAML kubectl get deploy app -n prod -o yaml 重点看 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1 memory: 512Mi 如果 limits 设置太小容器会被强制限制。 尤其是 Java 服务要特别注意。 比如容器内存是 512Mi但 JVM 堆内存就配置了 512m那肯定容易出问题。 因为除了堆内存还有元空间、线程栈、直接内存、JIT 等开销。 所以 Java 容器不要只看 -Xmx也要看容器总内存。 第六步看配置、环境变量、挂载文件 还有一种很常见的问题 Pod 启动就挂但日志里只有一句 config file not found 或者 no such file or directory 这时候要检查 ConfigMap 有没有挂上 Secret 有没有挂上 volumeMounts 路径对不对 环境变量名字有没有写错 启动命令有没有引用错误路径 可以进容器看 kubectl exec -it app-xxx -n prod -- sh 如果容器一直重启进不去可以临时把容器的启动命令改成 sleep command: [sh, -c, sleep 3600] 然后进去看文件是否存在。第七步看依赖服务是不是连不上有些 Pod 不是自己有问题而是依赖的服务不通。 比如mysql:3306 connect timeout redis:6379 connection refused nacos connection failed kafka broker unavailable这时候要在 Pod 里测试网络kubectl exec -it app-xxx -n prod -- sh然后查看端口nc -vz mysql 3306 nc -vz redis 6379 curl http://service-name:port/health如果容器里没有 nc 或 curl可以临时起一个调试 Podkubectl run net-test --rm -it --imagebusybox -- sh或者kubectl run net-test --rm -it --imagenicolaka/netshoot -- bash线上排障时网络问题经常被误判成应用问题。我的排查顺序一般是这样我一般不会一上来就乱改 YAML。顺序是kubectl get pod 看状态和重启次数kubectl logs --previous 看上一次崩溃日志kubectl describe pod 看 Last State / Exit Code / Events检查 livenessProbe / readinessProbe / startupProbe检查 CPU / 内存限制检查 ConfigMap / Secret / 环境变量 / 挂载路径检查依赖服务连接最后再考虑镜像、启动命令、代码本身这套顺序比较笨但很稳。最后说一句 kubectl describe pod 很有用但它不是全部。 Pod 一直重启真正要看的不是某一个命令而是完整链路日志 事件 退出码 探针 资源 配置 依赖 启动命令K8s 排障最怕什么 最怕看到一个 CrashLoopBackOff 就开始猜。 猜内存、猜探针、猜镜像、猜网络。 线上排障不能靠猜。 要一步一步看证据。 很多问题看起来很复杂其实只要顺序对了很快就能找到原因。