Jenkins CI/CD 实战博客教程

🚀 Jenkins CI/CD 实战博客教程

定位 :从零搭建企业级自动化流水线 | 适合开发/运维/DevOps 工程师

特色 :每篇含「真实场景 + 完整脚本 + 坑点排查 + 企业最佳实践」

环境 :基于华为云ECS实战环境(ecs-fddb CI/CD集群 + ecs-2c98 K8s集群)

所有代码可直接复制运行(已适配 Jenkins 2.4+ LTS)


📋 目录

  1. [第一部分:Jenkins 入门与核心架构(打好地基)](#第一部分:Jenkins 入门与核心架构(打好地基))
  2. [第二部分:Pipeline 脚本核心语法(Declarative & Scripted)](#第二部分:Pipeline 脚本核心语法(Declarative & Scripted))
  3. [第三部分:CI 流水线实战(代码 → 构建 → 测试)](#第三部分:CI 流水线实战(代码 → 构建 → 测试))
  4. [第四部分:CD 流水线实战(构建 → 部署 → 验证)](#第四部分:CD 流水线实战(构建 → 部署 → 验证))
  5. 第五部分:高可用与企业级进阶
  6. 第六部分:真实企业案例合集
  7. 附录:必备工具包

第一部分:Jenkins 入门与核心架构(打好地基)

1.1 为什么企业都选 Jenkins?

CI/CD 本质:从"人肉发布"到"机器自动流水线"

传统发布流程的痛苦

复制代码
开发人员 → 本地构建 → 手动打包 → 上传服务器 → 手动部署 → 发现问题 → 回滚 → 重复...
  • ❌ 耗时:每次发布需要30分钟~2小时
  • ❌ 易错:手动操作容易出错(漏配环境变量、传错包等)
  • ❌ 不可追溯:出了问题不知道是谁、哪个环节出的问题

CI/CD 流水线自动化

复制代码
代码提交 → 自动构建 → 自动测试 → 自动打包 → 自动部署 → 自动验证 → 通知
  • ✅ 快速:每次发布只需2~5分钟
  • ✅ 可靠:标准化流程,减少人为错误
  • ✅ 可追溯:每次构建都有完整记录,可快速定位问题
Jenkins vs GitLab CI vs GitHub Actions 对比(附选型决策树)
维度 Jenkins GitLab CI GitHub Actions
部署方式 自部署(服务器/容器) 自部署/GitLab SaaS SaaS(GitHub托管)
插件生态 ⭐⭐⭐⭐⭐(1000+插件) ⭐⭐⭐(内置功能丰富) ⭐⭐⭐⭐(Marketplace)
灵活性 ⭐⭐⭐⭐⭐(完全可定制) ⭐⭐⭐⭐(YAML配置) ⭐⭐⭐⭐(YAML配置)
学习曲线 ⭐⭐⭐⭐(较陡峭) ⭐⭐⭐(中等) ⭐⭐(较简单)
企业级功能 ⭐⭐⭐⭐⭐(权限/HA/分布式) ⭐⭐⭐⭐(内置) ⭐⭐⭐(需企业版)
适用场景 大型企业、复杂流水线、混合云 GitLab用户、一体化DevOps GitHub用户、开源项目

选型决策树

复制代码
你的代码在GitHub上?
├─ 是 → 用 GitHub Actions
└─ 否 → 你的代码在GitLab上?
    ├─ 是 → 用 GitLab CI
    └─ 否 → 你需要高度定制化?
        ├─ 是 → 用 Jenkins
        └─ 否 → 用 GitLab CI 或 GitHub Actions

企业选择Jenkins的核心原因

  1. 插件生态丰富:几乎能集成所有工具(Docker、K8s、SonarQube、钉钉等)
  2. 高度可定制:支持Groovy脚本,可实现任意复杂的流水线逻辑
  3. 企业级功能:完善的权限管理、高可用架构、分布式构建
  4. 成熟稳定:10+年历史,大量企业最佳实践可参考
实战:5 分钟部署 Jenkins(Docker + Helm + 二进制三种方式)

方式一:Docker 部署(推荐,最快)

bash 复制代码
# 创建Jenkins数据目录
mkdir -p /data/jenkins_home
chmod 777 /data/jenkins_home

# 运行Jenkins LTS容器
docker run -d \
  --name jenkins \
  -p 8080:8080 \
  -p 50000:50000 \
  -v /data/jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /usr/bin/docker:/usr/bin/docker \
  --restart=always \
  jenkins/jenkins:lts

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

参数详解

  • -p 8080:8080:Web访问端口
  • -p 50000:50000:Agent连接端口
  • -v /data/jenkins_home:/var/jenkins_home:持久化Jenkins数据
  • -v /var/run/docker.sock:/var/run/docker.sock:让Jenkins能调用宿主机的Docker(用于构建镜像)
  • -v /usr/bin/docker:/usr/bin/docker:让Jenkins容器能执行docker命令
  • --restart=always:开机自启动

方式二:Helm 部署(K8s环境推荐)

bash 复制代码
# 添加Jenkins Helm仓库
helm repo add jenkins https://charts.jenkins.io
helm repo update

# 创建命名空间
kubectl create namespace jenkins

# 安装Jenkins
helm install jenkins jenkins/jenkins \
  -n jenkins \
  --set controller.servicePort=8080 \
  --set controller.targetPort=8080

方式三:二进制部署(传统方式)

bash 复制代码
# 安装JDK17
apt-get update && apt-get install -y openjdk-17-jdk

# 下载Jenkins WAR包
wget https://get.jenkins.io/war-stable/latest/jenkins.war -O /opt/jenkins.war

# 后台运行Jenkins
nohup java -jar /opt/jenkins.war --httpPort=8080 > /var/log/jenkins.log 2>&1 &

企业最佳实践

  • ✅ 生产环境推荐用Docker或Helm部署(易于迁移和备份)
  • ✅ 一定要挂载数据卷(防止容器删除后数据丢失)
  • ✅ 配置自动备份(每天定时备份/var/jenkins_home目录)

1.2 Jenkins 架构解剖

Master/Agent 架构图解(含通信机制:JNLP vs SSH)
复制代码
┌─────────────────────────────────────────────────────────────┐
│                        Jenkins Master                        │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │ Web UI   │  │  Build   │  │  Plugin  │  │  Credentials │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘ │
│           │
│           ▼
│  ┌─────────────────────────────────────────────────────────┐ │
│  │         Agent 通信机制(JNLP vs SSH)                  │ │
│  ├─────────────────────────────────────────────────────┤ │
│  │ JNLP(默认):Agent主动连接Master(TCP 50000)       │ │
│  │ SSH:Master主动连接Agent(SSH 22端口)               │ │
│  └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────────────────────────────┐
│                     Jenkins Agent(s)                         │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                │
│  │ Agent-1  │  │ Agent-2  │  │ Agent-3  │  ...           │
│  │ (Build)  │  │ (Test)   │  │ (Deploy) │                │
│  └──────────┘  └──────────┘  └──────────┘                │
└─────────────────────────────────────────────────────────────┘

JNLP vs SSH 对比

维度 JNLP(默认) SSH
连接方向 Agent主动连接Master Master主动连接Agent
端口 TCP 50000(需开放) SSH 22(通常已开放)
适用场景 Agent在防火墙内/容器里 Agent在公网/DMZ区
配置复杂度 低(Agent自动连接) 中(需配置SSH凭据)
企业推荐 ✅ 容器化环境(K8s Pod as Agent) ✅ 传统虚拟机环境
插件生态全景:Pipeline、Credentials、Git、Docker、Kubernetes 等核心插件

企业级核心插件清单(必装)

插件名称 用途 优先级
Pipeline 支持Jenkinsfile流水线 ⭐⭐⭐⭐⭐
Credentials Binding 凭据管理(SSH Key、密码等) ⭐⭐⭐⭐⭐
Git Git仓库集成 ⭐⭐⭐⭐⭐
Docker Pipeline Docker镜像构建与推送 ⭐⭐⭐⭐⭐
Kubernetes K8s云配置(动态Pod Agent) ⭐⭐⭐⭐⭐
Active Choices 参数化构建下拉联动 ⭐⭐⭐⭐
SonarQube Scanner 代码质量扫描 ⭐⭐⭐⭐
Role-based Authorization 权限管理(RBAC) ⭐⭐⭐⭐
Prometheus 监控指标暴露 ⭐⭐⭐
Backup 数据备份 ⭐⭐⭐

插件安装命令(自动化)

bash 复制代码
# 在Jenkins容器内执行
jenkins-plugin-cli --plugins \
  pipeline \
  credentials-binding \
  git \
  docker-workflow \
  kubernetes \
  active-choices \
  sonar \
  role-strategy \
  prometheus \
  backup
安全加固:CSRF、Script Security、权限矩阵(Role-Based Strategy)

1. CSRF防护

复制代码
Jenkins → 系统管理 → 全局安全配置 → CSRF保护 → 启用
  • ✅ 防止跨站请求伪造攻击
  • ✅ 企业级Jenkins必须开启

2. Script Security

复制代码
Jenkins → 系统管理 → 全局安全配置 → 脚本安全 → 启用
  • ✅ 限制Groovy脚本执行权限
  • ✅ 防止恶意脚本执行

3. 权限矩阵(Role-Based Strategy)

角色 权限
admin 全部权限
dev 构建、查看、取消构建
ops 构建、部署、配置系统
viewer 仅查看

配置步骤

  1. 安装Role-based Authorization Strategy插件
  2. 系统管理 → 管理用户 → 创建用户(dev1、ops1、viewer1)
  3. 系统管理 → 系统配置 → 全局安全配置 → 授权策略 → Role-Based Strategy
  4. 系统管理 → Manage and Assign Roles → Manage Roles
  • 创建dev角色:勾选Job → Build、Cancel、Read
  • 创建ops角色:勾选Job → Build、Deploy、Configure、Read
  • 创建viewer角色:勾选Job → Read
  1. 系统管理 → Manage and Assign Roles → Assign Roles
  • dev1分配给dev角色
  • ops1分配给ops角色
  • viewer1分配给viewer角色

1.3 基础配置实战

配置全局工具(JDK/Maven/Git/Docker)

自动化配置脚本(Groovy)

groovy 复制代码
import jenkins.model.*
import hudson.model.*
import hudson.tools.*

// 配置JDK17
def jdkTool = Jenkins.instance.getExtensionList(hudson.tools.JDK.DescriptorImpl.class)[0]
def jdkInstaller = new hudson.tools.JDKInstaller("17", true)
def jdk = new hudson.tools.JDK("JDK17", "/usr/lib/jvm/java-17-openjdk")
jdkTool.setInstallations(jdk)

// 配置Maven3.8.8
def mavenTool = Jenkins.instance.getExtensionList(hudson.tasks.Maven.DescriptorImpl.class)[0]
def maven = new hudson.tasks.Maven.MavenInstallation("Maven3", "/opt/maven", [])
mavenTool.setInstallations(maven)

// 配置Git
def gitTool = Jenkins.instance.getExtensionList(hudson.plugins.git.GitTool.DescriptorImpl.class)[0]
def git = new hudson.plugins.git.GitTool("Git", "/usr/bin/git", [])
gitTool.setInstallations(git)

// 保存配置
Jenkins.instance.save()

执行方式

bash 复制代码
# 在Jenkins Master上执行
curl -X POST -u admin:Admin@123456 \
  http://localhost:8080/script \
  --data-urlencode "script=$(cat config-tools.groovy)"
凭据管理:SSH Key、Username/Password、Docker Registry Token

1. 添加SSH Key凭据(用于Git克隆)

bash 复制代码
# 生成SSH密钥对
ssh-keygen -t rsa -b 4096 -f ~/.ssh/jenkins_git_key -N ""

# 将公钥添加到Git服务器(如GitLab/GitHub)
cat ~/.ssh/jenkins_git_key.pub

# 在Jenkins中添加SSH私钥
# 方式:Jenkins → 凭据 → 系统 → 全局凭据 → 添加凭据
# 类型:SSH Username with private key
# ID:git-ssh-key
# Username:git
# Private Key:粘贴 ~/.ssh/jenkins_git_key 的内容

2. 添加Username/Password凭据(用于Git账号密码认证)

复制代码
Jenkins → 凭据 → 系统 → 全局凭据 → 添加凭据
类型:Username with password
ID:git-username-password
Username:your-git-username
Password:your-git-password

3. 添加Docker Registry Token(用于推送镜像)

复制代码
Jenkins → 凭据 → 系统 → 全局凭据 → 添加凭据
类型:Username with password
ID:docker-registry
Username:your-docker-username
Password:your-docker-password

自动化添加凭据(Groovy脚本)

groovy 复制代码
import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.domains.*
import com.cloudbees.plugins.credentials.impl.*

// 添加SSH Key凭据
def sshKey = new BasicSSHUserPrivateKey(
  CredentialsScope.GLOBAL,
  "git-ssh-key",
  "git",
  new BasicSSHUserPrivateKey.FileOnMasterPrivateKeySource("/var/jenkins_home/.ssh/id_rsa"),
  "",
  "Git SSH Key"
)

// 添加Username/Password凭据
def usernamePassword = new UsernamePasswordCredentialsImpl(
  CredentialsScope.GLOBAL,
  "git-username-password",
  "Git Username Password",
  "your-git-username",
  "your-git-password"
)

// 添加Docker Registry凭据
def dockerRegistry = new UsernamePasswordCredentialsImpl(
  CredentialsScope.GLOBAL,
  "docker-registry",
  "Docker Registry",
  "your-docker-username",
  "your-docker-password"
)

// 保存凭据
def store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore()
store.addCredentials(Domain.global(), sshKey)
store.addCredentials(Domain.global(), usernamePassword)
store.addCredentials(Domain.global(), dockerRegistry)

// 保存配置
Jenkins.instance.save()
项目类型选择:Freestyle vs Pipeline(为什么必须用 Pipeline?)
维度 Freestyle Project Pipeline Project
配置方式 Web UI界面配置 Jenkinsfile(代码化)
可维护性 ❌ 配置分散在UI,难以版本控制 ✅ Jenkinsfile可提交到Git,版本控制
复杂性支持 ❌ 仅支持简单流水线 ✅ 支持复杂逻辑(条件、循环、并行)
可复用性 ❌ 难以复用 ✅ 可共享库(Shared Library)
企业级适用 ❌ 不适合 ✅ 行业标准

为什么必须用Pipeline?

  1. 代码化配置:Jenkinsfile可以提交到Git,方便版本控制和代码审查
  2. 复杂流水线支持:支持条件判断、循环、并行执行等复杂逻辑
  3. 可复用:通过Shared Library实现流水线逻辑复用
  4. 企业标准:所有现代企业CI/CD都使用Pipeline

企业最佳实践

  • ✅ 禁止使用Freestyle Project(难以维护)
  • ✅ 所有流水线必须用Declarative Pipeline(更易读)
  • ✅ Jenkinsfile必须提交到项目仓库(根目录或ci/目录)

第二部分:Pipeline 脚本核心语法(Declarative & Scripted)

2.1 Declarative Pipeline 快速上手

基础结构
groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
    }
}
agent / stages / steps / post 四大核心块详解

1. agent(代理)

  • 指定流水线在哪个Agent上执行
  • 可选值:
    • any:任意可用Agent
    • none:每个stage需指定自己的agent
    • label 'agent-label':指定标签的Agent
    • docker 'image-name':在Docker容器中执行
groovy 复制代码
pipeline {
    agent none  // 顶层不指定agent

    stages {
        stage('Build') {
            agent { label 'build-agent' }  // 这个阶段在build-agent上执行
            steps {
                sh 'mvn clean package'
            }
        }
        stage('Test') {
            agent { docker 'maven:3.8.8-openjdk-17' }  // 这个阶段在Maven容器中执行
            steps {
                sh 'mvn test'
            }
        }
    }
}

2. stages(阶段)

  • 包含一个或多个stage(阶段)
  • 每个stage代表流水线的一个步骤(如Build、Test、Deploy)

3. steps(步骤)

  • 包含一个或多个步骤(如sh、echo、git等)
  • 每个step代表一个具体操作

4. post(后置处理)

  • 在流水线或stage结束后执行
  • 可选条件:
    • always:总是执行
    • success:成功时执行
    • failure:失败时执行
    • aborted:取消时执行
groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
    }

    post {
        always {
            echo 'This runs always'
        }
        success {
            echo 'This runs only on success'
        }
        failure {
            echo 'This runs only on failure'
        }
    }
}
环境变量注入:environment { KEY = 'value' }
groovy 复制代码
pipeline {
    agent any

    environment {
        APP_NAME = 'spring-petclinic'
        DOCKER_REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
        IMAGE_TAG = "${BUILD_NUMBER}"
    }

    stages {
        stage('Build') {
            steps {
                echo "Building ${APP_NAME} with tag ${IMAGE_TAG}"
                sh 'mvn clean package'
            }
        }
    }
}

