CI/CD 基础概念回顾(Jenkins, GitLab CI, GitHub Actions 等)

说实话,很多团队把 CI/CD 当成「一键部署」的开关。但干久了你会发现——CI/CD 的核心不是「部署」,而是让每一次代码变动都经过可重复、可验证的质量关卡。我这几年踩过的坑,十有八九都是因为对基础概念理解不透彻。

持续集成到底在「集成」什么?

持续集成(Continuous Integration)要求开发人员频繁地将代码合并到主干,每次合并都自动触发构建和测试。我见过不少项目把 CI 当成「跑个编译就行」,结果集成测试跑了一天才发现兼容性问题。我个人习惯在 CI 管道里至少塞三样东西:

  • 静态检查(lint / 代码规范)—— 卡住低级错误
  • 单元测试 + 覆盖率门禁 —— 保证逻辑质量
  • 快速集成测试(比如 API 契约测试)—— 提前暴露接口问题

我的经验:CI 管道前 5 分钟就应该跑完核心检查,否则开发人员会等得失去耐心,然后绕过管道直接合并。

持续交付 vs 持续部署:就差一个「人」

持续交付(Continuous Delivery)让每次提交都可以随时部署到生产,但由人手动触发。持续部署(Continuous Deployment)则是自动推送到生产。我记得帮一家金融客户做改造时,他们坚持用持续交付——因为合规要求必须有人点击「确认」。换个角度理解:一个是对生产保持敬畏,一个是相信自动化到极致。

主流的 CI/CD 工具,我挑三个聊

市面上工具很多,但 Jenkins、GitLab CI、GitHub Actions 这三个覆盖了从老牌自托管到云原生的主流场景。下面我结合个人体验逐个说。

Jenkins:老兵不死,但容易长胖

Jenkins 的好处是插件多到离谱,几乎能对接一切系统。但坏处也是插件——我维护过一个 Jenkins 实例,装了两百多个插件,每次升级都像拆炸弹。它的核心概念是 Pipeline(Declarative Pipeline 或 Scripted Pipeline),用 Jenkinsfile 定义。

// 一个简单的 Declarative Pipeline
pipeline {
    agent any
    stages {
        stage('Checkout') { steps { git 'https://…' } }
        stage('Build')    { steps { sh 'mvn clean package' } }
        stage('Test')     { steps { sh 'mvn test' } }
        stage('Deploy')   { steps { sh 'deploy.sh' } }
    }
}

注意:Jenkins 的 agent 可以是 Docker 容器,我建议至少把编译环境隔离到容器里,否则「在我机器上能跑」的梗会天天上演。

GitLab CI:仓库即管道

GitLab CI 把 CI/CD 直接嵌入到代码仓库中,配置文件是 .gitlab-ci.yml。它最妙的是 Runner 机制——你可以用共享 Runner(GitLab 提供)或自建 Runner。我用它做性能回归测试时,常常在管道里挂载 artifacts 来传递基准数据。

stages:
  - build
  - test
  - deploy

build-job:
  stage: build
  script: mvn clean package
  artifacts:
    paths:
      - target/*.jar

test-job:
  stage: test
  script: mvn test
  needs: ["build-job"]

我个人特别喜欢 needs 关键字——它让管道可以并行执行,比 Jenkins 默认的顺序执行快很多。避坑指南:GitLab CI 的默认超时是 60 分钟,性能测试跑很久的话记得手动设置 timeout。

GitHub Actions:生态绑架也是优势

如果你项目在 GitHub 上,GitHub Actions 几乎是最省心的选择。它的核心是 Workflow,用 .github/workflows/*.yml 声明。市场上有大量现成的 Action 拿来即用,比如 actions/checkout、actions/setup-java。我曾经只用三行就搭了一个 lint 管道:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run lint

不过要注意:GitHub Actions 的免费额度对小型团队很友好,但跑性能测试(比如 10 分钟以上)容易超出限额。我建议在自托管 Runner 上运行长耗时任务。

三个工具,怎么选?

下面这张表是我根据几个真实项目比对整理的,供你参考。

维度JenkinsGitLab CIGitHub Actions
配置方式Jenkinsfile (Groovy).gitlab-ci.yml (YAML)Workflow (YAML)
触发机制Polling / WebhookWebhook + Pipeline scheduleWebhook + 事件驱动
插件/生态系统极丰富(>1800 插件)中等(自建 + Templates)丰富(Marketplace)
托管方式通常自托管SaaS 或自托管 RunnerSaaS 或自托管 Runner
学习成本较高(插件多,Groovy 门槛)中等(YAML + 概念简洁)低(上手快)
适合场景复杂流水线、跨环境集成中小团队、与 GitLab 深度集成GitHub 原生项目、快速启动

警告:不要迷信「万能工具」。我在一个项目中用 Jenkins 实现了一个极其复杂的参数化管道,两个月后 GitLab CI 已经原生支持了同样的功能。选工具前先想清楚团队的维护能力和基础设施。

一张图看懂三者脉络

下面这张 SVG 图展示了从代码提交到部署的 CI/CD 核心流程,以及三个工具在触发阶段的差异。你可以看到:触发是管道的入口,构建/测试是质量门禁,部署是终局。

CI/CD 管道核心流程与工具关系 代码提交 CI 触发层(Webhook / Polling / Scheduled) Jenkins GitLab CI GitHub Actions 构建 / 单元测试 / 集成测试 部署(生产 / 预发布) 管线核心:触发 → 质量门禁 → 部署

性能回归测试在管道里的位置

你可能会问:「这篇文章不是讲性能回归测试吗?怎么还没提?」别急,CI/CD 基础是地基。性能回归测试通常放在集成测试阶段之后、部署之前。我会在后几章展开讲怎么在 Jenkins 里集成 JMeter、在 GitLab CI 里用 k6、在 GitHub Actions 里跑 Locust。但你得先明白:性能回归测试不是一次性的,它必须成为管道的一个门禁阶段——比如接口响应时间超过基线 10% 就阻断。

我个人在配置性能回归时,倾向于在管道中保留上一次的测试结果作为性能基线,然后对比当前结果。这个思路三个工具都支持(通过 artifacts、cache 或外部存储)。不过具体实现细节,咱们后面章节细聊。