环境搭建:CI 服务器(Jenkins / GitLab CI)配置

这一章,我们聊聊 CI 服务器的配置。说白了,就是给回滚分层找个“跑道”。你想想,如果没有一个稳定、清晰的 CI 环境,后面那些分层策略根本跑不起来。我个人习惯把 CI 服务器当成整个回滚体系的“调度中心”——它不光要跑构建、跑测试,还得能快速生成可回滚的版本。

我先说说 Jenkins,再说 GitLab CI。这两种我都用过,各有千秋。Jenkins 我用了快八年,GitLab CI 最近三年才开始深度接触。说实话,如果你团队小、代码托管已经用了 GitLab,那直接上 GitLab CI 最省事;但如果你们要搞复杂的流程编排,Jenkins 的 Pipeline 依然是老大哥。

4.1 Jenkins 配置要点

上一家公司,我接手过一套 Jenkins 集群。那个项目有十几个微服务,回滚要求特别高。我记得一开始大家配置的方式特别“野”——每个人在 Jenkins 上手动点构建,参数乱填,经常出现构建产物对不上版本。后来我推了一套标准化配置,才算稳下来。

关键配置项有这几个:

  • Pipeline 脚本(Jenkinsfile):必须包含回滚阶段的钩子。我习惯在 post 块里加一个 rollback 条件。
  • 参数化构建:CHOICE 参数选择回滚到的版本号,STRING 参数指定环境。
  • 凭据管理:把 Docker Registry、K8s 的 kubeconfig 都放进 Jenkins 的 Credential Store,别写死。
  • Agent 标签:按业务分组,比如 java-build、k8s-deploy,避免资源争抢。

一个典型的 Jenkinsfile 片段:

pipeline {
    agent { label 'k8s-deploy' }
    parameters {
        choice(name: 'ROLLBACK_VERSION', choices: ['v1.0.0', 'v1.1.0', 'v1.2.0'], description: '回滚到的版本')
        string(name: 'TARGET_ENV', defaultValue: 'staging', description: '目标环境')
    }
    stages {
        stage('Build') {
            steps {
                sh 'docker build -t myapp:${ROLLBACK_VERSION} .'
            }
        }
        stage('Deploy') {
            steps {
                sh 'kubectl set image deployment/myapp myapp=myapp:${ROLLBACK_VERSION} -n ${TARGET_ENV}'
            }
        }
    }
    post {
        success {
            emailext subject: "部署成功: ${ROLLBACK_VERSION} to ${TARGET_ENV}"
        }
        failure {
            // 自动触发回滚机制
        }
    }
}

小技巧: Jenkins 的 Job 建议用 Multibranch Pipeline,这样每个分支自动扫描,省去手动建 Job。我在实战中发现,只要分支名规范(比如 release/v* 打回滚基线),扫描策略就设成“定期 + webhook”,效率很高。

4.2 GitLab CI 配置要点

GitLab CI 的配置核心是 .gitlab-ci.yml。这个文件必须放在仓库根目录,而且 CI/CD 变量的管理比 Jenkins 更整洁。我个人特别喜欢它的“环境”管理和“手动操作”按钮——用在回滚场景太合适了。

比如,你想让某个 job 只在手动触发时才执行,那就加 when: manual。结合 environment 里的 action: rollback,完美。

示例配置:

stages:
  - build
  - test
  - deploy
  - rollback

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

build:
  stage: build
  script:
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE
  only:
    - tags

rollback:
  stage: rollback
  environment:
    name: production
    action: rollback
  script:
    - kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
  when: manual
  only:
    - tags

这里有个坑:回滚 job 必须和原始部署 job 共享同一个 environment 名称,否则 GitLab 的环境页面不会关联历史。我以前踩过这个坑,回滚后环境页面上显示两行记录,运维同事以为出了问题,紧张了半天。

警告: GitLab CI 的 Runner 如果串行执行,回滚 job 可能被其他 job 阻塞。建议给回滚专用 Runner 打标签 rollback,并在 runner 配置里设置 concurrent = 1 + check_interval = 0,保证回滚操作独占资源。