环境变量优先级(从高到低):

  1. withEnv块内定义的环境变量
  2. environment块内定义的环境变量
  3. Jenkins全局环境变量(如BUILD_NUMBERJOB_NAME
  4. 系统环境变量

2.2 高级语法实战

条件分支:when { branch 'main' } / expression { params.env == 'PROD' }
groovy 复制代码
pipeline {
    agent any

    parameters {
        choice(name: 'ENV', choices: ['DEV', 'TEST', 'PROD'], description: '部署环境')
    }

    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }

        stage('Deploy to DEV') {
            when {
                branch 'main'
                environment name: 'ENV', value: 'DEV'
            }
            steps {
                echo 'Deploying to DEV environment'
                sh 'kubectl apply -f k8s/dev/'
            }
        }

        stage('Deploy to PROD') {
            when {
                branch 'main'
                environment name: 'ENV', value: 'PROD'
            }
            steps {
                echo 'Deploying to PROD environment'
                sh 'kubectl apply -f k8s/prod/'
            }
        }
    }
}
并行执行:parallel { stage('A') { ... }; stage('B') { ... } }
groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Parallel Testing') {
            parallel {
                stage('Unit Test') {
                    steps {
                        sh 'mvn test -Dtest=UnitTest'
                    }
                }
                stage('Integration Test') {
                    steps {
                        sh 'mvn test -Dtest=IntegrationTest'
                    }
                }
                stage('Code Quality') {
                    steps {
                        sh 'mvn sonar:sonar'
                    }
                }
            }
        }
    }
}
错误处理:catchError / try-catch / timeout / retry

1. catchError(捕获错误,继续执行)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    sh 'mvn test'
                }
            }
        }
    }
}

2. try-catch(Groovy语法)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Deploy') {
            steps {
                script {
                    try {
                        sh 'kubectl apply -f k8s/'
                    } catch (Exception e) {
                        echo "Deployment failed: ${e}"
                        // 执行回滚
                        sh 'kubectl rollout undo deployment/app'
                        error "Deployment failed"
                    }
                }
            }
        }
    }
}

3. timeout(超时控制)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                timeout(time: 10, unit: 'MINUTES') {
                    sh 'mvn clean package'
                }
            }
        }
    }
}

4. retry(重试)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Deploy') {
            steps {
                retry(3) {
                    sh 'kubectl apply -f k8s/'
                }
            }
        }
    }
}
参数化构建:parameters { string(...) } + 下拉联动(如您提供的环境-服务联动)

基础参数化构建

groovy 复制代码
pipeline {
    agent any

    parameters {
        string(name: 'APP_VERSION', defaultValue: '1.0.0', description: '应用版本')
        choice(name: 'ENV', choices: ['DEV', 'TEST', 'PROD'], description: '部署环境')
        booleanParam(name: 'SKIP_TEST', defaultValue: false, description: '跳过测试')
    }

    stages {
        stage('Build') {
            steps {
                echo "Building version ${params.APP_VERSION} for ${params.ENV}"
                if (!params.SKIP_TEST) {
                    sh 'mvn test'
                }
                sh 'mvn clean package'
            }
        }
    }
}

高级下拉联动(Active Choices插件)

groovy 复制代码
properties([
    parameters([
        activeChoice(
            name: 'ENVIRONMENT',
            choiceType: 'SELECT',
            script: activeChoiceGroovyScript(
                script: '''
                    return ['DEV', 'TEST', 'PROD']
                '''
            )
        ),
        reactiveChoice(
            name: 'SERVICE',
            choiceType: 'CHECKBOX',
            referencedParameters: 'ENVIRONMENT',
            script: reactiveChoiceGroovyScript(
                script: '''
                    if (ENVIRONMENT == 'DEV') {
                        return ['app-dev-1', 'app-dev-2']
                    } else if (ENVIRONMENT == 'TEST') {
                        return ['app-test-1', 'app-test-2']
                    } else if (ENVIRONMENT == 'PROD') {
                        return ['app-prod-1', 'app-prod-2', 'app-prod-3']
                    }
                '''
            )
        )
    ])
])

2.3 Scripted Pipeline 进阶

node / stage / script 动态控制流
groovy 复制代码
node('build-agent') {
    stage('Checkout') {
        git url: 'https://github.com/spring-projects/spring-petclinic.git', branch: 'main'
    }

    stage('Build') {
        sh 'mvn clean package'
    }

    stage('Test') {
        sh 'mvn test'
    }

    stage('Package') {
        sh 'docker build -t spring-petclinic:${BUILD_NUMBER} .'
    }
}
实战:根据 Git Tag 自动触发发布流程
groovy 复制代码
node('build-agent') {
    // 获取Git Tag
    def gitTag = sh(script: 'git describe --tags --abbrev=0', returnStdout: true).trim()

    stage('Check Tag') {
        if (gitTag.startsWith('v')) {
            echo "Release tag detected: ${gitTag}"
        } else {
            echo "Not a release tag, skipping deployment"
            return
        }
    }

    stage('Build Release') {
        sh "mvn clean package -Dversion=${gitTag}"
    }

    stage('Deploy to PROD') {
        input message: '确认部署到生产环境?', ok: '确认'
        sh "kubectl set image deployment/app app=spring-petclinic:${gitTag} -n prod"
    }
}
与 Groovy 深度集成:自定义函数、类、异常处理
groovy 复制代码
// 自定义函数
def buildImage(String appName, String version) {
    sh "docker build -t ${appName}:${version} ."
    sh "docker push ${appName}:${version}"
}

def deployToK8s(String namespace, String deployment, String image) {
    sh "kubectl set image deployment/${deployment} ${deployment}=${image} -n ${namespace}"
    sh "kubectl rollout status deployment/${deployment} -n ${namespace}"
}

