3、Docker基础与vLLM镜像:Dockerfile编写、构建vLLM推理镜像、推送镜像到私有仓库
说实话,很多刚接触K8s的朋友,第一步就卡在镜像上。
你想想看,K8s本身不跑代码,它只跑容器。容器从哪来?从镜像来。镜像怎么造?靠Dockerfile。所以这一章,咱们就把vLLM的镜像从零到一搞出来,再推到私有仓库里存着。这是后续所有部署工作的基础,绕不开的。
核心逻辑一句话:写Dockerfile → 构建镜像 → 推送到仓库 → K8s拉取运行。
3.1 编写Dockerfile——从零搭一个vLLM推理环境
Dockerfile说白了就是一张「菜谱」。你告诉Docker:用什么锅(基础镜像)、放什么料(依赖)、最后怎么上菜(启动命令)。
我个人习惯,写Dockerfile之前先想清楚三件事:
- 基础镜像选什么?——vLLM官方推荐用CUDA 12.x以上的镜像,因为要用到GPU。
- 装哪些东西?——Python、PyTorch、vLLM包、还有你的模型文件(或者挂载路径)。
- 启动命令是什么?——一般是
vllm serve或者你自己写的入口脚本。
下面是我在实际项目中用过的一个Dockerfile,精简过,但核心骨架都在:
# 1. 基础镜像 —— 带CUDA的Python环境
FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04
# 2. 设置工作目录
WORKDIR /app
# 3. 安装系统依赖
RUN apt-get update && apt-get install -y \
python3 python3-pip git \
&& rm -rf /var/lib/apt/lists/*
# 4. 安装vLLM(这里指定版本,避免踩坑)
RUN pip3 install vllm==0.4.0
# 5. 复制你的启动脚本(如果有)
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# 6. 暴露端口(vLLM默认用8000)
EXPOSE 8000
# 7. 启动命令
ENTRYPOINT ["/app/entrypoint.sh"]
💡 我的经验:别把模型文件直接打进镜像里。镜像会变得巨大(动不动几十G),每次推送都痛苦。我一般用K8s的PVC或者HostPath挂载模型目录,镜像只装推理引擎。
3.2 构建vLLM推理镜像——docker build实战
Dockerfile写好了,接下来就是构建。命令很简单,但有几个细节我吃过亏,得跟你念叨念叨。
# 在Dockerfile所在目录执行
docker build -t vllm-inference:0.4.0 .
-t 是给镜像打标签,格式是 镜像名:版本号。我强烈建议你别偷懒用 latest,原因后面讲。
构建过程中,你会看到一层一层的输出。每一行 RUN、COPY 都会生成一个镜像层。层数太多会导致镜像臃肿,所以:
- 能用
&&合并的RUN命令就合并 - 用完的临时文件记得删掉(比如
apt-get clean)
⚠️ 我曾经踩过的坑:有一次构建镜像时网络超时,pip下载vLLM包到一半断了。后来我加了 --timeout=120 参数,或者用国内镜像源(比如 -i https://pypi.tuna.tsinghua.edu.cn/simple),才稳定下来。
构建完成后,用 docker images 看一眼:
REPOSITORY TAG IMAGE ID CREATED SIZE
vllm-inference 0.4.0 abc123def456 2 minutes ago 3.2GB
嗯,3.2GB,正常。vLLM + CUDA runtime 本身就这个体量。
3.3 推送镜像到私有仓库——让K8s能拉到你
镜像在本地构建好了,但K8s集群里的节点怎么拿到它?你得推到一个仓库里。公有仓库(Docker Hub)速度慢,而且公司内部一般不允许。所以私有仓库是标配。
我常用的是Harbor,当然你也可以用阿里云ACR、华为云SWR,或者自建一个Docker Registry。流程都一样:
- 给镜像打上仓库地址的标签
- 登录私有仓库
- 推送
# 1. 打标签 —— 格式:仓库地址/项目名/镜像名:版本
docker tag vllm-inference:0.4.0 \
harbor.internal.com/ai-team/vllm-inference:0.4.0
# 2. 登录
docker login harbor.internal.com
# 输入用户名和密码
# 3. 推送
docker push harbor.internal.com/ai-team/vllm-inference:0.4.0
关键点:K8s在拉取镜像时,会完全匹配这个地址。所以你在K8s的YAML里写 image: harbor.internal.com/ai-team/vllm-inference:0.4.0,它就能找到。
推送成功后,你可以在Harbor的Web界面看到镜像的详细信息,包括层数、大小、推送时间。我每次都会确认一下,确保没推错。
3.4 避坑指南——我替你们踩过的雷
这一节全是干货,都是真金白银换来的教训。
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 镜像构建失败,提示CUDA版本不匹配 | 基础镜像的CUDA版本和vLLM要求的PyTorch版本冲突 | 去vLLM官方GitHub看Release Notes,找到对应的CUDA版本 |
| 推送镜像超时 | 镜像太大,网络带宽不够 | 分块推送(默认就是分层的),或者换内网仓库 |
K8s拉取镜像报错 ImagePullBackOff |
镜像标签写错了,或者私有仓库没配Secret | 检查YAML里的image字段,确保和推送时一致;配置 imagePullSecrets |
| 容器启动后立刻退出 | ENTRYPOINT脚本有问题,或者模型路径不对 | 先本地 docker run -it 调试,确认能正常启动再推 |
💡 一个小习惯:我每次构建完镜像,都会先在本地跑一下 docker run --gpus all vllm-inference:0.4.0,确认GPU能识别、模型能加载。这一步能省掉后面K8s排错的大量时间。
3.5 本章小结
到这一步,你已经掌握了:
- 怎么写一个能用的Dockerfile
- 怎么构建vLLM推理镜像
- 怎么推送到私有仓库
说白了,镜像就是K8s世界的「可执行文件」。没有它,后面所有编排都是空谈。下一章我们会用这个镜像,真正在K8s上跑起一个推理服务。但今天先把基础打牢——镜像搞不定,后面全是坑。