Jenkins相关

19 minute read

Jenkins 持续集成/持续交付 完整系统学习大纲


一、CI/CD 与 Jenkins 概述

1.1 什么是 CI/CD

  • 持续集成(CI, Continuous Integration)
    • 定义:频繁地将代码变更合并到主干分支,并通过自动化构建和测试快速验证
    • 核心思想:早集成、早发现、早修复
    • 流程:开发提交代码 → 自动拉取 → 自动构建 → 自动测试 → 反馈结果
  • 持续交付(CD, Continuous Delivery)
    • 定义:在 CI 基础上,确保代码可以随时安全地部署到生产环境
    • 特点:每次构建产物都是”可发布”状态,但需要手动触发实际部署
  • 持续部署(CD, Continuous Deployment)
    • 定义:在持续交付基础上更进一步,通过自动化测试的变更自动部署到生产环境
    • 区别:持续交付需要手动批准,持续部署完全自动化
阶段 构建 测试 部署到测试环境 部署到生产
持续集成 自动化 自动化 可选 无
持续交付 自动化 自动化 自动化 手动
持续部署 自动化 自动化 自动化 自动化

1.2 Jenkins 简介

  • 定义:Jenkins 是一个开源的、基于 Java 的自动化服务器,用于实现 CI/CD
  • 历史:
    • 前身是 Sun Microsystems 的 Hudson(2004年)
    • 2011年 Oracle 与社区分裂,社区分支更名为 Jenkins
    • 目前由 CloudBees 和社区共同维护
  • 核心特性:
    • 开源免费,MIT 许可证
    • 跨平台:Windows、macOS、Linux
    • 丰富的插件生态:1800+ 官方插件
    • 支持分布式构建(Master-Agent 架构)
    • Pipeline as Code(Jenkinsfile)
    • 强大的可视化(Blue Ocean、Stage View)
    • 支持多种 SCM(Git、SVN、Mercurial 等)

1.3 Jenkins 与其他 CI/CD 工具对比

特性 Jenkins GitLab CI GitHub Actions TeamCity
开源 是 CE 版开源 公开仓库免费 专业版收费
安装方式 独立部署 集成 GitLab 集成 GitHub 独立部署
配置方式 Pipeline as Code YAML YAML Kotlin DSL/UI
插件生态 极丰富(1800+) 有限 Actions 市场 有限
学习曲线 中等 较低 较低 中等
分布式构建 原生支持 需要 Runner 需要 Runner 原生支持
适用场景 灵活定制、企业级 GitLab 用户 GitHub 用户 中小型团队

二、环境搭建与安装

2.1 系统要求

资源 最低要求 推荐配置
CPU 1 核 4 核+
内存 256 MB 4 GB+
磁盘 1 GB(不含构建空间) 50 GB+
Java JDK 8 或 JDK 11 JDK 11 或 JDK 17
操作系统 Windows / macOS / Linux Linux(推荐)

2.2 安装方式

2.2.1 Windows 安装

# 方式一:下载 MSI 安装包
# 访问 https://jenkins.io/download/ 下载 Windows 安装包
# 双击运行,按向导安装,会自动注册为 Windows 服务

# 方式二:使用 WAR 包运行
java -jar jenkins.war --httpPort=8080

2.2.2 Linux(Debian/Ubuntu)安装

# 1. 添加 Jenkins 仓库密钥和源
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee \
  /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

# 2. 安装 Java(Jenkins 需要 Java 运行环境)
sudo apt update
sudo apt install fontconfig openjdk-17-jre

# 3. 安装 Jenkins
sudo apt update
sudo apt install jenkins

# 4. 启动 Jenkins 并设置开机自启
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins

2.2.3 Docker 安装

# 拉取官方镜像
docker pull jenkins/jenkins:lts-jdk17

# 运行容器
docker run -d \
  --name jenkins \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkins/jenkins:lts-jdk17

# 获取初始管理员密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

2.3 初始化配置

  • 解锁 Jenkins:访问 http://localhost:8080,输入初始管理员密码
    • Linux: /var/lib/jenkins/secrets/initialAdminPassword
    • Docker: docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
  • 安装推荐插件:选择”安装推荐的插件”(推荐新手)或”选择插件来安装”
  • 创建管理员账户:设置用户名、密码、邮箱
  • 配置 Jenkins URL:设置 Jenkins 的访问地址(用于邮件、通知等)
Jenkins 目录结构详解
目录/文件 说明
JENKINS_HOME/ Jenkins 主目录(Linux 默认 /var/lib/jenkins/)
JENKINS_HOME/jobs/ 每个 Job 的配置、构建记录、工作空间
JENKINS_HOME/plugins/ 已安装的插件
JENKINS_HOME/workspace/ 构建工作区(每个 Job 一个目录)
JENKINS_HOME/users/ 用户配置和权限信息
JENKINS_HOME/secrets/ 密钥文件(如初始密码)
JENKINS_HOME/config.xml Jenkins 全局配置文件
JENKINS_HOME/logs/ Jenkins 日志文件

三、核心概念与术语

3.1 核心术语

