4、流水线配置入门(以GitLab CI为例)
好,我们正式开始配置第一条完整的 CI 流水线。很多新手在接触 GitLab CI 时,上来就复制别人的 .gitlab-ci.yml,结果跑起来各种报错。我个人建议:先理解它的骨架逻辑,再动手写。
说白了,GitLab CI 的配置文件就是一个 YAML 文件,名字固定叫 .gitlab-ci.yml,放在项目根目录。GitLab 检测到它后,每次推送代码就会触发流水线。
核心概念一句话总结:流水线 = 阶段(stages)+ 任务(jobs),任务运行在阶段中,阶段按顺序执行。
4.1 最简单的例子:先跑起来
我们从一个能跑的 demo 开始。假设你有一个 Python 项目,只想跑一下单元测试。
stages:
- test
run_tests:
stage: test
script:
- pip install -r requirements.txt
- pytest
解释一下:
stages定义所有阶段,这里只有一个test。run_tests是任务名称,可以随便起,但最好有意义。stage: test指明这个任务属于哪个阶段。script是实际执行的 shell 命令。
简单吧?但别急着上线。我在项目里遇到过,有人把 script 拼写成了 scripts,结果管道跑了半天没动静——YAML 对大小写非常敏感。嗯,这一点要牢记。
4.2 多阶段流水线:从构建到部署
现实项目通常需要三个阶段:构建 → 测试 → 部署。举个例子,一个 Java Maven 项目:
stages:
- build
- test
- deploy
build_jar:
stage: build
script:
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
expire_in: 1 hour
run_unit_test:
stage: test
script:
- mvn test
needs: [build_jar]
deploy_staging:
stage: deploy
script:
- scp target/*.jar user@staging-server:/app/
only:
- main
这里有三个知识点:
- artifacts:把构建产物(jar 包)传给后续任务,默认会自动下载到后续任务的同路径下。
- needs:明确依赖关系——测试任务必须等构建任务成功后才会开始,不然测试时 jar 包还没生成呢。
- only:限定部署仅在
main分支触发,防止其他分支把开发中的代码推到生产环境。
小技巧:expire_in 设置过期时间,避免 artifacts 挤爆服务器。我个人习惯设成 1 小时,因为大多数管道跑完后 10 分钟就不需要 jar 包了。如果你不设,GitLab 默认保留 30 天。
4.3 环境变量:让配置跑起来
代码里硬编码密码?那简直是定时炸弹。GitLab CI 支持在项目设置里配置变量,然后在 .gitlab-ci.yml 中引用:
deploy_prod:
stage: deploy
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push my-app:latest
only:
- tags
$CI_REGISTRY_USER 这类以 CI_ 开头的变量是 GitLab 预定义的。你还可以在 Settings → CI/CD → Variables 中自定义变量(如 SSH_PRIVATE_KEY),注意勾选 Masked 来脱敏。
⚠️ 踩坑记录:我曾经把数据库密码存在 Variables 里,但忘了加 Masked,结果打印日志时密码直接露出来了……幸好只是测试环境。
4.4 before_script 与 after_script
如果每个任务都要安装依赖或清理环境,重复写 script 太麻烦了。可以用 before_script 和 after_script 抽公共步骤:
default:
before_script:
- echo "开始时间: $(date)"
- npm ci
stages:
- lint
- build
lint_code:
stage: lint
script:
- eslint .
build_app:
stage: build
script:
- npm run build
after_script:
- echo "构建完成,清理临时文件"
- rm -rf node_modules
注意:default 块的 before_script 会作用于所有任务,除非某个任务单独覆盖。
4.5 流水线核心逻辑一览
光看文字可能不够直观。我画了一张图,把以上内容串起来——你可以一眼看清整个流程:
4.6 任务间的依赖控制
默认情况下,同一阶段的任务会并行执行。但有时候你需要控制先后顺序,比如先跑 lint 再跑单元测试,免得太大的代码问题后面才发现。这时可以这样:
lint:
stage: test
script:
- flake8
unit:
stage: test
script:
- pytest
needs: [lint]
needs 不仅跨阶段,也适用于同阶段的不同任务。需要注意的是,如果使用了 needs,GitLab 不会自动等待该阶段所有任务完成,而是只等待你指定的任务完成。
秘诀:needs 可以大幅提升流水线速度。以前我试用 parallel 多任务时,发现测试 job 一直在空等,加上 needs 后构建完立即触发测试,节省了 30% 的时间。
4.7 实用表格:常用关键字速查
| 关键字 | 用途 | 示例 |
|---|---|---|
| script | 执行的命令列表 | - echo Hello |
| before_script | 任务前执行 | - npm ci |
| after_script | 任务后执行(失败也会执行) | - rm -rf tmp |
| artifacts | 保留产物供后续任务 | paths: [dist/] |
| needs | 声明依赖任务 | needs: [build] |
| only/except | 分支/标签过滤 | only: - main |
| variables | 自定义变量 | KEY: value |
4.8 常见陷阱与解决
我列出几个自己踩过的坑,希望能帮你少走弯路。
- YAML 缩进错误:GitLab CI 用空格(别用 Tab),缩进不一致会直接报错。建议在本地用
yamllint检查。 - 任务名不能包含特殊字符:比如
run test有空格,编译不过。用下划线run_test或驼峰runTest。 - artifacts 下载路径:后续任务的
script执行时,工作目录是项目根目录,而 artifacts 会解压到当前目录,所以直接使用相对路径即可。我见过有同事在needs里指定任务却忘了写artifacts: true——实际上needs默认会传递 artifacts,不需要额外声明,但容易混淆。 - dependencies 与 needs 的差异:
dependencies是旧式语法,只在需要限制 artifacts 下载时才用。新项目建议统一用needs。
4.9 个人经验:从小项目开始
如果你刚开始接触 GitLab CI,我强烈建议只配一个 test 阶段,跑通一个简单的 echo 或 pytest。别一上来就上五六个阶段。我在团队里见过很多次,新人信心满满写了个大配置文件,结果跑不通也不知道哪里错。先让流水线绿起来,再逐步加功能。
另外,.gitlab-ci.yml 的 CI Lint 工具很好用——在 GitLab 界面里找到 CI/CD → Pipelines → CI Lint,粘进去就能验证语法。嗯,别偷懒,每次改完先验证。