《端到端MLOps全流程深度解析》

3. 环境搭建:开发环境、版本控制、依赖管理、容器化基础

3.1 核心原理:环境即代码的起点

在端到端 MLOps 中,环境搭建不是一次性“装软件”的动作,而是为后续所有自动化流程奠定基座。机器学习项目与普通软件项目最大的差异在于:模型的可复现性高度依赖环境的一致性。训练时用的 Python 版本、CUDA 驱动、依赖库版本,甚至系统环境变量,都会影响模型权重和推理结果。若环境不可复现,则实验对比、模型上线、回滚操作都失去意义。

本章围绕四个层次展开:开发环境(本地编码与调试)、版本控制(代码与数据变更追踪)、依赖管理(Python 包与系统库锁定)、容器化(运行环境标准化)。四者层层递进,最终目标是让“在我机器上能跑”变成“在任何地方都能跑”。

MLOps 环境四层架构与流转关系 ① 开发环境 本地 IDE(VS Code / PyCharm)· Jupyter Notebook · 远程 SSH 开发机 关键点:统一 Python 解释器路径,避免系统 Python 与虚拟环境混淆 ② 版本控制 Git 管理代码 · DVC 管理数据/模型 · Git LFS 处理大文件 关键点:.gitignore 排除虚拟环境、数据集、模型权重、临时文件 ③ 依赖管理 requirements.txt / pyproject.toml · pip-tools 锁定传递依赖 · conda 管理系统库 关键点:区分直接依赖与传递依赖,锁定完整版本哈希 ④ 容器化 Docker 镜像封装 OS + Python + 依赖 + 代码 · 保证开发/测试/生产环境一致 核心目标 可复现性 可移植性 可协作性 可审计性 版本可回溯 依赖可锁定 环境可重建 开发即生产 一键启动 零配置迁移

3.2 方法流程:从零搭建可复现的 ML 项目环境

3.2.1 开发环境:统一 Python 版本管理

推荐使用 pyenv 管理 Python 版本,避免系统级 Python 被项目依赖污染。每个项目创建独立的虚拟环境(python -m venv .venv 或 conda create -n mlops python=3.10)。在 VS Code 中通过 Python: Select Interpreter 指定项目虚拟环境路径,确保终端、Jupyter Kernel、调试器使用同一解释器。

# 安装 pyenv 后安装指定 Python 版本
pyenv install 3.10.12
pyenv local 3.10.12

# 创建项目虚拟环境
python -m venv .venv
source .venv/bin/activate   # Linux/macOS
# .venv\Scripts\activate    # Windows

3.2.2 版本控制:代码与数据分离管理

代码使用 Git 管理,数据与模型权重使用 DVC(Data Version Control)。DVC 将大文件存储在远程存储(S3、OSS、NAS),仓库中只保留 .dvc 指针文件。这样 Git 仓库保持轻量,同时数据版本可回溯。

# 初始化 DVC
dvc init
dvc remote add -d myremote s3://my-bucket/ml-data

# 跟踪数据目录
dvc add data/raw/
git add data/raw.dvc .gitignore
git commit -m "add raw dataset"

# 切换历史版本时同步数据
git checkout v1.0
dvc checkout
实践建议:在 .gitignore 中至少忽略以下内容:.venv/、__pycache__/、.pytest_cache/、*.pyc、*.pkl、*.h5、*.pt、data/raw/(若用 DVC 管理)、models/(若用 DVC 管理)。

3.2.3 依赖管理:锁定直接与传递依赖

仅使用 requirements.txt 记录顶层依赖是不够的——传递依赖(如 numpy 的底层 openblas 版本)可能在不同时间安装出不同结果。推荐使用 pip-tools 生成完整锁定文件:

# requirements.in 只写直接依赖
numpy==1.26.0
pandas==2.1.0
scikit-learn==1.3.0
torch==2.0.1

# 编译生成完整锁定文件
pip-compile requirements.in -o requirements.txt

# 按锁定文件安装
pip install -r requirements.txt

对于 GPU 相关依赖,建议使用 conda 管理 CUDA 和 cuDNN,再用 pip 管理 Python 包,避免 pip 安装的 CUDA 版本与系统驱动冲突。

3.2.4 容器化:Docker 镜像构建与分层缓存

Dockerfile 的编写顺序直接影响构建速度和缓存命中率。将不常变化的依赖层放在前面,频繁变化的代码层放在后面:

FROM python:3.10-slim

WORKDIR /app

# 先复制依赖锁定文件
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 再复制项目代码
COPY src/ ./src/
COPY config/ ./config/

CMD ["python", "src/train.py"]

构建镜像并推送至私有仓库:

docker build -t mlops-registry:5000/ml-project:latest .
docker push mlops-registry:5000/ml-project:latest

3.3 案例:一个典型 ML 项目的环境配置文件

以下是一个包含数据版本控制、依赖锁定和容器化的完整项目结构:

ml-project/
├── .gitignore
├── .dvc/
├── data/
│   ├── raw/               # DVC 跟踪
│   └── processed/         # DVC 跟踪
├── models/                # DVC 跟踪
├── src/
│   ├── train.py
│   ├── evaluate.py
│   └── utils.py
├── requirements.in        # 直接依赖
├── requirements.txt       # 锁定依赖
├── Dockerfile
├── docker-compose.yml     # 本地开发编排
└── README.md

对应的 docker-compose.yml 可一键启动开发环境:

version: "3.8"
services:
  dev:
    build: .
    volumes:
      - ./src:/app/src
      - ./data:/app/data
    environment:
      - PYTHONPATH=/app
      - CUDA_VISIBLE_DEVICES=0
    command: jupyter lab --ip=0.0.0.0 --port=8888

3.4 误区与复盘

常见误区 后果 正确做法
直接使用系统 Python 安装包 版本冲突、污染系统环境 每个项目独立虚拟环境
requirements.txt 不锁定版本 不同时间安装结果不同 使用 pip-compile 锁定完整依赖
将数据集和模型权重提交到 Git 仓库膨胀、clone 极慢 使用 DVC + 远程存储
Dockerfile 先复制全部代码再装依赖 每次代码变更都重新安装依赖 先复制依赖文件,利用层缓存
忽略 CUDA 版本与 PyTorch 的匹配 GPU 不可用或运行报错 用 conda 管理 CUDA,pip 安装对应版本 PyTorch
重要提醒:容器化并不能完全解决 GPU 驱动问题。Docker 容器共享宿主机的 GPU 驱动,因此宿主机必须安装与 CUDA 版本兼容的 NVIDIA 驱动。建议在 Dockerfile 中明确标注所需的 CUDA 基础镜像版本(如 nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu20.04),并在 README 中记录宿主机驱动的最低版本要求。

环境搭建的核心原则可以概括为:一切变更都应有记录,一切依赖都应有锁定,一切运行环境都应有镜像。当团队新成员加入时,只需执行 git clone、dvc pull、docker compose up 三步即可获得与生产一致的开发环境。这不仅是效率问题,更是 MLOps 中“可复现实验”和“可靠交付”的基础保障。