Appearance
Jenkins 详解:CI/CD 自动化
Jenkins 是业界最流行的开源 CI/CD 工具,负责自动化构建、测试、部署。代码提交后,Jenkins 自动拉取、编译、打包镜像、推送仓库、部署到服务器,让开发只关注写代码。
一、为什么需要 Jenkins?
1.1 没有 CI/CD 的痛
手工发布流程(每次 30 分钟 ~ 2 小时):
1. 开发提交代码到 Git
2. 开发本地 mvn package 打包
3. 手动上传 jar 到服务器
4. 手动停掉旧服务
5. 手动启动新服务
6. 手动检查日志确认启动成功
7. 如果失败,手动回滚
问题:
❌ 重复劳动,容易出错
❌ 没人知道"当前线上跑的是哪个版本"
❌ 多人同时发布时互相干扰
❌ 回滚困难,只能手动替换 jar1.2 有了 Jenkins 之后
自动化发布流程(每次 5 分钟,全自动):
1. 开发提交代码到 Git
└── Jenkins 自动检测到代码变更
2. Jenkins 自动拉取代码
└── mvn clean package
3. Jenkins 自动构建 Docker 镜像
└── docker build -t order-service:v1.2.3
4. Jenkins 自动推送镜像到仓库
└── docker push registry/order-service:v1.2.3
5. Jenkins 自动部署到 K8s
└── kubectl set image deployment/order-service order-service=registry/order-service:v1.2.3
6. Jenkins 自动验证
└── curl http://order-service/actuator/health
7. 如果失败,自动回滚到上一版本
优点:
✅ 全过程自动化,人工介入为 0
✅ 每次发布都有记录,可追溯
✅ 一键回滚到任意版本
✅ 发布频率从"月级"提升到"天级"甚至"小时级"二、核心概念
2.1 CI/CD 是什么?
CI(Continuous Integration,持续集成):
代码频繁合并到主干,每次合并自动构建 + 测试
目标:尽早发现集成问题,避免"合并地狱"
CD(Continuous Delivery / Deployment,持续交付/部署):
通过 CI 的代码自动部署到测试/生产环境
目标:随时可以一键发布,发布不再是"大事"2.2 Jenkins 架构
┌──────────────────────────────────────────────────────────────────┐
│ Jenkins 架构 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Jenkins Master(主控) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 任务调度 │ │ 插件管理 │ │ 用户管理 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ │ 负责:调度任务、管理配置、展示结果,不执行构建 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Agent 1 │ │ Agent 2 │ │ Agent 3 │ │
│ │ (Linux) │ │ (Linux) │ │ (Windows) │ │
│ │ │ │ │ │ │ │
│ │ 执行构建 │ │ 执行构建 │ │ 执行构建 │ │
│ │ Maven/Docker│ │ Maven/Docker│ │ .NET/MSBuild│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ★ Master 不干活,只调度 │
│ ★ Agent 是真正执行构建的节点,可横向扩展 │
└──────────────────────────────────────────────────────────────────┘三、快速上手
3.1 Docker 部署 Jenkins
bash
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:lts3.2 安装推荐插件
首次启动推荐安装:
✅ Git — 代码拉取
✅ Pipeline — 流水线核心
✅ Docker Pipeline — Docker 构建
✅ Kubernetes CLI — K8s 部署
✅ Blue Ocean — 可视化流水线
✅ Maven Integration — Maven 构建
✅ SonarQube Scanner — 代码质量扫描
✅ Slack Notification — 通知四、Pipeline 流水线
4.1 为什么用 Pipeline?
传统 Jenkins Job(自由风格):
❌ 配置写在 Web UI 里,无法版本管理
❌ 迁移到新 Jenkins 需要重新配置
❌ 多人协作时,改了什么无法追踪
Pipeline as Code(声明式流水线):
✅ Jenkinsfile 文件,随代码一起提交到 Git
✅ 重构、回滚、审查都像代码一样
✅ 换 Jenkins 服务器,只要代码在,流水线就在4.2 声明式 vs 脚本式
groovy
// 声明式(推荐,结构清晰,适合大多数场景)
pipeline {
agent any
stages {
stage('编译') {
steps {
sh 'mvn clean compile'
}
}
stage('测试') {
steps {
sh 'mvn test'
}
}
}
}
// 脚本式(灵活,适合复杂逻辑)
node {
stage('编译') {
sh 'mvn clean compile'
}
stage('测试') {
sh 'mvn test'
}
}4.3 微服务完整 Pipeline
groovy
pipeline {
agent any
// ============ 环境变量 ============
environment {
DOCKER_REGISTRY = 'registry.example.com'
DOCKER_IMAGE = "${DOCKER_REGISTRY}/order-service"
K8S_NAMESPACE = 'production'
K8S_DEPLOYMENT = 'order-service'
}
// ============ 参数(手动触发时可选择) ============
parameters {
choice(name: 'ENVIRONMENT', choices: ['dev', 'test', 'prod'], description: '部署环境')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '跳过测试')
}
stages {
// ============ 阶段 1:代码检出 ============
stage('Checkout') {
steps {
checkout scm
// 获取当前分支和 commit ID
script {
env.GIT_BRANCH = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()
env.GIT_COMMIT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
env.IMAGE_TAG = "${env.GIT_COMMIT}-${env.BUILD_NUMBER}"
}
}
}
// ============ 阶段 2:编译 ============
stage('Compile') {
steps {
sh 'mvn clean compile'
}
}
// ============ 阶段 3:单元测试 ============
stage('Unit Test') {
when {
expression { !params.SKIP_TESTS }
}
steps {
sh 'mvn test'
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
// ============ 阶段 4:代码扫描 ============
stage('SonarQube') {
steps {
sh 'mvn sonar:sonar -Dsonar.projectKey=order-service'
}
}
// ============ 阶段 5:打包 ============
stage('Package') {
steps {
sh 'mvn package -DskipTests'
}
}
// ============ 阶段 6:构建 Docker 镜像 ============
stage('Docker Build') {
steps {
script {
docker.build("${DOCKER_IMAGE}:${IMAGE_TAG}")
}
}
}
// ============ 阶段 7:推送镜像 ============
stage('Docker Push') {
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials') {
docker.image("${DOCKER_IMAGE}:${IMAGE_TAG}").push()
}
}
}
}
// ============ 阶段 8:部署到 K8s ============
stage('Deploy to K8s') {
when {
expression { params.ENVIRONMENT == 'prod' }
}
steps {
sh """
kubectl set image deployment/${K8S_DEPLOYMENT} \
${K8S_DEPLOYMENT}=${DOCKER_IMAGE}:${IMAGE_TAG} \
-n ${K8S_NAMESPACE} --record
"""
// 等待部署完成
sh """
kubectl rollout status deployment/${K8S_DEPLOYMENT} \
-n ${K8S_NAMESPACE} --timeout=5m
"""
}
}
// ============ 阶段 9:健康检查 ============
stage('Health Check') {
steps {
script {
sleep 30 // 等待服务启动
def response = sh(
script: 'curl -s -o /dev/null -w "%{http_code}" http://order-service/actuator/health',
returnStdout: true
).trim()
if (response != '200') {
error("健康检查失败,HTTP 状态码: ${response}")
}
}
}
}
}
// ============ 全局 Post 处理 ============
post {
// 成功时通知
success {
script {
echo "构建成功!版本: ${IMAGE_TAG}"
// slackSend(channel: '#deploy', message: "✅ order-service ${IMAGE_TAG} 部署成功")
}
}
// 失败时通知 + 回滚
failure {
script {
echo "构建失败!版本: ${IMAGE_TAG}"
// slackSend(channel: '#deploy', message: "❌ order-service ${IMAGE_TAG} 部署失败")
// 如果 K8s 部署失败,回滚到上一版本
sh """
kubectl rollout undo deployment/${K8S_DEPLOYMENT} -n ${K8S_NAMESPACE} || true
"""
}
}
// 无论成功失败都清理
always {
cleanWs()
}
}
}五、多分支流水线
多分支流水线自动为每个分支创建独立的构建任务:
仓库分支 Jenkins 自动创建的 Job
─────────────────────────────────────────────────
main ──────────→ order-service/main
develop ──────────→ order-service/develop
feature/xxx ──────────→ order-service/feature-xxx
hotfix/yyy ──────────→ order-service/hotfix-yyy
PR 合并到 main 时自动触发 main 分支构建 + 部署到生产
push 到 develop 时自动触发 develop 分支构建 + 部署到测试groovy
// Jenkinsfile 中按分支做不同处理
pipeline {
agent any
stages {
stage('Deploy') {
steps {
script {
if (env.BRANCH_NAME == 'main') {
deployTo('prod')
} else if (env.BRANCH_NAME == 'develop') {
deployTo('test')
} else {
deployTo('dev')
}
}
}
}
}
}六、与 Git 集成
6.1 Webhook 自动触发
Git 平台(GitHub/GitLab/Gitee) Jenkins
┌──────────────────────────┐ ┌──────────────────┐
│ │ │ │
│ 开发者 push 代码 │ │ 接收 Webhook │
│ │ │ POST │ │ │
│ ▼ │─────────→│ ▼ │
│ 触发 Webhook │ │ 解析分支信息 │
│ http://jenkins:8080/ │ │ │ │
│ github-webhook │ │ ▼ │
│ │ │ 触发对应分支 │
│ │ │ 的 Pipeline │
└──────────────────────────┘ └──────────────────┘6.2 配置步骤
1. Jenkins 安装 "GitHub Integration" / "GitLab" 插件
2. Pipeline 配置中勾选 "GitHub hook trigger for GITScm polling"
3. Git 平台设置 Webhook URL:http://jenkins:8080/github-webhook/
4. 选择触发事件:Push events + Pull Request events七、面试要点
Q1:CI/CD 是什么?CI 和 CD 有什么区别?
CI(持续集成)是频繁合并代码并自动构建测试,尽早发现集成问题。CD 有两种:持续交付(手动部署)和持续部署(自动部署到生产)。CI 保证代码质量,CD 保证快速交付。
Q2:Jenkins Pipeline 有哪两种?
声明式(Declarative)和脚本式(Scripted)。声明式结构固定(pipeline → stages → stage → steps),易读易维护,推荐日常使用。脚本式是 Groovy 代码,灵活但复杂,适合高级场景。
Q3:Agent 和 Master 的区别?
Master 负责调度任务、管理配置、展示结果,不执行构建。Agent 是真正执行构建的节点,可以横向扩展。生产环境 Master 不要执行构建,避免资源抢占。
Q4:如何实现构建失败自动回滚?
在 Pipeline 的 post { failure {} } 块中执行 kubectl rollout undo 回滚 K8s 部署,或执行 docker service update --rollback 回滚 Docker Swarm。
Q5:如何实现多环境部署?
通过 Pipeline 参数(choice parameter)选择环境,或按分支自动判断(main → prod,develop → test)。每个环境对应不同的 K8s namespace 或配置文件。
Q6:Jenkins 如何实现高可用?
方案一:Master + 多个 Agent(Agent 高可用)。方案二:Jenkins Master 主备(通过共享存储 + 冷备)。方案三:CloudBees Jenkins(企业版高可用)。实际中,Agent 扩展比 Master 高可用更重要。