node('build-agent') {
    stage('Build & Deploy') {
        script {
            def image = "spring-petclinic:${BUILD_NUMBER}"

            // 调用自定义函数
            buildImage('spring-petclinic', BUILD_NUMBER)

            // 部署到不同环境
            if (env.BRANCH_NAME == 'main') {
                deployToK8s('test', 'app', image)

                // 等待审批
                input message: '部署到生产环境?', ok: '确认'
                deployToK8s('prod', 'app', image)
            } else {
                deployToK8s('dev', 'app', image)
            }
        }
    }
}


第三部分:CI 流水线实战(代码 → 构建 → 测试)

3.1 多语言项目构建实战

Java(Maven/Gradle):跳过测试、指定 profile、上传制品

完整Jenkinsfile(Maven项目)

groovy 复制代码
pipeline {
    agent any

    tools {
        maven 'Maven3'
        jdk 'JDK17'
    }

    environment {
        APP_NAME = 'spring-petclinic'
        NEXUS_URL = 'http://nexus.example.com:8081'
    }

    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/spring-projects/spring-petclinic.git', branch: 'main'
            }
        }

        stage('Build') {
            steps {
                // 跳过测试,使用dev profile
                sh 'mvn clean package -DskipTests -Pdev'
            }
        }

        stage('Test') {
            steps {
                // 只运行单元测试
                sh 'mvn test -Dtest=UnitTest*'
            }
        }

        stage('Package') {
            steps {
                // 打包并上传到Nexus
                sh 'mvn deploy -DskipTests'
            }
        }
    }

    post {
        always {
            // 归档构建产物
            archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
        }
    }
}

Maven关键参数详解

  • -DskipTests:跳过测试编译和执行
  • -Dmaven.test.skip=true:跳过测试编译和执行
  • -Pdev:激活dev profile(在pom.xml中定义)
  • mvn deploy:上传制品到远程仓库(如Nexus)
Node.js:缓存 node_modules、Linter 集成

完整Jenkinsfile(Node.js项目)

groovy 复制代码
pipeline {
    agent any

    environment {
        APP_NAME = 'vue-app'
        NODE_VERSION = '18'
    }

    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/your-repo/vue-app.git', branch: 'main'
            }
        }

        stage('Install Dependencies') {
            steps {
                // 缓存node_modules(加速构建)
                cache(maxCacheSize: 512, caches: [
                    arbitraryFileCache(path: 'node_modules', includes: '**/*')
                ]) {
                    sh 'npm install'
                }
            }
        }

        stage('Lint') {
            steps {
                // 代码风格检查
                sh 'npm run lint'
            }
        }

        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }

        stage('Test') {
            steps {
                sh 'npm run test:unit'
            }
        }
    }

    post {
        always {
            // 归档构建产物
            archiveArtifacts artifacts: 'dist/**', fingerprint: true
        }
    }
}

企业最佳实践

  • ✅ 使用cache步骤缓存node_modules(减少构建时间50%+)
  • ✅ 在package.json中配置pre-commit钩子(配合Husky)
  • ✅ 使用.npmrc配置私有NPM仓库
Python:venv 管理、pytest 报告生成

完整Jenkinsfile(Python项目)

groovy 复制代码
pipeline {
    agent any

    environment {
        APP_NAME = 'django-app'
        PYTHON_VERSION = '3.11'
    }

    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/your-repo/django-app.git', branch: 'main'
            }
        }

        stage('Setup Virtual Environment') {
            steps {
                sh '''
                    python3 -m venv venv
                    source venv/bin/activate
                    pip install -r requirements.txt
                '''
            }
        }

        stage('Lint') {
            steps {
                sh '''
                    source venv/bin/activate
                    flake8 app/
                '''
            }
        }

        stage('Test') {
            steps {
                sh '''
                    source venv/bin/activate
                    pytest --junitxml=test-results.xml
                '''
            }
        }

        stage('Build Docker Image') {
            steps {
                sh 'docker build -t django-app:${BUILD_NUMBER} .'
            }
        }
    }

    post {
        always {
            // 发布测试报告
            junit 'test-results.xml'
        }
    }
}
企业技巧:构建缓存加速(cache step + NFS/S3)

使用Jenkins Cache Step

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build with Cache') {
            steps {
                cache(maxCacheSize: 512, caches: [
                    arbitraryFileCache(path: 'target', includes: '**/*'),
                    arbitraryFileCache(path: '.m2/repository', includes: '**/*')
                ]) {
                    sh 'mvn clean package'
                }
            }
        }
    }
}

使用NFS共享缓存(多Agent共享)

bash 复制代码
# 在Jenkins Master上配置NFS服务器
apt-get install -y nfs-kernel-server
echo '/data/jenkins-cache *(rw,sync,no_root_squash)' >> /etc/exports
systemctl restart nfs-kernel-server

# 在所有Agent上挂载NFS
mkdir -p /data/jenkins-cache
mount -t nfs jenkins-master:/data/jenkins-cache /data/jenkins-cache

使用S3存储缓存(云环境推荐)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build with S3 Cache') {
            steps {
                withAWS(region: 'cn-hangzhou') {
                    s3Download(file: 'm2-repo.tar.gz', bucket: 'jenkins-cache', key: 'm2-repo.tar.gz')
                    sh 'tar -xzf m2-repo.tar.gz -C ~'
                    sh 'mvn clean package'
                    sh 'tar -czf m2-repo.tar.gz -C ~ .m2/repository'
                    s3Upload(file: 'm2-repo.tar.gz', bucket: 'jenkins-cache', key: 'm2-repo.tar.gz')
                }
            }
        }
    }
}

3.2 质量门禁实战

SonarQube 集成:质量阈值检查 + 失败拦截

1. 安装SonarQube Scanner插件

2. 配置SonarQube服务器

复制代码
Jenkins → 系统管理 → 系统配置 → SonarQube servers → 添加SonarQube
Name: SonarQube
Server URL: http://sonarqube.example.com:9000
Server authentication token: 添加凭据(Secret text类型)

3. 在Jenkinsfile中集成SonarQube

groovy 复制代码
pipeline {
    agent any

    tools {
        sonarQubeScanner 'SonarQube Scanner'
    }

    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/your-repo/java-app.git', branch: 'main'
            }
        }

        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }

        stage('SonarQube Analysis') {
            steps {
                withSonarQubeEnv('SonarQube') {
                    sh 'mvn sonar:sonar'
                }
            }
        }

        stage('Quality Gate') {
            steps {
                // 等待质量门禁结果,失败则中断流水线
                timeout(time: 5, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
    }
}

4. 配置质量阈值(SonarQube Web UI)

复制代码
SonarQube → Quality Gates → 创建质量门禁
条件:
- 覆盖率 < 80% → 失败
- 重复代码 > 5% → 失败
- 新增代码覆盖率 < 80% → 失败
- 严重漏洞 > 0 → 失败

企业最佳实践

  • ✅ 为不同类型项目配置不同的质量阈值(核心业务 vs 工具类)
  • ✅ 使用abortPipeline: true(质量不达标则中断部署)
  • ✅ 在PR阶段就进行SonarQube扫描(提早发现质量问题)
单元测试覆盖率报告(JaCoCo / Cobertura)

JaCoCo配置(Maven项目)

pom.xml配置:

xml 复制代码
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.10</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Jenkinsfile配置

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }

        stage('Coverage Report') {
            steps {
                // 发布JaCoCo报告
                publishCoverage adapters: [jacocoAdapter('target/site/jacoco/jacoco.xml')], 
                                  sourceFileResolver: sourceFiles('STORE_LAST_BUILD')
            }
        }
    }
}

Cobertura配置(Python项目)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                sh '''
                    source venv/bin/activate
                    pytest --cov=app --cov-report=xml
                '''
            }
        }

        stage('Coverage Report') {
            steps {
                // 发布Cobertura报告
                cobertura coberturaReportFile: 'coverage.xml'
            }
        }
    }
}
安全扫描:OWASP Dependency-Check / Snyk

OWASP Dependency-Check(开源,免费)

1. 安装OWASP Dependency-Check插件

2. Jenkinsfile配置

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Dependency Check') {
            steps {
                dependencyCheck arguments: '--project "Spring PetClinic" --scan ./ --format XML', 
                                      odcInstallation: 'OWASP Dependency-Check'
            }
        }

        stage('Publish Results') {
            steps {
                dependencyCheckPublisher pattern: 'dependency-check-report.xml', 
                                            failedNewCritical: '0', 
                                            failedNewHigh: '0'
            }
        }
    }
}

Snyk(商业版,功能更强)

1. 安装Snyk Security插件

2. 配置Snyk API Token

复制代码
Jenkins → 系统管理 → 系统配置 → Snyk → 添加API Token

3. Jenkinsfile配置

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Snyk Security Scan') {
            steps {
                snykSecurity failOnIssues: true, 
                            organisation: 'your-org', 
                            projectName: 'spring-petclinic', 
                            snykInstallation: 'Snyk', 
                            snykTokenId: 'snyk-api-token'
            }
        }
    }
}

企业最佳实践

  • ✅ 在CI阶段就进行安全扫描(提早发现漏洞)
  • ✅ 设置失败阈值(如:高危漏洞>0则失败)
  • ✅ 定期更新漏洞数据库(OWASP Dependency-Check daily update)
关键配置:waitForQualityGate(abortPipeline: true)
groovy 复制代码
stage('Quality Gate') {
    steps {
        timeout(time: 5, unit: 'MINUTES') {
            // abortPipeline: true → 质量门禁失败则中断流水线
            // abortPipeline: false → 质量门禁失败只警告,不中断流水线
            waitForQualityGate abortPipeline: true
        }
    }
}

参数详解

  • abortPipeline: true:质量门禁失败 → 流水线失败(推荐用于生产环境)
  • abortPipeline: false:质量门禁失败 → 只记录警告,流水线继续(推荐用于开发环境)
  • timeout:等待质量门禁结果的最长时间(防止流水线无限等待)

3.3 多环境参数化构建

下拉联动实现(Active Choice + Reactive Choice)

完整配置脚本(Groovy)

groovy 复制代码
properties([
    parameters([
        // 第一个下拉框:选择环境
        activeChoice(
            name: 'ENVIRONMENT',
            choiceType: 'SELECT',
            description: '选择部署环境',
            script: activeChoiceGroovyScript(
                script: '''
                    // 返回环境列表
                    return ['DEV', 'TEST', 'PROD']
                ''',
                fallbackScript: '''
                    return ['ERROR']
                '''
            )
        ),

        // 第二个下拉框:根据环境选择服务(联动)
        reactiveChoice(
            name: 'SERVICE',
            choiceType: 'CHECKBOX',
            description: '选择要部署的服务(可多选)',
            referencedParameters: 'ENVIRONMENT',  // 引用第一个下拉框
            script: reactiveChoiceGroovyScript(
                script: '''
                    // 根据ENVIRONMENT的值返回不同的服务列表
                    if (ENVIRONMENT == 'DEV') {
                        return ['app-dev-1', 'app-dev-2', 'app-dev-3']
                    } else if (ENVIRONMENT == 'TEST') {
                        return ['app-test-1', 'app-test-2']
                    } else if (ENVIRONMENT == 'PROD') {
                        return ['app-prod-1', 'app-prod-2', 'app-prod-3']
                    } else {
                        return ['请先选择环境']
                    }
                ''',
                fallbackScript: '''
                    return ['ERROR']
                '''
            )
        ),

        // 第三个下拉框:根据环境和服务选择版本(二级联动)
        reactiveChoice(
            name: 'VERSION',
            choiceType: 'SELECT',
            description: '选择版本',
            referencedParameters: ['ENVIRONMENT', 'SERVICE'],  // 引用前两个下拉框
            script: reactiveChoiceGroovyScript(
                script: '''
                    // 这里可以调用API获取可用版本
                    // 示例:从Nexus获取可用版本
                    def versions = ['1.0.0', '1.0.1', '1.0.2']
                    return versions
                ''',
                fallbackScript: '''
                    return ['ERROR']
                '''
            )
        )
    ])
])