4.3 两种 CI 的对比选型

你可能会问:“那我究竟选 Jenkins 还是 GitLab CI?” 没有答案,我列个表帮你决策:

维度JenkinsGitLab CI
配置方式Jenkinsfile(Groovy).gitlab-ci.yml(YAML)
学习成本中高(Pipeline 语法复杂)低(YAML 直观)
回滚支持需手动写条件 / 插件原生 environment + manual
Agent 扩展灵活,可动态扩展依赖 GitLab Runner
UI 友好度传统,稍显笨重简洁,环境追踪清晰
多分支管理Multibranch Pipeline原生分支管道

我个人建议:如果你的 CI 链条需要大量插件(比如 SonarQube、OWASP)、或者要跟团队外的系统集成,Jenkins 更稳;如果你们团队小、链路轻,GitLab CI 完全够用。 我自己现在的项目就是用 GitLab CI + Helm,回滚一条命令搞定。

4.4 CI 服务器配置的核心流程

无论你选哪种,CI 服务器的配置逻辑都跑不出下面这个图。这个我画了很多遍——毕竟要讲给团队新人听,得让他们一图看懂。

CI 服务器配置核心流程 代码仓库触发 (Webhook / Poll SCM) 构建环境准备 (Docker Agent / Node) 构建与测试 (Maven / Gradle / pytest) 制品归档 (镜像推送 / 包存储) 部署与回滚准备 (K8s / Ansible / Helm) 通知与报告 (钉钉 / 邮件 / 日志) 关键要点 • 回滚版本号必须与环境绑定 • 制品归档需保留最近N个版本 • 部署与回滚用同一套脚本 • 通知包含版本、环境、触发者 • 所有步骤都需有ID回溯 分层实践关键 每一层配置都对应回滚 策略中的不同角色

这张图里,每到一个环节,你都要问自己:“如果这一步失败了,回滚怎么做?” 比如制品归档这一步,我经历过一次因为镜像标签覆盖导致的回滚失败——之前我图省事用了 latest,结果回滚时拿到的镜像根本不是之前上线的版本。从那以后,我强制要求每次构建必须打明确的版本标签(比如 v1.2.3-build20250314)。

4.5 回滚场景下的特殊配置

不论用 Jenkins 还是 GitLab CI,有几点是共通的,我单独拿出来强调:

  • 构建产物保留策略:Jenkins 里设置 buildDiscarder 保留最近 10 个构建产物;GitLab CI 用 artifacts:expire_in 设为 1 week,但回滚专用的 tag 构建产物设为 never。
  • 环境变量管理:敏感信息(K8s 的 、DB 密码)一定走 CI 的 secret 变量。Jenkins 用 Credential Binding 插件,GitLab 直接走 CI/CD Settings > Variables。
  • 测试阶段的回滚拦截:如果单元测试通过率低于阈值,自动阻断流水线。我在 Jenkins Pipeline 里加过 junit 分析,失败率 > 1% 直接 error。GitLab 可以用 allow_failure: false + coverage 规则。
  • 回滚操作的可观测性:每个回滚操作都要记录到日志系统(Elasticsearch 或者 Datadog),包括谁发起的、哪个版本、执行结果。Jenkins 可以配合 post 块发送 JSON 到 webhook;GitLab CI 可以直接调 API 写日志。

我曾经踩过的一个坑: 有次 GitLab CI 配置里忘了给回滚 job 加 resource_group,结果同时触发了两次回滚操作,导致 K8s 端出现资源竞争,Pod 状态变成 CrashLoopBackOff。后来我在每个 job 里加了 resource_group: production_rollback,保证同一时间只有一个回滚 job 在跑。

配置 CI 服务器这件事,看着简单,但每一个细节都可能影响回滚的成败。我的建议是:先跑通最简单的“构建->部署->回滚”链路,然后再逐步添加测试、通知、安全扫描这些层。别一上来就搞大而全,容易翻车。