项目部署

整体部署思路

whiteboard_exported_image (20)

在企业正常的项目部署中,可能会涉及多台服务器共同使用,如上图中的Nginx、Tomcat、Redis、MySQL都会有对应的单独的服务器,让各个服务器进行互相通信即可完成部署

  • 其中的微信小程序必须租腾讯的服务器才能完成部署
  • 在学习阶段,所有的服务都使用同一个虚拟机来完成项目的部署

环境准备

软件环境:

软件 版本 安装方式
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

shell脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/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安装完成"

Jenkins介绍和安装

参考文档:Jenkins(扩展文档)

资料提供的虚拟机中已经使用docker安装了jenkins,启动命令:docker start jenkins

478531c6-8f3d-4c21-a200-9d3ad3b65286

访问地址:http://虚拟机ip地址:8000/

5dc01b46-a6b8-488d-9f4e-bfcddd98ac25

用户名:zzyl 密码:itcast

登录后的效果:

6faa38f0-f946-4b1b-8e5d-ae7c6debd471

部署后端项目

多环境说明

在项目开发部署的过程中,一般都会有三套项目环境

  • Development :开发环境
  • Production :生产环境
  • Test :测试环境

例如:开发环境连接的是本地安装的MySQL,生产环境需要连接线上的MySQL环境

  1. 合并配置文件(application.yml和application-druid.yml),合并之后的内容如下:
    • 把application-druid.yml拷贝到application.yml中,注意格式,application-druid.yml的配置都在spring的配置下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# 项目相关配置
ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.9.0
# 版权年份
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
profile: E:/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:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://192.168.2.17: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
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
# profiles:
# active: druid
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# redis 配置
redis:
# 地址
host: 192.168.2.17
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: 123456
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms

# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 1800

# 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/*

# 阿里云OSS
aliyun:
oss:
endpoint: https://oss-cn-shanghai.aliyuncs.com
bucketName: norlcyan-javaweb-ai
region: cn-shanghai

# 百度千帆API
baidu:
qianfan:
apiKey: bce-v3/ALTAK-tBQEfdmxk9589E4u5kfEO/7f113d2b37f73ebc98a0e7afbfc8037ddadba43b
baseUrl: https://qianfan.baidubce.com/v2/
model: ernie-4.0-8k

打开后端项目,基于原来的application.yml文件再再拷贝三份配置文件,命名如下:

  • application-dev.yml 开发环境
  • application-test.yml 测试环境
  • application-prod.yml 正式环境 为了区分不同的环境生效,把prod的服务端口改为9000

修改application.yml文件内容,在这里只留一个开关,需要激活哪套环境:

1
2
3
spring:
profiles:
active: prod

什么是流水线

Jenkins 流水线配置是 Jenkins 中用于定义和执行自动化构建、测试和部署过程的一种方式

0422ffcf-9356-4781-a4f2-77732fa69be1

在声明式流水线中,整个流水线过程被定义在一个 pipeline 块中,该块包含了流水线执行所需的所有指令和阶段

  1. 基本结构

声明式流水线的基本结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pipeline {  
agent any // 指定流水线运行的节点,any 表示任何可用的节点
stages {
stage('Stage Name1') { // 定义阶段
steps {
// 定义在该阶段执行的步骤
echo 'Hello, World!'
}
},
stage('Stage Name2') { // 定义阶段
steps {
// 定义在该阶段执行的步骤
echo 'Hello, World!'
}
}
}
}
  1. 主要指令和阶段
  • agent:指定流水线或特定阶段在哪个节点上执行
  • stages:包含流水线中的所有阶段(stage),阶段是流水线的主要分组单元
  • stage:定义一个阶段,阶段内可以包含一个或多个步骤
  • steps:定义在某个阶段内执行的步骤,步骤是构建过程中的具体操作
  • post:定义在所有阶段完成后执行的操作,可以基于不同的条件(如成功、失败、总是)来执行
  • environment:定义流水线中的环境变量
  • options:定义全局选项和配置,如超时设置、并行执行等
  • parameters:定义流水线的参数,用于接收用户输入
  • triggers:定义触发流水线执行的条件或事件,如定时触发、代码推送触发等

