『博客开发日记』之关于项目的部署

本文最后更新于 2026年6月20日 下午

关于项目的部署


前言

由于项目整体已经开发完

现在进入项目部署阶段


服务器与域名的购买

我这里是在阿里云购买的服务器(学生优惠可以白嫖)和域名

买到服务器和域名后自行备案和解析

服务器建议买Linux系统的我买的系统是CentOS 7.9 64位

在服务器安全组中给一些必要的端口放行



在系统中安装docker

在安装docker前先准备两个小软件

WinSCP, 用于和云服务器进行文件传输


WinSCP操作界面


putty, 用于与云服务器进行远程连接(终端)

putty 安装教程

putty操作界面


有了软件之后就去安装docker

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
# 安装依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# 添加 Docker 官方仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 设置阿里docker镜像仓库地址
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 配置docker加速 (https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors)自行设置自己的
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

# 安装 Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io

# 启动 Docker
sudo systemctl start docker

# 设置开机自启
sudo systemctl enable docker

# 查看 Docker 版本
docker version

出现 docker 版本号则为成功


常用的 docker 命令

查看 Docker 信息

1
2
3
4
5
6
7
8
# 查看 Docker 版本
docker version

# 查看 Docker 系统信息
docker info

# 查看 Docker 磁盘占用
docker system df

镜像相关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看本地镜像
docker images

# 拉取镜像
docker pull nginx:latest

# 删除镜像
docker rmi nginx:latest

# 强制删除镜像
docker rmi -f 镜像ID

# 搜索镜像
docker search mysql

# 构建镜像
docker build -t 镜像名:版本号 .

# 给镜像打标签
docker tag 本地镜像名:版本号 仓库地址/镜像名:版本号

# 推送镜像到远程仓库
docker push 仓库地址/镜像名:版本号

容器相关命令

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
# 查看正在运行的容器
docker ps

# 查看所有容器,包括已停止的容器
docker ps -a

# 启动容器
docker start 容器名或容器ID

# 停止容器
docker stop 容器名或容器ID

# 重启容器
docker restart 容器名或容器ID

# 删除容器
docker rm 容器名或容器ID

# 强制删除正在运行的容器
docker rm -f 容器名或容器ID

# 查看容器日志
docker logs 容器名或容器ID

# 实时查看容器日志
docker logs -f 容器名或容器ID

# 进入容器
docker exec -it 容器名或容器ID /bin/bash

# 如果容器没有 bash,可以使用 sh
docker exec -it 容器名或容器ID /bin/sh

在 docker 中下载镜像

一般来说,不需要单独下载镜像

因为在运行 docker run 命令进行运行容器时

如果当前环境中没有对应的镜像则会自动先下载对应的镜像

你也可以去 Docker Hub 学习一些常用命令和下载镜像

Docker Hub


运行 docker 中的容器————前台后端部署

下面以部署我项目后端为例子

在开始前先要对后端项目进行打包

打包项目完成后会在对应路径下生成 jar 包,这是前期准备之一


然后在 docker 中创建网络 方便后续后端的容器能在同一个网络下运行

docker network create 网络名

运行 mysql 容器

有了网络之后运行 mysql 容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
docker run --restart always \
-e TZ=Asia/Shanghai \
--name yun_blog_sql \
-d \
--network blog_network \
-p 3306:3306 \
-v /home/mysql_date:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=your_password \
mysql:8.0

# 下面是逐行解释

docker run --restart always \ # 启动一个新的容器,并设置重启策略为 always:Docker 启动时自动启动,异常退出自动重启
-e TZ=Asia/Shanghai \ # 容器系统时区设成东八区
--name yun_blog_sql \ # 为容器指定名称 yun_blog_sql(可自定义),方便后续管理
-d \ # 后台运行容器(detached 模式)
--network blog_network \ # 将容器加入名为 blog_network 的 Docker 网络,便于容器间通过名称互相访问
-p 3306:3306 \ # 将宿主机 3306 端口映射到容器 3306 端口,用于访问 MySQL
-v /home/mysql_date:/var/lib/mysql \ # 将宿主机目录挂载到容器数据目录,实现 MySQL 数据持久化,其中要挂载到容器目录的哪个位置可以去 Docker Hub 对应镜像里找
-e MYSQL_ROOT_PASSWORD=your_password \ # 设置 MySQL root 用户密码为 your_password
mysql:8.0 # 使用 mysql:8.0 镜像创建容器


然后去 WinSCP 查看对应的 /home/mysql_date 路径可以看到已经有对应的目录

这个就是用来存储和与容器里数据互通的文件目录


然后去数据库测试连接一下看通没通


