3. 模型打包与容器化
模型训练完只是第一步,真正让它跑起来、对外提供服务,还得靠容器化。我见过太多团队在模型交付时手忙脚乱——环境不一致、依赖冲突、镜像几个G大……说白了,容器化就是给模型穿上“标准工装”,到哪都能跑。
🐳 用 Docker 打包模型——别让环境坑了你
我刚开始做模型部署时,最头疼的就是“在我机器上能跑啊”。后来我学乖了,所有模型一律用 Docker 封装。你想想看,训练时用的 Python 3.8,生产环境却是 3.10,一个语法糖就能让你服务挂掉。
一个典型的模型 Dockerfile 长这样:
# 多阶段构建:先装依赖,再拷贝模型
FROM python:3.9-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.9-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY model/ ./model/
COPY serve.py .
EXPOSE 8080
CMD ["python", "serve.py"]
💡 个人习惯:我一般把模型权重单独放在一个卷里,而不是打进镜像。这样更新模型不用重新 build 镜像,省时间。
⚡ 模型服务化:Flask / FastAPI / Triton 怎么选?
模型打包好之后,得给它配个“嘴巴”对外说话。这里我聊三个主流方案,每个我都踩过坑。
| 框架 | 适用场景 | 个人评价 |
|---|---|---|
| Flask | 快速原型、简单推理 | 轻量但性能一般,适合内部工具 |
| FastAPI | 高并发、异步推理 | 我最近的主力,自动文档太香了 |
| Triton | 多模型、GPU 优化、生产级 | 配置稍重,但吞吐量是真的猛 |
拿 FastAPI 举个例子,我经常这样写推理接口:
from fastapi import FastAPI
from pydantic import BaseModel
import joblib
app = FastAPI()
model = joblib.load("/app/model/model.pkl")
class InputData(BaseModel):
features: list[float]
@app.post("/predict")
async def predict(data: InputData):
pred = model.predict([data.features])
return {"prediction": pred.tolist()}
⚠️ 我曾经踩过的坑:用 Flask 时忘了加
threading 锁,结果高并发下模型 predict 出了乱数据。换成 FastAPI 后,异步 + 进程池就稳多了。
🔧 镜像优化技巧——把镜像从 2G 瘦到 300M
镜像太大,拉取慢、启动慢、存储贵。我接手过一个项目,镜像 2.3GB,每次部署等 5 分钟。后来我做了三件事:
- 选对基础镜像:用
python:3.9-slim甚至alpine,别上来就 full 版本。 - 多阶段构建:编译依赖和运行时分离,只拷贝必要的包。
- 清理缓存:
pip install --no-cache-dir,apt 装完立刻rm -rf /var/lib/apt/lists/*。
优化后的 Dockerfile 示例:
FROM python:3.9-slim AS base
RUN apt-get update && apt-get install -y --no-install-recommends \
libgomp1 && rm -rf /var/lib/apt/lists/*
FROM base AS builder
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM base
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY model/ ./model/
COPY serve.py .
CMD ["uvicorn", "serve:app", "--host", "0.0.0.0", "--port", "8080"]
📦 镜像层数不是越少越好——我习惯把变化频率不同的内容分开放。比如模型权重单独一层,依赖一层,代码一层。这样改代码时不用重新下载依赖。
🧪 Triton 容器化——生产级推理的正确姿势
如果你要部署多个模型,或者要用 GPU 做高性能推理,Triton 是绕不开的。我去年帮一个客户把三个模型塞进一个 Triton 容器,吞吐量翻了 4 倍。
它的容器化其实很标准,但要注意模型仓库的目录结构:
model_repository/
├── model_a/
│ ├── 1/
│ │ └── model.onnx
│ └── config.pbtxt
├── model_b/
│ ├── 1/
│ │ └── model.pt
│ └── config.pbtxt
然后一行命令启动:
docker run --gpus all -p 8000:8000 -p 8001:8001 \
-v /path/to/model_repository:/models \
nvcr.io/nvidia/tritonserver:23.12-py3 \
tritonserver --model-repository=/models
🔁 我的避坑指南:记得给 Triton 容器设置
--shm-size=1g,否则并发高时共享内存会爆。我曾经因为这个排查了一整天……
📊 镜像优化前后对比
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 镜像大小 | 2.1 GB | 312 MB |
| 构建时间 | 4 分 20 秒 | 1 分 05 秒 |
| 容器启动时间 | 12 秒 | 3 秒 |
| 首次推理延迟 | 8.5 秒 | 1.2 秒 |
你看,优化不是玄学,每一步都有实实在在的收益。我个人习惯把优化清单写进 CI 里,每次构建自动检查镜像大小,超过 500MB 就报警。
🎯 核心总结:模型打包与容器化,说白了就是三件事——标准化环境、选对服务框架、把镜像做小做快。别小看这一步,它决定了你后续流水线是丝滑还是卡顿。