从脚本走到自动化:AI持续集成笔记

最近在做AI项目的时候,发现一个很现实的问题:每次模型训练完成、测试通过,推送到生产环境的时候,都得手动走一堆流程。

痛定思痛,决定认真搞一套针对AI项目的CI/CD流水线。

背景和需求

为什么要折腾这个

最近在做AI项目的时候,发现一个很现实的问题:每次模型训练完成、测试通过,推送到生产环境的时候,都得手动走一堆流程。打包、上传、部署、验证,光是想想就头疼。

更糟糕的是,有次因为手动操作失误,把测试环境的模型直接推到了生产环境,造成了不小的影响。痛定思痛,决定认真搞一套针对AI项目的CI/CD流水线。

现实中的痛点

AI项目的CI/CD跟传统软件项目不太一样,主要有这几个难点:

  • 模型文件巨大:一个训练好的模型动辄几百MB甚至几个GB,传输速度慢,存储成本高
  • 依赖环境复杂:PyTorch、TensorFlow、CUDA版本、Python版本,一个不对就跑不起来
  • 推理性能验证:不能只看"能跑就行",还得验证响应时间、显存占用这些指标
  • 数据漂移监控:生产环境的数据分布可能跟训练时不一样,需要及时发现

实现过程

技术选型

在技术选型上,我们面临几个选择:

  • Jenkins vs GitHub Actions vs GitLab CI:最后选了GitHub Actions,原因很简单:项目代码本来就在GitHub上,用Actions最省事,而且免费额度对我们这个小团队够用了
  • Docker vs 宿主机部署:毫无疑问选Docker,环境隔离是必须的
  • 模型存储方案:考虑过自己搭MinIO,但最后还是用了阿里云OSS,成本更低,也更稳定

整体架构

先上一个架构图,让大家有个整体概念:

graph LR A[代码提交] --> B[GitHub Actions触发] B --> C[单元测试] C --> D[模型训练/微调] D --> E[模型打包] E --> F[Docker镜像构建] F --> G[镜像推送到Registry] G --> H[部署到Staging] H --> I{自动化测试} I -->|通过| J[部署到Production] I -->|失败| K[回滚] J --> L[监控告警]

具体实现

1. 单元测试阶段

这个阶段主要是保证代码质量,我们用了pytest + coverage:

- name: Run unit tests
  run: |
    pip install pytest pytest-cov
    pytest --cov=. --cov-report=xml

这里有个小坑:模型的单元测试怎么写?不能真的把完整的训练流程跑一遍,那样太慢了。我们的做法是:

  • 用非常小的mock数据
  • 只测试模型结构是否正确
  • 测试推理接口是否工作

2. 模型训练/微调

如果是训练新模型,这个阶段会很耗时。我们做了一些优化:

- name: Train model
  env:
    USE_GPU: "true"
  run: |
    python scripts/train.py \
      --epochs 10 \
      --batch-size 32 \
      --output-dir ./models

