4. vLLM镜像拉取与版本兼容
镜像拉取失败,说白了就是K8s集群里最常见的翻车现场。我刚开始搞vLLM推理集群那会儿,光是在镜像这块就踩了不下七八个坑。今天咱们就把这块彻底聊透。
4.1 镜像仓库认证:ImagePullSecret
先说说最基础的问题——你连镜像都拉不下来,还谈什么推理?
私有仓库的认证,K8s里靠的是ImagePullSecret。我见过不少新手直接写imagePullPolicy: Always,然后镜像死活拉不下来,日志里一堆401 Unauthorized。嗯,说白了就是没配认证。
创建Secret的方式很简单,我习惯用命令行直接搞:
kubectl create secret docker-registry my-registry-key \
--docker-server=harbor.mycompany.com \
--docker-username=admin \
--docker-password=yourpassword \
--docker-email=admin@mycompany.com \
-n vllm-namespace
然后在Pod的YAML里引用它:
spec:
imagePullSecrets:
- name: my-registry-key
containers:
- name: vllm
image: harbor.mycompany.com/vllm/vllm-openai:latest
还有一个坑——如果你用的是ServiceAccount,可以在SA上直接挂ImagePullSecret,这样该SA创建的所有Pod都自动继承。我在项目中遇到过,团队里有人改了SA但忘了更新Pod,结果新Pod一直拉取失败,排查了半天。
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "my-registry-key"}]}' -n vllm-namespace
4.2 vLLM版本与CUDA版本的对应关系
镜像拉下来了,但跑不起来?大概率是版本不匹配。
vLLM对CUDA版本有硬性要求。我整理了一张表,你直接对照着看:
| vLLM版本 | 最低CUDA版本 | 推荐CUDA版本 | PyTorch版本 |
|---|---|---|---|
| 0.4.x | 11.8 | 12.1 | 2.1.x |
| 0.5.x | 12.1 | 12.1 / 12.4 | 2.2.x |
| 0.6.x | 12.1 | 12.4 | 2.3.x |
| 0.7.x(最新) | 12.4 | 12.4 / 12.6 | 2.4.x |
为什么会这样?因为vLLM底层依赖了FlashAttention和Triton,这些库对CUDA版本非常敏感。你想想看,CUDA 11.8上跑vLLM 0.6.x,编译阶段就直接报错了。
我个人习惯是:用官方镜像。vLLM官方在Docker Hub和NVIDIA NGC上都发布了预编译镜像,比如:
# 官方推荐
vllm/vllm-openai:latest
vllm/vllm-openai:v0.6.3
# NGC镜像(适合NVIDIA环境)
nvcr.io/nvidia/vllm:v0.6.3
nvidia/cuda:12.4.0-runtime-ubuntu22.04,别用devel版本,太大了。
4.3 常见镜像拉取失败错误
好,现在咱们看看实际报错长什么样。
4.3.1 ErrImagePull
这个错误最常见。Pod状态显示ErrImagePull,你用kubectl describe pod一看,原因可能是:
- 镜像不存在:拼写错误、tag写错、私有仓库里没有这个镜像
- 认证失败:ImagePullSecret没配、或者Secret里的密码过期了
- 网络不通:节点访问不了镜像仓库,尤其是自建Harbor没配证书
我曾经遇到过一个奇葩问题——镜像tag写的是v0.6.3-cuda12.4,但仓库里只有v0.6.3。K8s不会自动帮你匹配,它只会老老实实去拉你指定的tag。
4.3.2 ImagePullBackOff
ImagePullBackOff比ErrImagePull更让人头疼。它意味着K8s尝试拉取多次都失败了,然后进入退避状态。默认退避策略是:第一次等10秒,第二次20秒,40秒,80秒...直到5分钟。
我建议你直接看Pod的事件:
kubectl describe pod your-vllm-pod -n vllm-namespace
重点关注Events部分。如果是Failed to pull image "xxx": rpc error: code = Unknown desc = Error response from daemon: manifest for xxx not found,那就是镜像tag不存在。
如果是denied: requested access to the resource is denied,那就是认证问题。
kubectl describe pod看Eventskubectl logs看容器日志(如果容器启动过)- 手动在节点上
docker pull测试,排除网络问题
4.3.3 镜像拉取超时
大镜像(vLLM镜像通常5-10GB)拉取超时也很常见。K8s默认的拉取超时是5分钟,如果镜像太大或者网络慢,就会超时。
解决办法:
- 用
imagePullPolicy: IfNotPresent,避免每次重启都重新拉取 - 在节点上提前
docker pull好镜像 - 配置本地镜像仓库或使用镜像加速器
4.4 知识体系总览
我把这一章的核心逻辑画成了图,方便你理解:
4.5 避坑指南
最后,分享几个我实际踩过的坑:
- 镜像tag不要用
latest:在K8s里用latest,哪天镜像更新了,Pod重启后拉到的版本可能不一样。我建议固定到具体版本号。 - 注意镜像架构:如果你的节点是arm架构(比如Graviton),但镜像只支持amd64,那拉下来也跑不了。vLLM官方目前主要支持x86。
- 镜像拉取策略:开发环境用
Always,生产环境用IfNotPresent。别搞反了,否则生产环境每次重启都拉镜像,万一仓库挂了就全完了。
git commit hash作为tag,然后在K8s部署时指定这个tag。这样既能追踪版本,又不会出现意外覆盖。
嗯,镜像这块就聊到这儿。记住一句话:镜像拉不下来,先看认证,再看版本,最后查网络。按这个顺序排查,90%的问题都能解决。
公众号:蓝海资料掘金营,微信deep3321