企业级实战:从外部系统获取选项

groovy 复制代码
reactiveChoice(
    name: 'SERVICE',
    choiceType: 'SELECT',
    referencedParameters: 'ENVIRONMENT',
    script: reactiveChoiceGroovyScript(
        script: '''
            // 从CMDB系统获取服务列表
            def cmdbUrl = "http://cmdb.example.com/api/services?env=${ENVIRONMENT}"
            def response = new URL(cmdbUrl).text
            def services = new groovy.json.JsonSlurper().parseText(response)
            return services.collect { it.name }
        ''',
        fallbackScript: '''
            return ['无法连接CMDB']
        '''
    )
)
实战:DEV/TEST/PROD/Tenant 四环境差异化构建策略
groovy 复制代码
pipeline {
    agent any

    parameters {
        choice(name: 'ENVIRONMENT', choices: ['DEV', 'TEST', 'PROD', 'Tenant'], description: '部署环境')
        string(name: 'TENANT_ID', defaultValue: '', description: '租户ID(仅Tenant环境需要)')
    }

    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/your-repo/app.git', branch: 'main'
            }
        }

        stage('Build') {
            steps {
                script {
                    // 根据环境使用不同的构建策略
                    if (params.ENVIRONMENT == 'DEV') {
                        // DEV环境:快速构建,跳过部分测试
                        sh 'mvn clean package -DskipTests -Pdev'
                    } else if (params.ENVIRONMENT == 'TEST') {
                        // TEST环境:完整构建,运行所有测试
                        sh 'mvn clean package -Ptest'
                    } else if (params.ENVIRONMENT == 'PROD') {
                        // PROD环境:完整构建,运行所有测试,生成文档
                        sh 'mvn clean package -Pprod'
                        sh 'mvn site'
                    } else if (params.ENVIRONMENT == 'Tenant') {
                        // Tenant环境:多租户构建,需要TENANT_ID
                        if (params.TENANT_ID == '') {
                            error 'Tenant环境需要提供TENANT_ID'
                        }
                        sh "mvn clean package -Ptenant -Dtenant.id=${params.TENANT_ID}"
                    }
                }
            }
        }

        stage('Deploy') {
            steps {
                script {
                    // 根据环境使用不同的部署策略
                    if (params.ENVIRONMENT == 'DEV') {
                        // DEV环境:直接部署
                        sh 'kubectl apply -f k8s/dev/'
                    } else if (params.ENVIRONMENT == 'TEST') {
                        // TEST环境:蓝绿部署
                        sh 'kubectl apply -f k8s/test/green/'
                        sh 'kubectl delete -f k8s/test/blue/'
                    } else if (params.ENVIRONMENT == 'PROD') {
                        // PROD环境:金丝雀发布(需要人工审批)
                        input message: '确认部署到生产环境?', ok: '确认'
                        sh 'kubectl apply -f k8s/prod/canary/'
                    }
                }
            }
        }
    }
}
动态 Jenkinsfile:根据 params.Environment 切换构建逻辑

方案一:在Jenkinsfile中使用条件判断(适合简单场景)

groovy 复制代码
stage('Deploy') {
    steps {
        script {
            if (params.ENVIRONMENT == 'DEV') {
                sh 'deploy-dev.sh'
            } else if (params.ENVIRONMENT == 'PROD') {
                sh 'deploy-prod.sh'
            }
        }
    }
}

方案二:使用共享库(Shared Library)(适合复杂场景)

复制代码
├── src
│   └── org
│       └── example
│           └── Deploy.groovy  // 部署逻辑
├── vars
│   └── deploy.groovy          // 全局变量
└── resources
    └── org
        └── example
            └── deploy.sh      // 部署脚本

vars/deploy.groovy

groovy 复制代码
def call(String environment) {
    if (environment == 'DEV') {
        sh 'kubectl apply -f k8s/dev/'
    } else if (environment == 'PROD') {
        input message: '确认部署到生产环境?', ok: '确认'
        sh 'kubectl apply -f k8s/prod/'
    }
}

Jenkinsfile

groovy 复制代码
@Library('jenkins-shared-library') _

pipeline {
    agent any

    parameters {
        choice(name: 'ENVIRONMENT', choices: ['DEV', 'PROD'], description: '部署环境')
    }

    stages {
        stage('Deploy') {
            steps {
                script {
                    // 调用共享库
                    deploy(params.ENVIRONMENT)
                }
            }
        }
    }
}

企业最佳实践

  • ✅ 环境差异配置使用配置文件(如config/dev.propertiesconfig/prod.properties
  • ✅ 敏感配置(如数据库密码)使用Jenkins凭据管理
  • ✅ 使用共享库复用部署逻辑(减少重复代码)

第四部分:CD 流水线实战(构建 → 部署 → 验证)

4.1 Docker 镜像构建与推送

多平台镜像:buildx build --platform=linux/amd64,linux/arm64

启用Docker Buildx(支持多平台构建):

bash 复制代码
# 创建并使用多平台构建器
docker buildx create --name multi-platform-builder --use
docker buildx inspect --bootstrap

Jenkinsfile配置

groovy 复制代码
pipeline {
    agent any

    environment {
        DOCKER_REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
        APP_NAME = 'spring-petclinic'
        IMAGE_TAG = "${BUILD_NUMBER}"
    }

    stages {
        stage('Build Docker Image') {
            steps {
                script {
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry') {
                        // 构建多平台镜像
                        sh """
                            docker buildx build \
                              --platform linux/amd64,linux/arm64 \
                              -t ${DOCKER_REGISTRY}/your-namespace/${APP_NAME}:${IMAGE_TAG} \
                              -t ${DOCKER_REGISTRY}/your-namespace/${APP_NAME}:latest \
                              --push .
                        """
                    }
                }
            }
        }
    }
}

避坑指南

  • ❌ 错误:error: failed to solve: platform linux/arm64 not found
  • ✅ 解决:确保Buildx构建器支持QEMU模拟(docker buildx inspect --bootstrap
镜像标签策略:{branch}-{commit_short}-{timestamp}
groovy 复制代码
pipeline {
    agent any

    environment {
        DOCKER_REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
        APP_NAME = 'spring-petclinic'
    }

    stages {
        stage('Generate Image Tag') {
            steps {
                script {
                    // 获取分支名
                    def branchName = env.BRANCH_NAME ?: 'main'

                    // 获取短commit hash(7位)
                    def commitShort = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()

                    // 获取时间戳
                    def timestamp = new Date().format('yyyyMMddHHmmss', TimeZone.getTimeZone('Asia/Shanghai'))

                    // 生成镜像标签:{branch}-{commit_short}-{timestamp}
                    def imageTag = "${branchName}-${commitShort}-${timestamp}"

                    // 设置环境变量
                    env.IMAGE_TAG = imageTag

                    echo "镜像标签:${imageTag}"
                }
            }
        }

        stage('Build & Push') {
            steps {
                script {
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry') {
                        sh "docker build -t ${DOCKER_REGISTRY}/your-namespace/${APP_NAME}:${env.IMAGE_TAG} ."
                        sh "docker push ${DOCKER_REGISTRY}/your-namespace/${APP_NAME}:${env.IMAGE_TAG}"
                    }
                }
            }
        }
    }
}

企业标签策略最佳实践

  • ✅ 开发环境:{branch}-{commit_short}(如main-a1b2c3d
  • ✅ 测试环境:{version}-{build_number}(如1.0.0-123
  • ✅ 生产环境:{version}-{timestamp}(如1.0.0-20260607230000
  • ✅ 标记latest标签(方便手动部署)
私有仓库认证:Docker Credentials + withCredentials

方式一:使用Docker Pipeline插件(推荐)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build & Push') {
            steps {
                script {
                    // 使用Docker Pipeline插件的withRegistry方法
                    docker.withRegistry('https://registry.cn-hangzhou.aliyuncs.com', 'docker-registry') {
                        def customImage = docker.build("your-namespace/app:${BUILD_NUMBER}")
                        customImage.push()
                    }
                }
            }
        }
    }
}

方式二:使用withCredentials(更灵活)

groovy 复制代码
pipeline {
    agent any

    environment {
        DOCKER_REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
        DOCKER_USERNAME = ''
        DOCKER_PASSWORD = ''
    }

    stages {
        stage('Login & Push') {
            steps {
                script {
                    // 获取Docker凭据
                    withCredentials([usernamePassword(credentialsId: 'docker-registry', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
                        // 登录Docker仓库
                        sh "docker login -u ${DOCKER_USER} -p ${DOCKER_PASS} ${DOCKER_REGISTRY}"

                        // 构建镜像
                        sh "docker build -t ${DOCKER_REGISTRY}/your-namespace/app:${BUILD_NUMBER} ."

                        // 推送镜像
                        sh "docker push ${DOCKER_REGISTRY}/your-namespace/app:${BUILD_NUMBER}"

                        // 登出Docker仓库
                        sh "docker logout ${DOCKER_REGISTRY}"
                    }
                }
            }
        }
    }
}
避坑指南:docker login 在 Agent 中失效的解决方案

问题现象

复制代码
+ docker login -u admin -p **** https://registry.cn-hangzhou.aliyuncs.com
Error response from daemon: Get "https://registry.cn-hangzhou.aliyuncs.com/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

原因分析

  1. Agent无法访问Docker仓库(网络问题)
  2. Agent的Docker守护进程未运行
  3. 凭据在Agent上未正确传递

解决方案

方案一:使用宿主机Docker(推荐)

groovy 复制代码
pipeline {
    agent {
        docker {
            image 'maven:3.8.8-openjdk-17'
            args '-v /var/run/docker.sock:/var/run/docker.sock'  // 挂载宿主机Docker socket
        }
    }

    stages {
        stage('Build & Push') {
            steps {
                script {
                    // 在容器内使用宿主机的Docker
                    sh 'docker build -t app:latest .'
                }
            }
        }
    }
}

方案二:在Agent上安装Docker

groovy 复制代码
pipeline {
    agent { label 'docker-agent' }  // 这个Agent已安装Docker

    stages {
        stage('Install Docker') {
            steps {
                sh '''
                    # 安装Docker
                    apt-get update
                    apt-get install -y docker.io
                    systemctl start docker
                    systemctl enable docker
                '''
            }
        }

        stage('Build & Push') {
            steps {
                script {
                    docker.withRegistry('https://registry.cn-hangzhou.aliyuncs.com', 'docker-registry') {
                        sh 'docker build -t app:latest .'
                    }
                }
            }
        }
    }
}

方案三:使用Kaniko(无需Docker守护进程)

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build with Kaniko') {
            steps {
                script {
                    // 使用Kaniko构建镜像(无需Docker守护进程)
                    sh """
                        /kaniko/executor \
                          --context `pwd` \
                          --dockerfile `pwd`/Dockerfile \
                          --destination ${DOCKER_REGISTRY}/your-namespace/app:${BUILD_NUMBER}
                    """
                }
            }
        }
    }
}

