Docker部署Jenkins实现完整CI/CD流水线
📋 概述
本文将详细介绍如何使用Docker容器化部署Jenkins,并配置实现Spring Boot和Vue项目的CI/CD自动化部署流程。通过本教程,你将学会搭建一个完整的持续集成和持续部署环境。
文档特色
✨ 全面性: 从环境准备到高级Pipeline配置的完整流程
🔧 实用性: 提供真实项目的配置示例和部署脚本
🚀 先进性: 包含滚动更新、多项目并行构建等企业级实践
📚 易学性: 详细的步骤说明和错误排查指南
📖 你将学到:
- 🐳 使用Docker快速部署Jenkins环境
- ⚙️ 配置完整的CI/CD流水线
- 📦 实现Spring Boot项目自动化构建部署
- 🌐 配置Vue.js前端项目自动化发布
- 🔄 掌握滚动更新和零停机部署
- 🔧 高级Pipeline脚本编写技巧
📚 目录导航
📋 文档结构概览
🚀 快速开始部分:
- 🔄 部署流程图 - 整体架构和流程概览
- 🛠️ 环境准备 - 前置要求和环境检查
- 🚀 Jenkins部署 - Docker化部署Jenkins
⚙️ 基础配置部分:
- 🔧 Jenkins配置 - 插件安装和基础设置
- 🔌 插件安装 - 必要插件和安装指南
- 🛠️ 全局工具配置 - Maven、JDK、Git等工具设置
📝 项目实战部分:
- 📝 创建项目任务 - 创建和配置构建任务
- 💾 源码管理配置 - Git仓库集成
- 📦 Maven构建配置 - 构建脚本设置
- 🚀 部署Shell脚本 - 自动化部署脚本
🧪 测试验证部分:
- 🧪 测试验证 - 完整的测试流程
- 🎯 准备测试项目 - 示例项目介绍
- 🔄 CI/CD 流水线执行 - 实际运行验证
🚀 高级特性部分:
- 🚀 高级扩展 - 企业级实践
- 🔄 多项目Pipeline脚本部署 - 高级流水线配置
- 🔧 Node.js环境配置 - 前端项目支持
- 🏗️ 后端项目部署配置 - 后端服务部署
📚 总结学习部分:
🔄 部署流程图
整个部署操作流程如下:

流程说明
- 左侧:项目发布的整体配置流程
- 右侧:配置过程中的详细步骤
- 核心思想:将本地项目打包部署的过程拆解为模块化操作,在Jenkins环境中自动化执行
🛠️ 环境准备
✅ 前置要求
在开始之前,请确保服务器满足以下要求:
- ✅ Docker环境:已安装并正常运行
- ✅ Docker Compose:已安装(推荐v2.0+)
- ✅ 网络端口:确保以下端口可用
9090
:Jenkins Web界面8091
:应用服务端口50001
:Jenkins代理通信端口
注意事项
如果使用云服务器,请在安全组中开放相应端口
🚀 Jenkins部署
📁 文件准备

