3、vLLM镜像构建:Dockerfile编写、多阶段构建、镜像优化与推送至私有仓库
镜像构建这事儿,说白了就是给模型搭个「家」。你想想看,vLLM 跑在 K8s 上,总不能每台节点都手动装一遍环境吧?镜像就是那个标准化的运行环境。我刚开始搞 vLLM 部署时,镜像动不动就 5、6 个 G,拉取慢、启动慢,后来踩了不少坑才摸到门道。
3.1 基础镜像选型:别在起点就输了
选基础镜像,我个人的习惯是「够用就好」。vLLM 官方推荐用 NVIDIA CUDA 镜像,但版本得对上。我遇到过有人用 CUDA 12.0 跑 vLLM 0.4.0,结果算子编译不过去——说白了就是版本不匹配。
这里给个参考表:
| vLLM 版本 | 推荐 CUDA 版本 | 基础镜像 |
|---|---|---|
| 0.4.x | 11.8 | nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04 |
| 0.5.x | 12.1 | nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04 |
| 0.6.x | 12.4 | nvidia/cuda:12.4.0-cudnn9-devel-ubuntu22.04 |
注意:千万别用 runtime 镜像来构建!runtime 缺了编译工具链,vLLM 的 CUDA kernel 编译不过。我当初图省事用了 runtime,结果折腾了两天才发现是镜像的问题。
3.2 Dockerfile 编写:多阶段构建是王道
多阶段构建,说白了就是把「编译环境」和「运行环境」分开。你想想看,编译时需要 gcc、cmake、python-dev,运行时这些全用不上,留在镜像里就是浪费空间。
这是我常用的 Dockerfile 结构:
# ===== 第一阶段:编译阶段 =====
FROM nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04 AS builder
# 设置工作目录
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
git \
python3.10 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# 先拷贝依赖文件,利用 Docker 缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 安装 vLLM(从源码编译)
COPY . .
RUN pip install --no-cache-dir -e .
# ===== 第二阶段:运行阶段 =====
FROM nvidia/cuda:12.1.0-cudnn8-runtime-ubuntu22.04 AS runtime
WORKDIR /app
# 只拷贝编译产物和必要文件
COPY --from=builder /usr/local/lib/python3.10 /usr/local/lib/python3.10
COPY --from=builder /usr/local/bin /usr/local/bin
COPY --from=builder /app/vllm /app/vllm
# 暴露端口
EXPOSE 8000
# 设置入口
ENTRYPOINT ["python3", "-m", "vllm.entrypoints.openai.api_server"]
小技巧:把
requirements.txt 单独拷贝并安装,能充分利用 Docker 的层缓存。只要依赖没变,这层就不会重新构建,能省不少时间。
3.3 镜像优化:从 5G 到 1.5G 的蜕变
我刚开始做的镜像,足足 5.2G。拉一次镜像要等 5 分钟,K8s 滚动更新时 Pod 半天起不来。后来我做了三件事,把镜像压到了 1.5G:
- 清理 pip 缓存:
pip install --no-cache-dir能省 200-300MB - 删除编译中间文件:
.o、.pyc、__pycache__这些全删掉 - 使用 --no-install-recommends:apt-get 安装时加上这个,能省掉一堆用不上的推荐包
优化后的 Dockerfile 片段:
# 清理编译产物
RUN find /app -type f -name "*.pyc" -delete && \
find /app -type d -name "__pycache__" -delete && \
rm -rf /root/.cache/pip
# 使用 --no-install-recommends
RUN apt-get update && apt-get install -y --no-install-recommends \
python3.10 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
核心思路:镜像里只留「运行时必须的」东西。模型文件?别放镜像里,用 PVC 挂载。日志?写到 stdout,别落盘。配置文件?用 ConfigMap 注入。
3.4 推送至私有仓库:安全又高效
镜像构建好了,得推到仓库里。我一般用 Harbor 做私有仓库,支持镜像扫描和复制,比 Docker Hub 靠谱多了。
推送步骤:
- 打标签:
docker tag vllm:latest harbor.example.com/library/vllm:0.6.0 - 登录仓库:
docker login harbor.example.com - 推送镜像:
docker push harbor.example.com/library/vllm:0.6.0
避坑指南:我曾经因为没配置 Docker 的 insecure-registries,导致推送 HTTPS 证书验证失败。如果你用的是自签名证书,记得在
/etc/docker/daemon.json 里加上:{"insecure-registries": ["harbor.example.com"]}
3.5 自动化构建:CI/CD 集成
手动构建太累了,我习惯用 GitLab CI 或 GitHub Actions 自动化。这里给个 GitLab CI 的示例:
build-vllm:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
only:
- tags
每次打 tag 就自动构建并推送,省心省力。
3.6 本章知识体系
我把整个镜像构建的流程画了张图,方便你理解:
这张图把整个流程串起来了。从选基础镜像开始,到多阶段构建、优化、推送,最后用 CI/CD 自动化。每一步都有坑,但走通了之后,你会发现镜像构建其实挺有章法的。
我的经验:镜像构建这事儿,别追求一步到位。先跑起来,再慢慢优化。我第一个 vLLM 镜像 5G 多,现在稳定在 1.5G 左右。每次迭代都做减法,慢慢就找到感觉了。