4.2 Kubernetes 部署实战

方案一:kubectl set image(适用于 Deployment)

完整Jenkinsfile

groovy 复制代码
pipeline {
    agent any

    environment {
        K8S_MASTER = 'k8s-master.example.com'
        NAMESPACE = 'prod'
        DEPLOYMENT = 'spring-petclinic'
        IMAGE = 'registry.cn-hangzhou.aliyuncs.com/your-namespace/spring-petclinic:${BUILD_NUMBER}'
    }

    stages {
        stage('Deploy to K8s') {
            steps {
                script {
                    // 方式一:直接使用kubectl(需要配置kubeconfig)
                    sh "kubectl set image deployment/${DEPLOYMENT} ${DEPLOYMENT}=${IMAGE} -n ${NAMESPACE}"

                    // 等待部署完成
                    sh "kubectl rollout status deployment/${DEPLOYMENT} -n ${NAMESPACE}"
                }
            }
        }
    }
}

企业最佳实践

  • ✅ 使用kubectl rollout status等待部署完成
  • ✅ 设置--timeout=5m(防止无限等待)
  • ✅ 部署前备份当前版本(kubectl get deployment -o yaml > backup.yaml
方案二:Helm Chart + helm upgrade(推荐)

项目结构

复制代码
helm-chart/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-test.yaml
├── values-prod.yaml
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    └── ingress.yaml

Jenkinsfile配置

groovy 复制代码
pipeline {
    agent any

    parameters {
        choice(name: 'ENVIRONMENT', choices: ['dev', 'test', 'prod'], description: '部署环境')
    }

    environment {
        APP_NAME = 'spring-petclinic'
        IMAGE_TAG = "${BUILD_NUMBER}"
    }

    stages {
        stage('Package Helm Chart') {
            steps {
                sh """
                    # 更新Helm依赖
                    helm dependency update helm-chart/

                    # 打包Helm Chart
                    helm package helm-chart/ --version ${IMAGE_TAG}
                """
            }
        }

        stage('Deploy with Helm') {
            steps {
                script {
                    def env = params.ENVIRONMENT

                    sh """
                        # 使用Helm升级(如果不存在则安装)
                        helm upgrade --install ${APP_NAME} helm-chart/ \
                          --namespace ${env} \
                          --values helm-chart/values-${env}.yaml \
                          --set image.tag=${IMAGE_TAG} \
                          --wait \
                          --timeout 5m
                    """
                }
            }
        }
    }
}

values-prod.yaml示例

yaml 复制代码
replicaCount: 3

image:
  repository: registry.cn-hangzhou.aliyuncs.com/your-namespace/spring-petclinic
  tag: "latest"
  pullPolicy: IfNotPresent

service:
  type: LoadBalancer
  port: 80

resources:
  limits:
    cpu: "2"
    memory: "2Gi"
  requests:
    cpu: "1"
    memory: "1Gi"

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80
方案三:Kubernetes Plugin(声明式部署)

1. 安装Kubernetes插件

2. 配置Kubernetes云

复制代码
Jenkins → 系统管理 → 系统配置 → Cloud → 添加Cloud → Kubernetes
Kubernetes URL: https://k8s-master:6443
Kubernetes Namespace: jenkins
Credentials: 添加kubeconfig凭据

3. Jenkinsfile配置

groovy 复制代码
pipeline {
    agent {
        kubernetes {
            yaml '''
                apiVersion: v1
                kind: Pod
                spec:
                  containers:
                  - name: maven
                    image: maven:3.8.8-openjdk-17
                    command: ['cat']
                    tty: true
                  - name: docker
                    image: docker:24.0.5
                    command: ['cat']
                    tty: true
                    volumeMounts:
                    - name: docker-sock
                      mountPath: /var/run/docker.sock
                  volumes:
                  - name: docker-sock
                    hostPath:
                      path: /var/run/docker.sock
            '''
        }
    }

    stages {
        stage('Build') {
            steps {
                container('maven') {
                    sh 'mvn clean package'
                }
            }
        }

        stage('Build Docker Image') {
            steps {
                container('docker') {
                    sh 'docker build -t app:latest .'
                }
            }
        }

        stage('Deploy to K8s') {
            steps {
                // 使用kubectl部署
                sh 'kubectl apply -f k8s/'
            }
        }
    }
}
滚动更新控制:--max-unavailable=25% --max-surge=25%

在Helm Chart中配置

yaml 复制代码
# values.yaml
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 25%
    maxSurge: 25%

在K8s Deployment中配置

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-petclinic
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%   # 最多25%的Pod不可用(即1个)
      maxSurge: 25%           # 最多额外创建25%的Pod(即1个)
  template:
    spec:
      containers:
      - name: app
        image: spring-petclinic:latest

企业最佳实践

  • ✅ 生产环境建议:maxUnavailable: 25%, maxSurge: 25%(保证服务可用性)
  • ✅ 开发环境建议:maxUnavailable: 50%, maxSurge: 50%(加快部署速度)
  • ✅ 关键服务建议:maxUnavailable: 0%, maxSurge: 1(滚动更新更平滑)

4.3 多环境部署策略

环境 部署方式 验证方式
DEV 直接更新 自动化 Smoke Test
TEST Helm + Canary Postman API 测试集
PROD 蓝绿部署 / 金丝雀 人工审批 + 监控看板
Demo 多架构镜像 + docker-compose 页面截图验证
DEV环境:直接更新 + Smoke Test
groovy 复制代码
stage('Deploy to DEV') {
    steps {
        script {
            // 直接更新Deployment
            sh 'kubectl set image deployment/app app=registry.cn-hangzhou.aliyuncs.com/your-namespace/app:${BUILD_NUMBER} -n dev'

            // 等待部署完成
            sh 'kubectl rollout status deployment/app -n dev --timeout=5m'

            // 运行冒烟测试
            sh 'curl -f http://app-dev.example.com/health || exit 1'
        }
    }
}
TEST环境:Helm + Canary + Postman测试
groovy 复制代码
stage('Deploy to TEST (Canary)') {
    steps {
        script {
            // 部署金丝雀版本(10%流量)
            sh """
                helm upgrade --install app-canary helm-chart/ \
                  --namespace test \
                  --values helm-chart/values-test.yaml \
                  --set image.tag=${BUILD_NUMBER} \
                  --set replicaCount=1 \
                  --set canary.enabled=true \
                  --set canary.weight=10
            """

            // 等待金丝雀部署完成
            sh 'kubectl rollout status deployment/app-canary -n test --timeout=5m'

            // 运行Postman测试集
            sh 'newman run postman/api-tests.postman_collection.json -e postman/test.postman_environment.json'

            // 如果测试通过,扩大金丝雀流量到100%
            sh """
                helm upgrade app-canary helm-chart/ \
                  --namespace test \
                  --set canary.weight=100
            """
        }
    }
}
PROD环境:蓝绿部署 / 金丝雀 + 人工审批

蓝绿部署

groovy 复制代码
stage('Deploy to PROD (Blue-Green)') {
    steps {
        script {
            // 人工审批
            input message: '确认部署到生产环境?', ok: '确认'

            // 部署绿色环境
            sh """
                helm upgrade --install app-green helm-chart/ \
                  --namespace prod \
                  --values helm-chart/values-prod.yaml \
                  --set image.tag=${BUILD_NUMBER}
            """

            // 等待绿色环境部署完成
            sh 'kubectl rollout status deployment/app-green -n prod --timeout=10m'

            // 验证绿色环境
            sh 'curl -f http://app-green-prod.example.com/health || exit 1'

            // 切换流量到绿色环境
            sh 'kubectl patch service app -n prod -p {"spec":{"selector":{"version":"green"}}}'

            // 等待流量切换完成
            sleep time: 30, unit: 'SECONDS'

            // 验证生产环境
            sh 'curl -f http://app-prod.example.com/health || exit 1'

            // 删除蓝色环境
            sh 'kubectl delete deployment app-blue -n prod'
        }
    }
}

金丝雀发布

groovy 复制代码
stage('Deploy to PROD (Canary)') {
    steps {
        script {
            // 人工审批
            input message: '确认部署金丝雀版本到生产环境?', ok: '确认'

            // 部署金丝雀版本(10%流量)
            sh """
                kubectl apply -f k8s/prod/canary/
            """

            // 等待金丝雀部署完成
            sh 'kubectl rollout status deployment/app-canary -n prod --timeout=10m'

            // 验证金丝雀版本
            sh 'curl -f http://app-canary-prod.example.com/health || exit 1'

            // 监控指标(人工观察)
            input message: '请观察监控指标,确认无误后继续', ok: '继续'

            // 扩大金丝雀流量到100%
            sh """
                kubectl patch deployment app -n prod \
                  -p {"spec":{"template":{"metadata":{"labels":{"version":"canary"}}}}}
            """

            // 等待滚动更新完成
            sh 'kubectl rollout status deployment/app -n prod --timeout=10m'
        }
    }
}

4.4 部署后验证与回滚

健康检查:curl -f http://svc:8080/health
groovy 复制代码
stage('Health Check') {
    steps {
        script {
            def healthCheckUrl = 'http://app-prod.example.com/health'
            def maxRetries = 10
            def retryInterval = 10  // 秒

            for (int i = 0; i < maxRetries; i++) {
                try {
                    sh "curl -f ${healthCheckUrl}"
                    echo "健康检查通过!"
                    break
                } catch (Exception e) {
                    if (i == maxRetries - 1) {
                        error "健康检查失败,已重试${maxRetries}次"
                    }
                    echo "健康检查失败,第${i+1}次重试..."
                    sleep time: retryInterval, unit: 'SECONDS'
                }
            }
        }
    }
}

K8s健康检查配置(更可靠):

yaml 复制代码
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  template:
    spec:
      containers:
      - name: app
        image: app:latest
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3
自动化验收测试(Cypress / Selenium)

Cypress配置(现代Web应用推荐):

groovy 复制代码
stage('E2E Test with Cypress') {
    steps {
        script {
            // 运行Cypress测试
            sh '''
                npm install cypress --save-dev
                npx cypress run --config baseUrl=http://app-test.example.com
            '''

            // 发布Cypress测试报告
            publishHTML([
                reportDir: 'cypress/reports',
                reportFiles: 'index.html',
                reportName: 'Cypress Test Report',
                reportTitles: ''
            ])
        }
    }
}

Selenium配置(传统Web应用):

groovy 复制代码
stage('E2E Test with Selenium') {
    steps {
        script {
            // 运行Selenium测试
            sh 'mvn test -Dtest=SeleniumTest'

            // 发布Selenium测试报告
            publishHTML([
                reportDir: 'target/surefire-reports',
                reportFiles: 'index.html',
                reportName: 'Selenium Test Report',
                reportTitles: ''
            ])
        }
    }
}
一键回滚:记录历史镜像版本 + kubectl rollout undo

完整回滚Jenkinsfile

groovy 复制代码
pipeline {
    agent any

    parameters {
        booleanParam(name: 'ROLLBACK', defaultValue: false, description: '是否回滚?')
    }

    environment {
        NAMESPACE = 'prod'
        DEPLOYMENT = 'spring-petclinic'
    }

    stages {
        stage('Check Rollback Flag') {
            steps {
                script {
                    if (params.ROLLBACK) {
                        // 执行回滚
                        echo "开始回滚..."

                        // 查看历史版本
                        sh "kubectl rollout history deployment/${DEPLOYMENT} -n ${NAMESPACE}"

                        // 回滚到上一个版本
                        sh "kubectl rollout undo deployment/${DEPLOYMENT} -n ${NAMESPACE}"

                        // 等待回滚完成
                        sh "kubectl rollout status deployment/${DEPLOYMENT} -n ${NAMESPACE}"

                        // 验证回滚结果
                        sh "curl -f http://app-prod.example.com/health"

                        currentBuild.result = 'SUCCESS'
                        return
                    }
                }
            }
        }

        stage('Deploy') {
            steps {
                script {
                    // 记录当前版本(用于回滚)
                    sh "kubectl get deployment ${DEPLOYMENT} -n ${NAMESPACE} -o yaml > backup-${BUILD_NUMBER}.yaml"

                    // 部署新版本
                    sh "kubectl set image deployment/${DEPLOYMENT} ${DEPLOYMENT}=registry.cn-hangzhou.aliyuncs.com/your-namespace/app:${BUILD_NUMBER} -n ${NAMESPACE}"

                    // 等待部署完成
                    sh "kubectl rollout status deployment/${DEPLOYMENT} -n ${NAMESPACE}"
                }
            }
        }
    }
}

使用Helm回滚(更简单):

bash 复制代码
# 查看Helm发布历史
helm history app -n prod

# 回滚到指定版本
helm rollback app 1 -n prod

# 回滚到上一个版本
helm rollback app 0 -n prod

企业最佳实践

  • ✅ 每次部署前自动备份当前版本(kubectl get deployment -o yaml > backup.yaml
  • ✅ 在Jenkins中提供"一键回滚"按钮(参数化构建)
  • ✅ 回滚后自动验证服务健康状态
  • ✅ 记录回滚原因(方便事后分析)


第五部分:高可用与企业级进阶

5.1 Jenkins 高可用架构

主从模式:Master 负载均衡 + Agent 弹性伸缩(K8s Pod as Agent)

架构图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    负载均衡(HAProxy/Nginx)                  │
│                    (VIP:192.168.0.100)                  │
└─────────────────────────────────────────────────────────────┘
           │                           │
           ▼                           ▼
┌──────────────────┐         ┌──────────────────┐
│   Jenkins Master 1 │         │   Jenkins Master 2 │
│   (Active)       │         │   (Standby)       │
└──────────────────┘         └──────────────────┘
           │                           │
           └───────────┬─────────────┘
                       ▼
        ┌──────────────────────────────┐
        │   共享存储(NFS/Ceph)        │
        │   /var/jenkins_home            │
        └──────────────────────────────┘
                       │
                       ▼
        ┌──────────────────────────────┐
        │       K8s 动态 Agent          │
        │  (Pod 自动创建/销毁)        │
        └──────────────────────────────┘

配置步骤

1. 配置共享存储(NFS)

bash 复制代码
# 在Master节点上安装NFS服务器
apt-get install -y nfs-kernel-server

# 配置共享目录
echo '/data/jenkins_home *(rw,sync,no_root_squash)' >> /etc/exports

# 重启NFS服务
systemctl restart nfs-kernel-server

# 在所有Jenkins Master节点上挂载NFS
mkdir -p /data/jenkins_home
mount -t nfs nfs-server:/data/jenkins_home /data/jenkins_home

2. 配置K8s云(动态Pod Agent)

复制代码
Jenkins → 系统管理 → 系统配置 → Cloud → 添加Cloud → Kubernetes
  • Kubernetes URL : https://k8s-master:6443
  • Kubernetes Namespace : jenkins
  • Credentials: 添加K8s凭据(Secret file类型,上传kubeconfig)
  • Jenkins URL : http://jenkins-master:8080
  • Pod Template :
    • Name : jnlp-agent
    • Docker image : jenkins/inbound-agent:latest
    • Working directory : /home/jenkins/agent

3. 配置Pod模板(支持Docker构建)

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    jenkins: agent
spec:
  containers:
  - name: jnlp
    image: jenkins/inbound-agent:latest
    resources:
      limits:
        cpu: "2"
        memory: "2Gi"
      requests:
        cpu: "1"
        memory: "1Gi"
  - name: docker
    image: docker:24.0.5
    command: ['cat']
    tty: true
    volumeMounts:
    - name: docker-sock
      mountPath: /var/run/docker.sock
  volumes:
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock
共享存储:Jenkins Home 挂载 NFS / S3

方式一:挂载NFS(推荐用于本地机房)

bash 复制代码
# 在所有Jenkins Master节点上执行
mkdir -p /data/jenkins_home
mount -t nfs nfs-server:/data/jenkins_home /data/jenkins_home

# 配置开机自动挂载
echo 'nfs-server:/data/jenkins_home /data/jenkins_home nfs defaults 0 0' >> /etc/fstab

# 启动Jenkins时指定JENKINS_HOME
docker run -d \
  --name jenkins \
  -p 8080:8080 \
  -v /data/jenkins_home:/var/jenkins_home \
  --restart=always \
  jenkins/jenkins:lts

方式二:使用S3存储(推荐用于云环境)

bash 复制代码
# 安装S3插件(S3 Artifact Storage Plugin)

# 配置S3存储
# Jenkins → 系统管理 → 系统配置 → S3 Profile
# Profile Name: s3-jenkins
# Bucket Name: jenkins-artifacts
# Credentials: 添加AWS Access Key凭据

企业最佳实践

  • ✅ 生产环境必须使用共享存储(防止单点故障)
  • ✅ 定期备份Jenkins Home(每天定时备份到S3/NAS)
  • ✅ 使用SSD存储(提高Jenkins性能)
备份恢复:backup-plugin + 定时快照

安装ThinBackup插件(推荐):

复制代码
Jenkins → 系统管理 → 插件管理 → 搜索ThinBackup → 安装

配置ThinBackup

复制代码
Jenkins → 系统管理 → ThinBackup Settings
  • Backup directory : /data/jenkins_backup
  • Backup schedule : 0 2 * * *(每天凌晨2点备份)
  • Max number of backup sets : 7(保留7天备份)
  • Excluded files : workspace/**, builds/**(排除工作空间和构建记录)

手动备份/恢复

bash 复制代码
# 手动备份
docker exec jenkins java -jar /var/jenkins_home/war/WEB-INF/jenkins-cli.jar \
  -s http://localhost:8080 \
  -auth admin:Admin@123456 \
  thinBackupPerformBackup

# 恢复备份
docker exec jenkins java -jar /var/jenkins_home/war/WEB-INF/jenkins-cli.jar \
  -s http://localhost:8080 \
  -auth admin:Admin@123456 \
  thinBackupRestore

企业级备份策略

复制代码
┌──────────────────────────────────────────────┐
│              备份策略                           │
├──────────────────────────────────────────────┤
│ 1. 每天凌晨2点全量备份(ThinBackup)        │
│ 2. 备份文件上传到S3(跨地域容灾)           │
│ 3. 保留30天备份(符合等保要求)             │
│ 4. 每月进行一次恢复演练(验证备份有效性)     │
└──────────────────────────────────────────────┘

5.2 安全加固实战

最小权限原则:Agent 使用非 root 用户

Docker Agent配置(非root用户)

dockerfile 复制代码
# Dockerfile-agent
FROM jenkins/inbound-agent:latest

# 切换到root用户安装依赖
USER root

# 安装构建工具
RUN apt-get update && apt-get install -y \
    maven \
    nodejs \
    npm \
    python3 \
    && rm -rf /var/lib/apt/lists/*

# 创建非root用户
RUN useradd -m -u 1000 builder && \
    chown -R builder:builder /home/jenkins

# 切换回非root用户
USER builder

WORKDIR /home/jenkins/agent

K8s Pod Agent配置(非root用户)

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    jenkins: agent
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
  containers:
  - name: jnlp
    image: your-registry/jenkins-agent:latest
    securityContext:
      allowPrivilegeEscalation: false
      runAsNonRoot: true

企业最佳实践

  • ✅ Agent必须使用非root用户运行(防止容器逃逸)
  • ✅ 禁用特权容器(privileged: false
  • ✅ 使用只读根文件系统(readOnlyRootFilesystem: true
敏感信息管理:credentials-binding + withCredentials

方式一:使用credentials-binding插件(推荐):

groovy 复制代码
pipeline {
    agent any

    environment {
        // 绑定SSH Key凭据
        SSH_KEY = credentials('git-ssh-key')

        // 绑定Username/Password凭据
        DB_PASSWORD = credentials('db-password')

        // 绑定Secret text凭据
        API_TOKEN = credentials('api-token')
    }

    stages {
        stage('Use Credentials') {
            steps {
                // SSH_KEY包含SSH_KEY_USR和SSH_KEY_PSW(实际上SSH_KEY是私钥文件路径)
                sh 'ssh -i $SSH_KEY user@server'

                // DB_PASSWORD包含DB_PASSWORD_USR和DB_PASSWORD_PSW
                sh 'echo "Password: $DB_PASSWORD_PSW"'

                // API_TOKEN是Secret text
                sh 'curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com'
            }
        }
    }
}

方式二:使用withCredentials步骤

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Deploy') {
            steps {
                withCredentials([sshUserPrivateKey(credentialsId: 'git-ssh-key', keyFileVariable: 'SSH_KEY')]) {
                    sh 'ssh -i $SSH_KEY user@server'
                }

                withCredentials([usernamePassword(credentialsId: 'db-password', usernameVariable: 'DB_USER', passwordVariable: 'DB_PASS')]) {
                    sh 'mysql -u $DB_USER -p$DB_PASS -e "SHOW DATABASES;"'
                }
            }
        }
    }
}

企业最佳实践

  • ✅ 所有敏感信息(密码、Token、密钥)必须存储在Jenkins凭据中
  • ✅ 禁止使用明文密码(如sh 'mysql -u root -p123456'
  • ✅ 定期轮换凭据(每90天)
审计日志:audit-trail-plugin

安装audit-trail-plugin

复制代码
Jenkins → 系统管理 → 插件管理 → 搜索audit-trail → 安装

配置审计日志

复制代码
Jenkins → 系统管理 → 系统配置 → Audit Trail
  • Log File : /var/jenkins_home/logs/audit.log
  • Log Events: 勾选所有事件(用户登录、任务创建、构建触发等)

审计日志示例

复制代码
2026-06-07 23:30:15 +0800  INFO  admin  Login succeeded  /jenkins/login
2026-06-07 23:31:20 +0800  INFO  admin  Build triggered  /jenkins/job/spring-petclinic/build
2026-06-07 23:32:15 +0800  INFO  admin  Configuration changed  /jenkins/configure

企业最佳实践

  • ✅ 启用审计日志(符合等保要求)
  • ✅ 审计日志定期归档到S3/ELK
  • ✅ 设置日志告警(如:异常时间登录、敏感操作)
防止脚本注入:禁用 script step(仅允许 Declarative)

方式一:使用Pipeline: Groovy Plugin 的安全选项

复制代码
Jenkins → 系统管理 → 系统配置 → Pipeline: Groovy Script → 启用脚本安全

方式二:使用Declarative Pipeline Only插件

复制代码
Jenkins → 系统管理 → 插件管理 → 搜索Declarative Pipeline Only → 安装

配置后效果

  • ❌ 禁止使用script步骤(Groovy脚本)
  • ✅ 只允许使用Declarative语法
  • ✅ 防止恶意Groovy脚本执行

企业最佳实践

  • ✅ 生产环境禁用Scripted Pipeline(防止脚本注入)
  • ✅ 使用Shared Library复用逻辑(而不是在Jenkinsfile中写Groovy)
  • ✅ 启用Pipeline Linter(检查Jenkinsfile语法)

5.3 性能优化与监控

构建耗时分析:Performance Plugin

安装Performance Plugin

复制代码
Jenkins → 系统管理 → 插件管理 → 搜索Performance → 安装

在Jenkinsfile中配置

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
    }

    post {
        always {
            // 收集构建性能数据
            performanceReport parsers: [junitParser(operation: 'Add', glob: 'target/surefire-reports/*.xml')]

            // 生成性能趋势图
            publishPerformanceReport sourceDataFiles: 'target/surefire-reports/*.xml', mode: 'ByBuild'
        }
    }
}

分析构建耗时

复制代码
Jenkins → 任务页面 → 性能报告 → 查看构建耗时趋势
资源监控:Prometheus + Grafana 接入 Jenkins Metrics

1. 安装Prometheus Plugin

复制代码
Jenkins → 系统管理 → 插件管理 → 搜索Prometheus → 安装

2. 配置Prometheus Plugin

复制代码
Jenkins → 系统管理 → 系统配置 → Prometheus
  • URL : /prometheus
  • Collect disk usage: 勾选
  • Collect offline executors: 勾选

3. 配置Prometheus抓取Jenkins指标

yaml 复制代码
# prometheus.yml
scrape_configs:
  - job_name: 'jenkins'
    metrics_path: '/prometheus'
    static_configs:
      - targets: ['jenkins-master:8080']

4. 导入Grafana仪表盘

关键监控指标

指标 说明 告警阈值
jenkins_builds_total 构建总数 -
jenkins_builds_success_total 成功构建数 -
jenkins_builds_failure_total 失败构建数 > 10%
jenkins_build_duration_seconds 构建耗时 > 10分钟
jenkins_executors_total 执行器总数 -
jenkins_executors_busy 忙碌执行器数 > 80%
流水线优化:并行阶段、缓存复用、轻量 Agent

优化一:并行执行阶段

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Parallel Testing') {
            parallel {
                stage('Unit Test') {
                    steps {
                        sh 'mvn test -Dtest=UnitTest'
                    }
                }
                stage('Integration Test') {
                    steps {
                        sh 'mvn test -Dtest=IntegrationTest'
                    }
                }
                stage('Code Quality') {
                    steps {
                        sh 'mvn sonar:sonar'
                    }
                }
            }
        }
    }
}

优化二:缓存复用

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build with Cache') {
            steps {
                cache(maxCacheSize: 512, caches: [
                    arbitraryFileCache(path: '.m2/repository', includes: '**/*'),
                    arbitraryFileCache(path: 'node_modules', includes: '**/*')
                ]) {
                    sh 'mvn clean package'
                }
            }
        }
    }
}