文件说明
配置文件已在 lxf-dev-tech-jenkins 工程中提供
文件结构说明:
- 📄 compose-down.sh:Docker Compose启动脚本
- 📄 jdk-down.sh:JDK17自动下载和解压脚本
- 📁 maven/settings.xml:Maven配置文件(已配置阿里云镜像源)
部署步骤:
- 将
dev-ops
目录完整上传到服务器根目录 - 确保脚本具有执行权限:
chmod +x *.sh
⚙️ Docker Compose配置详解
version: '3.9'
# 执行命令:docker-compose -f docker-compose-v1.0.yml up -d
# 更新源:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
services:
jenkins:
image: jenkins/jenkins:2.510
container_name: jenkins
privileged: true
user: root
ports:
- "9090:8080" # Jenkins Web界面
- "50001:50000" # Jenkins代理通信端口
volumes:
# 时区同步配置
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
# Jenkins数据持久化
- ./jenkins_home:/var/jenkins_home
# Docker-in-Docker配置
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/local/bin/docker
# Maven配置挂载
- ./maven/conf/settings.xml:/usr/local/maven/conf/settings.xml
- ./maven/apache-maven-3.9.9:/usr/local/maven/apache-maven-3.9.9
# JDK配置(可选)
# - ./jdk/jdk-17.0.15:/usr/local/jdk-17.0.15
environment:
- TZ=Asia/Shanghai
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false # 跳过初始化向导
restart: unless-stopped
volumes:
jenkins_home:
version: '3.9'
# 生产环境配置示例
services:
jenkins:
image: jenkins/jenkins:2.510-lts
container_name: jenkins-prod
privileged: true
user: root
ports:
- "8080:8080" # 生产环境使用标准端口
- "50000:50000"
volumes:
# 生产环境数据持久化
- /var/jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/local/bin/docker
# SSL证书挂载(可选)
- /etc/ssl/certs:/etc/ssl/certs:ro
environment:
- TZ=Asia/Shanghai
- JAVA_OPTS=-Xmx4g -Djenkins.install.runSetupWizard=false
restart: always
networks:
- jenkins-network
networks:
jenkins-network:
driver: bridge
🔧 配置参数详解
🕐 时区配置
/etc/localtime
:包含当前时区的具体时间数据/etc/timezone
:设置系统时区(Debian/Ubuntu)
💾 数据持久化
./jenkins_home:/var/jenkins_home
:持久化Jenkins配置、插件、任务等数据
🐳 Docker集成
/var/run/docker.sock
:暴露Docker daemon给容器,使Jenkins能执行Docker命令/usr/bin/docker
:挂载Docker CLI到容器内
📦 构建工具配置
- Maven settings.xml:统一仓库镜像、私服认证配置
- Maven发行版:避免重复下载,确保版本一致性
🌍 环境变量
TZ=Asia/Shanghai
:设置容器时区JAVA_OPTS
:禁用安装向导(如需向导可删除此配置)
🔐 初始化配置重要提示
如果启用了安装向导,使用以下命令获取初始密码:
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
安全提示:
- 初始密码仅在首次安装时使用
- 请及时更改默认管理员密码
- 建议配置双因子认证加强安全
🚀 启动Jenkins
在配置文件目录下执行以下命令启动Jenkins:
docker compose -f docker-compose-v1.0.yml up -d
启动成功示例:
WARN[0000] /work/jenkins/dev-ops/docker-compose-v1.0.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 13/13
✔ jenkins Pulled 85.1s
✔ cf05a52c0235 Pull complete 30.0s
✔ abe2f5ac32ce Pull complete 32.0s
✔ cc732753ed96 Pull complete 32.1s
# ... 更多镜像拉取日志
[+] Running 2/2
✔ Network dev-ops_default Created 0.1s
✔ Container jenkins Started
✅ 验证部署
看到 Container jenkins Started
表示Jenkins部署成功!
访问 http://你的服务器IP:9090
即可打开Jenkins界面
🔧 Jenkins配置
🌐 访问Jenkins
访问地址:http://你的服务器IP:9090
🔌 插件安装
步骤1:配置镜像源

- 进入
Manage Jenkins
>Manage Plugins
>Advanced
- 设置更新站点:
https://eastamerica.cloudflare.jenkins.io/current/update-center.json
- 点击
Submit
并重启Jenkins
步骤2:安装必要插件

🔌 必装插件清单
- Chinese (Simplified) - 中文汉化插件
- Maven Integration - Maven项目支持
- Git Plugin - Git版本控制
- Docker Plugin - Docker集成
- Publish Over SSH - SSH发布插件
⚠️ 插件安装注意事项
安装前检查:
- 确保网络连接稳定
- 检查磁盘空间是否足够
- 备份现有Jenkins配置
安装策略:
- 每个插件安装后都需要重启Jenkins才能生效
- 网络问题导致安装失败时,可重新搜索安装
- 建议一个一个安装,避免批量安装失败
- 如遇到依赖冲突,可尝试安装兼容性版本
常见问题解决:
- 插件下载失败:更换插件源或使用代理
- 插件冲突:检查插件版本兼容性
- 功能异常:清理浏览器缓存并重启
🛠️ 全局工具配置

进入 Manage Jenkins
> Global Tool Configuration
配置构建环境:
Name: Maven-3.9.9
MAVEN_HOME: /usr/local/maven/apache-maven-3.9.9
Name: JDK-17
JAVA_HOME: /usr/local/jdk-17.0.15
(如果挂载了JDK)
Name: Default
Path: /usr/bin/git
Name: Docker
Installation root: /usr/local/bin/docker
💡 配置提示
确保所有工具路径与Docker容器内的实际路径一致
🔑 凭证管理