术语 说明
Job / Project 一个构建任务,定义了构建的步骤和配置
Build 一次具体的构建执行,对应一个构建编号(#1, #2, …)
Workspace 构建的工作目录,存放检出的源码和生成的文件
Node 执行构建的节点(服务器),可以是 Master 或 Agent
Master Jenkins 主节点,负责调度任务、管理配置
Agent / Slave Jenkins 从节点,接收 Master 任务,执行实际的构建工作
Executor 执行器,一个节点上可并行执行的构建线程数
View 视图,用于组织和筛选 Job 的显示方式
SCM 源码管理(Source Code Management),如 Git、SVN
Trigger 触发器,定义何时启动构建
Artifact 构建产物,如 JAR 包、WAR 包、测试报告等

3.2 Jenkins 系统架构

┌─────────────────────────────────────────────────────┐
│                    Jenkins Master                   │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────┐   │
│  │  Web UI  │  │ 调度器    │  │  配置/插件管理    │   │
│  └──────────┘  └──────────┘  └──────────────────┘   │
│                        │                            │
│             分发构建任务到 Agent                     │
└────────────────────────┼────────────────────────────┘
                         │
          ┌──────────────┼──────────────┐
          ▼              ▼              ▼
   ┌──────────┐   ┌──────────┐   ┌──────────┐
   │ Agent 1  │   │ Agent 2  │   │ Agent 3  │
   │ (Linux)  │   │ (Windows)│   │ (macOS)  │
   │ 构建 Java │  │ 构建 .NET │   │ 构建 iOS │
   └──────────┘   └──────────┘   └──────────┘

四、Freestyle 项目(自由风格项目)

4.1 创建 Freestyle 项目

  • 创建步骤:Dashboard → 新建 Item → 输入名称 → 选择”Freestyle project” → 确定
  • 适用场景:简单任务、快速上手学习、不需要 Pipeline 脚本的小型项目

4.2 核心配置项

  • General(通用配置)
    • 描述:对 Job 的文字说明
    • 丢弃旧的构建:设置保留天数和最大构建数,清理磁盘空间
    • 参数化构建:允许在触发构建时传入参数
    • 并发构建:是否允许同一个 Job 同时执行多次
  • 源码管理(SCM)
    • Git:配置仓库 URL、分支、凭证
    • Subversion(SVN):配置 SVN 仓库地址
    • None:不从源码管理拉取代码
  • 构建触发器
    • 定时构建(Build periodically):Cron 表达式
    • 轮询 SCM(Poll SCM):定期检查仓库变更
    • 远程触发:通过 URL 触发构建
    • 其他项目构建后触发
  • 构建环境
    • Delete workspace before build starts
    • Use secret text(s) or file(s)
    • Add timestamps to the Console Output
    • Set environment variables
  • 构建步骤(Build Steps)
    • Execute shell(Linux)/ Execute Windows batch command
    • Invoke Ant / Invoke Gradle / Invoke top-level Maven targets
    • Docker 相关步骤
  • 构建后操作(Post-build Actions)
    • 归档成品(Archive the artifacts)
    • 发布 JUnit 测试结果报告
    • 邮件通知(E-mail Notification)
    • 触发其他项目构建

4.3 Freestyle 项目示例

# 执行 Shell 步骤示例
# 项目名称:simple-java-build
# 源码管理:Git - https://github.com/example/java-demo.git
# 构建步骤 - Execute shell:

echo "=== 开始构建 ==="
echo "工作空间: ${WORKSPACE}"
echo "构建编号: ${BUILD_NUMBER}"

# 清理旧构建产物
mvn clean

# 编译打包
mvn package -DskipTests

# 运行单元测试
mvn test

echo "=== 构建完成 ==="
Cron 表达式详解(定时触发)
格式:分 时 日 月 周

示例:
H/15 * * * *         # 每15分钟构建一次
H H * * *            # 每小时构建一次(每天24次)
H H(9-18) * * *      # 工作日9-18点,每小时构建一次
H H(9-18) * * 1-5    # 周一到周五,9-18点,每小时构建一次
H 2 * * *            # 每天凌晨2点构建一次
H H 1,15 * *         # 每月1号和15号各构建一次

注意:
- H 表示 Hash,Jenkins 使用哈希值来均匀分散负载
- 与标准 Cron 的区别:Jenkins 不支持秒级调度
- 建议使用 H 代替具体数值,避免多任务同时触发

五、Pipeline(流水线)

5.1 Pipeline 概述

  • 定义:Pipeline 是一种用代码描述 CI/CD 流程的方式(Pipeline as Code)
  • 优势:
    • 可版本控制:Pipeline 脚本可以存入 Git 仓库
    • 可复用:可通过共享库提取公共逻辑
    • 可恢复:Jenkins 重启后 Pipeline 自动继续执行
    • 可视化:Stage View 直观展示每个阶段的耗时和状态
    • 支持复杂逻辑:条件判断、循环、并行、错误处理等

5.2 Declarative Pipeline(声明式) vs Scripted Pipeline(脚本式)

对比维度 Declarative Pipeline Scripted Pipeline
语法风格 结构化、声明式,注重”做什么” 命令式、程序化,注重”怎么做”
学习难度 较低,语法简洁 较高,需要 Groovy 基础
代码位置 pipeline { } 块内 node { } 块内
错误检查 启动时语法校验 运行时才报错
内置功能 when, post, environment 等指令 需要手动编程实现
灵活性 受限于 DSL 语法 完全灵活,Groovy 全能力
推荐场景 大多数 CI/CD 场景 复杂自定义逻辑
起始标记 pipeline {} node {} 或 stage {}

5.3 Declarative Pipeline 核心语法

5.3.1 基本结构

pipeline {
    agent any                          // 1. agent - 指定在哪执行
    
    environment {                      // 2. environment - 环境变量
        APP_NAME = 'my-app'
        APP_VERSION = '1.0.0'
    }
    
    parameters {                       // 3. parameters - 参数化构建
        string(name: 'BRANCH', defaultValue: 'main', description: '分支名')
        choice(name: 'ENV', choices: ['dev', 'test', 'prod'], description: '部署环境')
        booleanParam(name: 'SKIP_TEST', defaultValue: false, description: '是否跳过测试')
    }
    
    triggers {                         // 4. triggers - 触发器
        cron('H 2 * * *')
        pollSCM('H/5 * * * *')
    }
    
    tools {                            // 5. tools - 工具配置
        maven 'maven-3.8'
        jdk 'jdk-11'
    }
    
    options {                          // 6. options - 选项配置
        timestamps()                   // 控制台输出时间戳
        timeout(time: 1, unit: 'HOURS') // 超时设置
        disableConcurrentBuilds()      // 禁止并发构建
        buildDiscarder(logRotator(numToKeepStr: '10')) // 保留最近10次构建
    }
    
    stages {                           // 7. stages - 阶段集合
        stage('Checkout') {            // 8. stage - 单个阶段
            steps {                    // 9. steps - 步骤集合
                checkout scm
                echo "当前分支: ${env.BRANCH_NAME}"
            }
        }
        
        stage('Build') {
            steps {
                sh 'mvn clean compile'
            }
        }
        
        stage('Test') {
            when {                     // 10. when - 条件执行
                expression { params.SKIP_TEST == false }
            }
            steps {
                sh 'mvn test'
            }
        }
        
        stage('Deploy') {
            parallel {                 // 11. parallel - 并行执行
                stage('Deploy to Dev') {
                    steps {
                        echo '部署到开发环境...'
                    }
                }
                stage('Deploy to Test') {
                    steps {
                        echo '部署到测试环境...'
                    }
                }
            }
        }
    }
    
    post {                             // 12. post - 构建后操作
        always {                       // 总是执行
            echo '清理工作空间...'
        }
        success {                      // 构建成功时
            emailext (
                subject: "构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: "构建成功!详情请查看: ${env.BUILD_URL}",
                to: 'team@example.com'
            )
        }
        failure {                      // 构建失败时
            emailext (
                subject: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: "构建失败!详情请查看: ${env.BUILD_URL}",
                to: 'team@example.com'
            )
        }
        unstable {                     // 构建不稳定时
            echo '测试未通过,构建标记为不稳定'
        }
        changed {                      // 构建状态发生变化时
            echo '构建状态发生了变化'
        }
    }
}

5.3.2 agent 详解

pipeline {
    // agent 指定 Pipeline 的执行位置
    
    // 1. any - 在任何可用的 agent 上执行
    agent any
    
    // 2. none - 不在顶层指定 agent,每个 stage 各自指定
    agent none
    
    // 3. label - 在有特定标签的 agent 上执行
    agent { label 'linux && docker' }
    
    // 4. docker - 在 Docker 容器中执行
    agent {
        docker {
            image 'maven:3.8-openjdk-11'
            args '-v /tmp:/tmp'
        }
    }
    
    // 5. dockerfile - 通过 Dockerfile 构建镜像并运行
    agent {
        dockerfile {
            filename 'Dockerfile.build'
            dir 'docker'
            additionalBuildArgs '--build-arg version=1.0'
        }
    }
    
    // 6. kubernetes - 在 Kubernetes Pod 中执行
    agent {
        kubernetes {
            yaml '''
                apiVersion: v1
                kind: Pod
                spec:
                  containers:
                  - name: maven
                    image: maven:3.8-openjdk-11
                '''
        }
    }
}

5.3.3 when 条件详解

stage('条件示例') {
    when {
        // 分支条件
        branch 'main'
        
        // 或:匹配多个分支
        // anyOf { branch 'main'; branch 'develop' }
        
        // 环境变量条件
        environment name: 'DEPLOY_TO', value: 'production'
        
        // 表达式条件(支持 Groovy 表达式)
        expression { return params.DEPLOY == true }
        
        // 非条件
        not { branch 'master' }
        
        // 所有条件都满足
        allOf {
            branch 'main'
            environment name: 'APPROVED', value: 'true'
        }
        
        // 任一条件满足
        anyOf {
            branch 'main'
            branch 'hotfix'
        }
        
        // 变更触发条件
        changeset '**/*.java'
        
        // 判断是否有特定文件变更
        changeset "src/**/*"
    }
    steps {
        echo '条件满足,执行此阶段'
    }
}

5.4 Scripted Pipeline 示例

node('linux') {
    // 定义环境变量
    def appName = 'my-app'
    def buildResult = ''
    
    try {
        stage('Checkout') {
            checkout scm
            echo "当前工作目录: ${pwd()}"
        }
        
        stage('Build') {
            // 使用 try-catch 处理单个阶段错误
            try {
                sh 'mvn clean compile'
            } catch (Exception e) {
                echo "编译失败: ${e.message}"
                error("编译阶段失败")
            }
        }
        
        stage('Test') {
            // 并行执行多个测试
            parallel(
                unit: {
                    sh 'mvn test'
                },
                integration: {
                    sh 'mvn verify -P integration-test'
                }
            )
        }
        
        // 使用 Groovy 循环动态创建 stage
        def environments = ['dev', 'test', 'staging']
        for (envName in environments) {
            stage("Deploy to ${envName}") {
                echo "正在部署到 ${envName} 环境..."
                // 这里写部署逻辑
                sleep 5
            }
        }
        
        buildResult = 'SUCCESS'
        
    } catch (Exception e) {
        buildResult = 'FAILURE'
        echo "Pipeline 执行异常: ${e.message}"
        throw e
        
    } finally {
        // 清理操作
        stage('Cleanup') {
            echo "构建结果: ${buildResult}"
            cleanWs()
        }
    }
}

六、Jenkinsfile

6.1 Jenkinsfile 概述

  • 定义:Jenkinsfile 是一个文本文件,包含 Pipeline 的定义,通常存放在项目源码仓库的根目录
  • 文件名:默认 Jenkinsfile(可自定义)
  • 位置:项目根目录或子目录
  • 好处:
    • 构建流程与代码一起版本管理
    • Code Review 时可以审查 CI/CD 流程
    • 审计追踪:谁对 Pipeline 做了哪些修改

6.2 在 Jenkins 中使用 Jenkinsfile

创建 Pipeline Job 引用 Jenkinsfile:

  1. 新建 Item → 输入名称 → 选择”Pipeline” → 确定
  2. 在”Pipeline”配置项中,Definition 选择”Pipeline script from SCM”
  3. SCM 选择 Git → 填写仓库 URL → 填写分支
  4. Script Path → 填写 Jenkinsfile(默认值)

6.3 Jenkinsfile 最佳实践

Jenkinsfile 编写最佳实践
  • 使用 Declarative Pipeline(推荐):语法清晰、易于维护
  • 使用共享库:提取公共逻辑,避免重复代码
  • 禁用 Groovy Sandbox 时要谨慎:仅在信任的代码中使用
  • 避免在 Pipeline 中直接使用 input 阻塞:会占用 Executor
  • 合理使用 timeout:防止构建无限等待
  • 使用环境变量:而非硬编码值
  • 凭证使用 withCredentials:安全处理敏感信息
  • 使用 when 指令:条件化执行 stage
  • 合理设置 agent:避免所有 stage 在同一个 agent 执行
  • 使用 post 块:处理通知和清理
  • 避免在 Pipeline 中执行 sleep:应使用合适的等待机制
  • 添加恰当的错误处理:catchError、retry 等

七、插件管理

7.1 插件概念

  • 插件是 Jenkins 的核心扩展机制,所有功能几乎都通过插件实现
  • 1800+ 官方插件覆盖了版本管理、构建工具、云平台、通知、报告等各方面

7.2 插件管理操作

管理路径:Dashboard → Manage Jenkins → Plugins → Available plugins
# 安装方式:
# 1. 在线安装(推荐):在 Available 标签页搜索并勾选,点击 Install
# 2. 离线安装:上传 .hpi 文件(Advanced settings → Upload Plugin)
# 3. 命令行安装(需重启 Jenkins)
java -jar jenkins-cli.jar -s http://localhost:8080/ install-plugin <plugin-name>

7.3 常用必备插件

插件名称 用途
Git Git 源码管理
Pipeline Pipeline 核心功能
Pipeline: Stage View Pipeline 阶段可视化
Blue Ocean 现代化 UI,Pipeline 可视化编辑器
Credentials Binding 凭证管理与绑定
Email Extension 增强邮件通知
JUnit JUnit 测试报告展示
HTML Publisher HTML 报告展示
Build Timestamp 构建时间戳
Workspace Cleanup 工作空间清理
Docker Pipeline Docker 集成
Kubernetes 动态创建 K8s Agent
SonarQube Scanner 代码质量扫描
Allure Allure 测试报告
Role-based Authorization Strategy 基于角色的权限管理
Config File Provider 配置文件管理
Locale 汉化插件
Git Parameter Git 参数化构建
AnsiColor 控制台彩色输出

八、凭证管理与安全

8.1 凭证类型

凭证类型 说明 使用场景
Username with password 用户名+密码 Git 仓库认证、API 认证
SSH Username with private key SSH 用户名+私钥 Git SSH 密钥认证
Secret text 加密文本 API Token、Token 等
Secret file 加密文件 kubeconfig、证书文件等
Certificate 证书 PKCS#12 证书

8.2 在 Pipeline 中使用凭证

pipeline {
    agent any
    
    environment {
        // 方式一:使用 environment 指令直接绑定
        SONAR_TOKEN = credentials('sonar-token-id')
    }
    
    stages {
        stage('示例:使用凭证') {
            steps {
                // 方式二:使用 withCredentials 代码块
                withCredentials([
                    usernamePassword(
                        credentialsId: 'git-credentials',
                        usernameVariable: 'GIT_USER',
                        passwordVariable: 'GIT_PASS'
                    ),
                    string(
                        credentialsId: 'api-token',
                        variable: 'API_TOKEN'
                    ),
                    sshUserPrivateKey(
                        credentialsId: 'ssh-key',
                        keyFileVariable: 'SSH_KEY_FILE',
                        usernameVariable: 'SSH_USER'
                    )
                ]) {
                    sh '''
                        echo "使用 Git 凭证..."
                        git clone https://${GIT_USER}:${GIT_PASS}@github.com/repo.git
                        
                        echo "使用 API Token: ${API_TOKEN}"
                        
                        echo "使用 SSH 密钥: ${SSH_KEY_FILE}"
                    '''
                }
            }
        }
    }
}

8.3 权限管理

权限管理策略
  • 安全域(Security Realm)
    • Jenkins 自有用户数据库
    • LDAP 集成
    • Active Directory 集成
    • OAuth(GitHub、GitLab、Google 等)
  • 授权策略(Authorization)
    • Logged-in users can do anything:登录用户全权限
    • Matrix-based security:基于矩阵的权限控制
    • Role-based Strategy:基于角色的权限控制(需安装插件)
    • Project-based Matrix Authorization:基于项目矩阵授权
  • Role-based Strategy 推荐角色划分:
    • admin:完全管理权限
    • developer:创建 Job、查看构建、配置 Job
    • viewer:只读权限
    • operator:运行 Job、查看构建

九、构建触发器详解

9.1 定时构建(Build periodically)

# Cron 表达式:分 时 日 月 周
# 每个工作日(周一到周五)早上 8 点构建
H 8 * * 1-5

# 每天晚上 10 点构建
H 22 * * *

# 每隔 2 小时构建一次
H */2 * * *

9.2 轮询 SCM(Poll SCM)

# 每 5 分钟检查一次 Git 仓库是否有变更
H/5 * * * *

# 工作日每 15 分钟检查一次
H/15 * * * 1-5

9.3 Webhook 触发

GitHub Webhook 配置
  1. Jenkins 端配置
    • 安装 “GitHub Integration” 插件
    • Pipeline 中添加 triggers { githubPush() } 或在 Freestyle 中勾选 “GitHub hook trigger for GITScm polling”
  2. GitHub 端配置
    • 仓库 → Settings → Webhooks → Add webhook
    • Payload URL: http://jenkins-server:8080/github-webhook/
    • Content type: application/json
    • Events: Just the push event
  3. 触发流程:开发者 Push 代码 → GitHub 发送 POST 请求到 Jenkins → Jenkins 触发对应 Job

9.4 远程触发

# 通过 URL 触发(需要配置身份验证令牌)
# Freestyle: 构建触发器 → 触发远程构建 → 设置 Token

# 触发命令
curl -X POST http://jenkins-server:8080/job/my-job/build?token=my-token

# 带参数触发
curl -X POST "http://jenkins-server:8080/job/my-job/buildWithParameters?token=my-token&BRANCH=develop"

# Pipeline 远程触发(需安装 Build with Parameters 或使用 Generic Webhook Trigger 插件)

十、构建工具集成

10.1 Maven 集成

pipeline {
    agent any
    
    tools {
        maven 'maven-3.8'  // 需先在 Global Tool Configuration 中配置
    }
    
    stages {
        stage('Maven Build') {
            steps {
                sh '''
                    mvn clean install \
                      -Dmaven.test.skip=false \
                      -Dmaven.javadoc.skip=true \
                      -B -V  # 批处理模式 + 显示版本
                '''
            }
        }
        
        stage('Publish Test Report') {
            steps {
                // 发布 JUnit 测试报告
                junit '**/target/surefire-reports/*.xml'
            }
        }
        
        stage('Archive Artifacts') {
            steps {
                // 归档构建产物
                archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
            }
        }
    }
}

10.2 Gradle 集成

stage('Gradle Build') {
    steps {
        // 使用 Gradle Wrapper(推荐)
        sh './gradlew clean build'
        
        // 或使用 Jenkins 管理的 Gradle 工具
        // tool name: 'gradle-7.6', type: 'gradle'
        // sh 'gradle clean build'
    }
}

10.3 Docker 集成

pipeline {
    agent any
    
    stages {
        stage('Docker Build') {
            steps {
                script {
                    // 构建镜像
                    def appImage = docker.build("my-app:${env.BUILD_NUMBER}", '-f Dockerfile .')
                    
                    // 推送到镜像仓库
                    docker.withRegistry('https://registry.example.com', 'registry-credentials') {
                        appImage.push()
                        appImage.push('latest')
                    }
                }
            }
        }
        
        stage('Docker Deploy') {
            steps {
                script {
                    // 使用 docker-compose 部署
                    sh '''
                        docker-compose down
                        docker-compose up -d
                    '''
                }
            }
        }
    }
    
    post {
        always {
            // 清理构建过程中创建的 Docker 镜像
            sh 'docker image prune -f'
        }
    }
}

十一、分布式构建(Master-Agent)

11.1 分布式架构概念

  • Master 节点职责:Web UI、调度构建任务、管理配置、存储构建记录
  • Agent 节点职责:接收任务、执行构建、返回结果

11.2 添加 Agent 节点

方式一:通过 SSH 连接(推荐)

Dashboard → Manage Jenkins → Nodes → New Node
→ Node Name → 选择 "Permanent Agent"

配置项:

  • Name: Agent 名称
  • # of executors: 并行构建数量
  • Remote root directory: Agent 上的工作目录,如 /home/jenkins/agent
  • Labels: 标签,如 linux java docker
  • Usage: 选择 Only build jobs with label expressions matching this node
  • Launch method: 选择 Launch agents via SSH
    • Host: Agent 的 IP 地址
    • Credentials: SSH 认证凭证
    • Host Key Verification Strategy: Non verifying Verification Strategy

方式二:通过 Java Web Start 连接

# 在 Agent 机器上执行(从 Jenkins Agent 配置页面获取命令)
java -jar agent.jar -jnlpUrl http://jenkins-master:8080/computer/agent-name/jenkins-agent.jnlp -secret <secret>

11.3 在 Pipeline 中使用标签分配 Agent

pipeline {
    // 整个 Pipeline 在带 linux 标签的 agent 上执行
    agent { label 'linux' }
    
    stages {
        stage('Build on Linux') {
            agent { label 'linux' }
            steps {
                sh 'mvn clean compile'
            }
        }
        
        stage('Test on Windows') {
            agent { label 'windows' }
            steps {
                bat 'mvn test'
            }
        }
        
        stage('Deploy with Docker') {
            agent {
                docker {
                    image 'node:16-alpine'
                    label 'docker-enabled'
                }
            }
            steps {
                sh 'node --version'
            }
        }
    }
}

十二、共享库(Shared Libraries)

12.1 共享库概念

  • 定义:将 Pipeline 中可复用的代码封装为共享库,供多个项目引用
  • 存储方式:独立的 Git 仓库,需包含特定目录结构

12.2 共享库目录结构

my-shared-library/                  # Git 仓库根目录
├── vars/                           # 全局变量(可被 Pipeline 直接调用)
│   ├── sayHello.groovy             # 自定义步骤
│   ├── mavenBuild.groovy           # Maven 构建封装
│   └── deployApp.groovy            # 部署封装
├── src/                            # Java/Groovy 源码
│   └── com/
│       └── company/
│           └── jenkins/
│               ├── BuildUtils.groovy
│               └── NotifyUtils.groovy
└── resources/                      # 资源文件
    └── templates/
        └── email-template.html

12.3 编写共享库步骤

文件 vars/mavenBuild.groovy:

#!/usr/bin/env groovy

/**
 * 执行 Maven 构建
 * @param args Map 类型参数
 *   - goals: Maven 目标(如 'clean install')
 *   - options: 额外选项(如 '-DskipTests')
 *   - pomPath: pom.xml 路径(默认为 '.')
 */
def call(Map args = [:]) {
    def goals = args.goals ?: 'clean install'
    def options = args.options ?: ''
    def pomPath = args.pomPath ?: '.'
    
    pipeline {
        agent any
        stages {
            stage('Maven Build') {
                steps {
                    script {
                        sh """
                            cd ${pomPath}
                            mvn ${goals} ${options} -B
                        """
                    }
                }
            }
        }
    }
}

12.4 配置和使用共享库

在 Jenkins 中配置:

Dashboard → Manage Jenkins → Configure System → Global Pipeline Libraries
→ 添加 Library
  - Name: my-shared-library
  - Default version: main
  - Retrieval method: Modern SCM → Git → 仓库 URL
  - 勾选 "Load implicitly"(所有 Pipeline 自动加载)

在 Jenkinsfile 中使用:

// 声明使用共享库
@Library('my-shared-library') _

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                // 直接调用共享库中的步骤
                mavenBuild(
                    goals: 'clean package',
                    options: '-DskipTests'
                )
                
                // 调用另一个共享库方法
                sayHello(name: 'Jenkins')
            }
        }
        
        stage('Deploy') {
            steps {
                script {
                    // 调用共享库中的工具类
                    def notifier = new com.company.jenkins.NotifyUtils()
                    notifier.sendNotification(
                        status: 'success',
                        to: 'team@example.com'
                    )
                }
            }
        }
    }
}