优化三:使用轻量Agent

groovy 复制代码
pipeline {
    agent {
        docker {
            image 'maven:3.8.8-openjdk-17-alpine'  // 使用Alpine版本(更小)
            args '-v /var/run/docker.sock:/var/run/docker.sock'
        }
    }

    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
    }
}

企业最佳实践

  • ✅ 并行执行独立任务(测试、代码扫描等)
  • ✅ 使用缓存(Maven本地仓库、Node modules等)
  • ✅ 使用轻量级Agent镜像(Alpine版本)
  • ✅ 定期清理旧构建记录(保留30天)

第六部分:真实企业案例合集

案例一:金融系统灰度发布流水线

环境:DEV → UAT → PRE → PROD

流水线设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                 金融系统部署流水线                           │
├─────────────────────────────────────────────────────────────┤
│ 1. 代码提交 → 触发CI流水线                                │
│ 2. CI流水线:构建 → 测试 → 打包 → 推送镜像                │
│ 3. CD流水线:                                              │
│    ├─ DEV环境:自动部署(kubectl)                         │
│    ├─ UAT环境:人工审批 + 部署(Helm)                    │
│    ├─ PRE环境:人工审批 + 金丝雀部署(10%流量)            │
│    └─ PROD环境:人工审批 + 蓝绿部署 + 监控                │
└─────────────────────────────────────────────────────────────┘

