这次在一台新加的 GPU 节点上复现推理环境,先踩到的不是模型加载错误,而是容器镜像没有拉下来。
Pod 状态一直停在:
ContainerCreating
事件里能看到:
Pulling image
context deadline exceeded
ImagePullBackOff
所以这次没有从模型参数开始看,而是先把节点镜像预热单独做了一遍。
镜像清单
这套环境里至少需要这些镜像:
| 镜像 | 作用 |
|---|---|
vllm/vllm-openai |
推理服务 |
nvidia/cuda |
CUDA runtime 验证 |
prometheus/prometheus |
指标采集 |
pause |
K8s 基础镜像 |
先在可以执行 Docker 的机器上按来源测:
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
这里的目标只是确认镜像入口可用,不代表推理服务已经验证完成。
节点侧 crictl 验证
目标节点使用 containerd,所以真正要测的是 crictl pull。
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
查看本地镜像:
crictl images | grep -E "vllm|cuda|prometheus|pause"
如果这一步失败,Deployment 里继续调副本数也没有用。Pod 还是会停在镜像阶段。
临时 DaemonSet
节点多的时候,我会用一个临时 DaemonSet 做预热。下面这个配置只演示思路,实际使用时要按节点标签和镜像版本调整。
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
执行后观察:
kubectl apply -f gpu-image-prewarm.yaml
kubectl rollout status daemonset/gpu-image-prewarm -n ops
kubectl get pod -n ops -o wide
预热完成后,再扩容真正的推理服务。
两个小细节
第一个细节是不要只在本地 Docker 验证。K8s 节点如果是 containerd,还是要在节点上用 crictl pull 走一遍。
第二个细节是不要长期依赖 latest。这次示例里为了阅读简单用了 latest,正式环境更适合固定 tag 或 digest,避免预热的是一个版本,发布时拉到另一个版本。
记录
这次复现后,我把顺序记成了:
- 先看 Pod event。
- 如果是
ImagePullBackOff,先处理镜像阶段。 - 按来源预拉 Docker Hub、NVIDIA、Quay、K8s 镜像。
- 目标节点用
crictl pull验证。 - 镜像通过后,再看 RuntimeClass、GPU device plugin、模型路径和应用日志。
这样能把镜像问题和推理问题分开。否则容器还没创建成功,就开始调模型参数,很容易浪费时间。