十三、Blue Ocean

13.1 Blue Ocean 概述

  • 定义:Jenkins 的全新现代化用户界面,专为 Pipeline 设计
  • 访问方式:http://jenkins-server:8080/blue/
  • 安装:在插件管理中搜索 “Blue Ocean” 安装

13.2 Blue Ocean 核心功能

功能 说明
可视化 Pipeline 编辑器 拖拽式创建 Pipeline,无需手写代码
Pipeline 可视化 树形展示每个 stage 的执行状态和时间
分支识别 自动发现 Git 仓库的多分支,为每个分支创建独立的 Pipeline
个性化仪表盘 可自定义关注的 Pipeline 视图
测试报告集成 直接在 UI 中查看测试结果和失败详情
日志查看 分步骤查看构建日志,易于定位问题
GitHub/GitLab 集成 直接从 UI 创建 GitHub/GitLab 分支的 Pipeline

十四、邮件与通知

14.1 邮件通知配置

// Jenkins 系统管理配置(Manage Jenkins → Configure System):
// - Jenkins URL: http://jenkins.example.com:8080/
// - 系统管理员邮件地址: jenkins@example.com
// - Extended E-mail Notification(高级配置)

pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean compile'
            }
        }
    }
    
    post {
        success {
            // 方式一:标准邮件通知
            mail (
                to: 'dev-team@example.com',
                subject: "构建成功: ${env.JOB_NAME} - #${env.BUILD_NUMBER}",
                body: """
                    构建项目: ${env.JOB_NAME}
                    构建编号: #${env.BUILD_NUMBER}
                    构建结果: 成功
                    构建 URL: ${env.BUILD_URL}
                    构建时间: ${currentBuild.durationString}
                """
            )
            
            // 方式二:增强邮件通知(需安装 Email Extension 插件)
            emailext (
                subject: "✅ 构建成功: ${env.JOB_NAME} - #${env.BUILD_NUMBER}",
                body: '''${SCRIPT, template="groovy-html.template"}''',
                to: 'dev-team@example.com',
                attachLog: true,         // 附加构建日志
                compressLog: true        // 压缩日志
            )
        }
        
        failure {
            emailext (
                subject: "❌ 构建失败: ${env.JOB_NAME} - #${env.BUILD_NUMBER}",
                body: '''${SCRIPT, template="groovy-html.template"}''',
                to: 'dev-team@example.com',
                recipientProviders: [
                    [ $class: 'DevelopersRecipientProvider' ],   // 发送给变更提交者
                    [ $class: 'CulpritsRecipientProvider' ]      // 发送给责任人
                ]
            )
        }
    }
}

