igozhang

——

    WAL导致Prometheus 启动失败

    ENV

    类别 信息
    环境 基地A · Kubernetes 预发集群
    节点 OS Ubuntu 20.04.6 LTS,Kernel 5.4.0-216-generic
    容器运行时 Docker 25.0.5
    Kubernetes v1.24.6(Client/Server)
    监控栈 Helm kube-prometheus-stack v56.21.4,Prometheus v2.50.1
    存储 NFS(nfs-client),挂载点 8.8.238.39:/data/data_nfs/...
    Prometheus PVC 50Gi RWO,实际使用约 7%
    Grafana grafana-hu.igozhang.cn(Ingress nginx)

    1. 问题现象

    对象 状态
    prometheus-monitoring-kube-prometheus-prometheus-0 Running 但 /-/ready 返回 503
    StatefulSet 0/1 Ready(巡检时)
    容器重启 累计 2159
    prom-q Failed(非监控组件,手工测试 Pod 残留)

    误导项: prom-q(curl 测试 Pod,54 天前失败)与监控栈无关,可忽略或删除。


    2. 问题原因

    2.1 定位推导过程

    Step 1 — 确认 Ready 探针失败

    timeout 10 kubectl exec -n monitoring \
      prometheus-monitoring-kube-prometheus-prometheus-0 -c prometheus \
      -- wget -qO- --timeout=5 http://127.0.0.1:9090/-/ready 2>&1
    

    关键输出:HTTP/1.1 503 Service Unavailable

    Step 2 — 查看启动日志

    timeout 10 kubectl logs prometheus-monitoring-kube-prometheus-prometheus-0 \
      -n monitoring -c prometheus --tail=60 2>&1
    

    关键输出:停在 Replaying on-disk memory mappable chunks,TSDB 启动中。

    Step 3 — 检查 WAL 与磁盘

    timeout 10 kubectl exec -n monitoring \
      prometheus-monitoring-kube-prometheus-prometheus-0 -c prometheus \
      -- sh -c "df -h /prometheus; ls -lhS /prometheus/wal/ | head -n 10" 2>&1
    

    关键输出:

    /prometheus  983G  64.6G  7%   (NFS)
    wal/  total 57G,2615 个段文件,最后写入 4 月 28–29 日
    

    Step 4 — 估算 Replay 与探针窗口

    timeout 10 kubectl get pod prometheus-monitoring-kube-prometheus-prometheus-0 \
      -n monitoring -o jsonpath='startupProbe={.spec.containers[0].startupProbe.failureThreshold}{" x "}{.spec.containers[0].startupProbe.periodSeconds}{"s\n"}'
    

    关键输出:startupProbe=60 x 15s容忍 15 分钟

    Replay 进度日志:

    WAL segment loaded  segment=175  maxSegment=2703   (~2.5s/段 → 总耗时 ~110 分钟)
    

    Step 5 — 确认死亡螺旋

    15 分钟探针超时 → 杀容器 → 从头 Replay → 再 503 → 再杀 → 2159 次重启
    

    Step 6 — 修复过程 secondary 错误

    清空 WAL 时未先经 CR 缩容,Operator 重建 Pod 与清理 Pod 同时挂载 NFS →

    opening storage failed: lock DB directory: resource temporarily unavailable
    

    2.2 根因结论

    层级 原因
    直接原因 WAL 57GB / 2703 段,启动 Replay 需 ~110 分钟
    触发机制 Startup 探针仅容忍 15 分钟,Replay 未完成即杀容器
    结果 「Replay → 503 → 被杀 → 再 Replay」死亡螺旋
    存储因素 NFS 上 TSDB,flock/I/O 弱,Replay 更慢、维护时易 lock 冲突

    2.3 形成原因推测

    运维在 4 月下旬 Prometheus 首次启动变慢后,未排查 WAL 膨胀根因而反复 delete/restart Pod;开发/运维 部署 Helm Chart 时使用默认 startup 探针(15 分钟)且 TSDB 放在 NFS,未配置 terminationGracePeriodSecondsretentionSize。Pod 异常退出后 WAL 从 4/28 累积至 57GB 且 2 个月未完成一次完整 Replay,最终进入 2159 次重启的死循环。prom-q运维 手工 curl 测试后未清理,造成巡检误判。


    3. 解决方案

    3.1 最终恢复步骤(已执行,TSDB 全新初始化)

    ① 经 CR 停止(防 Operator 重建)

    kubectl patch prometheus monitoring-kube-prometheus-prometheus -n monitoring \
      --type=merge -p '{"spec":{"replicas":0}}'
    kubectl delete pod prometheus-monitoring-kube-prometheus-prometheus-0 \
      prom-wal-clean -n monitoring --ignore-not-found --grace-period=0 --force
    

    ② 确认无 Pod 占用 PVC

    kubectl get pod -n monitoring --no-headers | grep -E "prometheus-monitoring|prom-wal-clean" || echo "无相关 Pod"
    

    ③ 清空 TSDB 目录(丢失全部历史指标,全新采集)

    # 临时 Pod 挂载 PVC 执行
    # rm -rf /prometheus/* && mkdir -p /prometheus/wal && sync && echo FRESH_TSDB_OK
    

    ④ 经 CR 启动

    kubectl patch prometheus monitoring-kube-prometheus-prometheus -n monitoring \
      --type=merge -p '{"spec":{"replicas":1}}'
    

    3.2 验证

    timeout 10 sh -c "
    kubectl get pod prometheus-monitoring-kube-prometheus-prometheus-0 -n monitoring \
      -o jsonpath='{range .status.containerStatuses[*]}{.name}{\": ready=\"}{.ready}{\" restarts=\"}{.restartCount}{\"\n\"}{end}';
    kubectl exec -n monitoring prometheus-monitoring-kube-prometheus-prometheus-0 -c prometheus \
      -- wget -qO- --timeout=5 http://127.0.0.1:9090/-/ready 2>&1
    "
    

    期望输出:

    prometheus: ready=true restarts=0
    Prometheus Server is Ready.
    

    3.3 清理残留测试 Pod

    kubectl delete pod prom-q -n monitoring
    

    3.4 Helm 部署优化(防止复发)

    在原部署命令基础上增加:

    helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
      --namespace monitoring \
      --version 56.21.4 \
      # ... 原有 grafana/alertmanager 配置保持不变 ... \
      --set prometheus.prometheusSpec.retention='15d' \
      --set prometheus.prometheusSpec.retentionSize='45GB' \
      --set prometheus.prometheusSpec.walCompression=true \
      --set prometheus.prometheusSpec.terminationGracePeriodSeconds=600 \
      --set prometheus.prometheusSpec.resources.requests.memory='4Gi' \
      --set prometheus.prometheusSpec.resources.limits.memory='8Gi' \
      --set prometheus.prometheusSpec.containers[0].name='prometheus' \
      --set prometheus.prometheusSpec.containers[0].startupProbe.failureThreshold=480 \
      --set prometheus.prometheusSpec.containers[0].startupProbe.periodSeconds=15 \
      --set prometheus.prometheusSpec.containers[0].startupProbe.httpGet.path='/-/ready' \
      --set prometheus.prometheusSpec.containers[0].startupProbe.httpGet.port=9090
    

    升级后验证探针:

    timeout 10 kubectl get prometheus -n monitoring \
      -o jsonpath='failureThreshold={.items[0].spec.containers[0].startupProbe.failureThreshold}{"\n"}'
    

    期望:failureThreshold=480


    4. 后续预防措施

    开发

    • Helm values 固化 startup 探针、retentionSizewalCompression,禁止依赖 Operator 默认值。
    • 评估 Prometheus TSDB 使用本地 SSD StorageClass,Grafana/Alertmanager 可继续 NFS。
    • 限制高基数 label 与 scrape target 数量,控制 WAL 增长速度。

    运维

    • 维护标准流程:Prometheus CR replicas=0 → 确认 Pod 消失 → 再操作 PVC → 再 replicas=1;禁止对 Running Pod --force 删除。
    • 禁止 kubectl run 测试 Pod 长期残留;用完即删。
    • 监控告警:PrometheusNotReady(10m)、PodRestartBurst(1h > 3 次)。
    • 定期巡检 WAL 段数(> 500 预警)与 PVC 使用率。
    • 密码通过 Secret 管理,不写进 shell history。

    5. 配置参数建议

    参数 当前值 建议值 原因/后果
    startupProbe.failureThreshold 60 480 60×15s=15min,小于 Replay 需求(~110min),必触发死亡螺旋
    startupProbe.periodSeconds 15 15(保持) 配合 failureThreshold=480 → 120min 窗口
    terminationGracePeriodSeconds 默认 30s 600 过短强杀导致 WAL 无法正常 flush,持续膨胀
    retention 15d 15d(保持) 已合理
    retentionSize 未设置 45GB 50Gi PVC 留余量,防止磁盘写满
    walCompression 未开启 true 降低 WAL 体积与 Replay 时间
    resources.limits.memory 未设置 8Gi 无 limit 易 OOM 强杀,留下大量 WAL
    storageClassName nfs-client 本地盘(如 local-path NFS flock/I/O 弱,Replay 慢且维护易 lock 冲突
    PVC size 50Gi 50Gi(保持) 当前够用;配合 retentionSize 即可

    附录:死亡螺旋机制

    Pod 启动 → Replay 57GB WAL(~110min)
             → /-/ready = 503
             → startup 探针 15min 超时
             → kubelet 杀容器
             → RestartCount +1
             → 从头 Replay(永不完成)
    

    优化后:WAL 控制在小体积 + startup 120min 窗口 + 优雅退出 → 正常运行时 Ready 在分钟级。

    MP3