进入 Manage Jenkins
> Manage Credentials
添加必要的凭证:
Git仓库凭证:
- Kind: Username with password
- Username: 你的Git用户名
- Password: 你的Git密码或Token
- ID: git-credentials(自定义)
SSH服务器凭证(可选):
- Kind: SSH Username with private key
- Username: 服务器用户名
- Private Key: SSH私钥内容
🔐 安全加强建议
凭证管理最佳实践:
Git仓库访问:
- 优先使用 Personal Access Token 而非密码
- 为Token设置最小权限原则(仅repo权限)
- 定期轮换Token(建议3-6个月)
SSH密钥管理:
- 使用独立的SSH密钥对,不复用个人密钥
- 为密钥设置强密码保护
- 定期更新SSH密钥
凭证存储:
- 使用Jenkins内置的加密存储
- 避免在脚本中硬编码敏感信息
- 为凭证设置清晰的描述和标签
访问控制:
- 定期审查凭证使用情况
- 删除不再使用的凭证
- 记录凭证访问日志
📝 创建项目任务
在Jenkins中,一个任务就是一条完整的CI/CD流水线。
🔧 任务配置

创建步骤:
- 点击
新建任务
- 输入任务名称
- 选择
构建一个自由风格的软件项目
- 点击
确定
💾 源码管理配置