14.2 企业微信/钉钉/飞书通知

钉钉机器人通知示例
// 需安装 HTTP Request 插件,或使用 DingTalk 插件

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean compile'
            }
        }
    }
    post {
        success {
            script {
                def dingtalkUrl = 'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN'
                def message = [
                    msgtype: 'markdown',
                    markdown: [
                        title: "构建通知",
                        text: """## ✅ Jenkins 构建成功
                        > **项目**: ${env.JOB_NAME}
                        > **构建编号**: #${env.BUILD_NUMBER}
                        > **分支**: ${env.BRANCH_NAME}
                        > **耗时**: ${currentBuild.durationString}
                        > [查看详情](${env.BUILD_URL})
                        """
                    ]
                ]
                httpRequest (
                    url: dingtalkUrl,
                    httpMode: 'POST',
                    contentType: 'APPLICATION_JSON',
                    requestBody: groovy.json.JsonOutput.toJson(message)
                )
            }
        }
        failure {
            script {
                // 类似的失败通知逻辑
                echo "发送失败通知到钉钉..."
            }
        }
    }
}

十五、常用操作与技巧

15.1 常用内置环境变量

变量名 说明 示例值
BUILD_NUMBER 构建编号 42
BUILD_ID 构建 ID 2024-01-01_12-00-00
BUILD_URL 构建 URL http://jenkins/job/my-job/42/
JOB_NAME 项目名称 my-job
JOB_URL 项目 URL http://jenkins/job/my-job/
WORKSPACE 工作空间路径 /var/jenkins/workspace/my-job
NODE_NAME 节点名称 master
NODE_LABELS 节点标签 linux docker
BRANCH_NAME 分支名(Multibranch Pipeline) main
GIT_COMMIT Git 提交哈希 a1b2c3d...
GIT_BRANCH Git 分支 origin/main
BUILD_TAG 构建标签 jenkins-my-job-42
EXECUTOR_NUMBER 执行器编号 0
JENKINS_URL Jenkins 根 URL http://jenkins:8080/
JENKINS_HOME Jenkins 主目录 /var/lib/jenkins

