1. Kubernetes Pod 频繁重启的根因定位与修复

Kubernetes Pod 频繁重启的根因定位与修复 学习卡片

从“重启次数”到“退出码”:一条可复现的定位链

Pod 频繁重启时,RESTARTS 只是表象。真正决定根因方向的是三组证据:容器退出码(进程为什么结束)、探针事件(是进程自己退出,还是被 kubelet 判定失败后杀掉)、资源限制(是否被 OOMKilled 或 CPU 节流拖死)。下面用一条可复现的排查链,把这三组证据串起来。

现象:Pod 频繁重启 RESTARTS 持续增长 ① 容器退出码 kubectl describe pod Last State / Exit Code ② 探针与事件 Events / Probe 日志 Liveness / Readiness ③ 资源限制 requests / limits OOMKilled / CPU 节流 退出码判读 137 / 143 / 1 / 0 区分信号与主动退出 137 = SIGKILL OOMKilled 或 探针失败被杀 143 = SIGTERM 优雅终止 滚动更新/驱逐 1 / 0 = 应用退出 配置错误、依赖 不可用、启动失败 修复配置对照(按根因选择) OOMKilled(137) • 提高 memory limits • 排查内存泄漏 • 设置合理 requests • 检查 JVM/堆参数 探针失败(137) • 调大 initialDelaySeconds • 放宽 timeoutSeconds • 修正探针路径/端口 • 增加 failureThreshold 应用退出(1/0) • 检查启动命令/参数 • 校验 ConfigMap/Secret • 确认依赖服务可达 • 查看应用启动日志

第一步:用退出码锁定方向

先看 kubectl describe pod 的 Last State 字段。退出码是分诊的第一把刀:

  • 137:进程收到 SIGKILL。最常见是 OOMKilled,其次是被 liveness 探针判定失败后 kubelet 杀掉。
  • 143:进程收到 SIGTERM,属于正常终止流程,通常来自滚动更新、节点驱逐或手动删除,不算异常重启。
  • 1 或 0:应用自己退出。0 是正常退出但容器主进程不应退出;1 是应用报错退出,需要看应用日志。

如果退出码是 137,必须再区分是 OOM 还是探针。看 Reason 字段:OOMKilled 直接指向内存;如果是 Error 且 Events 里有 Liveness probe failed,则是探针问题。

第二步:用事件和探针日志确认触发者

执行 kubectl get events -n <ns> --sort-by=.lastTimestamp,重点看三类事件:

  • Liveness probe failed / Readiness probe failed:探针连续失败达到 failureThreshold 后,kubelet 会重启容器。
  • OOMKilling:内核 OOM killer 介入,说明容器内存超过 limit。
  • BackOff:重启间隔被指数退避拉长,说明重启已持续一段时间。

探针失败常见于:initialDelaySeconds 太短,应用还没启动完就被判死;timeoutSeconds 太小,慢请求被误判;探针路径写错或端口不对。

第三步:核对资源限制

查看 kubectl get pod <pod> -o yaml 中的 resources。如果 limits.memory 设置过低,而应用实际峰值超过它,就会反复 OOMKilled。注意 requests 影响调度,limits 影响运行时上限,两者都要合理。

不要只调大 limit 了事。如果应用存在内存泄漏,调大 limit 只是延长重启周期。应先确认内存增长曲线是否随时间单调上升。

修复配置示例

假设定位结果是:应用启动需要约 40 秒,但 initialDelaySeconds 只有 10 秒,导致 liveness 探针在启动阶段就失败并触发重启。修复如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  namespace: stability-lab
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
      - name: app
        image: demo-app:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 45
          periodSeconds: 10
          timeoutSeconds: 3
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 5
          timeoutSeconds: 2
          failureThreshold: 3

关键改动:initialDelaySeconds 从 10 提到 45,覆盖应用冷启动时间;timeoutSeconds 从 1 提到 3,避免慢响应误判;failureThreshold 保持 3,给偶发抖动留余量。

验证与判断

应用修复后,用以下命令观察 5 分钟:

kubectl get pod -n stability-lab -w
kubectl describe pod -n stability-lab <pod> | grep -A5 "Last State"
kubectl get events -n stability-lab --sort-by=.lastTimestamp | tail -20

通过标准:RESTARTS 不再增长;Last State 不再出现 137;Events 中不再有 Liveness probe failed。如果重启停止但 RESTARTS 计数保留历史值,这是正常的,计数不会清零。

排查顺序建议固定为:退出码 → 事件 → 探针日志 → 资源限制。这个顺序能最快把范围从“所有可能”压缩到“一个方向”,避免在无关配置上浪费时间。