在这之后你就可以把你自己的数据表导入数据库为后续做准备了

运行 redis 容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
docker run --restart always \
--name yun_blog_redis \
-d \
--network blog_network \
-p 6379:6379 \
-v /home/redis_date:/data \
redis redis-server --appendonly yes

# 下面是逐行解释

docker run --restart always \ # 启动一个新的容器,并设置重启策略为 always:Docker 启动时自动启动,异常退出自动重启
--name yun_blog_redis \ # 为容器指定名称 yun_blog_redis,方便管理
-d \ # 后台运行容器(detached 模式)
--network blog_network \ # 将容器加入名为 blog_network 的 Docker 网络,便于容器间通过名称互相访问
-p 6379:6379 \ # 将宿主机 6379 端口映射到容器 6379 端口,用于访问 Redis
-v /home/redis_date:/data \ # 将宿主机目录挂载到容器 /data,实现 Redis 数据持久化,其中要挂载到容器目录的哪个位置可以去 Docker Hub 对应镜像里找
redis redis-server --appendonly yes # 使用 redis 镜像,并以 redis-server 启动;开启 AOF 持久化(appendonly yes),原本 redis 只操作内存,加上这个设置之后 redis 可以将数据写入硬盘从而实现持久化


然后去 WinSCP 查看对应的 /home/redis_date 路径可以看到已经有对应的目录

这个就是用来存储和与容器里数据互通的文件目录


然后去 redis 测试连接一下看通没通


测试redis是否正常工作


回到 redis 看到能正常写入数据则连接成功


运行 java 容器

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
docker run --restart always \
-e TZ=Asia/Shanghai \
--name yun_blog_java \
-d \
--network blog_network \
-p 1314:1314 \
-v /home/yun_blog_java/mengze-blog:/yun_blog_java/mengze-blog \
openjdk:17-jdk java -jar /yun_blog_java/mengze-blog/mengze-blog-1.0-SNAPSHOT.jar \
"--spring.datasource.url=jdbc:mysql://yun_blog_sql:3306/yun_blog?characterEncoding=utf8&serverTimezone=Asia/Shanghai" \
"--spring.datasource.username=your_sql_username" \
"--spring.datasource.password=your_sql_password" \
"--spring.redis.host=yun_blog_redis" \
"--spring.mail.username=your_qq_email_username" \
"--spring.mail.password=your_qq_email_key" \
"--oss.accessKey=your_access_key" \
"--oss.secretKey=your_secret_key" \
"--oss.bucket=your_bucket_name" \
"--oss.domain=your_oss_visit_domain" \
"--aliyun.sms.access-key-id=your_access_key_id" \
"--aliyun.sms.access-key-secret=your_access_key_secret" \
"--blog.frontend.url=blog_frontend_url" \
"--weather.api.key-id=your_key_id" \
"--weather.api.project-id=your_project_id" \
"--weather.api.host=your_api_host" \
"--weather.api.private-key=your_weather_private_key" \
"--deepseek.api-key=your_deepseek_api_key"


# 下面是逐行解释

docker run --restart always \ # 启动一个新的容器,并设置重启策略为 always:Docker 启动时自动启动,异常退出自动重启
-e TZ=Asia/Shanghai \ # 容器系统时区设成东八区
--name yun_blog_java \ # 为容器指定名称 yun_blog_java,方便管理
-d \ # 后台运行容器(detached 模式)
--network blog_network \ # 将容器加入名为 blog_network 的 Docker 网络,便于容器间通过名称互相访问
-p 1314:1314 \ # 将宿主机 1314 端口映射到容器 1314 端口,用于访问 Java 服务
-v /home/yun_blog_java/mengze-blog:/yun_blog_java/mengze-blog \ # 将宿主机目录挂载到容器内,提供 jar 包与配置文件
openjdk:17-jdk java -Xms256m -Xmx384m -jar /yun_blog_java/mengze-blog/mengze-blog-1.0-SNAPSHOT.jar \ # 使用 openjdk:17-jdk 镜像运行 Java 程序,并分配256-384m内存
"--spring.datasource.url=jdbc:mysql://yun_blog_sql:3306/yun_blog?characterEncoding=utf8&serverTimezone=Asia/Shanghai" \ # 指定 MySQL 连接地址,使用容器名 yun_blog_sql 访问数据库
"--spring.datasource.username=your_sql_username" \ # 指定数据库用户名
"--spring.datasource.password=your_sql_password" \ # 指定数据库密码
"--spring.redis.host=yun_blog_redis" # 指定 Redis 主机地址,使用容器名 yun_blog_redis 访问 Redis
"--spring.mail.username=your_qq_email_username" \ # 邮箱账号(QQ 邮箱)
"--spring.mail.password=your_qq_email_key" \ # 邮箱授权码(非登录密码)
"--oss.accessKey=your_access_key" \ # OSS AccessKey(阿里云或其他)
"--oss.secretKey=your_secret_key" \ # OSS SecretKey(阿里云或其他)
"--oss.bucket=your_bucket_name" \ # OSS Bucket 名称
"--oss.domain=your_oss_visit_domain" \ # OSS 对外访问域名(CDN 或 Bucket 域名)
"--aliyun.sms.access-key-id=your_access_key_id" \ # 阿里云短信服务 AccessKey ID
"--aliyun.sms.access-key-secret=your_access_key_secret" \ # 阿里云短信服务 AccessKey Secret
"--blog.frontend.url=blog_frontend_url" \ # 前端访问地址(用于跨域、邮件跳转等)
"--weather.api.key-id=your_key_id" \ # 和风天气 API key-id
"--weather.api.project-id=your_project_id" \ # 和风天气 project-id
"--weather.api.host=your_api_host" \ # 和风天气 API host
"--weather.api.private-key=your_weather_private_key" \ # 和风天气私钥(用于签名)
"--deepseek.api-key=your_deepseek_api_key" # DeepSeek API Key
# 上面的这些指定的优先级都是比在项目中 application.yml 里的配置优先级高的,只要你在这里指定了都会覆盖你配置的参数

