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 运行时和推理参数,问题会清楚很多。

相关推荐
阿里云云原生15 小时前
AgentTeams 和 Claude Tag 都进入群聊模式,是新范式还是新叙事?
云原生·agent
阿里云云原生2 天前
Higress v2.2.3 发布:正式入驻 CNCF Sandbox,AI Gateway 与 Ingress 迁移能力双向加固
云原生
lichenyang4532 天前
Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像
docker·容器
lichenyang4532 天前
Docker 学习笔记(三):Docker 网络、bridge、子网和容器互通
docker·容器
lichenyang4532 天前
Docker 学习笔记(二):docker run 的参数到底在控制什么?
docker·容器
阿里云云原生3 天前
香港站【企业 AI Agent 工程化实战专场】来啦,邀您7月9日见!
云原生·agent
阿里云云原生3 天前
研发域与运维域的“数字握手”:通过 Agentic Skills 实现 DevOps 全链路自动化
云原生
运维开发故事5 天前
基于 Arthas 的多集群在线诊断系统设计与实现
kubernetes
Patrick_Wilson6 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
阿里云云原生7 天前
AI 开发新常态:当 Cursor、Claude、Codex 并行,如何统一管理散落的 Skill 资产?
云原生·ai编程