CI/CD 流水线
概述
CI/CD(持续集成/持续交付/持续部署)是现代软件开发实践中不可或缺的一环,旨在通过自动化手段加速代码从提交到上线的流转过程,减少人工干预,提高交付质量和效率。
基本概念
持续集成 (Continuous Integration, CI)
持续集成指开发人员频繁地将代码变更合并到主干分支,每次合并后自动触发构建和测试,以尽早发现集成问题。
- 提交频率:每日多次合并代码到主干
- 核心活动:代码编译、静态分析、单元测试、集成测试
- 主要收益:及早发现冲突和缺陷,降低集成风险
- 反馈周期:分钟级
持续交付 (Continuous Delivery, CD)
持续交付在持续集成的基础上,将通过自动化测试的代码自动部署到类生产环境,确保软件随时可以发布到生产环境。
- 核心活动:自动化测试通过后,自动部署到预发布/Staging 环境
- 发布决策:由人工决定是否推送到生产
- 主要收益:缩短发布周期,降低发布风险
持续部署 (Continuous Deployment, Continuous Deploy)
持续部署是持续交付的进一步延伸,将通过所有自动化测试的代码变更自动部署到生产环境。
- 核心活动:全自动化,从代码提交到生产上线无需人工介入
- 发布决策:由自动化流水线决定
- 主要收益:极高的交付速度,完全消除发布等待时间
- 适用场景:成熟的自动化测试体系、微服务架构、SaaS 产品
三种模式对比
| 维度 | 持续集成 | 持续交付 | 持续部署 |
|---|---|---|---|
| 自动化构建 | 是 | 是 | 是 |
| 自动化测试 | 是 | 是 | 是 |
| 部署到测试环境 | 手动或自动 | 自动 | 自动 |
| 部署到预发布环境 | 手动 | 自动 | 自动 |
| 部署到生产环境 | 手动 | 手动审批 | 自动 |
| 适合团队 | 所有团队 | 需要快速交付的团队 | 高度自动化的成熟团队 |
| 风险等级 | 低 | 中 | 较高 |
流水线标准阶段
一个典型的 CI/CD 流水线包含以下阶段,可根据项目需要裁剪:
代码提交 --> 构建(Build) --> 测试(Test) --> 打包(Package) --> 部署(Deploy)| 阶段 | 目标 | 典型操作 | 产出物 |
|---|---|---|---|
| 构建 (Build) | 验证代码可正确编译 | 依赖安装、代码编译、静态代码分析 | 编译产物(Jar/War/二进制) |
| 测试 (Test) | 验证代码功能和性能 | 单元测试、集成测试、代码覆盖率、性能测试 | 测试报告、覆盖率报告 |
| 打包 (Package) | 制作可部署的交付物 | 镜像构建、安装包制作、制品归档 | Docker 镜像、RPM/DEB 包 |
| 部署 (Deploy) | 将交付物发布到目标环境 | 环境配置、服务更新、健康检查 | 运行中的服务实例 |
GitLab CI
GitLab CI 是 GitLab 内置的持续集成/持续交付工具,通过 .gitlab-ci.yml 文件定义流水线,无需额外搭建 CI 服务器。
.gitlab-ci.yml 核心结构
# .gitlab-ci.yml
# 定义流水线阶段及其执行顺序
stages:
- build
- test
- package
- deploy
# 全局默认配置,可被 Job 级别覆盖
default:
image: maven:3.8-openjdk-11
tags:
- docker-runner
# 缓存 Maven 依赖,加速后续构建
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2/repository/主要关键字说明
| 关键字 | 描述 | 是否必需 |
|---|---|---|
stages | 定义流水线的阶段列表,Job 通过 stage 字段归属到对应阶段 | 否,默认为 build, test, deploy |
job | 流水线的基本执行单元,可以自定义名称 | 是,至少定义一个 Job |
script | Job 要执行的 Shell 命令序列 | 是 |
stage | 指定 Job 所属的阶段 | 否,默认为 test |
only / except | 控制 Job 在哪些分支或条件下执行/不执行 | 否 |
artifacts | 定义 Job 产生的文件制品,可在 Job 间传递 | 否 |
environment | 指定部署的目标环境名称 | 否(部署类 Job 常用) |
image | 指定 Job 运行所用的 Docker 镜像 | 否(可在 default 中全局指定) |
tags | 选择要使用哪个 Runner 执行 Job | 取决于 Runner 配置 |
needs | 允许 Job 无序执行,提前开始,不等待同阶段其他 Job | 否 |
rules | 更灵活的 only/except 替代方案,支持复杂条件 | 否 |
retry | 失败后自动重试次数 | 否 |
timeout | Job 超时时间 | 否 |
完整示例
stages:
- build
- test
- package
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
DOCKER_IMAGE_TAG: "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}"
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2/repository/
policy: pull-push
# 阶段 1:编译
compile:
stage: build
script:
- mvn compile -q
only:
- main
- develop
- merge_requests
# 阶段 2:单元测试 + 代码质量
unit-test:
stage: test
script:
- mvn test
- mvn verify -Pcoverage
artifacts:
name: "${CI_JOB_NAME}_${CI_COMMIT_SHORT_SHA}"
paths:
- target/site/jacoco/
reports:
junit: target/surefire-reports/TEST-*.xml
expire_in: 7 days
only:
- main
- develop
# 阶段 3:构建 Docker 镜像并推送
docker-build:
stage: package
image: docker:20.10
services:
- docker:dind
script:
- docker build -t ${DOCKER_IMAGE_TAG} .
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker push ${DOCKER_IMAGE_TAG}
- docker tag ${DOCKER_IMAGE_TAG} ${CI_REGISTRY_IMAGE}:latest
- docker push ${CI_REGISTRY_IMAGE}:latest
only:
- main
# 阶段 4:部署到 K8s
deploy-k8s:
stage: deploy
image: bitnami/kubectl:latest
script:
- sed -i "s|{{IMAGE_TAG}}|${CI_COMMIT_SHORT_SHA}|g" k8s/deployment.yaml
- kubectl apply -f k8s/deployment.yaml
- kubectl apply -f k8s/service.yaml
- kubectl rollout status deployment/app-name -n production
environment:
name: production
url: https://app.example.com
only:
- main
when: manual # 需要手动触发部署GitLab Runner
GitLab Runner 是执行 CI Job 的代理程序,需要注册到 GitLab 实例。
Runner 类型
| 类型 | 可见范围 | 适用场景 |
|---|---|---|
| Shared Runner | 对所有项目可见 | 公共或开源项目,节约资源 |
| Group Runner | 对组内所有项目可见 | 企业内部团队统一管理 |
| Specific Runner | 仅对指定项目可见 | 需要特定环境配置的项目 |
Docker 执行器
Docker 执行器是最常用的 Runner 执行器,每个 Job 在独立的容器中运行,保证环境隔离。
# config.toml 示例
[[runners]]
name = "docker-runner"
url = "https://gitlab.example.com"
token = "YOUR_REGISTRATION_TOKEN"
executor = "docker"
[runners.docker]
image = "maven:3.8-openjdk-11"
privileged = true
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]缓存与制品
- 缓存 (Cache):用于存储依赖包(如 Maven
.m2、npmnode_modules),在 Job 间共享,加速构建。缓存不保证每次都能命中,适合可重新下载的依赖。 - 制品 (Artifacts):用于在 Job 间传递构建产物,如编译后的 Jar 包、测试报告等。制品会被上传到 GitLab 存储,可下载查看。
# 缓存示例
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
policy: pull-push # pull: 只下载; push: 只上传; pull-push: 双向
# 制品示例
artifacts:
name: "build-${CI_JOB_ID}"
paths:
- target/*.jar
expire_in: 30 days
when: on_success # on_failure: 失败时仍保留; always: 总是保留完整 Java 微服务 CI 示例
以下是一个生产级别的 Java 微服务 CI 配置,包含从编译到部署到 K8s 全流程:
stages:
- compile
- unit-test
- code-analysis
- integration-test
- image-build
- deploy-staging
- deploy-production
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.plugins.shade=warn"
MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version"
DOCKER_IMAGE: "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}"
DOCKER_IMAGE_LATEST: "${CI_REGISTRY_IMAGE}:latest"
default:
image: maven:3.8-openjdk-11
tags:
- docker
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2/repository/
policy: pull-push
# 编译
compile:
stage: compile
script:
- mvn ${MAVEN_CLI_OPTS} compile
only:
- main
- develop
- /^release-.*$/
- merge_requests
# 单元测试 + 生成覆盖率报告
unit-test:
stage: unit-test
script:
- mvn ${MAVEN_CLI_OPTS} test
- mvn ${MAVEN_CLI_OPTS} jacoco:report
artifacts:
paths:
- target/site/jacoco/
reports:
junit:
- target/surefire-reports/TEST-*.xml
expire_in: 14 days
only:
- main
- develop
- /^release-.*$/
# 代码质量分析(SonarQube)
code-analysis:
stage: code-analysis
script:
- mvn ${MAVEN_CLI_OPTS} sonar:sonar
-Dsonar.host.url=${SONAR_HOST_URL}
-Dsonar.login=${SONAR_TOKEN}
-Dsonar.gitlab.commit.sha=${CI_COMMIT_SHA}
-Dsonar.gitlab.ref_name=${CI_COMMIT_REF_NAME}
only:
- main
- develop
- /^release-.*$/
# 集成测试
integration-test:
stage: integration-test
script:
- mvn ${MAVEN_CLI_OPTS} verify -Pintegration-test
artifacts:
paths:
- target/failsafe-reports/
expire_in: 7 days
only:
- main
- /^release-.*$/
# Docker 镜像构建与推送
image-build:
stage: image-build
image: docker:20.10-dind
services:
- docker:20.10-dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
script:
- mvn ${MAVEN_CLI_OPTS} package -DskipTests
- docker build -t ${DOCKER_IMAGE} .
- docker push ${DOCKER_IMAGE}
- docker tag ${DOCKER_IMAGE} ${DOCKER_IMAGE_LATEST}
- docker push ${DOCKER_IMAGE_LATEST}
only:
- main
# 部署到 Staging 环境
deploy-staging:
stage: deploy-staging
image: bitnami/kubectl:latest
script:
- sed -i "s/{{IMAGE_TAG}}/${CI_COMMIT_SHORT_SHA}/g" k8s/overlays/staging/deployment-patch.yaml
- kubectl apply -k k8s/overlays/staging/
- kubectl rollout status deployment/app-name -n staging --timeout=5m
environment:
name: staging
url: https://staging.app.example.com
only:
- main
variables:
KUBECONFIG: ${STAGING_KUBECONFIG}
# 部署到生产环境(手动审批)
deploy-production:
stage: deploy-production
image: bitnami/kubectl:latest
script:
- sed -i "s/{{IMAGE_TAG}}/${CI_COMMIT_SHORT_SHA}/g" k8s/overlays/production/deployment-patch.yaml
- kubectl apply -k k8s/overlays/production/
- kubectl rollout status deployment/app-name -n production --timeout=10m
- kubectl wait --for=condition=Available deployment/app-name -n production --timeout=10m
environment:
name: production
url: https://app.example.com
only:
- main
when: manual
allow_failure: false
variables:
KUBECONFIG: ${PRODUCTION_KUBECONFIG}GitHub Actions
GitHub Actions 是 GitHub 内置的 CI/CD 平台,通过 YAML 格式的 workflow 文件定义自动化流程,文件存放在仓库的 .github/workflows/ 目录下。
Workflow 核心结构
# .github/workflows/ci.yml
# Workflow 名称
name: Java CI/CD
# 触发条件
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
workflow_dispatch: # 允许手动触发
# 环境变量,对所有 Job 可见
env:
JAVA_VERSION: '11'
DOCKER_IMAGE: ghcr.io/${{ github.repository }}
# Job 定义
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
- name: Build with Maven
run: mvn clean compile主要关键字说明
| 关键字 | 描述 | 是否必需 |
|---|---|---|
name | Workflow 或 Step 的名称 | 否 |
on | 定义触发 Workflow 的事件 | 是 |
jobs | 包含一个或多个 Job 定义 | 是 |
runs-on | 指定 Job 运行的操作系统环境 | 是 |
steps | Job 中的执行步骤序列 | 是 |
uses | 引用某个 Action(可来自 marketplace) | 否 |
run | 执行 Shell 命令 | 否 |
with | 向 Action 传递输入参数 | 否 |
env | 设置环境变量 | 否 |
needs | 指定 Job 依赖的先行 Job | 否 |
if | 条件执行 Job 或 Step | 否 |
strategy | 定义矩阵构建等策略 | 否 |
services | 启动辅助服务容器(数据库等) | 否 |
触发事件 (on)
# 分支推送
on:
push:
branches:
- main
- 'releases/**'
tags:
- v*
# 定时触发(cron 表达式)
on:
schedule:
- cron: '0 2 * * 0' # 每周日凌晨2点
# 多个事件组合
on:
push:
branches: [main]
pull_request:
branches: [main]
release:
types: [published]
workflow_dispatch:复用市场 Actions
GitHub Marketplace 提供了丰富的社区 Action,可直接引用避免重复造轮子:
steps:
- uses: actions/checkout@v4 # 检出代码
- uses: actions/setup-java@v4 # 设置 JDK
- uses: actions/cache@v4 # 缓存依赖
- uses: docker/login-action@v3 # Docker 登录
- uses: docker/build-push-action@v5 # Docker 构建推送
- uses: nick-fields/assert-action@v2 # 断言验证
- uses: peaceiris/actions-gh-pages@v3 # 部署到 GitHub Pages矩阵构建 (Matrix Build)
矩阵构建可以同时在多个操作系统、语言版本或架构上运行测试,适用于需要验证兼容性的场景:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
java: [11, 17, 21]
include:
- os: ubuntu-latest
java: 21
coverage: true # 只在特定组合执行覆盖率
fail-fast: false # 一个失败不取消其他任务
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- run: mvn test
- name: Upload coverage
if: ${{ matrix.coverage }}
run: mvn jacoco:report
- uses: codecov/codecov-action@v4
if: ${{ matrix.coverage }}完整 Java CI/CD Workflow 示例
name: Java Microservice CI/CD
on:
push:
branches: [main, develop, 'release/*']
pull_request:
branches: [main]
workflow_dispatch:
env:
JAVA_VERSION: '17'
MAVEN_OPTS: '-Dmaven.repo.local=.m2/repository'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# --------------------------------------------------
# Job 1: 构建与单元测试
# --------------------------------------------------
build:
name: Build and Unit Test
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK ${{ env.JAVA_VERSION }}
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: maven
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build and run unit tests
run: mvn clean test --batch-mode
- name: Generate JaCoCo coverage report
run: mvn jacoco:report --batch-mode
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: target/site/jacoco/jacoco.xml
fail_ci_if_error: false
- name: Get project version
id: get-version
run: echo "version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: jar-artifacts
path: target/*.jar
retention-days: 7
# --------------------------------------------------
# Job 2: 集成测试(依赖 build)
# --------------------------------------------------
integration-test:
name: Integration Test
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: maven
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: jar-artifacts
path: target/
- name: Run integration tests
run: mvn verify -Pintegration-test --batch-mode
- name: Upload integration test reports
uses: actions/upload-artifact@v4
with:
name: integration-test-reports
path: target/failsafe-reports/
retention-days: 14
# --------------------------------------------------
# Job 3: Docker 镜像构建与推送(仅 main 分支)
# --------------------------------------------------
docker-build:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: [build, integration-test]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: jar-artifacts
path: target/
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=,format=short
type=ref,event=branch
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# --------------------------------------------------
# Job 4: 部署到 K8s(手动触发)
# --------------------------------------------------
deploy:
name: Deploy to Kubernetes
runs-on: ubuntu-latest
needs: docker-build
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://app.example.com
steps:
- uses: actions/checkout@v4
- name: Set up kubectl
uses: azure/setup-kubectl@v4
with:
version: 'latest'
- name: Configure Kubeconfig
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/app-name \
app-name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n production
kubectl rollout status deployment/app-name -n production --timeout=10m
- name: Health check
run: |
kubectl wait --for=condition=Available deployment/app-name -n production --timeout=5m
kubectl get pods -n production -l app=app-name环境保护规则
GitHub Actions 支持为特定环境设置保护规则,确保部署安全:
# 在 GitHub 仓库 Settings > Environments 中配置
# 这里使用 environment 关键字引用
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://app.example.com
# 要求必须有审批人通过才能执行
# 环境配置中可设置:
# - 必须审批者 (Required reviewers)
# - 等待时间 (Wait timer)
# - 分支限制 (Deployment branches)Jenkins
Jenkins 是目前历史最悠久、插件生态最丰富的 CI/CD 工具。支持通过 Declarative Pipeline(声明式流水线)以代码形式定义整个构建流程。
Pipeline as Code (Jenkinsfile)
Jenkinsfile 是基于文本的流水线定义文件,通常存放在代码仓库根目录。
Declarative Pipeline 核心结构
// Jenkinsfile - Declarative Pipeline
pipeline {
// 指定在哪个 Agent 上运行
agent any
// 全局环境变量
environment {
JAVA_HOME = '/usr/lib/jvm/java-11-openjdk'
MAVEN_HOME = '/usr/share/maven'
DOCKER_REGISTRY = 'registry.example.com'
APP_NAME = 'my-service'
}
// 流水线参数(构建时手动填写)
parameters {
string(name: 'BRANCH', defaultValue: 'main', description: '构建分支')
choice(name: 'ENV', choices: ['staging', 'production'], description: '部署环境')
}
// 流水线阶段
stages {
stage('Build') {
steps {
echo 'Building...'
sh 'mvn clean compile'
}
}
stage('Test') {
steps {
echo 'Testing...'
sh 'mvn test'
}
post {
success {
junit 'target/surefire-reports/**/*.xml'
}
}
}
stage('Deploy') {
steps {
echo "Deploying to ${params.ENV}..."
}
}
}
// 流水线执行后的操作
post {
always {
cleanWs()
}
success {
echo 'Pipeline succeeded!'
}
failure {
echo 'Pipeline failed!'
}
}
}关键字说明
| 关键字 | 描述 | 是否必需 |
|---|---|---|
pipeline | Declarative Pipeline 的根元素 | 是 |
agent | 指定流水线或阶段的执行节点 | 是 |
stages | 包含一个或多个 stage 的阶段集合 | 是 |
stage | 单个阶段,包含名称、steps 等 | 是 |
steps | 阶段内的具体执行步骤 | 是(至少一个步骤) |
post | 阶段或流水线执行后的条件操作块 | 否 |
environment | 定义环境变量 | 否 |
parameters | 定义构建参数 | 否 |
triggers | 定义触发方式(定时、webhook) | 否 |
tools | 自动配置工具(Maven、JDK 等) | 否 |
options | 流水线级别的配置选项 | 否 |
post 条件块
post {
always { /* 无论结果都执行 */ }
success { /* 成功时执行 */ }
failure { /* 失败时执行 */ }
unstable { /* 测试不稳定时执行 */ }
aborted { /* 被中止时执行 */ }
changed { /* 当前结果与上次不同时执行 */ }
regression { /* 当前状态比上次差时执行 */ }
fixed { /* 修复失败,当前成功时执行 */ }
}完整 Java 微服务 Jenkins Pipeline 示例
pipeline {
agent {
label 'docker-agent'
}
tools {
maven 'maven-3.8'
jdk 'jdk-11'
}
environment {
DOCKER_IMAGE = "${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.BUILD_NUMBER}"
DOCKER_IMAGE_LATEST = "${env.DOCKER_REGISTRY}/${env.APP_NAME}:latest"
KUBE_CONFIG_STAGING = credentials('kube-config-staging')
KUBE_CONFIG_PRODUCTION = credentials('kube-config-production')
}
triggers {
// 代码提交触发
upstream 'service-parent'
// 定时扫描仓库变化
pollSCM('H/5 * * * *')
}
options {
buildDiscarder(logRotator(numToKeepStr: '20'))
timeout(time: 30, unit: 'MINUTES')
timestamps()
ansiColor('xterm')
skipDefaultCheckout()
}
stages {
// --------------------------------------------------
// 阶段 1:检出代码
// --------------------------------------------------
stage('Checkout') {
steps {
checkout scm
}
}
// --------------------------------------------------
// 阶段 2:编译
// --------------------------------------------------
stage('Compile') {
steps {
sh 'mvn clean compile --batch-mode'
}
post {
success {
echo '编译成功'
}
failure {
error '编译失败,终止流水线'
}
}
}
// --------------------------------------------------
// 阶段 3:单元测试与代码覆盖率
// --------------------------------------------------
stage('Unit Test') {
steps {
sh 'mvn test --batch-mode'
sh 'mvn jacoco:report --batch-mode'
}
post {
success {
junit 'target/surefire-reports/**/*.xml'
jacoco(
execPattern: 'target/jacoco.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java',
exclusionPattern: '**/*Test*'
)
publishHTML(target: [
reportName : '覆盖率报告',
reportDir : 'target/site/jacoco',
reportFiles: 'index.html',
keepAll : false
])
}
}
}
// --------------------------------------------------
// 阶段 4:代码质量分析 (SonarQube)
// --------------------------------------------------
stage('Code Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar --batch-mode'
}
}
}
// --------------------------------------------------
// 阶段 5:集成测试
// --------------------------------------------------
stage('Integration Test') {
steps {
sh 'mvn verify -Pintegration-test --batch-mode'
}
post {
success {
junit 'target/failsafe-reports/**/*.xml'
}
}
}
// --------------------------------------------------
// 阶段 6:构建 Docker 镜像并推送
// --------------------------------------------------
stage('Docker Build & Push') {
steps {
script {
docker.withRegistry("https://${env.DOCKER_REGISTRY}",
'docker-registry-credentials') {
sh 'mvn package -DskipTests --batch-mode'
def app = docker.build("${env.DOCKER_IMAGE}")
app.push()
docker.image("${env.DOCKER_IMAGE}").push('latest')
}
}
}
}
// --------------------------------------------------
// 阶段 7:部署到 Staging
// --------------------------------------------------
stage('Deploy to Staging') {
when {
branch 'main'
}
steps {
script {
sh """
mkdir -p ~/.kube
echo "${KUBE_CONFIG_STAGING}" > ~/.kube/config
kubectl set image deployment/${env.APP_NAME} \
${env.APP_NAME}=${env.DOCKER_IMAGE} -n staging
kubectl rollout status deployment/${env.APP_NAME} \
-n staging --timeout=5m
"""
}
}
}
// --------------------------------------------------
// 阶段 8:部署到生产(需要审批)
// --------------------------------------------------
stage('Deploy to Production') {
when {
branch 'main'
expression { params.ENV == 'production' }
}
input {
message '确认部署到生产环境?'
ok '确认部署'
submitter 'admin,lead'
parameters {
string(name: 'RELEASE_TAG', defaultValue: '',
description: '发布标签(可选)')
}
}
steps {
script {
sh """
mkdir -p ~/.kube
echo "${KUBE_CONFIG_PRODUCTION}" > ~/.kube/config
kubectl set image deployment/${env.APP_NAME} \
${env.APP_NAME}=${env.DOCKER_IMAGE} -n production
kubectl rollout status deployment/${env.APP_NAME} \
-n production --timeout=10m
"""
}
}
post {
success {
echo "部署到生产环境成功: ${env.DOCKER_IMAGE}"
// 发送通知
emailext(
subject: "部署成功: ${env.APP_NAME} v${env.BUILD_NUMBER}",
body: "应用已成功部署到生产环境。",
to: 'team@example.com'
)
}
}
}
}
post {
always {
cleanWs(cleanWhenSuccess: true, cleanWhenFailed: true)
}
failure {
emailext(
subject: "流水线失败: ${env.APP_NAME} #${env.BUILD_NUMBER}",
body: "流水线失败,请查看 Jenkins 控制台日志。",
to: 'dev-team@example.com'
)
}
}
}Blue Ocean
Blue Ocean 是 Jenkins 的现代化 UI 界面,提供更直观的流水线可视化:
- 流水线执行状态图形化展示(通过/失败/进行中)
- 交互式的流水线日志查看
- 分支和拉取请求的动态过滤
- 个人化的仪表盘视图
安装方式:在 Jenkins 插件管理中搜索安装 "Blue Ocean" 插件套件。
与 GitLab/GitHub 集成
Jenkins + GitLab 集成
// 在 Jenkinsfile 中配置 GitLab 触发器
pipeline {
triggers {
gitlab(triggerOnPush: true,
triggerOnMergeRequest: true,
branchFilterType: 'RegexBasedFilter',
branchFilter: '^(main|develop|release/.*)$',
secretToken: "${env.GITLAB_TOKEN}")
}
}配置步骤:
- 安装 "GitLab Plugin" 插件
- 在 GitLab 项目中添加 Webhook,指向 Jenkins 地址
- 配置 Jenkins 凭据(GitLab Personal Access Token)
- 流水线中通过
triggers关键字配置触发规则
Jenkins + GitHub 集成
// 在 Jenkinsfile 中配置 GitHub 触发器
pipeline {
triggers {
pollSCM('H/5 * * * *') // 定期轮询
}
}更推荐的方式是通过 GitHub App 集成:
- 安装 "GitHub Integration Plugin" 插件
- 创建 GitHub App 并连接到 Jenkins
- 配置 Webhook 自动触发流水线
K8s 插件动态 Agent
Jenkins 可以通过 Kubernetes Plugin 实现在 Kubernetes 集群上动态创建和销毁 Agent Pod,实现按需弹性伸缩:
# Jenkins Kubernetes Plugin 配置示例 (Pod Template)
apiVersion: v1
kind: Pod
spec:
containers:
- name: jnlp
image: jenkins/inbound-agent:latest
args: ['\$(JENKINS_SECRET)', '\$(JENKINS_NAME)']
- name: maven
image: maven:3.8-openjdk-11
command: ['cat']
tty: true
volumeMounts:
- name: maven-cache
mountPath: /root/.m2
- name: docker
image: docker:20.10-dind
command: ['dockerd-entrypoint.sh']
securityContext:
privileged: true
volumeMounts:
- name: docker-socket
mountPath: /var/run/docker.sock
volumes:
- name: maven-cache
persistentVolumeClaim:
claimName: jenkins-maven-cache
- name: docker-socket
emptyDir: {}在 Jenkinsfile 中使用动态 Agent:
pipeline {
agent {
kubernetes {
yamlFile 'jenkins-pod-template.yaml'
defaultContainer 'maven'
label 'k8s-dynamic-agent'
}
}
stages {
stage('Build') {
steps {
container('maven') {
sh 'mvn clean compile'
}
}
}
stage('Docker Build') {
steps {
container('docker') {
sh 'docker build -t myapp:latest .'
}
}
}
}
}优势:
- 按需创建 Agent,避免资源闲置浪费
- 每个构建使用独立的 Pod,环境完全隔离
- 支持不同流水线使用不同规格的 Agent
- 无需维护固定的 Agent 节点池
三种工具对比选型
| 对比维度 | GitLab CI | GitHub Actions | Jenkins |
|---|---|---|---|
| 集成程度 | GitLab 原生集成 | GitHub 原生集成 | 独立部署,全 SCM 支持 |
| 安装部署 | GitLab 自带,零部署 | GitHub 自带,零部署 | 需单独部署和维护 |
| 配置语言 | YAML (.gitlab-ci.yml) | YAML (.github/workflows/*.yml) | Groovy (Jenkinsfile) |
| 学习曲线 | 较低 | 较低 | 较高(Groovy 语法) |
| 托管/自托管 | SaaS 或自托管 | SaaS 或自托管 Runner | 自托管为主 |
| 插件生态 | 有限(内置功能丰富) | 中等(Marketplace) | 极丰富(1500+ 插件) |
| 容器支持 | 原生 Docker 执行器 | 原生容器支持 | 需 Docker 插件或 K8s 插件 |
| 并行执行 | 通过 parallel 关键字 | 通过 strategy/matrix | 通过 parallel 关键字 |
| 流水线复用 | 模板/Include | 复合 Actions / 可复用 Workflow | Shared Libraries |
| 矩阵构建 | 通过 parallel + variables | 原生矩阵支持 | 需插件或脚本实现 |
| 部署环境管理 | 内置 Environment | 内置 Environment | 需插件 |
| 安全/密钥管理 | CI/CD Variables (Masked) | Secrets (加密存储) | Credentials Plugin |
| 权限控制 | 项目级/组级 Role | 仓库级/环境级 | 项目级/角色级矩阵 |
| K8s 集成 | 原生支持,通过 kubectl | 原生支持,通过 kubectl | K8s Plugin,动态 Agent |
| 构建分钟数 | 自带 Runner 无限制 | 免费额度(2000分钟/月) | 取决于自有资源 |
| UI 体验 | 流水线可视化一般 | 优秀的可视化体验 | 原生 UI 较老;Blue Ocean 较好 |
| 适合场景 | GitLab 生态的团队 | GitHub 生态的团队 | 复杂流水线、合规要求高的企业 |
| 开源 | 社区版免费 | 私有仓库需付费 | 完全开源免费 |
| 维护成本 | 低 | 低 | 高(需专职维护) |
选型建议
首选 GitLab CI,当:
- 团队已使用 GitLab 管理代码
- 需要自托管的 CI/CD 解决方案
- 希望 CI/CD 与代码仓库紧密集成
- 团队规模适中,流水线逻辑相对标准化
首选 GitHub Actions,当:
- 团队已使用 GitHub 管理代码
- 追求最简化的配置和体验
- 希望利用丰富的 Marketplace Action 生态
- 需要矩阵构建和多 OS 兼容性测试
首选 Jenkins,当:
- 流水线逻辑极其复杂,需要高度定制
- 已有 Jenkins 基础设施和运维经验
- 企业合规要求严格,需要审批流和审计
- 需要跨多个 SCM 平台(同时使用 GitLab/GitHub/SVN)
- 需要丰富的测试报告和第三方工具集成
- 需要物理机或特定硬件环境构建(如 GPU 测试)