Jenkinsfile(关键部分)

groovy 复制代码
pipeline {
    agent any

    parameters {
        choice(name: 'ENVIRONMENT', choices: ['DEV', 'UAT', 'PRE', 'PROD'], description: '部署环境')
    }

    stages {
        stage('Build & Test') {
            steps {
                sh 'mvn clean package'
                sh 'mvn test'
                sh 'docker build -t app:${BUILD_NUMBER} .'
                sh 'docker push registry.example.com/app:${BUILD_NUMBER}'
            }
        }

        stage('Deploy to DEV') {
            when {
                environment name: 'ENVIRONMENT', value: 'DEV'
            }
            steps {
                sh 'kubectl set image deployment/app app=registry.example.com/app:${BUILD_NUMBER} -n dev'
                sh 'kubectl rollout status deployment/app -n dev --timeout=5m'
            }
        }

        stage('Deploy to UAT') {
            when {
                environment name: 'ENVIRONMENT', value: 'UAT'
            }
            steps {
                input message: '确认部署到UAT环境?', ok: '确认'
                sh 'helm upgrade --install app helm-chart/ --namespace uat --set image.tag=${BUILD_NUMBER}'
            }
        }

        stage('Deploy to PRE (Canary)') {
            when {
                environment name: 'ENVIRONMENT', value: 'PRE'
            }
            steps {
                input message: '确认部署金丝雀版本到PRE环境?', ok: '确认'

                // 部署金丝雀版本(10%流量)
                sh 'kubectl apply -f k8s/pre/canary/'
                sh 'kubectl rollout status deployment/app-canary -n pre --timeout=10m'

                // 验证金丝雀版本
                sh 'curl -f http://app-canary-pre.example.com/health'

                // 观察监控指标
                input message: '请观察监控指标,确认无误后继续', ok: '继续'

                // 扩大流量到100%
                sh 'kubectl patch deployment app -n pre -p {"spec":{"template":{"metadata":{"labels":{"version":"canary"}}}}}'
            }
        }

        stage('Deploy to PROD (Blue-Green)') {
            when {
                environment name: 'ENVIRONMENT', value: 'PROD'
            }
            steps {
                input message: '确认部署到生产环境?', ok: '确认'

                // 部署绿色环境
                sh 'helm upgrade --install app-green helm-chart/ --namespace prod --set image.tag=${BUILD_NUMBER}'
                sh 'kubectl rollout status deployment/app-green -n prod --timeout=10m'

                // 验证绿色环境
                sh 'curl -f http://app-green-prod.example.com/health'

                // 切换流量到绿色环境
                sh 'kubectl patch service app -n prod -p {"spec":{"selector":{"version":"green"}}}'

                // 观察30秒
                sleep time: 30, unit: 'SECONDS'

                // 验证生产环境
                sh 'curl -f http://app-prod.example.com/health'

                // 删除蓝色环境
                sh 'helm uninstall app-blue -n prod'
            }
        }
    }
}
特色:人工审批网关 + 流量切分(Istio) + 熔断机制

1. 人工审批网关

  • 使用Jenkins的input步骤实现人工审批
  • 审批人:开发Leader + 运维Leader(双审批)
  • 审批记录:自动发送到企业微信/钉钉

2. 流量切分(Istio)

yaml 复制代码
# Istio VirtualService配置(金丝雀发布)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: app
spec:
  hosts:
  - app-prod.example.com
  http:
  - route:
    - destination:
        host: app.prod.svc.cluster.local
        subset: stable
      weight: 90
    - destination:
        host: app.prod.svc.cluster.local
        subset: canary
      weight: 10

3. 熔断机制(Istio)

yaml 复制代码
# Istio DestinationRule配置(熔断)
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: app
spec:
  host: app.prod.svc.cluster.local
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 30m

案例二:微服务多模块协同构建

问题:A 服务依赖 B 服务 SNAPSHOT 版本

场景描述

  • 服务A依赖服务B的SNAPSHOT版本(1.0.0-SNAPSHOT
  • 服务B更新后,服务A需要重新构建(但Nexus缓存导致服务A无法获取最新SNAPSHOT)

解决方案

1. 使用Maven Repository Proxy

xml 复制代码
<!-- pom.xml -->
<repositories>
    <repository>
        <id>nexus-snapshots</id>
        <url>http://nexus.example.com/repository/maven-snapshots/</url>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>  <!-- 每次构建都检查最新SNAPSHOT -->
        </snapshots>
    </repository>
</repositories>

2. 配置Nexus仓库更新策略

复制代码
Nexus → Repository → Repositories → maven-snapshots → Configuration
  • Snapshot Policy : Unique
  • Refresh Period : 1 minute

3. 在Jenkinsfile中强制更新依赖

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build Service A') {
            steps {
                // 强制更新SNAPSHOT依赖
                sh 'mvn clean package -U'
            }
        }
    }
}
解决:Maven Repository Proxy + mvn deploy 到 Nexus

完整Jenkinsfile(多模块协同构建)

groovy 复制代码
pipeline {
    agent any

    tools {
        maven 'Maven3'
        jdk 'JDK17'
    }

    stages {
        stage('Build Service B') {
            steps {
                dir('service-b') {
                    sh 'mvn clean deploy -DskipTests'
                }
            }
        }

        stage('Build Service A') {
            steps {
                dir('service-a') {
                    // 等待Service B部署完成
                    sleep time: 10, unit: 'SECONDS'

                    // 强制更新SNAPSHOT依赖
                    sh 'mvn clean package -U'
                }
            }
        }
    }
}

使用Jenkins Pipeline的build步骤触发下游任务

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build Service B') {
            steps {
                sh 'mvn clean deploy -DskipTests'
            }
        }
    }

    post {
        success {
            // 触发Service A的构建
            build job: 'service-a-build', wait: false
        }
    }
}

案例三:边缘计算设备 OTA 更新

场景:ARM 设备集群(IoT)

场景描述

  • 1000+ ARM架构的边缘设备(如智能网关、工业控制器)
  • 需要定期更新设备上的应用(OTA更新)
  • 设备分布在全国各地,网络不稳定

方案:Jenkins 构建 multi-arch 镜像 → MQTT 推送更新指令 → 设备自动拉取

1. Jenkins构建多架构镜像

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Build Multi-Arch Image') {
            steps {
                sh '''
                    # 启用Buildx
                    docker buildx create --name multi-arch-builder --use
                    docker buildx inspect --bootstrap

                    # 构建多架构镜像
                    docker buildx build \
                      --platform linux/amd64,linux/arm64,linux/arm/v7 \
                      -t registry.example.com/iot-app:${BUILD_NUMBER} \
                      --push .
                '''
            }
        }
    }
}

2. 推送更新指令到MQTT Broker

