K8s GPU 推理服务 ImagePullBackOff 排查与预热

一、环境和问题

这次记录一个 GPU 推理服务扩容时遇到的启动问题。

环境大致如下:

项目 内容
编排 Kubernetes
节点运行时 containerd
推理服务 vLLM OpenAI API
GPU 节点 新增节点池
现象 Pod 已调度,但容器一直没有 Running

先看 Pod 状态:

bash 复制代码
kubectl get pod -n inference -o wide
text 复制代码
infer-api-7fdbb8c9d9-q2p6x   0/1   ContainerCreating   gpu-node-08

继续看事件:

bash 复制代码
kubectl describe pod infer-api-7fdbb8c9d9-q2p6x -n inference
text 复制代码
Pulling image "vllm/vllm-openai:latest"
Failed to pull image: context deadline exceeded
Warning  Failed  kubelet  Error: ErrImagePull
Warning  Failed  kubelet  Error: ImagePullBackOff

这里不要直接排查模型文件和推理参数。Pod 还没进入运行阶段,当前问题在镜像拉取。

二、先把启动链路拆开

GPU 推理服务启动可以拆成四段:

阶段 检查点 常见命令
调度 Pod 是否落到 GPU 节点 kubectl get pod -o wide
镜像 镜像是否能拉取 kubectl describe podcrictl pull
运行时 NVIDIA runtime、device plugin、RuntimeClass kubectl describe node
推理 模型加载、接口健康检查、batch 参数 kubectl logs

如果事件里已经出现 ImagePullBackOff,优先处理第二段。

三、整理镜像来源

推理服务通常不只有业务镜像,还会包含 CUDA、监控、K8s 基础组件和 initContainer。可以先把 Deployment / Helm values 里的镜像列出来:

bash 复制代码
kubectl get deploy infer-api -n inference -o jsonpath="{..image}"

按来源分组后,做一次预检:

bash 复制代码
docker pull docker.1ms.run/vllm/vllm-openai:latest
docker pull nvcr.1ms.run/nvidia/cuda:12.4.1-runtime-ubuntu22.04
docker pull quay.1ms.run/prometheus/prometheus:latest
docker pull k8s.1ms.run/pause:3.10

毫秒镜像(1ms.run)在这里解决的是多源镜像入口和拉取稳定性问题。它不参与 GPU 调度,也不改变 vLLM 的推理行为。

这一步通过后,至少说明 Docker Hub、NVIDIA、Quay、K8s 这些来源的基础镜像链路可以先过。

四、containerd 节点上用 crictl 验证

Kubernetes 节点实际由 containerd 拉镜像,所以建议在目标节点上再执行:

bash 复制代码
crictl pull docker.1ms.run/vllm/vllm-openai:latest
crictl pull nvcr.1ms.run/nvidia/cuda:12.4.1-runtime-ubuntu22.04
crictl pull quay.1ms.run/prometheus/prometheus:latest
crictl pull k8s.1ms.run/pause:3.10

查看本地缓存:

bash 复制代码
crictl images | grep -E "vllm|cuda|prometheus|pause"

如果这里失败,Deployment 扩容后仍然大概率进入 ImagePullBackOff

如果这里通过,再继续看:

bash 复制代码
kubectl describe node gpu-node-08
kubectl get events -n inference --sort-by=.lastTimestamp
kubectl logs -n inference deploy/infer-api

五、预热前后对比

这次调整后,我把镜像预热放到了扩容前:

项目 预热前 预热后
新节点首次扩容 Pod 卡在 ContainerCreating 镜像阶段提前验证
排查方向 模型、GPU、网络混在一起看 先排除镜像拉取
回滚演练 新节点仍可能首次拉镜像 关键镜像已有缓存
值班处理 看到 ImagePullBackOff 才介入 发布前暴露镜像来源问题

这个对比不是为了证明推理更快,而是为了让问题边界更明确。

六、DaemonSet 预热示例

GPU 节点多的时候,可以用一个临时 DaemonSet 触发预热。示例只演示思路,生产环境建议固定 tag 或 digest。

yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gpu-image-prewarm
  namespace: ops
spec:
  selector:
    matchLabels:
      app: gpu-image-prewarm
  template:
    metadata:
      labels:
        app: gpu-image-prewarm
    spec:
      nodeSelector:
        accelerator: nvidia
      tolerations:
        - operator: Exists
      initContainers:
        - name: pull-vllm
          image: docker.1ms.run/vllm/vllm-openai:latest
          command: ["sh", "-c", "python -V || true"]
        - name: pull-cuda
          image: nvcr.1ms.run/nvidia/cuda:12.4.1-runtime-ubuntu22.04
          command: ["sh", "-c", "nvidia-smi || true"]
        - name: pull-metrics
          image: quay.1ms.run/prometheus/prometheus:latest
          command: ["sh", "-c", "prometheus --version || true"]
      containers:
        - name: hold
          image: k8s.1ms.run/pause:3.10

部署和观察:

bash 复制代码
kubectl apply -f gpu-image-prewarm.yaml
kubectl rollout status daemonset/gpu-image-prewarm -n ops
kubectl get pod -n ops -o wide

预热完成后,再进行推理服务扩容。

七、排查顺序

遇到 GPU 推理服务启动失败,可以按下面顺序排查:

  1. kubectl get pod -o wide 确认 Pod 是否已经调度到 GPU 节点。
  2. kubectl describe pod 查看是否是 ErrImagePullImagePullBackOff
  3. 按来源整理镜像,先做 docker pull 预检。
  4. 在目标节点上用 crictl pull 复现真实拉取路径。
  5. 镜像阶段通过后,再看 RuntimeClass、GPU device plugin、模型目录和应用日志。
  6. 生产发布前固定 tag 或 digest,不要长期依赖 latest

八、总结

GPU 推理服务冷启动慢,不一定是模型慢,也不一定是调度慢。

如果 Pod 已经调度到 GPU 节点,但事件里反复出现 Pulling imagecontext deadline exceededImagePullBackOff,先把镜像阶段拆出来处理。

镜像预热解决的是启动链路最前面的不确定性。先让 Docker Hub、NVIDIA、Quay、K8s 这些来源的镜像稳定通过,再排查 GPU 运行时和推理参数,问题会清楚很多。

相关推荐
鹤落晴春1 天前
【K8s】Pod调度、configMaps
云原生·容器·kubernetes
张忠琳1 天前
【runc 1.4.2】(Part 2)runc 1.4.2 超深度分析 — CLI层:main.go、命令文件、runner、信号处理、TTY
云原生·kubernetes·runc
极客先躯1 天前
高级java每日一道面试题-2026年02月02日-实战篇[Docker]-如何实现容器的持久化存储?
docker·容器·面试宝典·持久化·存储·韵味·java高级面试题
阿里云云原生1 天前
AI 提效是“假象”还是“红利”?用 LoongSuite + SLS 构建组织级 AI 编码度量看板
云原生
极客先躯1 天前
高级java每日一道面试题-2026年02月01日-实战篇[Docker]-Docker Volume 的生命周期管理是怎样的?
java·运维·docker·容器·持久化·架构图·容器卷
Java识堂1 天前
如何对微服务进行拆分?
微服务·云原生·架构
某林2121 天前
Isaac Sim 5.1.0 无头服务器部署与 RTX 显存段错误排障全记录
运维·服务器·docker·容器·isaac
m0_738120721 天前
Docker 环境下 Vulfocus 靶场搭建全流程(附镜像源问题解决方案)
运维·服务器·网络·安全·docker·容器
Plastic garden2 天前
K8s知识(3) Pod亲和性,调度
云原生·容器·kubernetes