在企业级项目中,多服务器协同部署是常态 ------Nginx、Tomcat、Redis、MySQL 各司其职,通过网络通信构建完整服务链路。但对于学习者而言,如何在一台虚拟机内复现这一流程,用 Jenkins 实现后端自动化部署、用 Nginx 搞定前端静态资源与反向代理?本文将手把手带你打通从环境配置到前后端联调的全流程,让复杂部署变得可操作、可复现。
一、 项目部署思路
在企业正常的项目部署中,可能会涉及多台服务器共同使用,如图中的Nginx、Tomcat、Redis、MySQL都会有对应的单独的服务器,让各个服务器进行互相通信即可完成部署
- 其中的微信小程序必须租腾讯的服务器才能完成部署,我们涉及不到
- 在学习阶段,我们所有的服务都使用同一个虚拟机来完成项目的部署

二. 准备Jenkins环境
2.1 基础环境要求
软件环境:
软件 | 版本 | 安装方式 |
---|---|---|
docker | Docker version 20.10.11 | shell |
docker-compose | 1.29.1 | shell |
Java JDK | 11.0.19 | shell |
Maven | 3.6.1 | shell |
Git | 1.8.3.1 | shell |
Redis | 7.2.4 | docker |
MySQL | 8.0.29 | docker |
Nginx | 1.25.5 | docker |
附:安装git、jdk、maven的shell脚本
bash
#!/bin/bash
# 安装 Git
echo "开始安装 Git..."
yum install git -y
echo "Git安装完成"
echo `git --version`
# 下载并安装 JDK 11
echo "开始下载并安装 JDK 11..."
#JDK_URL="https://download.oracle.com/otn/java/jdk/11.0.21%2B9/8819d0447e4d41b3bd1d9e1007728d17/jdk-11.0.21_linux-x64_bin.tar.gz" # 替换为实际的JDK下载地址
#wget $JDK_URL
# 本地将JDK上传到Linux服务器
tar -xzf jdk-11.0.21_linux-x64_bin.tar.gz
mv jdk-11.0.21 /usr/local/src/
chmod +x /usr/local/src/jdk-11.0.21/bin/*
# 设置环境变量
echo "export JAVA_HOME=/usr/local/src/jdk-11.0.21" >> /etc/profile
echo "export PATH=$PATH:$JAVA_HOME/bin" >> /etc/profile
source /etc/profile
echo `java -version`
echo "JDK 11安装完成"
# # 下载并安装 Maven
echo "开始下载并安装 Maven..."
MAVEN_URL="https://dlcdn.apache.org/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz" # 替换为实际的Maven下载地址
cd /usr/local/src
wget $MAVEN_URL
tar -xzf apache-maven-3.8.8-bin.tar.gz
mv apache-maven-3.8.8 /usr/local/src/
echo "Maven安装完成"
# 设置环境变量
echo "export MAVEN_HOME=/usr/local/src/apache-maven-3.8.8" >> /etc/profile
echo "export PATH=$PATH:$MAVEN_HOME/bin" >> /etc/profile
source /etc/profile
echo `mvn -version`
echo "JDK 11和Maven安装完成"
2.2 安装Jenkins
参考文档mfg6vszyp7.feishu.cn/wiki/XiHswm...
目前在提供的虚拟机中已经使用docker安装了jenkins,启动命令:docker start jenkins

访问地址:http://192.168.100.168:8000/
登录后的效果:

三. 部署后端项目
3.1 多环境说明
在项目开发部署的过程中,一般都会有三套项目环境
- Development :开发环境
- Production :生产环境
- Test :测试环境
例如:开发环境的mysql连接的是本地,生产环境需要连接线上的mysql环境
3.1.1 汇总配置文件
操作步骤:
- 将
application.yml
与application-druid.yml
所有内容合并成一个新的配置文件:application-dev.yml
- 将汇总后的配置文件中
spring.profiles.active=druid
这个配置项删除掉
最终的application-dev.yaml内容如下:
yaml
# 项目相关配置
ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.8.8
# 版权年份
copyrightYear: 2024
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
profile: D:/ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数,默认为100
accept-count: 1000
threads:
# tomcat最大线程数,默认为200
max: 800
# Tomcat启动初始化的线程数,默认值10
min-spare: 100
# 日志配置
logging:
level:
com.zzyl: debug
org.springframework: warn
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间(默认10分钟)
lockTime: 10
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# redis 配置
redis:
# 地址
host: 192.168.100.168
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: 123456
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://192.168.100.168:3306/zzyl?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: heima123
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 30
# MyBatisPlus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.zzyl.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 全局配置
global-config:
db-config:
id-type: auto #id生成策略为自增
configuration:
map-underscore-to-camel-case: true #字段与属性,自动转换为驼峰命名
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
aliyun:
oss:
endpoint: https://oss-cn-beijing.aliyuncs.com
bucketName: itheima-liuyp
baidu:
accessKey: ALTAKEtNuKDmzxnkR7umWk1U4y
secretKey: 2e475c56f878413c81e85aac94cb8fb6
qianfanModel: ERNIE-4.0-8K-Preview
3.1.2 准备多环境配置文件
将刚刚的application-dev.yml
再复制2份,分别命名为:application-test.yml
和application-prod.yml
。
最终有3个环境的配置文件,分别是:
- application-dev.yml 开发环境
- application-test.yml 测试环境
- application-prod.yml 正式环境(为了能演示出不同环境配置生效了,我们把prod环境的配置文件中端口改为
9000
)
再创建一个application.yml
作为默认配置文件,在配置文件中设置激活哪套环境:
yaml
spring:
profiles:
active: prod #prod是环境标识,表示要激活配置文件application-prod.yml

3.1.2 测试
直接在idea里运行启动类RuoYiApplication,查看能否正常启动,且日志中显示端口为9000
3.2 准备项目代码
3.2.1 Jenkins流水线介绍
Jenkins 流水线配置是 Jenkins 中用于定义和执行自动化构建、测试和部署过程的一种方式

在声明式流水线中,整个流水线过程被定义在一个 pipeline
块中,该块包含了流水线执行所需的所有指令和阶段。
声明式流水线的Jenkinsfile
文件(Groovy脚本)基本结构如下:
typescript
pipeline {
agent any // 指定流水线运行的节点,any 表示任何可用的节点
stages {
stage('Stage Name1') { // 定义阶段
steps {
// 定义在该阶段执行的步骤
echo 'Hello, World!'
}
},
stage('Stage Name2') { // 定义阶段
steps {
// 定义在该阶段执行的步骤
echo 'Hello, World!'
}
}
}
}
主要指令和阶段:
- agent:指定流水线或特定阶段在哪个节点上执行。
- stages:包含流水线中的所有阶段(stage),阶段是流水线的主要分组单元。
- stage:定义一个阶段,阶段内可以包含一个或多个步骤。
- steps:定义在某个阶段内执行的步骤,步骤是构建过程中的具体操作。
- post:定义在所有阶段完成后执行的操作,可以基于不同的条件(如成功、失败、总是)来执行。
- environment:定义流水线中的环境变量。
- options:定义全局选项和配置,如超时设置、并行执行等。
- parameters:定义流水线的参数,用于接收用户输入。
- triggers:定义触发流水线执行的条件或事件,如定时触发、代码推送触发等。
3.2.2 准备Jenkinsfile 文件
新建 Jenkinsfile 文件并上传到代码仓库中, 在Jenkins中完成流水线配置
将 Jenkinsfile 文件上传到Git做为共享配置使用,存储到**项目根目录
**,命名必须是:Jenkinsfile
文件内容如下:
如果gitee仓库非开源,则需要在脚本中准备gitee的账号密码。
- 在
stage('拉取Git代码')
的checkout
中,找到userRemoteConfigs
,添加credentialsId: 'Gitee_ID'
。- 在后续Jenkins中新增任务时,要为gitee指定账号密码,并设置id为
Gitee_ID
bash
pipeline {
agent any
options {
timestamps()
}
tools {
maven 'maven'
jdk 'jdk11'
}
stages {
stage('清除工作空间') {
steps {
cleanWs()
}
}
stage('拉取Git代码') {
steps {
echo "正在拉取代码..."
echo "当前分支:${GIT_TAG},当前服务:${services}"
checkout([$class: 'GitSCM',
branches: [[name: GIT_TAG]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[ url: GIT_URL]]
])
sh "pwd"
}
}
stage('重新Maven打包') {
steps {
script {
echo "正在执行maven打包...."
sh "mvn clean install -DskipTests"
}
}
}
stage('存在容器处理'){
steps {
script {
for (ws in services.tokenize(",")) {
def containerExists = sh(script: "docker ps -a --filter 'name=${ws}' --format '{{.Names}}' | grep -w '${ws}'", returnStatus: true) == 0
if (containerExists) {
echo "容器${ws}存在"
sh "docker rm -f ${ws}"
echo "容器${ws}已删除"
} else {
echo "容器${ws}不存在"
}
}
}
}
}
stage('重新构建镜像') {
steps {
echo "当前打镜像tag:${DOCKER_TAG}"
script {
for (ds in services.tokenize(",")) {
def imageExists = sh(script: "docker images ${ds}:${DOCKER_TAG} --format '{{.ID}}' | grep -q .", returnStatus: true) == 0
if (imageExists) {
echo "镜像${ds}:${DOCKER_TAG}存在"
def imageId = sh(script: "docker images ${ds}:${DOCKER_TAG} --format '{{.ID}}'", returnStdout: true).trim()
echo "获取镜像ID:${imageId}"
sh "docker rmi -f ${imageId}"
echo "镜像${ds}:${DOCKER_TAG}已删除"
} else {
echo "镜像${ds}:${DOCKER_TAG}不存在"
}
sh "pwd"
echo "进入target目录执行镜像打包......"
sh "cd ./${ds}/target/ && docker build -t ${ds}:${DOCKER_TAG} -f ../Dockerfile ."
}
}
}
}
stage('部署服务'){
steps {
script {
for (ws in services.tokenize(",")) {
sh "pwd"
sh "cd `pwd`"
echo "部署升级:${ws}服务"
sh "chmod +x ./${ws}/deploy.sh && sh ./${ws}/deploy.sh ${ws} ${DOCKER_TAG}"
}
}
}
}
}
post {
always {
echo '任务构建完毕'
}
}
}
3.2.3 项目中准备文件
在部署的项目模块(zzyl-admin)目录下 新增两个文件:
- Dockerfile
- deploy.sh
其中Dockerfile内容:
bash
# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# OSS配置
ENV OSS_ACCESS_KEY_ID LTAI5tN2ueyxxWtbEvJD949T
ENV OSS_ACCESS_KEY_SECRET dbbXOf1Mj8YwtevxvO1pOMmTflfzpA
# 拷贝jar包
COPY zzyl-admin.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]
deploy.sh文件内容:
bash
#!/bin/bash
# 容器名称
container_name=$1
# 镜像名称
image_name=$1
# 镜像tag
image_tag=$2
# 判断容器是否存在
if docker ps -a | grep $container_name | awk '{print $1}'; then
echo "容器 $container_name 存在"
if docker ps | grep $container_name | awk '{print $1}';then
echo "关闭正在运行的容器 $container_name"
docker stop `docker ps | grep $container_name | awk '{print $1}'`
else
echo "容器 $container_name 都已关闭"
fi
# 删除容器
echo "删除容器 $container_name"
docker rm `docker ps -a | grep $container_name | awk '{print $1}'`
else
echo "容器 $container_name 不存在"
fi
# 启动容器
echo "启动容器 $container_name"
if [ $container_name = "zzyl-admin" ]; then
docker run -d --restart=always --name $container_name -v /usr/local/zzyl-admin/logs:/home/ruoyi/logs -p 9000:9000 $image_name:$image_tag
fi
最终的代码结构

3.2.4 代码合并
务必要将代码提交并合并到master分支名,然后推送到远程仓库

3.4 准备Jenkins任务
3.4.1 新建任务

3.4.2 输入任务描述

3.4.3 参数化配置
准备参数services

定义服务列表:services,多个服务建议用 "," 隔开

准备参数GIT_URL
定义参数:GIT_URL

准备参数GIT_TAG
定义代码分支参数:GIT_TAG

准备参数Docker_TAG
设置Docker打包镜像版本 Docker_TAG:

3.4.4 流水线的配置
从代码仓库中读取Jenkinsfile文件

点击应用和保存,基础设置完成,最终效果可以查看首页:

点击 zzyl-admin 进入任务详情查看:

3.5 启动部署任务
3.5.1 部署项目
打开jenkins,找到刚才创建的项目,选择参数化构建,选择需要配置,如下图,点击Build开始构建

3.5.2 查看日志

3.5.3 查看docker容器
当项目部署成功之后,可以在docker中查看是否启动了容器:
3.6 前后端测试
我们可以在本地的前端代码中,修改访问后端的地址,来验证服务是否可以正常访问。
注意:
- 因为后端项目我们使用Jenkins部署到了服务器上,并且设置了生产环境激活application-prod.yml文件,其中配置的端口是9000
- 所以前端向后端发请求时,配置的后端地址中,ip是服务器ip,端口是9000

如果前端项目启动后,可以正常访问,并且可以获取数据,则表示成功

四. 部署前端项目
前端项目我们可以采用nginx进行部署,需要使用nginx中的两个特性,分别是静态服务器 和反向代理
- 首先我们把目前的vue项目进行打包,打包成静态资源
- 使用docker创建nginx容器
- 部署静态资源(前端打包之后的静态资源)
- 配置反向代理服务访问

4.1 多环境打包
在前端项目中也有多环境配置,如下面的三个文件,不同的文件对应了不同的环境配置项
- development 开发环境
- production 正式环境
- staging 预发布环境

指定环境打包,打包命令:npm run build:prod
(选择了正式环境)
- 打包成功之后,会在项目的根目录下生成一个文件夹
dist
- dist目录中存储的就是打包之后的静态文件,会压缩,存储较小,部署使用这里面内容
4.2 创建nginx容器
-
准备目录,创建容器需要做数据卷挂载
bashmkdir -p /usr/local/zzyl-vue/html mkdir -p /usr/local/zzyl-vue/conf
-
创建并启动容器:
bashdocker run -d \ --name zzyl-vue \ -v /usr/local/zzyl-vue/html:/usr/share/nginx/html \ -v /usr/local/zzyl-vue/conf:/etc/nginx/conf.d \ -v /usr/local/zzyl-vue/logs:/var/log/nginx \ -p 80:80 \ nginx:latest
4.3 部署前端
- 把前端项目打包之后的文件上传到服务器
/usr/local/zzyl-vue/html
目录中,注意:要连带dist目录一起上传,最终效果如下:

-
配置静态部署和反向代理
将当天资料中的zzyl-vue.conf文件上传到Linux的
/usr/local/zzyl-vue/conf
目录下。内容如下:bash# 配置代理服务 serve的地址为:中州养老后台管理服务ip+端口 upstream heima-admin{ server 192.168.100.168:9000; } server { listen 80; # 访问静态服务 location / { # 前端静态文件的目录 root /usr/share/nginx/html/dist/; index index.html; } # 访问后端服务接口 注意目前是prod-api(正式环境) location /prod-api/ { proxy_pass http://heima-admin/; # 访问代理服务 proxy_set_header HOST $host; # 不改变源请求头的值 proxy_pass_request_body on; #开启获取请求体 proxy_pass_request_headers on; #开启获取请求头 proxy_set_header X-Real-IP $remote_addr; # 记录真实发出请求的客户端IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #记录代理信息 } }
配置完成后,重启nginx容器 docker restart zzyl-vue
,打开浏览器访问http://192.168.100.168:80 ,没有问题则证明部署成功
⚡️当前端项目与后端项目的部署过程,全程测试完成以后,可以关闭虚拟机里的Jenkins、zzyl-admin后端服务容器、zzyl-vue前端服务容器
至此,从后端多环境配置、Jenkins 流水线构建,到前端打包部署、Nginx 反向代理,我们在单台虚拟机内完成了企业级项目部署的核心环节。无论是清理工作空间、构建 Docker 镜像的自动化步骤,还是静态资源挂载、接口代理的细节配置,每一步都为你铺就了从学习到实战的桥梁。动手实践这些步骤,你不仅能收获可运行的前后端服务,更能掌握部署领域的核心工具与思维,为应对更复杂的生产环境打下坚实基础。