groovy 复制代码
pipeline {
    agent any

    stages {
        stage('Push OTA Update Command') {
            steps {
                script {
                    // 使用MQTT Plugin推送更新指令
                    def mqttCommand = """
                    {
                        "version": "${BUILD_NUMBER}",
                        "image": "registry.example.com/iot-app:${BUILD_NUMBER}",
                        "checksum": "${sha256sum('Dockerfile')}"
                    }
                    """

                    // 发布到MQTT主题
                    mqttPublish(topic: 'iot/ota/update', message: mqttCommand)
                }
            }
        }
    }
}

3. 边缘设备自动拉取更新

python 复制代码
# 边缘设备上的OTA更新客户端(Python)
import paho.mqtt.client as mqtt
import subprocess
import json

def on_message(client, userdata, msg):
    payload = json.loads(msg.payload)
    version = payload['version']
    image = payload['image']

    # 拉取新镜像
    subprocess.run(['docker', 'pull', image])

    # 停止旧容器
    subprocess.run(['docker', 'stop', 'iot-app'])

    # 启动新容器
    subprocess.run(['docker', 'run', '-d', '--name', 'iot-app', image])

    # 上报更新结果
    client.publish('iot/ota/status', json.dumps({'version': version, 'status': 'success'}))

client = mqtt.Client()
client.on_message = on_message
client.connect('mqtt-broker.example.com', 1883, 60)
client.subscribe('iot/ota/update')
client.loop_forever()

企业最佳实践

  • ✅ 使用多架构镜像(支持不同CPU架构的设备)
  • ✅ OTA更新前验证镜像Checksum(防止篡改)
  • ✅ 支持灰度更新(先更新10%设备,验证通过后再全量更新)
  • ✅ OTA更新失败时自动回滚(设备上有旧版本镜像备份)

附录:必备工具包

📦 模板库:

1. Jenkinsfile.template(Java/Node/Python 通用版)
groovy 复制代码
pipeline {
    agent any

    tools {
        maven 'Maven3'
        jdk 'JDK17'
    }

    environment {
        APP_NAME = 'your-app'
        DOCKER_REGISTRY = 'registry.example.com'
        IMAGE_TAG = "${BUILD_NUMBER}"
    }

    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/your-repo/your-app.git', branch: 'main'
            }
        }

        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }

        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }

        stage('Build Docker Image') {
            steps {
                script {
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry') {
                        def customImage = docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG}")
                        customImage.push()
                    }
                }
            }
        }

        stage('Deploy to DEV') {
            steps {
                sh 'kubectl set image deployment/${APP_NAME} ${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG} -n dev'
                sh 'kubectl rollout status deployment/${APP_NAME} -n dev --timeout=5m'
            }
        }
    }

    post {
        always {
            archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
            junit 'target/surefire-reports/*.xml'
        }
        success {
            echo '构建成功!'
        }
        failure {
            echo '构建失败!'
            // 发送通知到企业微信/钉钉
            emailext subject: '构建失败:${JOB_NAME} #${BUILD_NUMBER}', 
                      body: '请检查构建日志:${BUILD_URL}', 
                      to: 'dev-team@example.com'
        }
    }
}
2. docker-compose.yml(本地调试环境)
yaml 复制代码
version: '3.8'

services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - JAVA_OPTS=-Xmx2g -Xms2g
    restart: always

  gitlab:
    image: gitlab/gitlab-ee:latest
    container_name: gitlab
    ports:
      - "80:80"
      - "443:443"
      - "22:22"
    volumes:
      - gitlab_config:/etc/gitlab
      - gitlab_logs:/var/log/gitlab
      - gitlab_data:/var/opt/gitlab
    restart: always

  sonarqube:
    image: sonarqube:latest
    container_name: sonarqube
    ports:
      - "9000:9000"
    volumes:
      - sonarqube_data:/opt/sonarqube/data
    environment:
      - SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true
    restart: always

volumes:
  jenkins_home:
  gitlab_config:
  gitlab_logs:
  gitlab_data:
  sonarqube_data:
3. sonar-project.properties(SonarQube 配置)
properties 复制代码
# 项目Key(唯一)
sonar.projectKey=your-app

# 项目名称
sonar.projectName=Your App

# 项目版本
sonar.projectVersion=1.0.0

# 源代码编码
sonar.sourceEncoding=UTF-8

# 源代码目录
sonar.sources=src/main/java

# 测试代码目录
sonar.tests=src/test/java

# 排除的文件
sonar.exclusions=**/generated/**,**/*.generated.java

# 语言
sonar.language=java

# Java版本
sonar.java.source=17

# 覆盖率报告路径
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml

# 测试报告路径
sonar.junit.reportsPath=target/surefire-reports

📊 检查清单:

1. CI/CD 流水线健康度评分表(10 项指标)
指标 权重 评分标准 得分
构建成功率 20% > 95%: 10分; 90-95%: 8分; < 90%: 0分
构建耗时 15% < 5分钟: 10分; 5-10分钟: 8分; > 10分钟: 0分
测试覆盖率 15% > 80%: 10分; 60-80%: 8分; < 60%: 0分
代码质量 10% SonarQube无严重问题: 10分; 有严重问题: 0分
安全扫描 10% 无高危漏洞: 10分; 有高危漏洞: 0分
部署频率 10% 每天多次: 10分; 每周一次: 8分; 每月一次: 0分
回滚时间 10% < 5分钟: 10分; 5-15分钟: 8分; > 15分钟: 0分
监控覆盖率 5% 100%服务有监控: 10分; 80-100%: 8分; < 80%: 0分
文档完整性 3% 有完整文档: 10分; 有部分文档: 8分; 无文档: 0分
团队协作 2% 多人协作顺畅: 10分; 有协作问题: 0分

总分计算总分 = Σ(各项得分 × 权重)

健康度评级

  • A级(90-100分):优秀,无需改进
  • B级(70-89分):良好,需小幅改进
  • C级(50-69分):合格,需大幅改进
  • D级(<50分):不合格,需立即整改
2. 安全合规 checklist(GDPR/等保)

GDPR合规检查清单

  • 个人数据加密存储(数据库字段加密)
  • 个人数据传输加密(HTTPS/TLS)
  • 数据访问日志记录(谁访问了哪些数据)
  • 数据导出功能(用户可导出自己的数据)
  • 数据删除功能(用户可删除自己的数据)
  • Cookie使用告知(弹窗告知用户)
  • 隐私政策页面(明确说明数据用途)

等保2.0三级合规检查清单

  • 身份鉴别(双因素认证)
  • 访问控制(基于角色的权限管理)
  • 安全审计(所有操作有日志记录)
  • 入侵防范(防火墙、WAF)
  • 数据完整性(数据备份、校验)
  • 数据保密性(敏感数据加密)
  • 数据备份恢复(每天备份、定期恢复演练)
  • 个人信息保护(脱敏处理)

🎯 学习路径图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│                  Jenkins CI/CD 学习路径                      │
├─────────────────────────────────────────────────────────────┤
│ 阶段1:入门(1-2周)                                      │
│   ├─ Jenkins安装与配置                                     │
│   ├─ Freestyle Project使用                                 │
│   └─ Pipeline基础语法                                     │
│                                                           │
│ 阶段2:进阶(2-4周)                                      │
│   ├─ Declarative Pipeline语法                             │
│   ├─ 多分支流水线                                         │
│   ├─ SonarQube集成                                        │
│   └─ Docker集成                                           │
│                                                           │
│ 阶段3:高级(1-2个月)                                    │
│   ├─ Kubernetes集成(动态Pod Agent)                       │
│   ├─ Helm部署                                             │
│   ├─ 蓝绿部署/金丝雀发布                                  │
│   └─ Shared Library                                      │
│                                                           │
│ 阶段4:专家(3-6个月)                                    │
│   ├─ Jenkins性能优化                                       │
│   ├─ 高可用架构                                           │
│   ├─ 安全加固                                             │
│   ├─ 企业级最佳实践                                       │
│   └─ GitOps(Argo CD)                                   │
└─────────────────────────────────────────────────────────────┘

推荐学习资源

  1. 官方文档https://www.jenkins.io/doc/
  2. Pipeline语法参考https://www.jenkins.io/doc/book/pipeline/syntax/
  3. 企业级案例https://www.jenkins.io/blog/
  4. 推荐书籍:《Jenkins 2权威指南》、《持续交付》

💡 每篇结尾设置:

「今日挑战」:

  1. 改造你的 Jenkinsfile,支持按 commit message 触发不同构建策略(如:commit message包含[skip ci]则跳过构建)
  2. 实现Jenkinsfile的自动生成工具(根据项目类型自动生成Jenkinsfile)
  3. 配置Jenkins的异地容灾(主备Jenkins,自动切换)

「企业真题」:

  1. 某大厂面试题:如何在 Jenkins 中实现构建失败自动通知企业微信?
  • 答案 :使用post步骤的failure条件,调用企业微信Webhook发送通知。
  1. 某大厂面试题:如何优化Jenkins的构建性能?
  • 答案:并行执行、缓存复用、轻量Agent、定期清理旧构建。
  1. 某大厂面试题:如何实现Jenkins的高可用?
  • 答案:主从模式+共享存储+负载均衡。

「彩蛋视频」:

  1. 关键操作录屏(如下拉联动配置、K8s 部署调试)
  2. 企业级流水线实战演示
  3. Jenkins性能优化实战

总结

本文从零开始,详细介绍了Jenkins CI/CD的全流程实战,包括:

  1. 基础篇:Jenkins安装、配置、插件管理
  2. 语法篇:Declarative Pipeline语法、高级特性
  3. CI篇:多语言项目构建、质量门禁、参数化构建
  4. CD篇:Docker镜像构建、K8s部署、多环境策略
  5. 进阶篇:高可用架构、安全加固、性能优化
  6. 实战篇:金融系统、微服务、IoT等真实案例

所有内容均基于企业级最佳实践,可直接应用于生产环境。

下一步学习建议

  1. 学习GitOps(Argo CD、Flux CD)
  2. 学习Serverless CI/CD(GitHub Actions、GitLab CI)
  3. 学习云原生CI/CD(Tekton、Jenkins X)

文档版本 :v1.0

最后更新 :2026-06-07

作者 :基于华为云ECS实战环境编写

适用版本:Jenkins 2.4+ LTS


版权声明:本文档仅供学习交流使用,未经授权不得用于商业用途。

相关推荐
Volunteer Technology2 小时前
SpringSecurity中的权限管理
java·数据库·servlet
Bigger3 小时前
记一次坑爹的 Cloudflare Pages 部署:Failed to load module script 是怎么把我的 SPA 搞挂的
前端·ci/cd·浏览器
江华森4 小时前
基于 Git 的自动集成交付(Git-Driven CI/CD)实战
git·ci/cd
夜雪闻竹5 小时前
版本管理:npm 发布 + Electron 打包 + CI/CD
ci/cd·npm·node.js·代码规范·chatcrystal
云原生指北5 小时前
告别 Jenkins UI:jk 让 AI Agent 也能操控 Jenkins
jenkins·devops
摇滚侠20 小时前
JavaWeb 全套教程 Listener 112-113
java·开发语言·servlet·tomcat·intellij-idea
我登哥MVP1 天前
Spring Boot 从“会用”到“精通”:自定义参数绑定原理
java·spring boot·后端·spring·servlet·maven·intellij-idea
我登哥MVP1 天前
Spring Boot 从“会用”到“精通”:参数绑定体系全景
java·spring boot·spring·servlet·maven·intellij-idea·mybatis
摇滚侠1 天前
JavaWeb 全套教程 Filter 107-111
java·开发语言·servlet