15.2 参数化构建

pipeline {
    agent any
    
    parameters {
        // 字符串参数
        string(name: 'VERSION', defaultValue: '1.0.0', description: '发布版本号')
        
        // 选择参数
        choice(name: 'ENVIRONMENT', choices: ['dev', 'test', 'staging', 'prod'], description: '部署环境')
        
        // 布尔参数
        booleanParam(name: 'RUN_TESTS', defaultValue: true, description: '是否运行测试')
        
        // 密码参数
        password(name: 'DEPLOY_KEY', defaultValue: '', description: '部署密钥')
        
        // 文件参数
        // file(name: 'CONFIG_FILE', description: '上传配置文件')
        
        // Git 参数(需安装 Git Parameter 插件)
        gitParameter(name: 'BRANCH', type: 'PT_BRANCH', defaultValue: 'main', description: '选择分支')
    }
    
    stages {
        stage('使用参数') {
            steps {
                echo "版本号: ${params.VERSION}"
                echo "部署环境: ${params.ENVIRONMENT}"
                echo "是否运行测试: ${params.RUN_TESTS}"
            }
        }
        
        stage('条件执行') {
            when {
                expression { params.RUN_TESTS == true }
            }
            steps {
                echo '运行测试...'
            }
        }
    }
}

15.3 重试机制

pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                // 方式一:使用 retry 步骤
                retry(3) {
                    sh '''
                        echo "尝试部署..."
                        # 模拟部署操作
                        sleep 2
                    '''
                }
            }
        }
        
        stage('Unstable Build') {
            steps {
                script {
                    try {
                        sh 'mvn test'
                    } catch (Exception e) {
                        // 标记构建为不稳定,而非失败
                        currentBuild.result = 'UNSTABLE'
                        echo "测试有失败,但构建继续"
                    }
                }
            }
        }
        
        stage('Skip Stage On Error') {
            steps {
                // 方式二:使用 catchError 捕获错误并继续
                catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
                    sh './optional-script.sh'
                }
            }
        }
    }
}

15.4 输入审批(Input)

pipeline {
    agent any
    
    stages {
        stage('Build and Test') {
            steps {
                echo '构建和测试完成'
            }
        }
        
        stage('Approval') {
            steps {
                // 等待人工审批
                input (
                    message: '是否部署到生产环境?',
                    ok: '确认部署',
                    submitter: 'admin,leader',       // 允许审批的用户
                    submitterParameter: 'APPROVER',  // 记录审批人
                    parameters: [
                        string(name: 'REASON', defaultValue: '', description: '部署原因')
                    ]
                )
            }
        }
        
        stage('Deploy to Production') {
            steps {
                echo "审批人: ${APPROVER}, 部署原因: ${REASON}"
                echo '开始生产部署...'
            }
        }
    }
}