完成这些之后去测试就可以看到spring可以跑


前面已经导入数据表,现在测试一下后端是否真正和数据库进行联动


如果测试报错,有可能是你的容器不在同一个网络中

或者你后端的一些数据库配置没设置好再打包

亦或者是你根本没有这个数据库

可以去问问ai如何解决


运行 nginx 容器(部署前后的前端)

在给前端项目打包前要注意配置好项目生产时的环境变量

要注意的是这里都加上了代理前缀

这样请求地址会是:http://yun-mi-manchi.top/api/article

然后经过 nginx 代理之后去除 /api 再发给后端 请求地址会是:http://yun-mi-manchi.top/article

后台生产环境


前台生产环境


我们要把前后台项目都部署在同一台 nginx 容器里

然后通过 nginx 转发不同的路径来实现前后台的跳转 如下:

http://admin.yun-mi-manchi ===== 跳转前台

http://admin.yun-mi-manchi/admin ===== 跳转后台

在运行容器之前先在云服务器对应路径中创建 default.conf 文件

配置default.conf

然后配置 default.conf

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

# ================================
# 前台配置(yun-mi-manchi.top)
# ================================
server {
listen 80;
server_name yun-mi-manchi.top;

# ---------------------------------------------------------
# 静态资源缓存(前台所有 js/css/img 等文件)
# 使用 root 而不是 alias,因为这是正则匹配
# ---------------------------------------------------------
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
root /usr/share/nginx/html/blog; # ⚠ 这里路径看起来像写错了(前台却指向 admin)
expires 30d;
add_header Cache-Control "public, immutable";
log_not_found off;
}

# ---------------------------------------------------------
# 前台 SPA 页面入口(Vue/React)
# alias 指向 blog 目录
# try_files 回退必须是 /index.html(不能写 /blog/index.html)
# ---------------------------------------------------------
location / {
alias /usr/share/nginx/html/blog/;
try_files $uri $uri/ /index.html;
}

