3、持续集成(CI)实战:Jenkins/GitLab CI流水线设计、自动化构建与触发策略、构建产物管理

持续集成这件事,说白了就是让代码从提交到可部署的中间过程自动化。我见过太多团队,每天花大量时间在「等构建」、「手动打包」、「找构建产物」上。其实这些都可以交给工具去做。

今天我们就聊聊 Jenkins 和 GitLab CI 这两大主流工具。我个人习惯是:小团队用 GitLab CI,大团队或复杂场景用 Jenkins。为什么?往下看你就明白了。

3.1 流水线设计:从手动到自动

流水线设计是 CI 的核心。你想想看,如果没有清晰的流水线,构建过程就像一团乱麻。

我建议把流水线分为三个阶段:

  • 触发阶段:代码提交、PR 合并、定时触发
  • 构建阶段:编译、单元测试、代码扫描
  • 产出阶段:打包、归档、通知

嗯,这里要注意:流水线不是越复杂越好。我在项目中遇到过,有人把部署也塞进 CI 流水线,结果每次构建都要等 20 分钟。其实 CI 只负责到「构建产物」这一步就够了。

3.1.1 Jenkins 流水线示例

Jenkins 的流水线用 Groovy 写。我个人喜欢用声明式语法,可读性更好。

pipeline {
    agent any

    stages {
        stage('检出代码') {
            steps {
                checkout scm
            }
        }
        stage('编译') {
            steps {
                sh 'mvn clean compile'
            }
        }
        stage('单元测试') {
            steps {
                sh 'mvn test'
            }
        }
        stage('打包') {
            steps {
                sh 'mvn package'
            }
        }
        stage('归档产物') {
            steps {
                archiveArtifacts artifacts: 'target/*.jar'
            }
        }
    }
}

这段代码看起来简单,但实际项目中我踩过坑。比如 archiveArtifacts 这一步,如果产物太大,Jenkins 的存储会爆炸。我曾经因为没限制归档大小,导致 Jenkins 磁盘满了,整个团队停摆半天。

避坑指南:Jenkins 归档产物建议设置保留天数(比如 7 天),或者只归档关键产物(如 jar 包,不要归档日志)。

3.1.2 GitLab CI 流水线示例

GitLab CI 用 YAML 写,更轻量。我特别喜欢它的 rules 语法,触发策略非常灵活。

stages:
  - build
  - test
  - package

build-job:
  stage: build
  script:
    - mvn clean compile
  only:
    - main
    - merge_requests

test-job:
  stage: test
  script:
    - mvn test
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      when: always
    - when: manual

package-job:
  stage: package
  script:
    - mvn package
  artifacts:
    paths:
      - target/*.jar
    expire_in: 7 days

你看,GitLab CI 的 artifacts 自带过期时间,这个设计比 Jenkins 聪明。我刚开始用 GitLab CI 时,没注意 expire_in 参数,结果产物越积越多。后来加了 7 天过期,磁盘压力小多了。

3.2 自动化构建与触发策略

触发策略是 CI 的「开关」。你想想看,如果每次保存代码都触发构建,那开发人员会疯掉的。

我常用的触发策略有三种:

触发方式 适用场景 注意事项
代码提交触发 主分支、开发分支 避免频繁提交导致排队
PR/MR 触发 功能分支合并前 必须通过所有检查才能合并
定时触发 夜间构建、全量回归 避开高峰期,比如凌晨 2 点

我个人习惯是:主分支用提交触发,功能分支用 PR 触发,全量回归用定时触发。这样既保证了效率,又不会浪费资源。

小技巧:在 GitLab CI 中,可以用 rules 实现「只有特定文件变更时才触发」。比如只改了 README,就没必要跑全量构建。

3.2.1 避免「构建风暴」

为什么会发生构建风暴?说白了就是触发太频繁。我记得有一次,团队在合并大功能时,20 个人同时提交,Jenkins 队列排了 50 多个任务。最后构建机 CPU 跑满,所有任务都超时了。

解决方案很简单:

  • 设置并发限制(Jenkins 的 throttle 插件)
  • 使用队列优先级(紧急构建优先)
  • 合并提交(GitLab CI 的 retry 机制)

3.3 构建产物管理

构建产物是 CI 的「成果」。你想想看,如果产物管理混乱,部署时找不到正确的版本,那 CI 就白做了。

我建议遵循三个原则:

  1. 版本化:每个产物都有唯一版本号(如 app-1.0.0-20231001.jar
  2. 可追溯:从产物能反查到对应的代码提交
  3. 可清理:设置保留策略,避免存储膨胀

3.3.1 产物存储方案

方案 优点 缺点
Jenkins 内置归档 简单、无需额外配置 占用 Jenkins 磁盘,不适合大文件
GitLab Artifacts 自带过期时间,集成度高 存储空间有限(免费版)
Nexus/Artifactory 专业制品库,支持多种格式 需要额外部署和维护

我个人推荐:小团队用 GitLab Artifacts,大团队用 Nexus。我在项目中用过 Nexus,它支持 Docker 镜像、Maven 包、npm 包等,而且有完善的权限控制。不过部署起来稍微麻烦点。

核心要点:构建产物管理不是「存起来就完事」。你要考虑谁需要它、怎么找到它、什么时候删除它。我见过最惨的案例:一个团队把 10GB 的产物存在 Jenkins 上,结果 Jenkins 挂了,所有历史构建都丢了。

3.3.2 产物命名规范

我习惯用这种命名格式:

{项目名}-{版本号}-{构建时间}-{构建号}.{扩展名}

举个例子:

order-service-1.2.3-20231001-45.jar

这样命名的好处是:一眼就能看出是哪个项目、什么版本、什么时候构建的。我曾经因为产物命名混乱,导致部署时拿错了版本,线上出了故障。从那以后,我强制团队统一命名规范。

3.4 实战经验总结

说了这么多,最后分享几个我踩过的坑:

  • 不要把所有事情都塞进 CI:CI 只负责构建和测试,部署交给 CD
  • 触发策略要精细:别让无关的提交触发构建
  • 产物要定期清理:设置过期时间,不然磁盘会爆
  • 流水线要可视化:用 Blue Ocean 或 GitLab 的流水线视图,方便排查问题

嗯,持续集成这件事,说白了就是「自动化 + 规范化」。工具只是手段,关键是流程设计。你想想看,如果每次构建都要手动操作,那还叫什么持续集成?

下一章我们会聊持续部署(CD),到时候会讲到怎么把构建产物自动部署到测试环境。敬请期待。