在 Kubernetes 上跑大模型推理服务,Pod 动不动就重启,很多时候不是模型本身崩了,而是 Probe 配置太激进。模型加载要几分钟,一次推理可能几十秒,默认的 LivenessProbe 等不及就把容器杀了,形成无限重启循环。这种重启看起来像是应用不稳定,实际上是 K8s 在「好心办坏事」。
判断思路是先确认重启原因:kubectl describe pod 看 Events 是 OOMKilled 还是 Liveness probe failed;再看 previous 日志有没有 panic。如果是 Probe 误杀,调整参数比重构模型快得多。
三种 Probe 的分工
| Probe | 作用 | 大模型场景注意 |
|---|---|---|
| startupProbe | 判断容器是否已启动 | 给足模型加载时间,失败阈值要设大 |
| readinessProbe | 决定是否接收流量 | 应检查 /v1/models 等真实就绪接口 |
| livenessProbe | 决定是否重启容器 | 阈值必须大于 P99 推理耗时,否则误杀 |
推荐 Probe 配置
startupProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 60
periodSeconds: 15
failureThreshold: 30 # 最长等 60+15*30=510s
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 0
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 5 # 总超时 150s,必须 > P99 推理延迟
readinessProbe:
httpGet:
path: /v1/models
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
关键点:liveness 的 periodSeconds × failureThreshold 要大于你的最长推理时间;readiness 用模型列表接口,确认模型已加载再放流量。
排查重启的真实原因
不要只看 Pod 在重启,要分清是谁杀的:
kubectl describe pod deepseek-v4-xxx | grep -A10 Events
如果看到 Liveness probe failed 后跟着 Killing,那就是 Probe 误杀。如果看到 OOMKilled,则是内存 / 显存不够,需要降 batch 或加资源。
再看 previous 容器日志,确认不是应用 panic:
kubectl logs deepseek-v4-xxx --previous --tail 100
资源限制与 HPA 陷阱
Probe 误杀之外,资源不足也是大模型 Pod 重启的常见原因。注意 GPU 显存不受 K8s memory limit 限制,需要单独监控:
# 查看 Pod 资源限制
kubectl describe pod deepseek-v4-xxx | grep -A5 "Limits"
# 查看节点 GPU 显存
kubectl exec -it deepseek-v4-xxx -- nvidia-smi
# 查看 DCGM 指标(需安装 dcgm-exporter)
kubectl top pod deepseek-v4-xxx
如果 kubectl describe pod 里显示 Last State: Terminated, Reason: OOMKilled,说明内存 limit 太小。注意大模型加载时先占 CPU 内存再迁 GPU,memory limit 要留足。
HPA 按 CPU/内存扩容对推理服务不敏感,建议用自定义指标(如请求队列长度或 GPU 利用率)来触发扩容,否则流量突增时扩容跟不上。
日志与监控建议
只靠 kubectl describe 不够,最好把关键指标接进 Prometheus:
- 推理延迟 P50/P99(判断 liveness 阈值是否合理)
- GPU 显存占用(防止 OOM)
- Pod 重启次数(restart count)
- 排队请求数(决定是否扩容)
应用日志里要打印模型加载完成事件,方便和 readiness 探针的 ready 时间对齐:
INFO Model loaded in 187s, ready to serve
INFO /v1/models returns 200
踩坑实录
我部署 vLLM 服务时,startupProbe 只给了 failureThreshold: 10,结果 A100 上模型加载到 85% 就被 kubelet 判定启动失败,Pod 反复重启。扩容到 failureThreshold: 30 后稳定。更坑的是 livenessProbe 默认 periodSeconds: 10, failureThreshold: 3,总超时 30 秒,而长文本生成 P99 latency 刚好 40 秒,推理中的请求还没返回就被杀了。改成 periodSeconds: 30, failureThreshold: 5 后,重启次数从每小时十几次降到 0。
常见问题
问:能不能干脆关掉 livenessProbe?
答:可以,但不推荐。如果服务真卡死,没有 liveness 就不会自动恢复。更好的做法是给它一个独立的 /health 端点,只做 TCP 或轻量 HTTP 检查,不触发推理。
问:startupProbe 和 readinessProbe 都要配吗?
答:慢启动服务建议都配。startupProbe 负责等加载完成,加载期间 readiness 失败不会导致重启;加载完成后 readiness 再决定流量。
问:GPU 显存 OOM 和 Probe 误杀怎么区分?
答:kubectl describe pod 里看 Last State。OOMKilled 的 reason 是 OOMKilled,Probe 误杀是 Error 或 Completed 且 Events 里有 Liveness probe failed。
如果你在 K8s 上跑大模型也遇到反复重启,欢迎在 fenij.com 留言,把 kubectl describe pod 的 Events 贴出来一起分析。