文章目录
-
- 一、写在前面
-
- 1.1 项目简介
- 1.2 源码获取与私有仓库迁移
- 1.3 整体架构(安全部署版)
- 1.4 服务器环境说明
- 1.5 所需环境清单
- 二、设计思路与目录规划
-
- 2.1 为什么不能直接用 root?
- 2.2 用户权限设计
- 2.3 目录结构设计
- 2.4 自动化与未来扩展
- 三、环境准备
-
- 3.1 创建系统用户
- 3.2 安装软件环境
- 3.3 开发工具与 Git 配置
-
- 3.3.1 配置 SSH 密钥(用于拉取私有仓库代码)
- 3.3.2 Git 常用配置
- 3.3.3 安装 tig(Git 日志查看工具)
- 3.3.4 解决 SSH 连接警告(服务器密钥变更)
- 3.4 配置用户权限
- 四、后端部署
-
- 4.1 克隆后端代码
- 4.2 配置Maven镜像
- 4.3 导入SQL到数据库
- 4.4 修改后端配置文件
- 4.5 编译和打包
-
- 4.5.1 编译安装所有模块
- 4.5.2 编译阶段常见问题及解决方案
- 4.5.3 打包启动模块
- 4.5.4 打包启动模块常见问题
- 4.6 创建软链接
- 4.7 systemd服务
-
- 4.7.1 创建systemd服务
- 4.7.2 systemctl 常用命令说明
- 4.8 各类错误问题排查和处理
-
- 4.8.1 查看错误日志的方法
- 4.8.2 问题列表及解决方案
- 4.8.3 通用排查流程
- 4.9 后端 Nginx 配置
-
- 4.9.1 安装Nginx
- 4.9.2 配置 Nginx 虚拟主机
- 4.9.3 后端 Nginx 配置
- 五、前端部署
-
- 5.1 克隆前端代码
- 5.2 配置环境变量
- 5.3 安装依赖并构建
- 5.4 部署静态文件到 Nginx 目录
- 5.5 前端 Nginx 配置
- 5.6 验证部署结果
- 六、自动化部署脚本
-
- 6.1 后端自动化部署脚本
- 6.2 前端自动化构建脚本
- 6.3 日常更新命令
- 七、多环境配置文件
-
- 7.1 配置文件规划
- 7.2 具体操作步骤
- 7.3 其他环境配置
- 7.4 进阶:使用环境变量(更安全)
- 八、常见问题排查
-
- Q1:Nginx 返回 403 Forbidden
- Q2:后端启动失败,日志显示 "Permission denied"
- Q3:sudo 执行脚本时提示没有权限
- Q4:前端刷新页面 404
- Q5:Maven 依赖下载失败
- 九、总结
-
- 未来扩展考虑
- 参考链接
作者笔记 :本文以最小权限原则 和清晰目录结构为核心,详细记录了在 CentOS 服务器上部署 Snowy(小诺)v3.6.2 前后端项目的完整过程。包含专用运维用户创建、私有 Git 仓库迁移、目录规划、自动化脚本、systemd 服务、Nginx 反向代理等生产级实践。所有命令已在阿里云 Linux 4(CentOS 兼容)上验证通过。
备注:昨天我已经在本地MacOS环境搭建了《Java Snowy v3.6.2 前后端本地环境搭建全流程(踩坑实录)》,感兴趣的可以点击阅读。
一、写在前面
1.1 项目简介
Snowy 是目前国内非常热门的 Java 前后端分离快速开发平台,基于以下技术栈:
| 层面 | 技术 |
|---|---|
| 后端 | SpringBoot 3.5 + MyBatis-Plus + Sa-Token + HuTool |
| 前端 | Vue 3 + Ant Design Vue 4 + Vite 5 |
| 数据库 | MySQL 8.0 |
| 缓存 | Redis |
| 特色 | 国密算法(SM2/SM3/SM4)、多租户、代码生成、工作流等 |
项目地址:https://gitee.com/xiaonuobase/snowy (Star 33K+)
1.2 源码获取与私有仓库迁移
生产环境部署时,应使用公司自己的代码仓库(如内部 GitLab、Gitee 私有仓库等),而不是每次都从官方仓库拉取。这样可保证版本稳定、便于二次开发。
首次迁移操作(在开发机或临时服务器执行一次):
bash
# 1. 克隆官方源码
git clone https://gitee.com/xiaonuobase/snowy.git
cd snowy
# 2. 删除原有 .git 目录,断开与官方仓库的关联
rm -rf .git
# 3. 移动前端代码到另一个目录
mv snowy-admin-web ../
# 4. 初始化新仓库并关联到公司自己的远程仓库
git init
git remote add origin https://git.yourcompany.com/your-team/snowy-backend.git # 替换为实际地址
git add .
git commit -m "Initial commit from Snowy"
git push -u origin main
提示 :需要将前后端拆分为两个独立仓库,便于独立版本管理。前端代码位于
snowy/snowy-admin-web目录,同样执行上述迁移步骤,推送到另一个仓库如snowy-frontend.git。
后续在服务器上部署时,直接从公司私有仓库克隆:
bash
git clone https://git.yourcompany.com/your-team/snowy-backend.git
https://crm-api-test.mywebsite.com
proxy_pass http://127.0.0.1:8082
git clone https://git.yourcompany.com/your-team/snowy-frontend.git
https://crm-web-test.mywebsite.com
1.3 整体架构(安全部署版)
后端_Spring_Boot
Nginx_服务器
浏览器
/api/* 请求
https://crm-web-test.mywebsite.com
Nginx :443
进程用户: nobody
静态文件
/home/snowy/app/code/web/crm-web-test
反向代理
proxy_pass localhost:82
后端服务 :82
进程用户: snowy
MySQL 8.0
:3306
Redis
:6379 db1/db2
1.4 服务器环境说明
本次部署的服务器信息如下:
bash
[root@iZ2zecuztvoy0v29eoraqjZ ~]# cat /etc/os-release
NAME="Alibaba Cloud Linux"
VERSION="4"
💡 阿里云 Linux 4 基于 OpenAnolis,命令与 CentOS 7/8 完全兼容,本文所有命令均可在 CentOS 7/8 及兼容系统上执行。
1.5 所需环境清单
| 组件 | 版本要求 | 安装方式 |
|---|---|---|
| JDK | 17+(推荐 21 LTS) | yum 安装 |
| MySQL | 8.0 | yum 安装 |
| Redis | 最新版 | yum 安装 |
| Maven | 3.6+ | yum 安装 |
| Node.js | 18+(推荐 20 LTS) | NodeSource 安装 |
| Git | 最新版 | yum 安装 |
二、设计思路与目录规划
2.1 为什么不能直接用 root?
- 安全风险:若 Java 应用被入侵,攻击者将获得 root 权限,可控制整台服务器。
- 权限最小化:应用只需要读写自己的日志、上传文件,不应拥有系统级权限。
- 便于审计:不同用户运行不同服务,问题定位更清晰。
因此,我们创建一个专用用户 snowy 来管理整个 Snowy 应用。
2.2 用户权限设计
设计思路:snowy 用户 可以登录,但受限。
运维人员需要使用该用户进行代码拉取、构建、重启服务等操作,所以 snowy 必须能够登录。但为了安全,我们通过 sudo 权限限制 使其只能执行特定命令(如 systemctl restart snowy-web-app、journalctl),而无法安装软件、修改系统配置。
思考 :如果将来有多个员工需要维护,可以创建独立的个人账号(如
zhangsan),加入snowy组,并同样授予有限 sudo 权限,避免共享账号。
2.3 目录结构设计
设计思路:集中、可扩展、易迁移。
考虑到未来可能在同一台服务器上部署多个 Snowy 项目(例如 app2、app3),我们将所有项目文件放在 /home/snowy/app 下,而不是散落在家目录根下。这样既整洁,又便于整体打包迁移。需要创建一个snowy的系统用户。
/home/snowy/
├── .bashrc # 用户配置文件(自然存在)
├── .ssh/ # SSH 密钥(自然存在)
├── .m2/ # Maven 本地仓库(自然存在)
└── app/ # 当前 Snowy 项目根目录
├── code/
│ ├── git/
│ │ ├── snowy-backend/ # 后端源码(从公司私有仓库克隆)
│ │ └── snowy-frontend/ # 前端源码(从公司私有仓库克隆)
│ ├── frontend/
│ │ └── pc-web/ # 前端静态文件(Nginx 读取)
│ │ └── miniprogram/ # 小程序文件(Nginx 读取)
│ └── backend/
│ └── api/ # 后端运行文件(软链接指向 JAR)
├── logs/
│ ├── backend/ # 后端日志
│ └── frontend/ # 前端构建日志(可选)
├── data/
│ ├── uploads/ # 用户上传文件(可选)
│ └── backups/ # 备份文件(可选)
└── scripts/
├── backend-deploy.sh # 后端部署脚本
└── frontend-build.sh # 前端构建脚本
权限要点:
- 整个
/home/snowy/app目录属主snowy:snowy,权限755(目录需要+x才能进入)。 - Nginx 静态文件目录
/home/snowy/app/code/web/crm-web-test需要被 Nginx worker 进程(用户nobody)读取,因此需开放755权限,且所有父目录(/home、/home/snowy、/home/snowy/app、/home/snowy/app/code、/home/snowy/app/code/web)也需对nobody有+x权限。 - 日志和数据目录只需
snowy用户读写,无需对外开放。
2.4 自动化与未来扩展
本文使用 git pull + shell 脚本 实现快速部署。后续可引入 Jenkins、GitLab CI 等自动化运维工具。
三、环境准备
提示:这里使用 root 安装系统软件。
3.1 创建系统用户
具体操作:创建 snowy 用户并设置目录
bash
# 创建可登录的 snowy 用户(用于管理代码和服务)
useradd -m -s /bin/bash snowy
passwd snowy # 可选,若使用密钥登录可跳过
# 创建 app 目录结构
mkdir -p /home/snowy/app/{code/{git,web,backend},logs/{backend,frontend},data/{uploads,backups},scripts}
chown -R snowy:snowy /home/snowy/app
3.2 安装软件环境
具体操作:安装 Git、JDK 21、Maven、Node.js、MySQL 8、Redis
bash
# 安装 Git
yum install -y git
# 安装 Java环境
yum install -y java-21-openjdk java-21-openjdk-devel
yum install -y maven
# (可选)前置检查:查看所有已安装的 Java 版本
# alternatives --config java # 如果输出有多个版本,建议卸载旧版本
# 卸载 Java旧版本
# yum remove -y java-11-openjdk java-11-openjdk-headless
# 安装 Node.js 20
curl -sL https://rpm.nodesource.com/setup_20.x | bash -
yum install -y nodejs
############# 如果用阿里云的数据库服务,就跳过安装MySQL和Redis #################
# 安装 MySQL 8
wget https://dev.mysql.com/get/mysql80-community-release-el7-11.noarch.rpm
yum localinstall -y mysql80-community-release-el7-11.noarch.rpm
yum install -y mysql-community-server
systemctl start mysqld
systemctl enable mysqld
# 配置 MySQL 数据库
# 获取临时密码
grep 'temporary password' /var/log/mysqld.log
mysql -u root -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassword123!';
CREATE DATABASE IF NOT EXISTS snowy DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
EXIT;
# 安装 Redis
yum install -y redis
systemctl start redis
systemctl enable redis
提示:如果用阿里云的数据库服务,就跳过安装MySQL和Redis。
安装后的效果查看(示例)
bash
[root@myServer ~]# git -v
git version 2.47.3
[root@myServer ~]# java -version
openjdk version "21.0.10.0.10" 2026-03-27
OpenJDK Runtime Environment (Alibaba Dragonwell Extended Edition) (build 21.0.10.0.10+21)
OpenJDK 64-Bit Server VM (Alibaba Dragonwell Extended Edition) (build 21.0.10.0.10+21, mixed mode, sharing)
[root@myServer ~]# mvn -v
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: /usr/local/maven
Java version: 21.0.10.0.10, vendor: Alibaba, runtime: /usr/lib/jvm/java-21-alibaba-dragonwell-21.0.10.0.10-1.1.alnx4.x86_64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "6.6.102-5.2.alnx4.x86_64", arch: "amd64", family: "unix"
[root@myServer ~]# node -v
v22.16.0
[root@myServer ~]# npm -v
10.9.2
3.3 开发工具与 Git 配置
3.3.1 配置 SSH 密钥(用于拉取私有仓库代码)
首先检查是否已有 SSH 密钥:ls -la ~/.ssh/
如果没有密钥,生成新密钥(一路回车即可):ssh-keygen
查看公钥内容:cat ~/.ssh/id_rsa.pub
将输出的公钥内容添加到 Git 仓库(如阿里云 Codeup、GitLab、GitHub)的 SSH 密钥设置中。然后就可以 git clone 代码了。
3.3.2 Git 常用配置
设置用户名和邮箱(用于记录提交信息):
bash
git config --global user.name "你的名字"
git config --global user.email "你的邮箱@example.com"
设置常用别名(提高效率):
bash
git config --global alias.s status # git s 代替 git status
git config --global alias.c checkout
git config --global alias.p pull
git config --global alias.b branch
git config --global alias.d diff
验证配置:git config --global --list
3.3.3 安装 tig(Git 日志查看工具)
tig 是一个基于终端的 Git 日志查看工具,可以用交互式界面浏览提交历史、查看文件变更,比命令行 git log 更直观高效。
安装步骤:
bash
# 安装编译依赖
yum install -y ncurses-devel make gcc
# 下载源码
cd /tmp
git clone https://github.com/jonas/tig.git
cd tig
# 编译安装
make
make install
# 验证安装
tig --version
使用方式:在 Git 仓库目录下执行 tig 即可打开交互式日志界面。
如果
git clone下载慢,可以跳过 tig,不影响核心部署。
3.3.4 解决 SSH 连接警告(服务器密钥变更)
当服务器系统升级或重装后,SSH 连接可能会报错:
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
Host key verification failed.
解决方法 :删除本地保存的旧密钥记录:ssh-keygen -R 服务器IP
例如:ssh-keygen -R 123.23.34.56
然后重新连接:ssh root@123.23.34.56,输入 yes 接受新的主机密钥即可。
原因说明:该警告是 SSH 的安全机制,防止中间人攻击。服务器重装系统后主机密钥会改变,本地需要更新记录。
3.4 配置用户权限
具体操作:为 snowy 用户配置有限 sudo 权限。
编辑 sudoers 文件(使用 visudo),添加以下行,使 snowy 能够重启服务、查看日志,但不能做其他系统管理操作:使用 visudo
命令,然后添加:
snowy ALL=(ALL) NOPASSWD: /bin/systemctl restart snowy-web-app, /bin/systemctl stop snowy-web-app, /bin/systemctl start snowy-web-app, /bin/systemctl status snowy-web-app, /bin/journalctl -u snowy-web-app
这样
snowy可以执行sudo systemctl restart snowy-web-app,但无法yum install或修改/etc下的文件。
四、后端部署
提示:这里以 snowy 用户操作。
4.1 克隆后端代码
具体操作:切换用户并克隆后端代码(从公司私有仓库)
bash
su - snowy
cd /home/snowy/app/code/git
git clone https://git.yourcompany.com/your-team/snowy-backend.git # 替换为实际仓库地址
cd snowy-backend
# 如需要,切换到对应分支(例如 main 或 master)
git checkout main
TODO:后续如果需要合并snowy官方的更新,等待后续我再想想兼容方案,到时候最好能写个自动化脚本自动更新同步。
4.2 配置Maven镜像
提示:配置 Maven 国内镜像(加速依赖下载)
bash
mkdir -p /home/snowy/.m2
cat > /home/snowy/.m2/settings.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<mirrors>
<mirror>
<id>aliyun</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>http://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
</settings>
EOF
4.3 导入SQL到数据库
bash
# 创建数据库
mysql -uroot -p
CREATE DATABASE snowy DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
# 导入数据
mysql -u root -p snowy < /home/snowy/app/code/git/snowy-backend/snowy-web-app/src/main/resources/_sql/snowy_mysql.sql
4.4 修改后端配置文件
bash
cd /home/snowy/app/code/git/snowy-backend/snowy-web-app/src/main/resources
vim application.properties
修改以下关键配置:
properties
# 数据库连接
spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/snowy?useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.master.password=YourStrongPassword123!
# Redis
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
# spring.data.redis.username=user1 #可选
spring.data.redis.password=123
spring.data.redis.database=1
# 服务端口
server.port=8082
# 文件上传路径
snowy.file.local.path=/home/snowy/app/data/uploads
为什么 server.port 用 8082 而不是 82?
Linux 系统规定:普通用户(非 root)无权绑定 1024 以下的端口(如 80、82、443 等)。如果使用 82 端口,启动时会报
java.net.BindException: Permission denied错误。解决方案有两种:① 改用大于 1024 的端口(如 8082);② 让 Java 服务以 root 用户运行(不推荐,有安全风险)。
因此,这里选择 8082 端口,既避免了权限问题,又符合最小权限安全原则。后续 Nginx 反向代理只需将请求转发到
127.0.0.1:8082即可。
4.5 编译和打包
Snowy 是一个多模块项目,需要先安装所有模块到本地仓库,再打包启动模块。
4.5.1 编译安装所有模块
bash
cd /home/snowy/app/code/git/snowy-backend
mvn clean install -DskipTests # 此命令会编译全部 21 个模块并安装到本地 Maven 仓库,首次执行大概需要3-5分钟(视服务器配置情况不同,时长也不同)
编译成功后的输出:
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for snowy 3.0.0:
[INFO]
[INFO] snowy .............................................. SUCCESS [ 6.818 s]
[INFO] snowy-common ....................................... SUCCESS [01:13 min]
[INFO] snowy-plugin-api ................................... SUCCESS [ 0.033 s]
[INFO] snowy-plugin-auth-api .............................. SUCCESS [ 3.361 s]
[INFO] snowy-plugin-sys-api ............................... SUCCESS [ 1.210 s]
[INFO] snowy-plugin-client-api ............................ SUCCESS [ 1.028 s]
[INFO] snowy-plugin-dev-api ............................... SUCCESS [ 19.019 s]
[INFO] snowy-plugin ....................................... SUCCESS [ 0.020 s]
[INFO] snowy-plugin-auth .................................. SUCCESS [ 9.300 s]
[INFO] snowy-plugin-biz-api ............................... SUCCESS [ 1.225 s]
[INFO] snowy-plugin-biz ................................... SUCCESS [ 10.680 s]
[INFO] snowy-plugin-client ................................ SUCCESS [ 6.626 s]
[INFO] snowy-plugin-dev ................................... SUCCESS [ 14.891 s]
[INFO] snowy-plugin-gen-api ............................... SUCCESS [ 0.762 s]
[INFO] snowy-plugin-mobile-api ............................ SUCCESS [ 0.964 s]
[INFO] snowy-plugin-gen ................................... SUCCESS [ 9.167 s]
[INFO] snowy-plugin-mobile ................................ SUCCESS [ 4.877 s]
[INFO] snowy-plugin-sys ................................... SUCCESS [ 16.970 s]
[INFO] snowy-web-app ...................................... SUCCESS [ 14.032 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:22 min
[INFO] Finished at: 2026-04-16T14:16:14+08:00
[INFO] ------------------------------------------------------------------------
4.5.2 编译阶段常见问题及解决方案
问题一:Maven 版本过低导致编译失败
-
错误信息 :
The plugin org.apache.maven.plugins:maven-clean-plugin:3.4.1 requires Maven version 3.6.3 -
原因:Snowy 3.0.0 需要 Maven 3.6.3 或更高版本,但系统自带的 Maven 版本较旧(如 3.6.2)。
-
解决方案:升级 Maven 到 3.9.9
bash# 卸载旧版 yum remove -y maven # 下载新版 cd /tmp wget https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz tar -xzf apache-maven-3.9.9-bin.tar.gz -C /usr/local/ ln -s /usr/local/apache-maven-3.9.9 /usr/local/maven # 配置环境变量 echo 'export MAVEN_HOME=/usr/local/maven' > /etc/profile.d/maven.sh echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> /etc/profile.d/maven.sh source /etc/profile.d/maven.sh # 验证版本 mvn -version # 应显示 3.9.x
问题二:Java 版本不正确
-
错误信息 :编译时提示 Java 版本不兼容,或
mvn -version显示 Java 11 而非 21 -
原因:系统同时安装了多个 JDK 版本,环境变量指向了旧版本。
-
解决方案:切换默认 Java 版本或卸载旧 JDK
bash# 查看所有已安装的 Java 版本 alternatives --config java # 选择 Java 21(输入对应编号 1/2/3/....) # 或卸载 Java 11(可选) yum remove -y java-11-openjdk java-11-openjdk-headless
问题三:Maven 依赖下载缓慢或失败
- 错误信息 :
Remote host terminated the handshake或下载超时 - 原因:访问国外 Maven 中央仓库网络不稳定。
- 解决方案:配置阿里云镜像(详见 4.2 节)
问题四:内存不足导致编译失败
-
错误信息 :
OutOfMemoryError或Java heap space -
原因:服务器内存不足(低于 2GB)或 Maven 分配的内存过小。
-
解决方案:增加 Maven 编译内存
bashexport MAVEN_OPTS="-Xmx1024m -XX:MaxPermSize=512m"或修改
.m2/settings.xml添加:xml<profiles> <profile> <id>myprofile</id> <properties> <MAVEN_OPTS>-Xmx1024m</MAVEN_OPTS> </properties> </profile> </profiles>
问题五:编译时卡住不动
- 原因:依赖下载过程中网络中断或 Maven 进程假死。
- 解决方案 :按
Ctrl + C终止进程,删除 Maven 本地仓库中下载失败的缓存文件:find ~/.m2/repository -name "*.lastUpdated" -delete,然后重新执行mvn clean install -DskipTests
4.5.3 打包启动模块
cd snowy-web-app
mvn clean package -DskipTests
package命令会生成可执行的 JAR 包,位于target/snowy-web-app-*.jar。
构建成功后,输出显示:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.651 s
[INFO] Finished at: 2026-04-16T14:18:32+08:00
[INFO] ------------------------------------------------------------------------
4.5.4 打包启动模块常见问题
问题一:找不到依赖模块(编译失败)
- 错误信息 :
Could not find artifact vip.xiaonuo:snowy-common:jar:3.0.0 - 原因 :直接执行
mvn package打包snowy-web-app时,其他模块(如snowy-common)尚未安装到本地 Maven 仓库。 - 解决方案 :先回到项目根目录执行
mvn clean install -DskipTests,再进入snowy-web-app执行mvn package。
问题二:打包生成的 JAR 包无法启动
-
错误信息 :
no main manifest attribute或Failed to launch application -
原因 :
pom.xml中缺少 Spring Boot Maven 插件配置,导致打包的不是可执行 JAR。 -
解决方案 :检查
snowy-web-app/pom.xml是否包含以下插件:xml<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>如果没有,添加后重新打包。
问题三:打包后 JAR 包体积异常小
- 现象:正常 JAR 包约 100-200MB,但生成的只有几十 KB。
- 原因:打包时跳过了依赖,只打包了项目本身的代码(未打依赖包)。
- 解决方案 :确保使用
spring-boot-maven-plugin且执行的是package而非jar插件。执行mvn clean package重新打包。
问题四:打包时测试用例失败
- 错误信息 :
There are test failures - 原因:某些单元测试依赖外部环境(如数据库、Redis)或测试用例本身有 bug。
- 解决方案 :
- 临时跳过测试:
mvn clean package -DskipTests - 或只编译测试不执行:
mvn clean package -Dmaven.test.skip=true
- 临时跳过测试:
问题五:打包过程中内存溢出
-
错误信息 :
OutOfMemoryError或GC overhead limit exceeded -
原因:项目模块较多,打包时内存不足。
-
解决方案:增加 Maven 编译内存
bashexport MAVEN_OPTS="-Xmx2048m -XX:MaxMetaspaceSize=512m" mvn clean package -DskipTests
问题六:打包成功后 JAR 包找不到
- 现象 :
ls target/*.jar没有文件。 - 原因 :
package命令执行成功但没有生成 JAR,可能是pom.xml中<packaging>类型不是jar。 - 解决方案 :检查
pom.xml中是否有<packaging>jar</packaging>,Spring Boot 项目默认为 jar。
经验总结:99% 的打包问题可以通过以下步骤解决:
- 确保在项目根目录执行过
mvn clean install -DskipTests- 确保 Maven 版本 ≥ 3.6.3
- 确保 Java 版本为 17 或 21
- 配置了阿里云镜像加速依赖下载
- 遇到
.lastUpdated缓存文件时删除后重试
4.6 创建软链接
由于每次打包生成的 JAR 文件名包含版本号(如 snowy-web-app-3.0.0.jar),而 systemd 服务需要固定路径。因此创建一个软链接,指向最新构建的 JAR 包,后续更新时只需重新链接,无需修改服务配置。
bash
# 创建后端运行目录
mkdir -p /home/snowy/app/code/backend/api
# 软链接指向最新构建的 JAR
ln -sf /home/snowy/app/code/git/snowy-backend/snowy-web-app/target/snowy-web-app-3.0.0.jar /home/snowy/app/code/backend/api/snowy-web-app.jar
# 软链接指向成功
ls -la
lrwxrwxrwx 1 snowy snowy 95 Apr 16 14:25 snowy-web-app.jar -> /home/snowy/app/code/git/snowy-backend/snowy-web-app/target/snowy-web-app-3.0.0.jar
# 如果 snowy-web-app-3.0.0.jar 找不到:就先搜索一下
ls -la /home/snowy/app/code/git/snowy-backend/snowy-web-app/target/*.jar
4.7 systemd服务
4.7.1 创建systemd服务
使用 systemd 管理后端服务,可实现开机自启、崩溃自动重启、统一日志管理。
提示:需要切换到 root 用户创建服务文件:su - root
bash
exit # 回到 root
cat > /etc/systemd/system/snowy-web-app.service << 'EOF'
[Unit]
Description=Snowy Web Application
After=network.target mysql.service redis.service
[Service]
Type=simple
User=snowy
Group=snowy
WorkingDirectory=/home/snowy/app/logs/backend
Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk"
ExecStart=/usr/bin/java -jar /home/snowy/app/code/backend/snowy-api/snowy-web-app.jar --spring.profiles.active=prod
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
启动并设置开机自启:
bash
systemctl daemon-reload
systemctl start snowy-web-app
systemctl enable snowy-web-app
systemctl restart snowy-web-app
systemctl stop snowy-web-app
systemctl status snowy-web-app
说明 :
User=snowy指定以 snowy 用户运行,WorkingDirectory为日志输出目录;
--spring.profiles.active=test激活测试环境配置。
--spring.profiles.active=prod激活正产环境配置。
4.7.2 systemctl 常用命令说明
| 命令 | 作用 | 使用场景 |
|---|---|---|
systemctl daemon-reload |
重新加载 systemd 配置文件 | 修改了 .service 文件后必须执行,否则修改不生效 |
systemctl start snowy-web-app |
启动服务 | 服务未运行时首次启动 |
systemctl stop snowy-web-app |
停止服务 | 需要暂停服务时使用(如维护数据库) |
systemctl restart snowy-web-app |
重启服务 | 修改配置或更新 JAR 包后,需要重启使改动生效 |
systemctl enable snowy-web-app |
设置开机自启 | 首次部署时执行一次,确保服务器重启后服务自动运行 |
systemctl status snowy-web-app |
查看服务状态 | 检查服务是否正常运行、查看最近日志、排查问题时首选 |
经验总结:
- 修改
.service文件后,必须执行daemon-reload,否则 systemd 不会识别修改。- 日常更新代码后,使用
restart即可,无需stop再start。status是最常用的命令,建议每次操作后都执行一次确认状态。
4.8 各类错误问题排查和处理
在部署过程中,我遇到了多个典型问题。以下是问题现象、原因分析及解决方案的完整记录。
4.8.1 查看错误日志的方法
在排查问题前,首先要学会查看日志:
bash
# 查看 systemd 服务最新日志(实时滚动)
journalctl -u snowy-web-app -f --no-pager
# 查看最近 50 行日志
journalctl -u snowy-web-app -n 50 --no-pager
# 只查看错误和异常(加上 | tail -10 表示查看最后10行)
journalctl -u snowy-web-app --no-pager | grep -E "ERROR|Exception|Caused" | tail -10
# 查看应用自身日志(如果配置了文件输出)
tail -100 /home/snowy/app/logs/backend/app-log/log_error.log
4.8.2 问题列表及解决方案
问题一:服务反复重启,状态显示 activating (auto-restart)
- 错误关键字 :
restart counter is at X - 原因:应用启动失败导致 systemd 不断重试
- 解决方法 :查看具体错误日志,定位根本原因:
journalctl -u snowy-web-app -n 50 --no-pager
问题二:数据库连接失败:Unknown database 'snowy'
- 错误关键字 :
SQLSyntaxErrorException: Unknown database 'snowy' - 原因:配置文件中的数据库名与实际不符,或数据库未创建
- 解决方法 :
- 确认数据库名:
mysql -u root -p -e "SHOW DATABASES;" - 修改
application.properties中的url为正确数据库名 - 重新打包 JAR
- 确认数据库名:
问题三:Tomcat 启动失败:BindException: Permission denied
- 错误关键字 :
java.net.BindException: Permission denied - 原因:普通用户无法绑定 1024 以下的端口(如 82)
- 解决方法:改用大于 1024 的端口(如 8082),或让服务以 root 运行(不推荐)
问题四:修改配置后不生效
- 错误关键字:无报错但行为仍旧
- 原因:JAR 包是旧版本,未重新打包
- 解决方法 :修改源码后必须执行
mvn clean package -DskipTests重新生成 JAR,并重启服务
问题五:软链接创建失败:No such file or directory
- 错误关键字 :
ln: target 'xxx': No such file or directory - 原因 :目标目录不存在,或通配符
*在软链接中无法展开 - 解决方法 :先创建目录
mkdir -p,然后使用具体的 JAR 文件名(如snowy-web-app-3.0.0.jar)创建软链接
问题六:Redis 连接失败:NOAUTH Authentication required
- 错误关键字 :
NOAUTH Authentication required - 原因:Redis 设置了密码,但配置文件中未填写
- 解决方法 :找到 Redis 密码(如通过
docker inspect查看环境变量或启动脚本),在application.properties中填写spring.data.redis.password=xxx
问题七:Maven 编译失败:找不到内部模块
- 错误关键字 :
Could not find artifact vip.xiaonuo:snowy-common:jar - 原因 :直接打包
snowy-web-app前未安装其他模块 - 解决方法 :先在根目录执行
mvn clean install -DskipTests,再进入snowy-web-app执行mvn package
问题八:Maven 依赖下载慢或 SSL 握手失败
- 错误关键字 :
Remote host terminated the handshake - 原因:访问国外中央仓库网络不稳定
- 解决方法 :配置阿里云镜像(使用 HTTP 而非 HTTPS):在
~/.m2/settings.xml中设置<url>http://maven.aliyun.com/repository/public</url>
问题九:服务启动后端口未监听
- 错误关键字 :
curl: (7) Failed to connect to port - 原因:应用启动未完成或启动失败
- 解决方法 :查看日志等待
Application is running!出现,再用netstat -tlnp | grep 端口检查
问题十:前端访问 API 返回 401 但后端正常
- 错误关键字 :
{"code":401,"msg":"未能读取到有效 token"} - 原因:这是正常现象,表示 API 需要认证,不是错误
- 解决方法:无需处理,登录后即可正常调用
4.8.3 通用排查流程
当服务出现问题时,按以下步骤快速定位:
bash
# 1. 检查服务状态
systemctl status snowy-web-app
# 2. 查看最近错误日志
journalctl -u snowy-web-app -n 50 --no-pager | grep -E "ERROR|Exception"
# 3. 确认端口是否监听
netstat -tlnp | grep 8082 # 替换为你的端口
# 4. 测试 API 是否响应
curl http://localhost:8082/api/auth/getCaptcha
# 5. 如果修改了代码,确保重新打包并更新软链接
cd /home/snowy/app/code/git/snowy-backend
mvn clean package -DskipTests
ln -sf snowy-web-app/target/snowy-web-app-*.jar /home/snowy/app/code/backend/api/snowy-web-app.jar
# 6. 重启服务
systemctl restart snowy-web-app
经验总结:80% 的问题出在配置文件未生效(未重新打包)、数据库名/密码错误、端口权限不足。养成修改配置后重新打包、重启服务的习惯,可以避免大部分坑。
4.9 后端 Nginx 配置
4.9.1 安装Nginx
编译安装:
bash
# 安装编译依赖
yum install -y gcc make pcre-devel zlib-devel openssl-devel
# 下载 Nginx 源码
cd /tmp
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -xzf nginx-1.24.0.tar.gz
cd nginx-1.24.0
# 配置(启用 SSL)
./configure --prefix=/usr/local/nginx --with-http_ssl_module
# 编译安装
make && make install
# 创建软链接(可选,方便命令行调用)
ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx
# 验证安装
nginx -v
配置开机自启(创建 systemd 服务):
bash
cat > /etc/systemd/system/nginx.service << 'EOF'
[Unit]
Description=Nginx HTTP Server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable nginx
systemctl start nginx
systemctl status nginx
验证:curl http://localhost,应该显示 Nginx 欢迎页。
之后统一用 systemd 管理 Nginx:
bash
systemctl status nginx # 查看状态
systemctl restart nginx # 重启
systemctl stop nginx # 停止
4.9.2 配置 Nginx 虚拟主机
为了方便管理,我们使用 conf.d 目录存放各个域名的独立配置文件。Nginx 会自动加载该目录下所有以 .conf 结尾的文件。
第一步:创建 conf.d 目录(如果不存在)
mkdir -p /usr/local/nginx/conf/conf.d
第二步:在主配置文件中引入 conf.d
编辑 /usr/local/nginx/conf/nginx.conf,在 http 块中添加:
http {
# ... 其他配置 ...
include conf.d/*.conf;
}
4.9.3 后端 Nginx 配置
如果希望直接通过域名访问后端 API(给外部系统调用),可以单独配置一个后端域名 crm-api-test.mywebsite.com。
操作 vi /usr/local/nginx/conf/conf.d/crm-api-test.mywebsite.com.conf
nginx
server {
listen 80;
server_name crm-api-test.mywebsite.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name crm-api-test.mywebsite.com;
ssl_certificate /usr/local/nginx/conf/ssl/mywebsite.com.pem;
ssl_certificate_key /usr/local/nginx/conf/ssl/mywebsite.com.key;
location / {
proxy_pass http://127.0.0.1:8082;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
说明:此配置直接将所有请求转发到后端 8082 端口,适合作为 API 网关使用。如果不需要对外暴露 API 域名,可以省略此配置。
还需要确保Nginx用户能读取静态文件。Nginx worker 进程以 nobody 用户运行,而静态文件目录属主是 crm,需要开放读取权限:
bash
chmod -R 755 /home/snowy
测试并重载 Nginx:
bash
# 测试配置文件语法
/usr/local/nginx/sbin/nginx -t
# 重载使配置生效
/usr/local/nginx/sbin/nginx -s reload
# 或 systemctl restart nginx
验证配置:
bash
# 测试后端 API(通过后端域名直接访问)
curl https://crm-api-test.mywebsite.com/api/auth/getCaptcha -k
{"code":401,"msg":"未能读取到有效 token","data":null,"traceId":null}%
备注:
-k参数 :如果使用自签名证书,curl 会报证书错误,加-k忽略证书验证。生产环境使用正规证书后无需此参数。
浏览器访问:
接口HOST:https://crm-api-test.mywebsite.com/
接口文档:https://crm-api-test.mywebsite.com/doc.html#/home

五、前端部署
前端项目是基于 Vue 3 + Vite 构建的,需要先安装依赖、配置环境变量,然后打包成静态文件,最后部署到 Nginx 目录。
5.1 克隆前端代码
从公司私有仓库克隆前端代码:
bash
cd /home/snowy/app/code/git
git clone git@codeup.aliyun.com:xxxxxx/snowy-frontend.git
cd snowy-frontend
说明 :如果仓库结构与官方 Snowy 一致,前端代码在
snowy-admin-web子目录中。如果是自定义仓库,可能直接在根目录。请根据实际情况进入对应目录。
5.2 配置环境变量
Vue 项目通过 .env.xxxx 文件指定不同环境的后端 API 地址。这里创建.env.test测试环境。
bash
cp .env.development .env.test
vim .env.test
修改或写入的内容:
# 接口地址
VITE_API_BASEURL = http://crm-api-test.xxxx.com
# 本地端口
VITE_PORT = 8081
# 开启设置抽屉
VITE_SET_DRAWER = true
# 本地环境
NODE_ENV = test
# 检测更新(本地建议关闭)
VITE_VERSION_UPDATE = false
5.3 安装依赖并构建
bash
# (可选)设置淘宝镜像(加速依赖下载)
# npm config set registry https://registry.npmmirror.com
# 安装依赖(首次约需 2-5 分钟)
npm install
# 生产环境打包
npm run build
构建成功后,会在 dist 目录(或 snowy-admin-web/dist)下生成静态文件。
💡 常见问题 :如果
npm install失败,可尝试删除node_modules和package-lock.json后重试。
5.4 部署静态文件到 Nginx 目录
将构建好的 dist 目录内容复制到 Nginx 配置的静态文件目录:
bash
# 创建目标目录(如果不存在)
mkdir -p ~/educational/code/frontend/pc-web
# 复制新构建的文件(基于当前位置)
cd /home/snowy/app/code/git/snowy-frontend
cp -r ./dist/* /home/snowy/app/code/web/crm-web-test
5.5 前端 Nginx 配置
为前端域名 crm-web-test.mywebsite.com 配置 Nginx,实现静态文件托管和 API 反向代理。
1. 创建 Nginx 配置文件 vi /usr/local/nginx/conf/conf.d/crm-web-test.mywebsite.com.conf
2. 写入以下配置
nginx
server {
listen 80;
server_name crm-web-test.mywebsite.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name crm-web-test.mywebsite.com;
ssl_certificate /usr/local/nginx/conf/ssl/mywebsite.com.pem;
ssl_certificate_key /usr/local/nginx/conf/ssl/mywebsite.com.key;
location / {
root /home/snowy/app/code/web/crm-web-test;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
root /home/snowy/app/code/web/crm-web-test;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
配置说明:
location /:托管前端静态文件,try_files支持 Vue Router 的 history 模式。- 静态资源单独缓存,提升页面加载速度。
3. 确保 Nginx 能读取静态文件
Nginx worker 进程以 nobody 用户运行,而静态文件目录属主是 snowy,需要开放读取权限:
bash
chmod 755 /home /home/snowy /home/snowy/app /home/snowy/app/code /home/snowy/app/code/web
chmod -R 755 /home/snowy/app/code/web/crm-web-test
4. 测试并重启 Nginx
bash
/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload
5.6 验证部署结果
1. 验证配置
bash
# 测试前端页面
curl https://crm-web-test.mywebsite.com -k
# 测试 API 代理
curl https://crm-web-test.mywebsite.com/api/auth/getCaptcha -k
💡
-k参数 :如果使用自签名证书,curl 会报证书错误,加-k可忽略证书验证。生产环境使用正规 SSL 证书后无需此参数。
2. 通过域名访问前端页面
浏览器访问 https://crm-web-test.mywebsite.com,应看到 Snowy 登录页面。
3. 测试登录
使用默认账号登录:
- 用户名:
admin - 密码:
123456
登录成功后,页面应跳转到后台管理界面。
如果出现500,查看错误日志:tail -20 /usr/local/nginx/logs/error.log;如果出现错误:Permission denied,说明 Nginx无法读取 前端的目录。解决:修改nginx.conf,将user nobody;改为user snowy;,然后nginx -s reload。

六、自动化部署脚本
6.1 后端自动化部署脚本
编写脚本实现一键拉取代码、编译打包、重启服务。
bash
su - snowy
cat > /home/snowy/app/scripts/deploy-backend.sh << 'EOF'
#!/bin/bash
set -e
BACKEND_DIR="/home/snowy/app/code/git/snowy-backend"
JAR_LINK="/home/snowy/app/code/backend/snowy-api/snowy-web-app.jar"
echo ">>> 拉取最新代码..."
cd $BACKEND_DIR
git pull origin main # 根据实际分支修改
echo ">>> 编译打包..."
mvn clean install -DskipTests -q
cd snowy-web-app
mvn clean package -DskipTests -q
echo ">>> 更新软链接..."
ln -sf $BACKEND_DIR/snowy-web-app/target/snowy-web-app-*.jar $JAR_LINK
echo ">>> 重启服务..."
sudo systemctl restart snowy-web-app
echo ">>> 后端更新完成!"
EOF
chmod +x /home/snowy/app/scripts/deploy-backend.sh
说明 :脚本使用
-q(quiet)模式减少 Maven 输出,set -e确保任何命令失败时立即退出。需要提前配置 snowy 用户的 sudo 权限(执行 systemctl 免密)。
6.2 前端自动化构建脚本
bash
cat > /home/snowy/app/scripts/build-frontend.sh << 'EOF'
#!/bin/bash
set -e
FRONTEND_DIR="/home/snowy/app/code/git/snowy-frontend"
DEPLOY_DIR="/home/snowy/app/code/web/crm-web-test"
echo ">>> 拉取最新代码..."
cd $FRONTEND_DIR
git pull origin main
# 如果前端代码在子目录中,请进入(根据实际结构调整)
if [ -d "snowy-admin-web" ]; then
cd snowy-admin-web
fi
echo ">>> 安装依赖..."
npm install
echo ">>> 构建生产包..."
npm run build
echo ">>> 部署到目标目录..."
rm -rf $DEPLOY_DIR/*
cp -r dist/* $DEPLOY_DIR/
echo ">>> 设置权限..."
chmod -R 755 $DEPLOY_DIR
echo ">>> 前端部署完成!"
EOF
chmod +x /home/snowy/app/scripts/build-frontend.sh
6.3 日常更新命令
- 更新后端 :
sudo -u snowy /home/snowy/app/scripts/deploy-backend.sh - 更新前端 :
sudo -u snowy /home/snowy/app/scripts/build-frontend.sh
你也可以直接用
su - snowy切换后执行脚本。
七、多环境配置文件
在部署生产环境之前,我们需要对后端配置文件进行改造。Snowy 默认将所有配置(包括数据库密码、Redis 密码等敏感信息)都写在 application.properties 中,这样做有两个问题:
- 安全性:数据库密码等敏感信息会随着代码一起提交到 Git 仓库,造成泄露风险。
- 环境隔离:开发、测试、生产环境使用的数据库地址、密码等通常不同,混在一起容易出错。
因此,我们采用 Spring Boot 原生的 Profile 多环境配置 方案,将敏感信息与通用配置分离。
7.1 配置文件规划
| 文件 | 是否提交 Git | 作用 |
|---|---|---|
application.properties |
✅ 提交 | 存放所有环境的通用配置(如端口、超时、日志级别等)。敏感项应清空或使用占位符。 |
application-demo.properties |
✅ 提交 | 配置模板,列出所有需要覆盖的敏感项(如数据库密码、Redis密码)。其他开发者复制此文件并修改。 |
application-dev.properties |
❌ 不提交 | 开发环境真实配置(每个开发者自己维护)。 |
application-test.properties |
❌ 不提交 | 测试环境真实配置(由测试服务器管理)。 |
application-prod.properties |
❌ 不提交 | 生产环境真实配置(由运维或部署系统管理)。 |
为什么这样设计?
- 通用配置(如
server.port=82)所有环境都一样,适合提交到版本库。- 密码、密钥等敏感信息绝对不能进入版本库,必须通过环境专属文件或环境变量注入。
- 提供
application-demo.properties模板,可以让新成员快速了解需要配置哪些项,而不会遗漏。
7.2 具体操作步骤
现在我的后端代码路径为:
/home/snowy/app/code/git/snowy-backend/snowy-web-app/src/main/resources
1. 修改 application.properties(清理敏感项)
使用 vi 编辑该文件,将以下敏感行清空或注释:
properties
# 数据库密码(清空,或保留占位符 ${DB_PASSWORD})
spring.datasource.dynamic.datasource.master.password=
# Redis 密码(清空)
spring.data.redis.password=
同时,确认以下通用配置符合项目需求(一般无需修改):
properties
server.port=82
spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/snowy?useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.master.username=root
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.database=1
snowy.file.local.path=/home/snowy/app/data/uploads
重要: 保持 Profile 激活行为如下(不要 改为 prod 或 test):
properties
# 默认使用 local 配置(开发环境)
spring.profiles.active=local
#spring.profiles.active=test
#spring.profiles.active=prod
⚠️ 绝对不要在
application.properties中写死spring.profiles.active=prod!
- 如果你提交了
prod,所有人拉取代码后都会尝试连接生产数据库(极其危险)。- 测试环境每次
git pull后都需要手动修改,容易出错且无法自动化。- 正确做法是:在启动命令或 systemd 服务中通过外部参数覆盖(见下文)。
2. 创建配置模板 application-demo.properties
bash
cd /home/snowy/app/code/git/snowy-backend/snowy-web-app/src/main/resources
cat > application-demo.properties << 'EOF'
# ========== 敏感配置示例(请复制此文件并去掉 -demo 后缀) ==========
# 数据库密码
spring.datasource.dynamic.datasource.master.password=你的数据库密码
# Redis 密码(如果没有密码则留空)
spring.data.redis.password=你的Redis密码
EOF
3. 创建生产环境配置文件 application-prod.properties(不提交 Git)
bash
cp application-demo.properties application-prod.properties
vi application-prod.properties
填入生产环境的真实密码:
properties
spring.datasource.dynamic.datasource.master.password=YourStrongPassword123!
spring.data.redis.password=123123
安全提示:生产环境的密码务必使用强密码,并且绝对不要提交到 Git。建议后续改用环境变量注入(见下文)。
4. 设置 .gitignore
在项目根目录的 .gitignore 文件(或 snowy-web-app/.gitignore)中添加以下内容,防止真实配置文件被提交:
# 忽略所有环境真实配置(但保留 demo 模板)
**/application-dev.properties
**/application-test.properties
**/application-prod.properties
**/application-*.properties
!**/application-demo.properties
5. 指定激活的 Profile(外部化)
- 本地开发 :无需任何额外操作,
application.properties中的spring.profiles.active=local会生效,此时会加载application-local.properties(如果存在)或仅使用默认配置。 - 测试环境 :在启动命令中通过
--spring.profiles.active=test覆盖。 - 生产环境 :同样在启动命令或 systemd 服务中指定
--spring.profiles.active=prod。
示例命令:
bash
# 测试环境启动
java -jar snowy-web-app.jar --spring.profiles.active=test
# 生产环境启动
java -jar snowy-web-app.jar --spring.profiles.active=prod
使用 systemd 服务时(推荐) :在服务文件的 ExecStart 中直接添加参数,或在 Environment 中设置 SPRING_PROFILES_ACTIVE。
ini
# /etc/systemd/system/snowy-backend.service
[Service]
ExecStart=/usr/bin/java -jar /home/snowy/app/code/backend/snowy-api/snowy-web-app.jar --spring.profiles.active=prod
或者使用环境变量:
ini
[Service]
Environment="SPRING_PROFILES_ACTIVE=prod"
ExecStart=/usr/bin/java -jar /home/snowy/app/code/backend/snowy-api/snowy-web-app.jar
7.3 其他环境配置
- 开发环境 :复制
application-demo.properties为application-dev.properties,填入本地数据库密码。 - 测试环境 :复制
application-demo.properties为application-test.properties,填入测试服务器密码。
7.4 进阶:使用环境变量(更安全)
如果你不希望任何密码出现在配置文件中(即使是 application-prod.properties),可以直接使用系统环境变量。Spring Boot 支持将配置项通过环境变量覆盖,规则是:将点号改为下划线,且大写。
例如,在 systemd 服务文件中添加:
ini
[Service]
Environment="SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_PASSWORD=YourStrongPassword123!"
Environment="SPRING_DATA_REDIS_PASSWORD=123123"
Environment="SPRING_PROFILES_ACTIVE=prod"
ExecStart=/usr/bin/java -jar /home/snowy/app/code/backend/snowy-api/snowy-web-app.jar
这样,application.properties 中留空的密码字段就会被环境变量填充。这种方式比文件更安全,适合生产环境。
总结 :通过 Profile 机制分离通用配置和敏感配置,并将环境激活行为外部化 ,既能保证代码仓库干净,又能灵活切换环境,避免因误提交导致的生产事故。后续新增任何中间件(如 Elasticsearch、Kafka)的密码,都遵循同样的模式:在
application.properties中保留配置项(值留空),在application-demo.properties中添加示例,在各环境专属文件或环境变量中填写真实值。
八、常见问题排查
Q1:Nginx 返回 403 Forbidden
- 检查
/home/snowy/app/code/web/crm-web-test及父目录权限是否为755。 - 验证
nobody用户能否读取:sudo -u nobody cat /home/snowy/app/code/web/crm-web-test/index.html
Q2:后端启动失败,日志显示 "Permission denied"
- 确保
/home/snowy/app/logs/backend和/home/snowy/app/data/uploads目录存在且属主为snowy:snowy。
Q3:sudo 执行脚本时提示没有权限
- 检查 visudo 中是否已添加
snowy用户对systemctl相关命令的 NOPASSWD 权限。
Q4:前端刷新页面 404
- 确认 Nginx 配置中包含
try_files $uri $uri/ /index.html;
Q5:Maven 依赖下载失败
- 确认
/home/snowy/.m2/settings.xml中镜像配置正确,且网络能访问http://maven.aliyun.com/repository/public。
九、总结
本文采用安全、可扩展、易维护的设计思路,完成了 Snowy 框架的服务器生产环境部署:
| 主题 | 关键实践 |
|---|---|
| 用户与权限 | 创建可登录的 snowy 用户,通过 sudo 限制其仅能操作服务,无法修改系统 |
| 私有仓库 | 将官方源码迁移到公司自己的 Git 仓库,部署时从私有仓库克隆 |
| 目录结构 | 所有项目文件集中在 /home/snowy/app 下,未来可扩展多个项目 |
| 自动化脚本 | deploy-backend.sh 和 build-frontend.sh 实现一键更新 |
| 进程管理 | systemd 服务以 snowy 用户运行,开机自启,崩溃自动重启 |
| Nginx 集成 | 静态文件 + 反向代理,支持 Vue Router history 模式 |
通过遵循最小权限原则和清晰的目录规划,我们既保证了安全性,又为未来的扩展和自动化留下了充足空间。
未来扩展考虑
本文采用了 git pull + 手动执行脚本 的方式,这已经能够满足小型团队的需求。但对于更专业的持续集成/持续部署(CI/CD),你可以考虑:
- Jenkins:配置 webhook,当代码推送到 Git 仓库时自动触发构建和重启。
- GitLab CI :在仓库中编写
.gitlab-ci.yml,实现一键发布。 - Ansible:编写 playbook,实现多服务器批量部署。
后续有时间了,我可能会在后续文章中详细介绍。目前,我们的手动脚本已经为自动化打下了良好基础(脚本可以很容易地被 CI 工具调用)。
另外,还有个TODO事项,就是后续如果需要合并snowy官方的更新,需要写个自动化脚本自动更新同步,但又不能影响自己团队内部的文件改动。等待后续我再想想兼容方案。
备注1:本文部分内容由DeepSeek生成,前提是我给DeepSeek投喂了很多我的想法 _
备注2:本文经历了多次在不同服务器的部署过程,可能存在不同段落的目录和文件名称存在差异,请自行甄别。但不影响主要部署流程。
参考链接
- Snowy 官方仓库:https://gitee.com/xiaonuobase/snowy
- Snowy 官方文档:https://xiaonuo.vip/doc
- Nginx 配置静态文件权限:https://nginx.org/en/docs/ngx_core_module.html#user
本文基于 Snowy v3.6.3 版本,部署环境为 阿里云 Linux 4 / CentOS 7+。