十六、常见问题与排查

16.1 常见问题

问题 1:Jenkins 内存不足(OOM)
  • 现象:Jenkins 频繁崩溃或重启,日志中出现 OutOfMemoryError
  • 原因:默认 JVM 堆内存太小,大量构建任务占用内存
  • 解决:
# 编辑 Jenkins 启动配置(Linux: /etc/default/jenkins)
JAVA_ARGS="-Xms1024m -Xmx4096m -XX:MaxPermSize=512m"

# Docker 方式
docker run -e JAVA_OPTS="-Xms1024m -Xmx4096m" jenkins/jenkins:lts-jdk17

# 重启 Jenkins 使配置生效
问题 2:磁盘空间不足
  • 现象:构建失败,提示磁盘空间不足
  • 解决:
    • 设置构建保留策略(Discard Old Builds):限制保留天数和数量
    • 定期清理工作空间:安装 Workspace Cleanup 插件
    • 使用 cleanWs() 在 Pipeline 结束时清理
    • 清理 JENKINS_HOME 大文件:du -sh /var/lib/jenkins/* | sort -rh | head -20
问题 3:Pipeline 脚本语法错误
  • 解决方式:
    • 使用 Jenkins 内置的 “Pipeline Syntax” 生成器(Pipeline Job → Pipeline Syntax)
    • 安装 “Pipeline Utility Steps” 插件获取更多工具步骤
    • 使用 Blue Ocean 的可视化编辑器减少语法错误
    • 在本地使用 jenkinsfile-runner 测试 Jenkinsfile
问题 4:插件兼容性问题
  • 现象:升级 Jenkins 后某些功能异常
  • 解决:
    • 升级前先备份 Jenkins Home 目录
    • 在测试环境先验证升级
    • 使用 Jenkins LTS(长期支持)版本,避免频繁升级
    • 检查插件更新日志中的兼容性说明
问题 5:Git 拉取超时或失败
  • 解决:
    • 检查凭证是否有效和过期
    • 检查网络连接和防火墙规则
    • 增加 Git 超时时间:git config --global http.lowSpeedLimit 0
    • 使用浅克隆(Shallow Clone)减少拉取时间:checkout([$class: 'GitSCM', extensions: [[$class: 'CloneOption', shallow: true, depth: 1]]])

16.2 备份与恢复

Jenkins 备份策略
# 推荐备份内容:整个 JENKINS_HOME 目录(通常是 /var/lib/jenkins/)

# 方式一:使用 ThinBackup 插件
# Dashboard → Manage Jenkins → ThinBackup → Settings → Backup Now

# 方式二:手动备份
#!/bin/bash
BACKUP_DIR="/backup/jenkins"
DATE=$(date +%Y%m%d_%H%M%S)
JENKINS_HOME="/var/lib/jenkins"

# 停止 Jenkins(可选,推荐夜间操作)
# sudo systemctl stop jenkins

# 创建备份
tar -czf "${BACKUP_DIR}/jenkins_backup_${DATE}.tar.gz" -C ${JENKINS_HOME} .

# 保留最近 30 天的备份
find ${BACKUP_DIR} -name "jenkins_backup_*.tar.gz" -mtime +30 -delete

# 启动 Jenkins
# sudo systemctl start jenkins

echo "备份完成: ${BACKUP_DIR}/jenkins_backup_${DATE}.tar.gz"

十七、性能优化

17.1 Jenkins 性能优化方案

优化方向 措施
JVM 优化 使用 G1 垃圾回收器:-XX:+UseG1GC,设置合理的堆大小
插件管理 卸载不需要的插件,定期更新插件
构建历史 限制保留的构建数量,定期清理旧构建
Master 节点 Master 节点不执行构建任务(设置 Executor 为 0)
分布式构建 将构建任务分散到多个 Agent 节点
文件系统 使用 SSD,避免 NFS 作为 JENKINS_HOME
索引优化 定期运行 java -jar jenkins-cli.jar clear-queue 清理队列
日志清理 配置日志轮转策略
构建超时 为每个 Pipeline 设置 timeout

17.2 最佳实践清单

  • ✅ 使用 Declarative Pipeline
  • ✅ Jenkinsfile 纳入版本管理
  • ✅ 使用共享库提取公共逻辑
  • ✅ Master 节点不执行构建任务
  • ✅ 设置合理的构建保留策略
  • ✅ 使用 LTS(长期支持)版本
  • ✅ 配置 SSL/HTTPS
  • ✅ 定期备份 JENKINS_HOME
  • ✅ 使用凭证管理敏感信息,不硬编码
  • ✅ 为 Pipeline 设置 timeout
  • ✅ 使用 when 条件控制 stage 执行
  • ✅ Agent 使用 label 进行任务分配
  • ✅ 配置构建失败通知
  • ✅ 合理使用并行执行加速构建

十八、实际项目 Pipeline 示例

18.1 完整的前后端项目 CI/CD Pipeline

pipeline {
    agent { label 'linux && docker' }
    
    environment {
        DOCKER_REGISTRY = 'registry.example.com'
        APP_NAME = 'web-application'
        SONAR_HOST = 'http://sonarqube.example.com:9000'
    }
    
    parameters {
        string(name: 'RELEASE_VERSION', defaultValue: '', description: '发布版本号(留空则使用构建编号)')
        choice(name: 'DEPLOY_ENV', choices: ['dev', 'test', 'staging', 'prod'], description: '部署环境')
        booleanParam(name: 'SKIP_SONAR', defaultValue: false, description: '跳过代码质量检查')
    }
    
    options {
        timeout(time: 2, unit: 'HOURS')
        disableConcurrentBuilds()
        buildDiscarder(logRotator(numToKeepStr: '30', daysToKeepStr: '90'))
        timestamps()
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                echo "分支: ${env.BRANCH_NAME}, 提交: ${env.GIT_COMMIT.take(8)}"
            }
        }
        
        stage('Version') {
            steps {
                script {
                    if (params.RELEASE_VERSION) {
                        env.APP_VERSION = params.RELEASE_VERSION
                    } else {
                        env.APP_VERSION = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
                    }
                    echo "构建版本: ${env.APP_VERSION}"
                }
            }
        }
        
        stage('Code Quality') {
            when { expression { !params.SKIP_SONAR } }
            steps {
                withSonarQubeEnv('SonarQube') {
                    sh """
                        mvn sonar:sonar \
                          -Dsonar.projectKey=${APP_NAME} \
                          -Dsonar.projectVersion=${APP_VERSION} \
                          -Dsonar.host.url=${SONAR_HOST}
                    """
                }
            }
        }
        
        stage('Build') {
            steps {
                sh "mvn clean package -DskipTests -Drevision=${APP_VERSION}"
            }
        }
        
        stage('Unit Test') {
            steps {
                sh 'mvn test'
            }
            post {
                always {
                    junit '**/target/surefire-reports/*.xml'
                }
            }
        }
        
        stage('Docker Build & Push') {
            steps {
                script {
                    def imageName = "${DOCKER_REGISTRY}/${APP_NAME}:${APP_VERSION}"
                    def dockerImage = docker.build(imageName)
                    
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-cred') {
                        dockerImage.push()
                        if (params.DEPLOY_ENV == 'prod') {
                            dockerImage.push('latest')
                        }
                    }
                    
                    echo "镜像已推送: ${imageName}"
                }
            }
        }
        
        stage('Deploy') {
            when {
                anyOf {
                    expression { params.DEPLOY_ENV != 'prod' }
                    expression {
                        input(
                            message: '确认部署到生产环境?',
                            ok: '确认'
                        )
                        return true
                    }
                }
            }
            steps {
                script {
                    echo "开始部署到 ${params.DEPLOY_ENV} 环境..."
                    
                    // 根据环境使用不同的部署方式
                    switch(params.DEPLOY_ENV) {
                        case 'dev':
                            sh "kubectl set image deployment/${APP_NAME} ${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${APP_VERSION} -n dev"
                            break
                        case 'test':
                            sh "kubectl set image deployment/${APP_NAME} ${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${APP_VERSION} -n test"
                            break
                        case 'staging':
                            sh "helm upgrade ${APP_NAME} ./helm-chart --set image.tag=${APP_VERSION} -n staging"
                            break
                        case 'prod':
                            sh "helm upgrade ${APP_NAME} ./helm-chart --set image.tag=${APP_VERSION} -n production"
                            break
                    }
                }
            }
        }
        
        stage('Smoke Test') {
            steps {
                sh '''
                    sleep 15  # 等待服务启动
                    curl -f http://service-endpoint/health || exit 1
                '''
            }
        }
    }
    
    post {
        success {
            script {
                def duration = currentBuild.durationString.replace(' and counting', '')
                emailext(
                    subject: "✅ [${params.DEPLOY_ENV}] ${APP_NAME} #${BUILD_NUMBER} 构建部署成功",
                    body: """
                        <h3>构建部署成功</h3>
                        <ul>
                            <li><b>项目:</b> ${APP_NAME}</li>
                            <li><b>版本:</b> ${APP_VERSION}</li>
                            <li><b>环境:</b> ${params.DEPLOY_ENV}</li>
                            <li><b>分支:</b> ${BRANCH_NAME}</li>
                            <li><b>耗时:</b> ${duration}</li>
                        </ul>
                        <p><a href="${BUILD_URL}">查看构建详情</a></p>
                    """,
                    to: 'dev-team@example.com',
                    mimeType: 'text/html'
                )
            }
        }
        
        failure {
            echo '构建或部署失败,请检查日志。'
        }
        
        always {
            cleanWs(
                cleanWhenNotBuilt: false,
                deleteDirs: true,
                disableDeferredWipeout: true
            )
        }
    }
}

总结

  • Jenkins 作为业界最成熟的开源 CI/CD 工具,具有极强的灵活性和可扩展性
  • Declarative Pipeline 是推荐的 Pipeline 编写方式,结构清晰、易于维护
  • Jenkinsfile + 共享库 是实现 Pipeline as Code 的核心模式
  • 插件生态 是 Jenkins 的核心竞争力,但需审慎管理插件数量
  • 分布式构建 是大规模项目的必要架构
  • 安全和备份 是生产环境必须重视的方面
  • 持续学习和实践是掌握 Jenkins 的最佳途径,建议从简单的 Freestyle 项目开始,逐步过渡到复杂的 Pipeline 和共享库