4、CI/CD基础:Jenkins Pipeline入门、GitLab CI配置、GitHub Actions工作流编写
聊到模型部署,很多人第一反应是「训练完模型,手动拷到服务器上跑一下」。嗯,我刚开始也是这么干的。直到有一次,我半夜三点被叫起来,因为生产环境的模型版本搞混了……那滋味,真不好受。
从那以后,我彻底拥抱了CI/CD。说白了,就是把「手动部署」变成「自动流水线」。今天咱们就聊聊三个主流工具:Jenkins、GitLab CI、GitHub Actions。你不需要全学,但至少得知道它们各自怎么玩。
4.1 Jenkins Pipeline:老牌劲旅,灵活但重
Jenkins 是我用得最早的工具。它最大的优点是「啥都能干」,缺点也是「啥都要自己配」。我个人习惯用 Pipeline as Code,也就是把流水线写在 Jenkinsfile 里。
4.1.1 声明式 Pipeline 基础
先看一个最简单的例子。这个流水线会拉代码、装依赖、跑测试:
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git 'https://github.com/your-repo/model-deploy.git'
}
}
stage('Install Dependencies') {
steps {
sh 'pip install -r requirements.txt'
}
}
stage('Test') {
steps {
sh 'pytest tests/'
}
}
}
}
你想想看,这个结构是不是很清晰?每个 stage 就是一个步骤。我在项目中遇到过一个问题:如果 agent 不指定具体节点,Jenkins 可能会把任务调度到一台没有 GPU 的机器上。所以,记得加标签:
agent { label 'gpu-node' }
4.1.2 模型部署的典型 Pipeline
对于模型部署,我一般会加几个关键步骤:模型验证、打包、推送镜像。看这个例子:
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.example.com'
MODEL_VERSION = "${BUILD_NUMBER}"
}
stages {
stage('Validate Model') {
steps {
sh 'python scripts/validate_model.py --model_path ./models/latest.pkl'
}
}
stage('Build Docker Image') {
steps {
sh 'docker build -t ${DOCKER_REGISTRY}/model-server:${MODEL_VERSION} .'
}
}
stage('Push Image') {
steps {
sh 'docker push ${DOCKER_REGISTRY}/model-server:${MODEL_VERSION}'
}
}
stage('Deploy to Staging') {
steps {
sh 'kubectl set image deployment/model-server model-server=${DOCKER_REGISTRY}/model-server:${MODEL_VERSION}'
}
}
}
}
这里有个小技巧:MODEL_VERSION 用构建号,这样每个版本都是唯一的。我曾经因为版本号重复,导致回滚时找不到正确的镜像……嗯,从那以后我再也不敢偷懒了。
4.2 GitLab CI:与代码仓库深度绑定
如果你团队用 GitLab,那 GitLab CI 是天然的选择。它不需要额外部署 Jenkins 服务器,直接在仓库里放一个 .gitlab-ci.yml 就行。
4.2.1 基础配置结构
GitLab CI 的核心概念是 job。每个 job 可以指定 stage、镜像、脚本。看这个:
stages:
- build
- test
- deploy
variables:
MODEL_PATH: "models/latest.pkl"
build-job:
stage: build
script:
- python scripts/preprocess.py
- python scripts/train.py
artifacts:
paths:
- models/
expire_in: 1 week
test-job:
stage: test
script:
- pytest tests/
dependencies:
- build-job
deploy-job:
stage: deploy
script:
- python scripts/deploy.py --model ${MODEL_PATH}
only:
- main
你发现没?GitLab CI 的写法更「声明式」。我特别喜欢它的 artifacts 功能——可以把模型文件传给后面的 job,不用手动存到共享目录。
only 和 except 关键字很实用。比如我只想在 main 分支上触发部署,就用 only: - main。避免开发分支的测试版本污染生产环境。
4.2.2 使用 Runner 加速
GitLab CI 需要 Runner 来执行任务。你可以用共享 Runner,也可以自建。我建议自建,尤其是模型训练需要 GPU 时。配置 Runner 很简单:
# 在 Runner 机器上执行
gitlab-runner register \
--url https://gitlab.com/ \
--registration-token YOUR_TOKEN \
--executor docker \
--docker-image python:3.9 \
--docker-gpu-enabled true
注册完后,在 .gitlab-ci.yml 里指定 tag:
deploy-job:
tags:
- gpu-runner
script:
- echo "Running on GPU runner"
为什么会这样?因为如果不指定 tag,Runner 可能不会接这个任务。我踩过这个坑,CI 任务一直 pending,查了半天才发现是 tag 没对上。
4.3 GitHub Actions:云原生,生态丰富
如果你的代码在 GitHub 上,那 GitHub Actions 是最省心的选择。它免费额度对个人项目完全够用,而且市场上有大量现成的 action 可以直接用。
4.3.1 工作流文件结构
GitHub Actions 的工作流文件放在 .github/workflows/ 目录下,YAML 格式。看一个模型部署的例子:
name: Model Deploy Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Run tests
run: |
pytest tests/
deploy:
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Deploy model
run: |
python scripts/deploy.py
env:
API_KEY: ${{ secrets.API_KEY }}
你看,GitHub Actions 的写法非常「模块化」。每个 step 可以是一个 action(比如 actions/checkout),也可以是一段 shell 命令。我特别喜欢它的 secrets 功能——API 密钥、数据库密码这些敏感信息,直接存在仓库的 Settings 里,工作流里用 ${{ secrets.XXX }} 引用。
strategy:
matrix:
python-version: [3.8, 3.9, 3.10]
它会自动生成 3 个并行 job,省时省力。
4.3.2 模型部署的进阶用法
对于模型部署,我经常用 GitHub Actions 配合 Docker。比如构建镜像并推送到 Docker Hub:
jobs:
docker-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: yourusername/model-server:latest
这里要注意:docker/login-action 和 docker/build-push-action 都是社区维护的 action,用之前最好看看文档。我曾经因为版本号写错,导致 action 不兼容……嗯,从那以后我学会了锁定版本。
4.4 三个工具的对比与选择
说了这么多,到底该选哪个?我整理了一个表格,方便你对比:
| 特性 | Jenkins | GitLab CI | GitHub Actions |
|---|---|---|---|
| 部署方式 | 自建服务器 | 自带 Runner | 云原生 |
| 配置复杂度 | 高(需维护 Jenkins 服务) | 中 | 低 |
| 生态丰富度 | 插件多,但质量参差不齐 | 中等 | 丰富,官方维护的 action 多 |
| GPU 支持 | 需要手动配置节点标签 | 通过 Runner 标签支持 | 需要自建 Runner |
| 适合场景 | 大型企业,已有 Jenkins 基础设施 | 使用 GitLab 的团队 | 开源项目、小团队、个人 |
我个人建议:如果你从零开始,优先选 GitHub Actions。它免费、易用、生态好。如果公司已经用了 GitLab,那就用 GitLab CI,省去维护 Jenkins 的麻烦。至于 Jenkins……嗯,除非你团队有专门的运维人员,否则不太推荐。
好了,CI/CD 基础就聊到这儿。下一章咱们会深入讲讲「模型版本管理与回滚」,到时候会用到今天学的这些流水线知识。你先把 Jenkinsfile 和 .gitlab-ci.yml 练熟,后面就轻松了。