在任务配置页面中配置Git仓库:
Git配置参数:
- Repository URL: 你的Git仓库地址
- Credentials: 选择之前配置的Git凭证
- Branch:
*/main
或*/master
📋 分支管理策略
推荐的分支策略:
- 生产环境: 使用
main
或master
分支 - 测试环境: 使用
develop
或test
分支 - 特性开发: 使用
feature/*
分支 - 热修复: 使用
hotfix/*
分支
最佳实践:
- 避免从开发分支直接部署到生产环境
- 使用Pull Request/Merge Request进行代码审查
- 配置分支保护规则,防止意外提交
📦 Maven构建配置

在 构建
部分添加 Invoke top-level Maven targets
:
Maven配置参数:
- Maven Version: 选择之前配置的Maven-3.9.9
- Goals:
clean install -Dmaven.test.skip=true
- Advanced Settings: 设置
Settings file path
为/usr/local/maven/conf/settings.xml
🔧 Maven Goals说明
clean install -Dmaven.test.skip=true
clean
: 清理乊次构建的产物install
: 编译、打包并安装到本地仓库-Dmaven.test.skip=true
: 跳过单元测试(可选)
🚀 部署Shell脚本
在 构建后操作
中添加 Execute shell
:
#!/bin/bash
# Jenkins 自动化部署脚本 - 基础版本
# 定义变量
APP_NAME="lxf-dev-tech-jenkins-app"
IMAGE_NAME="cactusli/lxf-dev-tech-jenkins-app"
CONTAINER_PORT="8091"
WORKSPACE_PATH="/var/jenkins_home/workspace/lxf-dev-tech-jenkins/lxf-dev-tech-jenkins-app"
# 清理旧容器和镜像
echo "🗑️ 清理旧资源..."
if [ "$(docker ps -a -q -f name=$APP_NAME)" ]; then
docker stop $APP_NAME
docker rm $APP_NAME
fi
if [ "$(docker images -q $IMAGE_NAME)" ]; then
docker rmi $IMAGE_NAME
fi
# 构建新镜像
echo "🔨 构建 Docker 镜像..."
cd $WORKSPACE_PATH
docker build -t $IMAGE_NAME .
# 运行新容器
echo "🚀 启动应用容器..."
docker run -itd -p $CONTAINER_PORT:$CONTAINER_PORT --name $APP_NAME $IMAGE_NAME
echo "✅ 部署完成!访问地址: http://服务器IP:$CONTAINER_PORT"
#!/bin/bash
# Jenkins 自动化部署脚本 - 增强版本
set -e # 遇到错误立即退出
# 定义变量
APP_NAME="lxf-dev-tech-jenkins-app"
IMAGE_NAME="cactusli/lxf-dev-tech-jenkins-app"
CONTAINER_PORT="8091"
WORKSPACE_PATH="/var/jenkins_home/workspace/lxf-dev-tech-jenkins/lxf-dev-tech-jenkins-app"
HEALTH_CHECK_URL="http://localhost:$CONTAINER_PORT/actuator/health"
MAX_WAIT_TIME=60
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 健康检查函数
health_check() {
local url=$1
local max_attempts=$2
local attempt=1
while [ $attempt -le $max_attempts ]; do
log "健康检查第 $attempt 次尝试..."
if curl -f -s $url > /dev/null; then
log "✅ 应用健康检查通过!"
return 0
fi
sleep 3
attempt=$((attempt + 1))
done
log "❌ 应用健康检查失败!"
return 1
}
log "🚀 开始部署流程..."
# 备份当前运行的容器(如果存在)
if [ "$(docker ps -q -f name=$APP_NAME)" ]; then
log "📦 备份当前容器为 ${APP_NAME}_backup..."
docker rename $APP_NAME ${APP_NAME}_backup
fi
# 清理旧镜像
log "🗑️ 清理旧镜像..."
if [ "$(docker images -q $IMAGE_NAME)" ]; then
docker rmi $IMAGE_NAME || true
fi
# 构建新镜像
log "🔨 构建新 Docker 镜像..."
cd $WORKSPACE_PATH
docker build -t $IMAGE_NAME .
# 运行新容器
log "🚀 启动新应用容器..."
docker run -itd \
-p $CONTAINER_PORT:$CONTAINER_PORT \
--name $APP_NAME \
--restart unless-stopped \
--health-cmd="curl -f http://localhost:$CONTAINER_PORT/actuator/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
$IMAGE_NAME
# 等待容器启动
log "⏳ 等待应用启动..."
sleep 10
# 执行健康检查
if health_check $HEALTH_CHECK_URL 20; then
# 清理备份容器
if [ "$(docker ps -a -q -f name=${APP_NAME}_backup)" ]; then
log "🗑️ 清理备份容器..."
docker rm -f ${APP_NAME}_backup
fi
log "🎉 部署成功完成!访问地址: http://服务器IP:$CONTAINER_PORT"
else
log "⚠️ 新容器启动异常,尝试回滚..."
docker rm -f $APP_NAME || true
if [ "$(docker ps -a -q -f name=${APP_NAME}_backup)" ]; then
docker rename ${APP_NAME}_backup $APP_NAME
docker start $APP_NAME
log "🔄 已回滚到前一版本"
fi
exit 1
fi

💡 高级部署策略
熟练使用后,可以探索更多部署策略:
- 使用 Docker Compose 统一管理
- 实现蓝绿部署策略
- 集成Kubernetes部署
🧪 测试验证
🎯 准备测试项目
项目地址:https://github.com/lixuanfengs/lxf-dev-tech/tree/main/lxf-dev-tech-jenkins/ 可以下载下来导入到 gitee 、gitlab 等仓库进行使用。
package cn.cactusli.jenkins.app;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
import java.io.IOException;
@SpringBootApplication
@RestController
@RequestMapping("/api/")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* http://localhost:8091/api/test
*/
@RequestMapping(value = "/test", method = RequestMethod.GET)
public ResponseBodyEmitter test(HttpServletResponse response) {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
String[] words = new String[]{"嗨,臭宝。\r\n", "恭喜💐 ", "你的", " Jenkins ", "部", "署", "测", "试", "成", "功", "了啦🌶!","\r\nBy 仙人球 https://cactusli.net"};
new Thread(() -> {
for (String word : words) {
try {
emitter.send(word);
Thread.sleep(250);
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
return emitter;
}
}
- 项目中提供了 RestApi; http://192.168.1.23:8091/api/test
🔄 CI/CD 流水线执行
访问地址:http://你的服务器IP:9090/job/你的任务名/

构建步骤:
- 📎 点击
立即构建
按钮 - 🔄 观察构建进度和日志输出
- ✅ 等待构建完成(显示蓝色圆点表示成功)
📊 构建状态说明
- 🕵️ 灰色:从未执行
- 🟠 黄色:构建中
- 🔵 蓝色:构建成功
- 🔴 红色:构建失败
✅ 验证部署结果
访问地址:http://你的服务器IP:8091/api/test

::: success 🎉 恭喜!
看到上图的响应结果,表示你已经成功搭建了完整的 Jenkins CI/CD 流水线!
你现在已经掌握:
- ✅ Docker 环境下部署 Jenkins
- ✅ 配置完整的 CI/CD 流水线
- ✅ 自动化构建、打包、部署流程
- ✅ Spring Boot 项目的自动化部署
:::
🚀 高级扩展
在掌握了基础的Jenkins CI/CD配置后,你可以探索更高级的功能和部署策略。以下是一个完整的Pipeline脚本示例,用于自动化构建、打包和部署两个前端项目。
🔄 多项目Pipeline脚本部署
这个Jenkins Pipeline脚本演示了如何同时构建和部署多个前端项目(Vue.js),并将它们整合到一个Nginx容器中:
📋 Pipeline功能特性
🎯 主要功能:
- 并行检出多个Git仓库代码
- 并行安装和构建多个前端项目
- 构建统一的Nginx Docker镜像
- 推送到私有Docker仓库
- 远程服务器滚动部署
🔧 技术栈:
- Jenkins Pipeline (Groovy)
- Node.js 18.20.8
- Docker & Docker Compose
- Nginx
- SSH远程部署
pipeline {
agent any
tools {
nodejs 'NodeJS-18.20.8' // 假设Jenkins已配置NodeJS工具
}
environment {
CACTUS_UI_REPO = 'http://192.168.1.19:8929/root/cactus-ui-admin-vue3.git'
SHENZHEN_WEB_REPO = 'http://192.168.1.19:8929/root/shenzhen-web.git'
SSH_CONFIG_NAME = 'sg-server' // Jenkins "Publish over SSH" 中配置的服务器名称
GIT_CREDENTIALS_ID = 'a71a582f-eb89-4a76-9ef0-7e572e572bad'
CACTUS_UI_DIR = 'cactus-ui-admin-vue3'
SHENZHEN_WEB_DIR = 'shenzhen-web'
DOCKER_CREDENTIALS_ID = '68b87c8d-77bc-4d4b-b09b-0b24bc7d7fa2'
// Docker 镜像名称 - 根据您的实际情况修改
DOCKER_IMAGE_NAME = "218.249.73.244:8443/cactuslixf/sg-nginx" // 例如:yourdockerhubusername/multi-static-app
DOCKER_IMAGE_TAG = "latest" // 例如:latest
// Docker Compose 相关配置 (需要在远程服务器 sg-server 上实际存在)
// 远程服务器上 docker-compose.yml 文件所在的绝对路径
REMOTE_COMPOSE_PROJECT_DIR = "/work/projects/cactus-pipeline/" // <<== 【用户必须修改】
// docker-compose.yml 文件中定义的 Nginx 服务名称
REMOTE_NGINX_SERVICE_NAME = "sg-nginx" // <<== 【用户必须修改】
}
stages {
stage('Checkout Projects') {
parallel {
stage('Checkout Cactus UI Project') {
steps {
dir("${env.CACTUS_UI_DIR}") {
git url: "${CACTUS_UI_REPO}", branch: 'main', credentialsId: "${GIT_CREDENTIALS_ID}"
}
}
}
stage('Checkout Shenzhen Web Project') {
steps {
dir("${env.SHENZHEN_WEB_DIR}") {
git url: "${SHENZHEN_WEB_REPO}", branch: 'master', credentialsId: "${GIT_CREDENTIALS_ID}"
}
}
}
}
}
stage('Install Project Dependencies') {
parallel {
stage('Install Cactus UI Dependencies') {
steps {
dir("${env.CACTUS_UI_DIR}") {
//sh 'rm -rf node_modules'
sh 'npm config set registry https://registry.npmmirror.com'
sh 'npm install'
}
}
}
stage('Install Shenzhen Web Dependencies') {
steps {
dir("${env.SHENZHEN_WEB_DIR}") {
//sh 'rm -rf node_modules'
sh 'npm config set registry https://registry.npmmirror.com'
sh 'npm install'
}
}
}
}
}
stage('Build Projects') {
parallel {
stage('Build Cactus UI Project') {
steps {
dir("${env.CACTUS_UI_DIR}") {
// 重要: 确保此构建命令生成的应用能够正确处理其运行时的基础路径 (base path)
// 例如,如果最终通过 Nginx 以 /cactus-ui/ 访问,Vue app 的 publicPath 需要配置为 /cactus-ui/
// sh 'rm -rf dist-dev'
sh 'npm run build:dev'
}
}
}
stage('Build Shenzhen Web Project') {
steps {
dir("${env.SHENZHEN_WEB_DIR}") {
// 重要: 同上,确保应用构建时考虑了最终的访问路径
// sh 'rm -rf dist'
sh 'npm run build'
}
}
}
}
}
stage('Prepare Docker Context') {
steps {
script {
sh 'mkdir -p docker-context/cactus-ui-dist'
sh 'mkdir -p docker-context/shenzhen-web-dist'
sh "cp -R ${env.CACTUS_UI_DIR}/dist-dev/* docker-context/cactus-ui-dist/"
sh "cp -R ${env.SHENZHEN_WEB_DIR}/dist/* docker-context/shenzhen-web-dist/"
// 写入简化后的 Dockerfile (不包含 Nginx 配置)
writeFile file: 'docker-context/Dockerfile', text: """\
FROM nginx:alpine
RUN mkdir -p /usr/share/nginx/html/cactus-ui-admin && \
mkdir -p /usr/share/nginx/html/shenzhen-web
COPY cactus-ui-dist /usr/share/nginx/html/v2_admin/
COPY shenzhen-web-dist /usr/share/nginx/html/v2/
EXPOSE 80
EXPOSE 443
"""
echo "Dockerfile prepared in docker-context directory."
}
}
}
stage('Build and Push Docker Image') {
steps {
script {
def fullImageNameWithTag = "${env.DOCKER_IMAGE_NAME}:${env.DOCKER_IMAGE_TAG}"
def fullImageNameLatest = "${env.DOCKER_IMAGE_NAME}:latest"
echo "Building Docker image: ${fullImageNameWithTag} and ${fullImageNameLatest}"
dir('docker-context') {
sh "docker build -t ${fullImageNameWithTag} ."
sh "docker tag ${fullImageNameWithTag} ${fullImageNameLatest}"
}
// (可选) 推送到 Docker 仓库
// 请确保在此之前已经配置了 Docker 仓库的登录凭据
withCredentials([usernamePassword(credentialsId: "${env.DOCKER_CREDENTIALS_ID}", passwordVariable: 'DOCKER_PASS', usernameVariable: 'DOCKER_USER')]) {
sh "echo \"\${DOCKER_PASS}\" | docker login -u \"\${DOCKER_USER}\" --password-stdin 218.249.73.244:8443"
// 替换为你的仓库地址
}
sh "docker push ${fullImageNameWithTag}"
sh "docker push ${fullImageNameLatest}"
echo "Docker images pushed: ${fullImageNameWithTag}, ${fullImageNameLatest}"
echo "Docker images built: ${fullImageNameWithTag}, ${fullImageNameLatest}"
}
}
}
stage('Deploy via Docker Compose') {
steps {
echo "Deploying service ${env.REMOTE_NGINX_SERVICE_NAME} on ${env.SSH_CONFIG_NAME} using image ${env.DOCKER_IMAGE_NAME}:latest"
sshPublisher(publishers: [
sshPublisherDesc(
configName: "${SSH_CONFIG_NAME}",
transfers: [
sshTransfer(
// 指定本地文件或目录路径
//sourceFiles: "docker-compose.yml, docker-context/**",
// 远程服务器的目标目录
//remoteDirectory: "${REMOTE_COMPOSE_PROJECT_DIR}",
// 执行的命令(可选)
execCommand: """
set -x # 执行命令前打印命令
cd ${env.REMOTE_COMPOSE_PROJECT_DIR}
docker compose -f cactus-server-pipeline-compose.yml down
docker compose -f cactus-server-pipeline-compose.yml pull
docker compose -f cactus-server-pipeline-compose.yml up -d
"""
)
]
)
])
}
}
}
post {
success {
echo "Pipeline completed successfully! Docker Image: ${env.DOCKER_IMAGE_NAME}:${env.DOCKER_IMAGE_TAG} and :latest"
}
failure {
echo 'Pipeline failed!'
}
always {
echo 'Cleaning up Jenkins workspace...'
sh 'rm -rf docker-context' // 清理 Docker 构建上下文目录
}
}
}
🔍 流水线阶段详解:
并行检出代码:
- 从 Git 仓库同时拉取多个项目代码
- 使用配置的 Git 凭据进行身份验证
- 支持不同分支(main/master)
并行安装依赖:
- 使用国内npm镜像源加速下载
- 在独立目录中执行
npm install
- 并行处理多个项目提高效率
并行构建项目:
cactus-ui-admin-vue3
:npm run build:dev
shenzhen-web
:npm run build
- 生成的静态文件将用于Nginx部署
整合部署:
- 创建统一的Docker构建上下文
- 基于
nginx:alpine
构建轻量镜像 - 推送到私有Docker仓库
自动化部署:
- SSH连接远程服务器
- 使用Docker Compose管理容器
- 实现零停机更新
📄 Docker Compose配置示例:
version: '3.8'
services:
sg-nginx:
image: 218.249.73.244:8443/cactuslixf/sg-nginx:latest
container_name: sg-nginx
ports:
- "8081:80" # HTTP端口
- "4431:443" # HTTPS端口
environment:
- TZ=Asia/Shanghai
volumes:
# Nginx配置文件挂载
- /work/projects/cactus-pipeline/nginx/nginx.conf:/etc/nginx/nginx.conf
- /work/projects/cactus-pipeline/nginx/conf.d:/etc/nginx/conf.d
- /work/projects/cactus-pipeline/nginx/logs:/var/log/nginx
- /work/projects/cactus-pipeline/nginx/cert:/etc/nginx/cert
# 静态文件在镜像中,不需要挂载
# - /work/projects/cactus-pipeline/nginx/html:/usr/share/nginx/html
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- sg_network
networks:
sg_network:
driver: bridge
💡 配置亮点
- 端口映射: 支持HTTP(80)和HTTPS(443)
- 日志轮转: 限制日志文件大小和数量
- 网络隔离: 使用自定义桥接网络
- 自动重启: 容器异常退出后自动重启
🔄 后期处理阶段:
📊 流水线状态管理
- ✅ 成功: 输出成功信息,包含Docker镜像名称和标签
- ❌ 失败: 记录失败原因,方便问题排查
- 🗑️ 清理: 自动清理构建中产的临时文件
🔧 Node.js环境配置
环境要求
由于Pipeline脚本使用了Node.js,需要在Jenkins中配置相应的全局工具。
方法1:容器内安装Node.js
进入Jenkins容器并安装Node.js:
# 进入Jenkins容器
docker exec -it jenkins bash
# 安装Node.js 18.x
apt-get update
apt-get install -y curl
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
apt-get install -y nodejs
# 验证安装
node -v
npm -v
方法2:使用Docker挂载
在docker-compose.yml中添加Node.js挂载:
volumes:
- /usr/local/node:/usr/local/node # 挂载宿主机Node.js
jenkins 全局工具配置nodejs:

🏗️ 后端项目部署配置
源码管理:

编译 Build:

Post Steps:


SSH Publishers(需要在插件管理安装 SSH Publisher),然后在系统管理
中配置远程 SSH Servers:


滚动升级后端项目执行脚本:
#!/bin/bash
# 变量定义
COMPOSE_FILE="/work/projects/cactus-server-all/sg-cactus-server.yml"
NGINX_CONF="/work/projects/cactus-pipeline/nginx/nginx.conf"
NGINX_CONTAINER="sg-nginx"
BACKEND_1="192.168.11.25:58080"
BACKEND_2="192.168.11.25:58081"
NEW_IMAGE="218.249.73.244:8443/cactuslixf/cactus-server:latest"
LOG_FILE="/work/projects/cactus-server-all/upgrade.log"
# 确保日志目录存在
mkdir -p $(dirname $LOG_FILE)
# 日志函数:添加时间戳并写入日志文件
log() {
local message="$1"
echo "$(date '+%Y-%m-%d %H:%M:%S') $message" | tee -a $LOG_FILE
}
# 备份当前配置
log "备份当前配置文件..."
cp $COMPOSE_FILE ${COMPOSE_FILE}.bak 2>&1 | tee -a $LOG_FILE
cp $NGINX_CONF ${NGINX_CONF}.bak 2>&1 | tee -a $LOG_FILE
# 拉取新镜像
log "拉取新镜像..."
docker pull $NEW_IMAGE 2>&1 | tee -a $LOG_FILE
# 函数:更新 Nginx 配置
update_nginx_config() {
local backend=$1
local weight=$2
log "更新 Nginx 配置: $backend weight=$weight"
sed -i "s/server $backend weight=[0-9];/server $backend weight=$weight;/" $NGINX_CONF
docker exec $NGINX_CONTAINER nginx -t 2>&1 | tee -a $LOG_FILE && \
docker exec $NGINX_CONTAINER nginx -s reload 2>&1 | tee -a $LOG_FILE
}
# 升级第一个实例
log "升级第一个实例 ($BACKEND_1)..."
# 将流量完全导向第二个实例
update_nginx_config $BACKEND_1 0
update_nginx_config $BACKEND_2 1
# 停止并更新第一个实例
log "停止并更新第一个实例..."
docker compose -f $COMPOSE_FILE stop sg-cactus-server-1 2>&1 | tee -a $LOG_FILE
docker compose -f $COMPOSE_FILE up -d sg-cactus-server-1 2>&1 | tee -a $LOG_FILE
# 验证新实例运行
log "验证 $BACKEND_1 运行状态..."
sleep 10
if docker compose -f $COMPOSE_FILE logs sg-cactus-server-1 2>&1 | grep -q "started"; then
log "$BACKEND_1 升级成功"
else
log "ERROR: $BACKEND_1 升级失败,恢复 Nginx 配置..."
cp ${NGINX_CONF}.bak $NGINX_CONF 2>&1 | tee -a $LOG_FILE
docker exec $NGINX_CONTAINER nginx -s reload 2>&1 | tee -a $LOG_FILE
exit 1
fi
# 恢复流量平衡
log "恢复流量平衡..."
update_nginx_config $BACKEND_1 1
update_nginx_config $BACKEND_2 1
# 升级第二个实例
log "升级第二个实例 ($BACKEND_2)..."
# 将流量完全导向第一个实例
update_nginx_config $BACKEND_2 0
update_nginx_config $BACKEND_1 1
# 停止并更新第二个实例
log "停止并更新第二个实例..."
docker compose -f $COMPOSE_FILE stop sg-cactus-server-2 2>&1 | tee -a $LOG_FILE
docker compose -f $COMPOSE_FILE up -d sg-cactus-server-2 2>&1 | tee -a $LOG_FILE
# 验证新实例运行
log "验证 $BACKEND_2 运行状态..."
sleep 10
if docker compose -f $COMPOSE_FILE logs sg-cactus-server-2 2>&1 | grep -q "started"; then
log "$BACKEND_2 升级成功"
else
log "ERROR: $BACKEND_2 升级失败,恢复 Nginx 配置..."
cp ${NGINX_CONF}.bak $NGINX_CONF 2>&1 | tee -a $LOG_FILE
docker exec $NGINX_CONTAINER nginx -s reload 2>&1 | tee -a $LOG_FILE
exit 1
fi
# 恢复流量平衡
log "恢复流量平衡..."
update_nginx_config $BACKEND_1 1
update_nginx_config $BACKEND_2 1
# 清理
log "清理旧镜像..."
docker image prune -f 2>&1 | tee -a $LOG_FILE
log "升级完成!"
远程机器上的 sg-cactus-server.yml
:
version: '3.8'
services:
sg-cactus-server-1:
image: 218.249.73.244:8443/cactuslixf/cactus-server
container_name: sg-cactus-server-1
privileged: true
environment:
- SPRING_PROFILES_ACTIVE=dev
- JAVA_OPTS=-Xms4096m -Xmx14336m
- TZ=Asia/Shanghai
ports:
- 58080:48080
volumes:
- /work/projects/cactus-server-all/sg-cactus-server-1:/root/logs/
restart: unless-stopped
networks:
- sg_network
sg-cactus-server-2:
image: 218.249.73.244:8443/cactuslixf/cactus-server
container_name: sg-cactus-server-2
privileged: true
environment:
- SPRING_PROFILES_ACTIVE=dev
- JAVA_OPTS=-Xms4096m -Xmx14336m
- TZ=Asia/Shanghai
ports:
- 58081:48080
volumes:
- /work/projects/cactus-server-all/sg-cactus-server-2:/root/logs/
restart: unless-stopped
networks:
- sg_network
networks:
sg_network:
driver: bridge
📚 总结与最佳实践
通过本教程,你已经掌握了使用Docker部署Jenkins并配置CI/CD流水线的完整流程。以下是一些最佳实践建议:
✅ 成功要点
🎯 核心收获
- ✅ 容器化部署: 使用Docker简化Jenkins环境管理
- ✅ 自动化流水线: 实现从代码到部署的全自动化
- ✅ 多项目支持: 支持并行构建多个项目
- ✅ 滚动更新: 零停机部署策略
🔧 性能优化
📈 优化建议
构建性能优化:
- 使用国内镜像源加速依赖下载
- 启用Docker层缓存机制
- 合理配置Jenkins构建代理
资源管理:
- 定期清理无用的Docker镜像
- 配置适当的JVM内存参数
- 设置合理的构建超时时间
安全加固:
- 定期更新Jenkins和插件
- 使用非root用户运行容器
- 配置HTTPS访问
🚀 进阶扩展
准备好迎接更高级的挑战了吗?
🎓 后续学习路径
- Kubernetes集成: 将Jenkins部署到K8s集群
- 多分支流水线: 支持特性分支自动部署
- 质量门禁: 集成代码质量检查工具
- 监控告警: 配置构建状态通知
- 蓝绿部署: 实现更安全的发布策略
📖 相关资源
🎉 恭喜你已经成为Jenkins CI/CD专家!
继续探索更多DevOps实践,打造更高效的开发流水线!