Java Snowy 框架生产环境安全部署全流程(服务器篇)

文章目录

    • 一、写在前面
      • 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-appjournalctl),而无法安装软件、修改系统配置。

思考 :如果将来有多个员工需要维护,可以创建独立的个人账号(如 zhangsan),加入 snowy 组,并同样授予有限 sudo 权限,避免共享账号。

2.3 目录结构设计

设计思路:集中、可扩展、易迁移。

考虑到未来可能在同一台服务器上部署多个 Snowy 项目(例如 app2app3),我们将所有项目文件放在 /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 节)

问题四:内存不足导致编译失败

  • 错误信息OutOfMemoryErrorJava heap space

  • 原因:服务器内存不足(低于 2GB)或 Maven 分配的内存过小。

  • 解决方案:增加 Maven 编译内存

    bash 复制代码
    export 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 attributeFailed 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

问题五:打包过程中内存溢出

  • 错误信息OutOfMemoryErrorGC overhead limit exceeded

  • 原因:项目模块较多,打包时内存不足。

  • 解决方案:增加 Maven 编译内存

    bash 复制代码
    export 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% 的打包问题可以通过以下步骤解决:

  1. 确保在项目根目录执行过 mvn clean install -DskipTests
  2. 确保 Maven 版本 ≥ 3.6.3
  3. 确保 Java 版本为 17 或 21
  4. 配置了阿里云镜像加速依赖下载
  5. 遇到 .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 即可,无需 stopstart
  • 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'
  • 原因:配置文件中的数据库名与实际不符,或数据库未创建
  • 解决方法
    1. 确认数据库名:mysql -u root -p -e "SHOW DATABASES;"
    2. 修改 application.properties 中的 url 为正确数据库名
    3. 重新打包 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_modulespackage-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 中,这样做有两个问题:

  1. 安全性:数据库密码等敏感信息会随着代码一起提交到 Git 仓库,造成泄露风险。
  2. 环境隔离:开发、测试、生产环境使用的数据库地址、密码等通常不同,混在一起容易出错。

因此,我们采用 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 激活行为如下(不要 改为 prodtest):

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.propertiesapplication-dev.properties,填入本地数据库密码。
  • 测试环境 :复制 application-demo.propertiesapplication-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.shbuild-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 v3.6.3 版本,部署环境为 阿里云 Linux 4 / CentOS 7+

相关推荐
web守墓人2 小时前
【linux】Mubuntu v1.0.10更新日志
linux·运维·服务器
宸津-代码粉碎机2 小时前
Spring Boot 4.0虚拟线程实战续更预告:高阶技巧、监控排查与分布式场景落地指南
java·大数据·spring boot·分布式·后端·python
Rsun045512 小时前
6、Java 适配器模式从入门到实战
java·开发语言·适配器模式
MaCa .BaKa2 小时前
52-考研备考服务平台系统-考研系统
java·spring boot·mysql·考研·tomcat·maven·mybatis
_深海凉_2 小时前
LeetCode热题100-最长公共子序列
java·开发语言·前端
赵钰老师2 小时前
最新Hermes Agent 技能封装与科研自动化:以 Meta-Analysis 为例-实现从文献检索到绘图的一站式工作流
运维·chatgpt·自动化·ai编程·ai写作
.小小陈.2 小时前
深度拆解 Linux 进程间通信(IPC):从管道到 System V 全链路详解
linux·服务器·网络·学习
8Qi82 小时前
Elasticsearch实战篇:索引库、文档与JavaRestClient操作指南
java·大数据·elasticsearch·搜索引擎·微服务·架构·springcloud
Sss_Ass2 小时前
跟着老师不迷路系列---跟着李述铜老师学习汇编语言之基本汇编程序指令集分类
开发语言·学习·学习方法·汇编语言·李述铜