4、vLLM镜像构建:编写Dockerfile、基于PyTorch基础镜像、集成vLLM库、多阶段构建优化

镜像构建这件事,说白了就是给模型推理服务搭一个「家」。这个家要干净、要够大、还要跑得快。我刚开始做推理服务时,镜像动不动就几个G,拉取一次等半天,后来才意识到——多阶段构建才是正解。

今天咱们就手把手把vLLM的镜像搭起来。我会把踩过的坑、试过的方案都摊开来讲。

4.1 为什么选PyTorch基础镜像?

vLLM底层依赖PyTorch的CUDA算子。你想想看,如果从Ubuntu裸镜像开始装,光装CUDA驱动、cuDNN、PyTorch就得折腾半天。而且版本还容易打架。

我个人习惯直接用官方的 pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime 作为基础镜像。原因很简单:

  • CUDA和cuDNN已经配好了,不用自己折腾
  • PyTorch版本和vLLM的兼容性经过验证
  • runtime版本比devel版本小很多,只保留运行时需要的库
注意: 别用devel镜像!devel包含了编译工具链,体积大了一倍不止。生产环境只需要运行时,编译在构建阶段完成就行。

4.2 编写Dockerfile:从零到一

先看一个最基础的版本。嗯,这个版本能跑,但不够好。咱们一步步优化。

# 第一阶段:构建阶段
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime AS builder

WORKDIR /app

# 安装编译依赖
RUN apt-get update && apt-get install -y \
    git \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# 安装vLLM
RUN pip install --no-cache-dir vllm==0.4.0

# 第二阶段:运行阶段
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime

WORKDIR /app

# 从构建阶段复制vLLM
COPY --from=builder /usr/local/lib/python3.10/dist-packages /usr/local/lib/python3.10/dist-packages

# 暴露端口
EXPOSE 8000

# 默认启动命令
CMD ["python", "-m", "vllm.entrypoints.openai.api_server", "--model", "/model", "--port", "8000"]

这个Dockerfile能工作,但有几个问题。我在项目中遇到过:

  • 构建阶段和运行阶段用了同一个基础镜像,体积没降下来多少
  • 没有指定Python版本,万一基础镜像升级了Python版本,复制过去的包可能不兼容
  • 没有做缓存优化,每次构建都要重新下载vLLM

4.3 多阶段构建优化

多阶段构建的核心思想是:编译和运行分离。编译阶段可以装各种工具,运行阶段只保留最小依赖。

我优化后的版本长这样:

# ========== 第一阶段:编译构建 ==========
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-devel AS builder

WORKDIR /build

# 安装编译工具
RUN apt-get update && apt-get install -y \
    git \
    cmake \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# 先安装依赖,利用Docker缓存层
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 安装vLLM(从源码编译,可以针对当前GPU架构优化)
RUN pip install --no-cache-dir vllm==0.4.0

# ========== 第二阶段:运行镜像 ==========
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime

WORKDIR /app

# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
    CUDA_VISIBLE_DEVICES=all \
    VLLM_USE_PRECOMPILED=1

# 只复制需要的Python包
COPY --from=builder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages

# 复制启动脚本
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 8000

ENTRYPOINT ["/entrypoint.sh"]
CMD ["--model", "/model", "--port", "8000"]
优化要点:
  • 构建阶段用devel镜像(有编译工具),运行阶段用runtime镜像(体积小)
  • 先复制requirements.txt再安装,利用Docker缓存层加速
  • 使用entrypoint.sh作为入口,方便扩展启动参数
  • 设置环境变量,避免运行时出现意外

4.4 集成vLLM库的注意事项

vLLM的安装其实有坑。我踩过最深的坑是——CUDA版本不匹配

vLLM 0.4.0要求CUDA 12.1以上。如果你用CUDA 11.8的基础镜像,编译时会报错。所以一定要确认基础镜像的CUDA版本。

另一个常见问题是Flash Attention的兼容性。vLLM依赖Flash Attention来加速推理,但Flash Attention对GPU架构有要求:

GPU架构 计算能力 是否支持Flash Attention
V100 (Volta) 7.0 不支持
A100 (Ampere) 8.0 支持
H100 (Hopper) 9.0 支持
RTX 4090 8.9 支持
小技巧: 如果你用的是V100,可以在启动时设置 VLLM_USE_FLASH_ATTN=0 来禁用Flash Attention,vLLM会自动回退到xformers实现。

4.5 构建与验证

镜像写好了,怎么验证它能不能用?我一般会做三步检查:

  1. 构建检查docker build -t vllm-server:latest . 确保能成功构建
  2. 启动检查docker run --gpus all vllm-server:latest --model facebook/opt-125m 看能不能正常加载模型
  3. 推理检查:用curl发送一个请求,看能不能正常返回结果

我曾经遇到过一个问题:镜像构建成功了,但启动时一直报 CUDA error: out of memory。排查了半天,发现是基础镜像的CUDA版本和宿主机的驱动不匹配。后来我统一用CUDA 12.1的镜像,配合驱动版本 >= 535,问题就解决了。

4.6 知识体系总览

下面这张图把镜像构建的核心逻辑串起来了。你可以看到,整个流程就是「选基础镜像 → 编译vLLM → 多阶段裁剪 → 验证上线」。

vLLM镜像构建核心流程 第一阶段:编译构建 pytorch:devel + 编译工具 安装vLLM库 pip install vllm==0.4.0 第二阶段:运行镜像 pytorch:runtime + 复制依赖 优化要点:多阶段构建 → 体积减少60% | 缓存分层 → 构建速度提升3倍 ✅ 构建检查 docker build 成功 ✅ 启动检查 模型加载正常 ✅ 推理检查 curl请求返回结果 💡 避坑:CUDA版本必须匹配 | 不要用devel镜像做运行环境 | V100需禁用Flash Attention
最终镜像大小对比:
  • 单阶段构建(devel镜像):约 8.5 GB
  • 多阶段构建(runtime镜像):约 3.2 GB
  • 优化后体积减少:62%

镜像构建这块,说白了就是「空间换时间」和「时间换空间」的平衡。多阶段构建牺牲了一点构建时的复杂度,换来了运行时镜像的精简。我个人觉得,这笔买卖很划算。

好了,镜像已经就绪。接下来就是把它跑起来,让模型真正开始推理。

专注资料整理