优化策略包括:

  • 只有在特定分支(如train/*)才触发完整训练
  • 训练时自动保存checkpoint,防止中途失败前功尽弃
  • 使用混合精度训练加速

3. 模型打包

模型打包这个环节,我们做了这些事情:

# scripts/package_model.py
import torch
import joblib
import hashlib

def package_model(model_path, output_path):
    # 1. 加载模型
    model = torch.load(model_path, map_location='cpu')

    # 2. 转换为更高效的格式
    scripted_model = torch.jit.script(model)

    # 3. 保存模型元数据
    metadata = {
        'version': '1.0.0',
        'created_at': datetime.now().isoformat(),
        'model_hash': hashlib.md5(open(model_path, 'rb').read()).hexdigest()
    }

    # 4. 打包
    joblib.dump({
        'model': scripted_model,
        'metadata': metadata
    }, output_path)

4. Docker镜像构建

Dockerfile的设计也花了不少心思:

FROM python:3.10-slim

# 安装必要的系统依赖
RUN apt-get update && apt-get install -y \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*

# 复制模型和代码
COPY models/ /app/models/
COPY src/ /app/src/

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["python", "-m", "src.api"]

这里有几个关键点:

  • slim版本的基础镜像,减小镜像体积
  • 显式安装必要的系统依赖,避免运行时出错
  • 分层构建,利用缓存加快构建速度

5. 部署到Staging

部署阶段我们用了Kubernetes:

- name: Deploy to staging
  uses: azure/k8s-deploy@v4
  with:
    manifests: |
      k8s/deployment.yaml
      k8s/service.yaml
    images: |
      ghcr.io/${{ github.repository }}:staging-${{ github.sha }}
    kubeconfig: ${{ secrets.KUBE_CONFIG_STAGING }}

6. 自动化测试

部署到Staging后,会自动运行一套集成测试:

# tests/integration_test.py
import requests
import time

def test_inference_endpoint():
    # 等待服务启动
    time.sleep(30)

    # 测试推理接口
    response = requests.post(
        'http://staging-api.example.com/predict',
        json={'input': 'test input'}
    )

    assert response.status_code == 200
    assert 'prediction' in response.json()

    # 验证响应时间
    assert response.elapsed.total_seconds() < 1.0

踩过的坑

坑1:模型文件太大,CI runner内存不够

刚开始把完整模型都放在GitHub仓库里,结果CI runner直接OOM了。

解决方案:

  • 大模型文件改用LFS(Large File Storage)管理
  • 训练过程中的临时文件保存在runner的临时目录,不提交到仓库
  • 最终模型上传到OSS,仓库里只保留引用

坑2:GPU资源分配问题

GitHub Actions的免费runner没有GPU,想用GPU得自己装self-hosted runner。

解决方案:

  • 训练阶段用自己搭建的GPU runner
  • 其他阶段(测试、打包、部署)用GitHub的免费runner
  • 通过workflow条件控制不同阶段在不同runner上执行

坑3:环境一致性

本地能跑,CI环境报错,Staging环境又不一样。

解决方案:

  • 严格固定依赖版本(不用>=这种模糊版本)
  • 用Docker保证环境一致性
  • 在每个环境都跑一遍相同的测试脚本

坑4:部署失败回滚

有次部署后才发现模型性能有问题,但已经来不及了。

解决方案:

  • 部署前先在Staging环境做完整的性能测试
  • 生产环境保留最近3个版本的镜像,方便快速回滚
  • 设置监控告警,发现异常立即触发回滚

结果和效果

折腾了一圈之后,现在的效果是这样的:

效率提升

  • 部署时间:从原来的2-3小时(手动)缩短到30分钟(自动化)
  • 故障恢复:从原来的半天缩短到10分钟
  • 人力投入:从原来每次部署需要2个人全程监控,变成只需偶尔看一眼

质量提升

  • 测试覆盖率:从60%提升到85%
  • 线上故障:从每个月2-3次降低到几乎为零
  • 模型性能:有了监控,能及时发现性能下降

团队协作

  • 代码评审变得更规范,因为每次合并都会触发CI
  • 新人上手更容易,有完整的文档和自动化流程
  • 减少了因为"这个环境能跑"而产生的争论

总结

搭建AI项目的CI/CD流水线,初期确实会花不少时间,尤其是要踩各种坑。但一旦搭建起来,带来的回报是巨大的。

最重要的经验是:不要追求一步到位。可以先从最简单的自动测试开始,然后逐步加入训练、打包、部署等环节。每个环节都要踩过坑、解决问题,这样整个流程才会真正稳固。

最后,记住一个原则:自动化是为了让人从重复劳动中解放出来,而不是为了自动化而自动化。如果某个自动化步骤反而增加了工作量,那就需要重新思考了。

这个过程就像搭积木,一块一块地搭建,一块一块地稳固。虽然耗时,但最终的成果会让你觉得一切都值得。

版权声明: 本文首发于 指尖魔法屋-从脚本走到自动化:AI持续集成笔记https://blog.thinkmoon.cn/post/294-ai-ci-cd-pipeline-script-automated-practice/) 转载或引用必须申明原指尖魔法屋来源及源地址!