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

相关推荐
姚不倒8 小时前
Go语言进阶:接口、错误处理与并发编程(goroutine/channel/context)
云原生·golang
仙柒41511 小时前
Docker存储原理
运维·docker·容器
快乐的哈士奇14 小时前
LangFuse 自托管实战:选型理由、Docker 部署与常用配置全解析
运维·人工智能·docker·容器
weixin_4492900116 小时前
Docker + MySQL 在 Windows 11 上的本地安装部署文档
mysql·docker·容器
Ysn071916 小时前
中文乱码:在 Docker 容器中设置中文语言环境
运维·python·docker·容器
米高梅狮子16 小时前
01.CentOS-Stream-8-packstack安装OpenStack
linux·云原生·容器·kubernetes·centos·自动化·openstack
ZStack开发者社区17 小时前
全球化2.0 | ZStack亮相印尼云计算与数据中心大会 以新一代云底座助力数字印尼建设
服务器·云计算·gpu算力
亚空间仓鼠18 小时前
Docker容器化高可用架构部署方案(十三)
docker·容器·架构
米高梅狮子19 小时前
01.mysql的备份与恢复
运维·数据库·mysql·docker·容器·kubernetes·github
云游牧者20 小时前
K8S存储体系全解-从PV-PVC-SC到StatefulSet持久化实战
云原生·容器·kubernetes·pvc·pv·sc·进阶存储卷