Jenkins实战 - 从代码提交到生产部署的完整CI/CD流程

一、前言
前面九篇文章,我们从Jenkins基础概念学到了通知配置。现在是时候把所有知识串起来,打造一个完整的CI/CD自动化部署流程了!本文将以一个真实的Spring Boot项目为例,从代码提交到生产部署,实现全自动化的持续交付。
二、项目背景
假设我们有一个Spring Boot + Docker的项目,部署架构如下:
开发流程:
开发人员 → Git Push → Jenkins自动构建 → 自动测试 → 自动部署
环境划分:
- Dev(开发环境):每次push自动部署
- Staging(预发布环境):develop分支合并后自动部署
- Production(生产环境):main分支合并后,需人工确认部署
三、项目准备
3.1 项目结构
my-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ └── resources/
│ │ └── application.yml
│ └── test/
│ └── java/
├── Dockerfile
├── docker-compose.yml
├── pom.xml
├── Jenkinsfile
├── deploy.sh
└── scripts/
├── health-check.sh
└── rollback.sh
3.2 Dockerfile
# 多阶段构建
# 第一阶段:编译
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests -B
# 第二阶段:运行
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV TZ=Asia/Shanghai
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"]
3.3 部署脚本 deploy.sh
#!/bin/bash
set -e
ENV=$1
IMAGE_TAG=$2
case $ENV in
dev)
SERVERS=("192.168.1.101")
;;
staging)
SERVERS=("192.168.1.201")
;;
production)
SERVERS=("192.168.1.301" "192.168.1.302")
;;
*)
echo "未知环境: $ENV"
exit 1
;;
esac
echo "========================================="
echo "部署环境: $ENV"
echo "镜像标签: $IMAGE_TAG"
echo "目标服务器: ${SERVERS[*]}"
echo "========================================="
for SERVER in "${SERVERS[@]}"; do
echo "部署到 $SERVER ..."
ssh deploy@$SERVER << EOF
docker pull $IMAGE_TAG
docker stop my-app || true
docker rm my-app || true
docker run -d \
--name my-app \
--restart=unless-stopped \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=$ENV \
-v /app/logs:/app/logs \
$IMAGE_TAG
EOF
echo "$SERVER 部署完成"
done
echo "所有服务器部署完成!"
3.4 健康检查脚本 health-check.sh
#!/bin/bash
ENV=$1
SERVER=$2
case $ENV in
dev) SERVER="192.168.1.101" ;;
staging) SERVER="192.168.1.201" ;;
production) SERVER="192.168.1.301" ;;
esac
MAX_RETRIES=30
RETRY_COUNT=0
echo "健康检查: http://$SERVER:8080/actuator/health"
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://$SERVER:8080/actuator/health" || echo "000")
if [ "$STATUS" = "200" ]; then
echo "✅ 健康检查通过!(尝试 $((RETRY_COUNT+1)) 次)"
exit 0
fi
RETRY_COUNT=$((RETRY_COUNT+1))
echo "等待服务启动... ($RETRY_COUNT/$MAX_RETRIES)"
sleep 5
done
echo "❌ 健康检查失败!服务未在预期时间内启动"
exit 1
四、完整Jenkinsfile
这是本文的核心——一个完整的、生产级别的Jenkinsfile:
// Jenkinsfile - 生产级CI/CD流水线
pipeline {
agent any
// 全局配置
options {
timeout(time: 2, unit: 'HOURS')
timestamps()
buildDiscarder(logRotator(numToKeepStr: '30'))
disableConcurrentBuilds()
quietPeriod(5)
}
// 参数配置
parameters {
choice(
name: 'DEPLOY_ENV',
choices: ['auto', 'dev', 'staging', 'production'],
description: '部署环境(auto=根据分支自动判断)'
)
booleanParam(
name: 'SKIP_TESTS',
defaultValue: false,
description: '跳过测试(紧急修复时使用)'
)
booleanParam(
name: 'FORCE_DEPLOY',
defaultValue: false,
description: '强制部署(即使测试失败也部署)'
)
}
// 环境变量
environment {
APP_NAME = 'my-application'
REGISTRY = 'registry.example.com'
IMAGE_NAME = "${REGISTRY}/${APP_NAME}"
GIT_COMMIT_SHORT = sh(
returnStdout: true,
script: 'git rev-parse --short HEAD 2>/dev/null || echo "unknown"'
).trim()
}
stages {
// ========== 阶段1:代码检出 ==========
stage('Checkout') {
steps {
checkout scm
script {
env.GIT_COMMIT_SHORT = sh(
returnStdout: true,
script: 'git rev-parse --short HEAD'
).trim()
env.IMAGE_TAG = "${IMAGE_NAME}:${BUILD_NUMBER}-${GIT_COMMIT_SHORT}"
env.IMAGE_LATEST = "${IMAGE_NAME}:latest"
def author = sh(
returnStdout: true,
script: 'git log -1 --pretty=format:"%an"'
).trim()
def message = sh(
returnStdout: true,
script: 'git log -1 --pretty=format:"%s"'
).trim()
echo """
===================================
📋 构建信息
===================================
提交者: ${author}
提交信息: ${message}
提交哈希: ${GIT_COMMIT_SHORT}
镜像标签: ${IMAGE_TAG}
===================================
"""
}
}
}
// ========== 阶段2:编译构建 ==========
stage('Build') {
agent {
docker {
image 'maven:3.8-openjdk-17'
args '-v $HOME/.m2:/root/.m2'
}
}
steps {
sh 'mvn clean package -DskipTests -B'
}
post {
success {
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
}
// ========== 阶段3:代码质量 & 测试(并行) ==========
stage('Quality & Test') {
parallel {
stage('Unit Tests') {
when {
expression { return !params.SKIP_TESTS }
}
agent {
docker {
image 'maven:3.8-openjdk-17'
args '-v $HOME/.m2:/root/.m2'
}
}
steps {
sh 'mvn test -B'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
}
}
stage('Code Quality') {
when {
expression { return !params.SKIP_TESTS }
}
agent {
docker {
image 'maven:3.8-openjdk-17'
args '-v $HOME/.m2:/root/.m2'
}
}
steps {
sh 'mvn sonar:sonar -Dsonar.qualitygate.wait=true'
}
}
stage('Security Scan') {
when {
expression { return !params.SKIP_TESTS }
}
steps {
sh 'mvn org.owasp:dependency-check-maven:check'
}
post {
always {
publishHTML(target: [
allowMissing: true,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/dependency-check-report',
reportFiles: 'dependency-check-report.html',
reportName: 'Security Report'
])
}
}
}
}
}
// ========== 阶段4:构建Docker镜像 ==========
stage('Docker Build') {
steps {
sh "docker build -t ${IMAGE_TAG} -t ${IMAGE_LATEST} ."
echo "✅ Docker镜像构建完成: ${IMAGE_TAG}"
}
}
// ========== 阶段5:推送镜像 ==========
stage('Docker Push') {
steps {
withCredentials([usernamePassword(
credentialsId: 'docker-registry-creds',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh "docker login -u ${DOCKER_USER} -p ${DOCKER_PASS} ${REGISTRY}"
sh "docker push ${IMAGE_TAG}"
sh "docker push ${IMAGE_LATEST}"
}
echo "✅ Docker镜像推送完成"
}
}
// ========== 阶段6:部署 ==========
stage('Determine Environment') {
steps {
script {
if (params.DEPLOY_ENV == 'auto') {
if (env.BRANCH_NAME == 'main') {
env.TARGET_ENV = 'production'
} else if (env.BRANCH_NAME == 'develop') {
env.TARGET_ENV = 'staging'
} else {
env.TARGET_ENV = 'dev'
}
} else {
env.TARGET_ENV = params.DEPLOY_ENV
}
echo "🎯 目标部署环境: ${env.TARGET_ENV}"
}
}
}
stage('Deploy') {
steps {
script {
// 生产环境需要审批
if (env.TARGET_ENV == 'production') {
timeout(time: 30, unit: 'MINUTES') {
input message: """
⚠️ 确认部署到生产环境?
镜像: ${IMAGE_TAG}
环境: ${env.TARGET_ENV}
""", ok: '确认部署'
}
}
// 执行部署
sshagent(credentials: ['deploy-ssh-key']) {
sh "bash deploy.sh ${env.TARGET_ENV} ${IMAGE_TAG}"
}
}
}
}
// ========== 阶段7:健康检查 ==========
stage('Health Check') {
steps {
script {
timeout(time: 3, unit: 'MINUTES') {
sh "bash scripts/health-check.sh ${env.TARGET_ENV}"
}
echo '✅ 健康检查通过!服务正常运行'
}
}
}
// ========== 阶段8:验收测试 ==========
stage('Smoke Test') {
when {
expression { env.TARGET_ENV in ['staging', 'production'] }
}
steps {
script {
sh 'mvn verify -P smoke-test'
echo '✅ 验收测试通过!'
}
}
}
}
// ========== 构建后处理 ==========
post {
always {
cleanWs()
sh "docker rmi ${IMAGE_TAG} ${IMAGE_LATEST} || true"
}
success {
echo """
==========================================
🎉 部署成功!
==========================================
项目: ${APP_NAME}
环境: ${env.TARGET_ENV}
版本: ${BUILD_NUMBER}-${GIT_COMMIT_SHORT}
镜像: ${IMAGE_TAG}
==========================================
"""
}
failure {
echo """
==========================================
❌ 部署失败!
==========================================
项目: ${APP_NAME}
环境: ${env.TARGET_ENV}
请检查构建日志排查问题
==========================================
"""
}
}
}
五、Git分支策略
配合上述Jenkinsfile,推荐使用Git Flow分支策略:
main ──●──────────●──────────●── (生产环境)
\\ / /
develop ───●──●──●──●──●──●──●─── (预发布环境)
\\ /
feature/xxx ───●──●── (开发环境)
hotfix/xxx ────────●──●── (紧急修复,直接上生产)
release/x.x ──────────●──●── (发布分支)
不同分支的自动部署策略:
- feature/* → 自动部署到Dev环境
- develop → 自动部署到Staging环境
- main → 需审批后部署到Production
- hotfix/* → 跳过测试,快速部署到Production
六、回滚方案
6.1 快速回滚脚本
#!/bin/bash
# rollback.sh - 快速回滚到指定版本
set -e
ENV=$1
TARGET_VERSION=$2
if [ -z "$TARGET_VERSION" ]; then
echo "用法: ./rollback.sh <env> <version>"
echo "示例: ./rollback.sh production 42"
exit 1
fi
IMAGE_TAG="registry.example.com/my-application:${TARGET_VERSION}"
echo "⚠️ 回滚到版本: ${TARGET_VERSION}"
# 拉取指定版本镜像并重新部署
ssh deploy@production-server << EOF
docker pull ${IMAGE_TAG}
docker stop my-app || true
docker rm my-app || true
docker run -d \
--name my-app \
--restart=unless-stopped \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=${ENV} \
${IMAGE_TAG}
EOF
echo "✅ 回滚完成"
6.2 在Jenkins中实现一键回滚
pipeline {
agent any
parameters {
booleanParam(name: 'ROLLBACK', defaultValue: false, description: '执行回滚')
string(name: 'ROLLBACK_VERSION', defaultValue: '', description: '回滚到哪个版本号')
}
stages {
stage('Rollback') {
when {
expression { return params.ROLLBACK }
}
steps {
script {
if (!params.ROLLBACK_VERSION) {
error '请指定回滚版本号'
}
echo "🔄 回滚到版本 ${params.ROLLBACK_VERSION}..."
sh "./scripts/rollback.sh production ${params.ROLLBACK_VERSION}"
sh "./scripts/health-check.sh production"
}
}
}
// 正常构建流程...
}
}
七、监控与告警
7.1 构建监控Dashboard
可以安装 Build Monitor View Plugin,创建一个实时监控面板:
- 1. 点击"+"创建新视图
- 2. 选择"Build Monitor View"
- 3. 选择要监控的任务
- 4. 投影到大屏幕上,团队实时看到构建状态
7.2 构建趋势分析
安装 Build Graph View Plugin,可以查看构建时间趋势图,帮你发现构建变慢的问题。
八、安全最佳实践
- 最小权限原则:Jenkins使用的部署账号只给必要权限
- 凭据管理:所有密码、密钥用Jenkins Credentials管理,不要硬编码
- 审批流程:生产部署必须有人工审批
- 审计日志:开启Jenkins审计日志,记录所有操作
- 网络安全:Jenkins不要直接暴露在公网
- 定期备份:备份Jenkins配置和数据
# 备份Jenkins
# Docker方式
docker cp jenkins:/var/jenkins_home /backup/jenkins-$(date +%Y%m%d)
# 系统安装方式
tar -czf /backup/jenkins-$(date +%Y%m%d).tar.gz /var/lib/jenkins/
九、性能优化建议
- 增量构建:只构建变更的模块,不是每次全量构建
- Maven缓存:挂载本地.m2仓库,避免每次下载依赖
- Docker层缓存:利用Docker BuildKit的缓存功能
- 并行测试:使用parallel阶段并行运行测试
- Agent分布式:多台Agent并行构建
- 浅克隆:大仓库使用shallow clone加速检出
# Maven并行构建
mvn -T 4 clean package # 使用4个线程构建
# Docker BuildKit缓存
DOCKER_BUILDKIT=1 docker build \
--cache-from registry.example.com/my-app:latest \
-t registry.example.com/my-app:${BUILD_NUMBER} .
十、完整CI/CD流程总结
让我们回顾一下整个自动化部署流程:
1. 开发人员提交代码到Git
↓
2. Git Webhook触发Jenkins构建
↓
3. Jenkins自动拉取代码
↓
4. 编译打包(Maven/Gradle)
↓
5. 运行单元测试 + 代码质量检查 + 安全扫描(并行)
↓
6. 构建Docker镜像
↓
7. 推送镜像到镜像仓库
↓
8. 根据分支自动判断部署环境
↓
9. 生产环境需人工审批
↓
10. SSH远程部署到目标服务器
↓
11. 健康检查确认服务正常
↓
12. 验收测试(Staging/Production)
↓
13. 通知团队部署结果
整个流程完全自动化(除了生产部署的审批环节),从代码提交到服务上线,通常只需要5-10分钟!
十一、系列总结
恭喜你完成了Jenkins学习系列的全部十篇文章!回顾一下我们的学习路径:
- 第1篇:Jenkins入门 - 理解CI/CD概念
- 第2篇:安装部署 - 搭建Jenkins环境
- 第3篇:界面操作 - 熟悉Jenkins界面
- 第4篇:Freestyle项目 - 创建第一个构建任务
- 第5篇:Pipeline入门 - 代码化构建流程
- 第6篇:Pipeline进阶 - 条件、并行、矩阵
- 第7篇:参数化构建 - 灵活控制构建行为
- 第8篇:Git集成 - Webhook自动触发
- 第9篇:通知配置 - 及时获取构建状态
- 第10篇:实战部署 - 完整CI/CD流水线
从零基础到能搭建生产级CI/CD流水线,你已经具备了Jenkins实战的核心能力。在实际工作中,还需要根据项目特点不断调整优化,积累更多经验。祝你CI/CD之路顺利!🎉
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。