Jenkins 流水线配置

新建任务

任务名称:zzyl-admin(根据引导类的项目名)

c0d97c67-9307-44af-a0fd-35a90f7c87ad

输入项目名称

c428af9a-525e-42e7-af90-3dd1fa45106e

参数化配置

3e56d5b5-5c50-4098-9f14-cc6e4c01f17e

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

2e7a86d9-3d49-4135-b3c8-ce6a56c39447

定义参数:GIT_URL

44d00485-8ddf-4548-9e2b-88ade26ee8a3

定义代码分支参数:GIT_TAG

ba13035f-04ec-4dbf-ab4c-d2d86b019214

设置代码拉取的密码:

98503c53-fe8d-45e3-a606-3e93e176b02a

设置登录凭证:

447bbe3a-2e7f-4f65-a05e-16eb00b41755

选择设置的密码参数类型

e0a734ec-c123-4c54-9511-362e8a57a5d7

设置Docker打包镜像版本 Docker_TAG:

e6c6511f-a27c-48c8-b50b-0e9c0c187831

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

b32ee306-598d-4075-87ec-971c37561aec

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

e91a404e-7384-43c9-85ee-27b6f9f74ed7

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

8e412635-23c1-4016-b7a4-d9e3ed16a008

新建 Jenkinsfile 文件并上传到代码仓库中, 在Jenkins中完成流水线配置

将 Jenkinsfile 文件上传到Git做为共享配置使用,存储到项目**根目录**,命名必须是:Jenkinsfile

d12307b2-54af-422c-bab7-c90d4bffe781