# ---------------------------------------------------------
# 前台 API 代理
# ^~ 表示优先匹配,不让正则 location 抢走
# ---------------------------------------------------------
location ^~ /api/ {
proxy_pass http://你自己服务器的公网ip:1314/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# ---------------------------------------------------------
# 前台 WebSocket 代理
# 必须设置 Upgrade / Connection 才能保持长连接
# ---------------------------------------------------------
location /ws/blog {
proxy_pass http://你自己服务器的公网ip:1314/ws/blog;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

# ---------------------------------------------------------
# 上传大小限制(防止 413 Request Entity Too Large)
# ---------------------------------------------------------
client_max_body_size 50M;
}

# ================================
# 后台配置(admin.yun-mi-manchi.top)
# ================================
server {
listen 80;
server_name admin.yun-mi-manchi.top;

# ---------------------------------------------------------
# 静态资源缓存(后台所有 js/css/img 等文件)
# 使用 root,因为这是正则匹配
# ---------------------------------------------------------
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
root /usr/share/nginx/html/admin;
expires 30d;
add_header Cache-Control "public, immutable";
log_not_found off;
}

# ---------------------------------------------------------
# 后台 SPA 页面入口(Vue/React)
# alias 指向 admin 目录
# try_files 回退必须是 /index.html(不能写 /admin/index.html)
# ---------------------------------------------------------
location / {
alias /usr/share/nginx/html/admin/;
try_files $uri $uri/ /index.html;
}

# ---------------------------------------------------------
# 后台 API 代理
# ^~ 保证优先匹配
# ---------------------------------------------------------
location ^~ /api/ {
proxy_pass http://你自己服务器的公网ip:7616/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# ---------------------------------------------------------
# 后台 WebSocket 代理
# ---------------------------------------------------------
location /ws/admin {
proxy_pass http://你自己服务器的公网ip:7616/ws/admin;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

# ---------------------------------------------------------
# 上传大小限制
# ---------------------------------------------------------
client_max_body_size 50M;
}

然后运行下面命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker run --restart always \
--name yun_blog_vue \
-d \
-p 80:80 \
-v /home/yun_blog_vue/blog/dist:/usr/share/nginx/html/blog \
-v /home/yun_blog_vue/admin/dist:/usr/share/nginx/html/admin \
-v /home/yun_blog_vue/nginx/conf.d:/etc/nginx/conf.d \
nginx

# 下面是逐行解释

docker run --restart always \ # 启动一个新的容器并设置容器停止时(如服务器重启)总是自动重启
--name yun_blog_vue \ # 指定容器名称
-d \ # 后台运行容器(detach 模式)
-p 80:80 \ # 端口映射:宿主机80端口 -> 容器80端口
-v /home/yun_blog_vue/blog/dist:/usr/share/nginx/html/blog \ # 挂载静态网站文件(将宿主机dist目录映射到nginx的/usr/share/nginx/html/blog \目录)
-v /home/yun_blog_vue/admin/dist:/usr/share/nginx/html/admin \ # 挂载静态网站文件(将宿主机dist目录映射到nginx的/usr/share/nginx/html/admin \目录)
-v /home/yun_blog_vue/nginx/conf.d:/etc/nginx/conf.d \ # 挂载nginx自定义配置文件目录
nginx # 使用的镜像名称

在浏览器浏览通了就是成功



后台后端部署

步骤和命令都差不多

注意开放的端口号是不同的

还有文件的目录不同(建议把不同项目放在不同目录下好区分)

还要注意你运行的 jar 包名和路径是否正确

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
docker run --restart always \
-e TZ=Asia/Shanghai \
--name yun_blog_admin_java \
-d \
--network blog_network \
-p 7616:7616 \
-v /home/yun_blog_java/mengze-admin:/yun_blog_java/mengze-admin \
openjdk:17-jdk java -Xms256m -Xmx384m -jar /yun_blog_java/mengze-admin/mengze-admin-1.0-SNAPSHOT.jar \
"--spring.datasource.url=jdbc:mysql://yun_blog_sql:3306/yun_blog?characterEncoding=utf8&serverTimezone=Asia/Shanghai" \
"--spring.datasource.username=your_sql_username" \
"--spring.datasource.password=your_sql_password" \
"--spring.redis.host=yun_blog_redis" \
"--spring.mail.username=your_qq_email_username" \
"--spring.mail.password=your_qq_email_key" \
"--oss.accessKey=your_access_key" \
"--oss.secretKey=your_secret_key" \
"--oss.bucket=your_bucket_name" \
"--oss.domain=your_oss_visit_domain" \
"--aliyun.sms.access-key-id=your_access_key_id" \
"--aliyun.sms.access-key-secret=your_access_key_secret" \
"--blog.frontend.url=blog_frontend_url" \
"--weather.api.key-id=your_key_id" \
"--weather.api.project-id=your_project_id" \
"--weather.api.host=your_api_host" \
"--weather.api.private-key=your_weather_private_key" \
"--deepseek.api-key=your_deepseek_api_key"

# 代码就不做解释了,运行后自行测试

需要注意的是

上面这两个后端部署的例子是极度不安全的

因为把密钥这些敏感信息全部暴露出去了

别人只要能登上你的服务器

运行 docker ps

就可以看到所有配置和敏感信息

关于这个

后续有解决方法






PS:该系列只做为作者学习开发项目做的笔记用

不一定符合读者来学习,仅供参考


预告

后续会记录博客的开发过程

每次学习会做一份笔记来进行发表

“一花一世界,一叶一菩提”


版权所有 © 2026 云梦泽
欢迎访问我的个人网站:https://hgt12.github.io/


『博客开发日记』之关于项目的部署
http://example.com/2026/06/13/『博客开发日记』之关于项目的部署/
作者
云梦泽
发布于
2026年6月13日
更新于
2026年6月20日
许可协议