文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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: [[credentialsId: 'Gitee_ID', url: GIT_URL]]
])
sh "pwd"
}
}
stage('重新Maven打包') {
steps {
script {
echo "正在执行maven打包...."
sh "mvn clean install -DskipTests"
}
}
}
stage('重新构建镜像') {
steps {
echo "当前打镜像tag:${DOCKER_TAG}"
script {
for (ds in services.tokenize(",")) {
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 '任务构建完毕'
}
}
}
  • 注意,其中的Gitee_ID要与jenkins中配置的gitee登录凭证ID一致

f7c1f95c-b313-4ba5-b077-964b66d952db

在部署的项目模块(zzyl-admin)目录下 新增两个文件

  • Dockerfile
1
2
3
4
5
6
7
8
9
10
11
# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV OSS_ACCESS_KEY_ID ....(自己的ACCESS_KEY_ID)
ENV OSS_ACCESS_KEY_SECRET ....(自己的ACCESS_KEY_SECRET)
# 拷贝jar包
COPY zzyl-admin.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

一定要把OSS_ACCESS_KEY_ID设置为自己的

  • deploy.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/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

最终的代码结构

d10bb685-4f7c-4903-a678-c5ebdd61238f

所有配置新增之后和修改之后,需要把代码提交,并且**推送到远程仓库**

代码合并

将代码从dev*开头的分支中,合并到master分支

  • 切换分支到master分支
    • 注意:切换分支之前,必须执行commit操作提交代码到本地仓库,防止代码丢失

项目部署

打开jenkins,找到刚才创建的项目,选择参数化构建,选择需要配置,如下图,点击Build开始构建

52007714-08b8-4d71-8e44-38f4082b9205

查看日志

a29da693-35c5-468b-9bd8-8eaf5057a683

查看docker容器

当项目部署成功之后,可以在docker中查看是否创建了对应的镜像和容器

9887ff2f-c9f5-49cf-84a6-1a2a38a7d6e0

也可以查看正在运行的容器

b941b6bf-9038-4c1f-bb78-baf50e7ac789

前后端测试

在本地的前端代码中,修改访问后端的地址,来验证服务是否可以正常访问

f19dec83-bfc1-44aa-9f15-71b76a447256

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

29a92684-e6ac-4b52-b242-57723d262ad9

OSS_ACCESS_KEY的安全传递方式(拓展内容)

在Jenkins流水线配置中,直接将OSS_ACCESS_KEY以明文的方式写死在了Dockerfile中,并且还将其上传到了公网Git仓库中,这是及其危险的行为

解决的方式:以Jenkins凭据的形式保存在Jenkins服务中,动态的获取到OSS_ACCESS_KEY

  1. 添加凭据:
    • 访问地址:http://ip:port/manage/credentials/store/system/domain/_/
    • 点击Add Credentials,进入添加凭据参数页面
    • 类型选择Secret Text,Secret这一栏填写AccessKey ID
    • ID这一栏叫OSS_ACCESS_KEY_ID即可,其他内容也行,不过后续要使用到,不能填写错误
    • 点击create创建完毕,随后再创建对应的AccessKey Secret,步骤和上面相同
  2. 修改项目根目录下的Jenkinsfile:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
stage('部署服务'){
steps {
script {
withCredentials([
string(credentialsId: 'OSS_ACCESS_KEY_ID', variable: 'OSS_KEY_ID'),
string(credentialsId: 'OSS_ACCESS_KEY_SECRET', variable: 'OSS_KEY_SECRET')
]) {
for (ws in services.tokenize(",")) {
sh "pwd"
sh "cd `pwd`"
echo "部署升级:${ws}服务"
sh "OSS_KEY_ID=${OSS_KEY_ID} OSS_KEY_SECRET=${OSS_KEY_SECRET} chmod +x ./${ws}/deploy.sh && sh ./${ws}/deploy.sh ${ws} ${DOCKER_TAG}"
}
}
}
}
}

withCredentials中的credentialsId就是之前ID栏填写的内容,用于获取到Secret栏的值,并保存到variable对应的参数中

  1. 修改Dockerfile文件,只需要把设置环境变量OSS_ACCESS_KEY_IDOSS_ACCESS_KEY_ID这两栏去掉即可
  2. 修改deploy.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/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 \
-e OSS_ACCESS_KEY_ID=$OSS_KEY_ID \
-e OSS_ACCESS_KEY_SECRET=$OSS_KEY_SECRET \
-v /usr/local/zzyl-admin/logs:/home/ruoyi/logs \
-p 9000:9000 $image_name:$image_tag
fi

在启动容器时以参数的形式设置环境变量,$OSS_KEY_ID$OSS_KEY_SECRET就是通过刚才修改的Jenkinsfile中获取的

部署前端项目

前端项目可以采用nginx进行部署,需要使用nginx中的两个特性,分别是静态服务器反向代理

如下图:

whiteboard_exported_image (21)
  • 首先把目前的vue项目进行打包,打包成静态资源
  • 使用docker创建nginx容器
  • 部署静态资源(前端打包之后的静态资源)
  • 配置反向代理服务访问

多环境选择打包

在前端项目中也有多环境配置,如下面的三个文件,不同的文件对应了不同的环境配置项

eeba8a5d-a4cd-48e1-96da-84d2cfbdba77

  • development 开发环境
  • production 正式环境
  • staging 预发布环境

指定环境打包,打包命令:npm run build:prod (选择了正式环境)

  • 打包成功之后,会在项目的根目录下生成一个文件夹 dist
  • dist目录中存储的就是打包之后的静态文件,会压缩,存储较小,部署使用这里面内容

0a07672e-8318-4827-a81b-ac747fdb1598

创建nginx容器

在目前提供的服务器中已经提供了nginx的镜像,可以直接使用这个镜像制作nginx容器。命令如下:

1
2
3
4
5
6
7
docker 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

部署前端和配置反向代理

  1. 把前端项目打包之后的文件上传到服务器/usr/local/zzyl-vue/html目录中,注意:要连带dist目录一起上传,最终效果如下:

049854d6-0ad1-45d2-b289-db8cde1b0320

  1. 配置反向代理

/usr/local/zzyl-vue/conf 目录下创建一个文件:zzyl-vue.conf

可以直接使用资料中提供的文件:

4df8e684-2b33-460e-90ed-6f8539effc55

详细内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 配置代理服务  serve的地址为:中州养老后台管理服务ip+端口
upstream heima-admin{
server ip:port;
}

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,效果:

db47f8d1-d00c-4f9b-87a1-ad35caf5c8b7