前言
本文记录了一个完整的全栈项目(Spring Boot + React)从本地开发到生产环境部署的全过程,包括遇到的所有问题及解决方案。项目最终部署在 CentOS 7 服务器上,通过 IP 地址直接访问。
项目技术栈:
- 后端:Java 17 + Spring Boot 3.1.5 + Spring Security + JWT
- 前端:React 18 + Ant Design + Vite
- 服务器:CentOS 7 + Nginx + 宝塔面板
最终效果:
- 访问地址:
http://服务器IP - 前端和后端分离部署
- Nginx 反向代理实现统一访问
一、服务器环境准备
1.1 服务器基本信息
操作系统:CentOS 7.9
管理工具:宝塔面板
服务器 IP:101.32.242.75(示例)
1.2 安装 Java 17
CentOS 7 默认的 Java 版本太低,需要手动安装 OpenJDK 17。
bash
# 1. 下载 OpenJDK 17
cd /opt
wget https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz
# 2. 解压
tar -zxvf openjdk-17.0.2_linux-x64_bin.tar.gz
# 3. 配置环境变量
cat >> /etc/profile << 'EOF'
export JAVA_HOME=/opt/jdk-17.0.2
export PATH=$JAVA_HOME/bin:$PATH
EOF
# 4. 使配置生效
source /etc/profile
# 5. 验证安装
java -version
# 输出:openjdk version "17.0.2" 2022-01-18
⚠️ 常见问题: 文件名不匹配
解决方案:下载后先用 ls 查看实际文件名,再解压。
bash
ls -lh /opt/*.tar.gz
tar -zxvf openjdk-17.0.2_linux-x64_bin.tar.gz # 使用实际文件名
1.3 安装 Maven
bash
# 1. 下载 Maven 3.9.9
cd /opt
wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz
# 2. 解压
tar -zxvf apache-maven-3.9.9-bin.tar.gz
# 3. 配置环境变量
cat >> /etc/profile << 'EOF'
export MAVEN_HOME=/opt/apache-maven-3.9.9
export PATH=$MAVEN_HOME/bin:$PATH
EOF
# 4. 使配置生效
source /etc/profile
# 5. 验证安装
mvn -version
优化建议: 配置国内镜像加速依赖下载
bash
cat > ~/.m2/settings.xml << 'EOF'
<settings>
<mirrors>
<mirror>
<id>aliyun</id>
<mirrorOf>central</mirrorOf>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
</settings>
EOF
1.4 安装 Nginx
通过宝塔面板安装 Nginx:
- 登录宝塔面板:
http://服务器IP:8888 - 软件商店 → 搜索 Nginx → 安装
或者命令行安装:
bash
yum install -y nginx
systemctl start nginx
systemctl enable nginx
二、后端部署
2.1 上传代码到服务器
bash
# 在服务器上创建项目目录
mkdir -p /home/restaurant-reservation
# 从本地上传后端代码
cd ~/Desktop/restaurant-reservation
scp -r backend root@服务器IP:/home/restaurant-reservation/
2.2 编译后端项目
bash
# SSH 连接到服务器
ssh root@服务器IP
# 进入后端目录
cd /home/restaurant-reservation/backend
# 编译项目(跳过测试)
mvn clean package -DskipTests
# 编译完成后检查 jar 文件
ls -lh target/*.jar
输出示例:
-rw-r--r-- 1 root root 50M Dec 10 18:00 reservation-system-1.0.0.jar
⚠️ 常见问题: 编译超时或依赖下载失败
解决方案: 确保已配置 Maven 阿里云镜像(见 1.3 节)
三、前端部署
3.1 为什么要在本地构建?
CentOS 7 的 glibc 版本过低,无法很好地支持最新的 Node.js 和 Vite,在服务器上构建前端会遇到以下错误:
bash
error during build:
TypeError: crypto$2.getRandomValues is not a function
最佳实践: 在本地 Mac/Windows 上构建,然后上传到服务器。
3.2 本地构建前端
bash
# 在本地进入前端目录
cd ~/Desktop/restaurant-reservation/frontend
# 安装依赖(首次)
npm install
# 构建生产版本
npm run build
# 构建成功后,dist 目录包含所有静态文件
ls -lh dist/
构建输出示例:
dist/index.html 0.58 kB │ gzip: 0.44 kB
dist/assets/index-BOrl-zwt.css 0.27 kB │ gzip: 0.22 kB
dist/assets/index-DlJaEm6z.js 2,717.96 kB │ gzip: 834.43 kB
✓ built in 13.07s
3.3 上传到服务器
bash
# 上传构建好的 dist 目录
scp -r dist root@服务器IP:/home/restaurant-reservation/frontend/
# 输入密码,等待上传完成
💡 提示: 输入密码时不会显示任何字符,这是正常的。
四、配置 Nginx
4.1 创建 Nginx 配置文件
bash
# SSH 到服务器
ssh root@服务器IP
# 创建配置文件
cat > /www/server/panel/vhost/nginx/restaurant.conf << 'EOF'
server {
listen 80;
server_name 服务器IP;
# 前端静态文件
root /home/restaurant-reservation/frontend/dist;
index index.html;
# 前端路由(SPA)
location / {
try_files $uri $uri/ /index.html;
}
# 后端 API 代理
location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
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;
}
}
EOF
配置说明:
listen 80:监听 80 端口(HTTP)root:前端静态文件目录location /:处理前端路由,所有路径都返回 index.html(SPA 必需)location /api/:反向代理后端 API,前端通过/api/xxx访问后端
4.2 测试并重启 Nginx
bash
# 测试配置文件语法
nginx -t
# 输出:nginx: configuration file test is successful
# 重启 Nginx
/etc/init.d/nginx restart
⚠️ 常见问题: nginx: [error] invalid PID number
解决方案: 使用宝塔提供的启动脚本,不要用 nginx -s reload
bash
/etc/init.d/nginx restart
五、启动服务
5.1 创建一键启动脚本
bash
cat > /home/restaurant-reservation/start-server.sh << 'EOF'
#!/bin/bash
echo "=========================================="
echo "启动餐厅预订系统"
echo "=========================================="
# 设置 Java 17
export JAVA_HOME=/opt/jdk-17.0.2
export PATH=$JAVA_HOME/bin:$PATH
# 重启 Nginx
echo "1. 重启 Nginx..."
/etc/init.d/nginx restart
sleep 2
# 停止旧的后端进程
echo "2. 停止旧的后端进程..."
pkill -f 'reservation-system'
sleep 2
# 启动后端服务
echo "3. 启动后端服务..."
cd /home/restaurant-reservation
nohup java -jar backend/target/reservation-system-1.0.0.jar > backend.log 2>&1 &
echo "后端进程 PID: $!"
# 等待启动
echo "4. 等待后端启动(15秒)..."
sleep 15
# 检查服务状态
echo "5. 检查服务状态..."
ps aux | grep nginx | grep -v grep | head -n 2
ps aux | grep java | grep reservation | grep -v grep
echo ""
echo "=========================================="
echo "✅ 系统启动完成!"
echo "=========================================="
echo "访问地址: http://服务器IP"
echo "查看日志: tail -f /home/restaurant-reservation/backend.log"
EOF
# 赋予执行权限
chmod +x /home/restaurant-reservation/start-server.sh
5.2 启动系统
bash
/home/restaurant-reservation/start-server.sh
输出示例:
==========================================
启动餐厅预订系统
==========================================
使用 Java 版本:
openjdk version "17.0.2" 2022-01-18
==========================================
1. 重启 Nginx...
==========================================
Starting nginx... done
==========================================
2. 停止旧的后端进程...
==========================================
==========================================
3. 启动后端服务...
==========================================
后端进程 PID: 25544
==========================================
4. 等待后端启动(15秒)...
==========================================
==========================================
5. 检查服务状态...
==========================================
Nginx 进程:
root 25519 0.0 0.2 143312 4804 ? Ss 18:16 0:00 nginx: master process
后端进程:
root 25544 132 13.8 3229216 282728 pts/1 Sl+ 18:16 0:19 java -jar backend/target/reservation-system-1.0.0.jar
==========================================
✅ 系统启动完成!
==========================================
访问地址: http://服务器IP
查看日志: tail -f /home/restaurant-reservation/backend.log
六、验证部署
6.1 测试前端
bash
curl -I http://localhost:80
期望输出:
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html
6.2 测试后端 API
bash
# 测试登录接口
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user1","password":"123456"}'
期望输出:
json
{
"token": "eyJhbGciOiJIUzI1NiJ9...",
"type": "Bearer",
"user": {
"id": 1,
"username": "user1",
"role": "USER"
}
}
6.3 浏览器访问
打开浏览器,访问: http://服务器IP
如果能看到登录页面,说明部署成功!
七、实战中遇到的问题及解决方案
问题 1:找不到 jar 文件
错误信息:
Error: Unable to access jarfile backend/target/reservation-system-1.0-SNAPSHOT.jar
原因: 启动脚本中的 jar 文件名与实际编译出的文件名不一致。
解决方案:
bash
# 1. 查找实际的 jar 文件
find /home/restaurant-reservation -name "*.jar" -type f
# 2. 修改启动脚本中的文件名,使用实际文件名
# 例如:reservation-system-1.0.0.jar 而不是 reservation-system-1.0-SNAPSHOT.jar
问题 2:前端构建失败
错误信息:
error during build:
TypeError: crypto$2.getRandomValues is not a function
原因: CentOS 7 的 glibc 版本过低,不支持新版 Node.js 和 Vite。
解决方案: 在本地构建,然后上传(见第三章)。
问题 3:API 返回 403 Forbidden
测试命令:
bash
curl http://localhost:8080/api/restaurants
返回:
json
{"timestamp":"2025-12-10T10:16:24.638+00:00","status":403,"error":"Forbidden","path":"/api/restaurants"}
说明: 这是正常现象!该接口需要 JWT 认证。公开接口(如登录接口)不会返回 403。
问题 4:Nginx PID 错误
错误信息:
nginx: [error] invalid PID number "" in "/www/server/nginx/logs/nginx.pid"
解决方案:
bash
# 不要使用 nginx -s reload
# 使用宝塔提供的启动脚本
/etc/init.d/nginx restart
问题 5:scp 上传失败
错误信息:
Permission denied, please try again.
ssh_dispatch_run_fatal: Connection to 服务器IP port 22: Broken pipe
解决方案:
- 确认密码正确(输入时不显示任何字符)
- 如果多次失败,使用宝塔面板的文件管理功能上传
- 访问宝塔面板 → 文件 → 上传
八、运维管理
8.1 常用命令
bash
# 启动系统
/home/restaurant-reservation/start-server.sh
# 停止后端
pkill -f 'reservation-system'
# 重启 Nginx
/etc/init.d/nginx restart
# 查看后端日志
tail -f /home/restaurant-reservation/backend.log
# 查看最后 100 行日志
tail -n 100 /home/restaurant-reservation/backend.log
# 查看进程状态
ps aux | grep java
ps aux | grep nginx
# 查看端口占用
netstat -tlnp | grep 8080
netstat -tlnp | grep 80
8.2 更新部署
更新后端:
bash
# 1. 上传新代码
scp -r backend root@服务器IP:/home/restaurant-reservation/
# 2. SSH 到服务器
ssh root@服务器IP
# 3. 重新编译
cd /home/restaurant-reservation/backend
mvn clean package -DskipTests
# 4. 重启服务
/home/restaurant-reservation/start-server.sh
更新前端:
bash
# 1. 本地构建
cd ~/Desktop/restaurant-reservation/frontend
npm run build
# 2. 上传到服务器
scp -r dist root@服务器IP:/home/restaurant-reservation/frontend/
# 3. 重启 Nginx
ssh root@服务器IP
/etc/init.d/nginx restart
九、架构说明
9.1 请求流程
用户浏览器
↓
http://服务器IP
↓
Nginx (端口 80)
├─ / → 前端静态文件 (React)
└─ /api/ → 反向代理到 http://127.0.0.1:8080/api/
↓
Spring Boot 后端 (端口 8080)
↓
H2 内存数据库
9.2 目录结构
/home/restaurant-reservation/
├── backend/
│ ├── src/
│ ├── pom.xml
│ └── target/
│ └── reservation-system-1.0.0.jar
├── frontend/
│ └── dist/
│ ├── index.html
│ └── assets/
├── backend.log
└── start-server.sh
十、总结
10.1 关键点回顾
- 环境准备是基础
- Java 17(不能用默认的 Java 8)
- Maven 3.9+
- Nginx
- 前端构建要灵活
- 服务器环境受限时,本地构建是最优解
- dist 目录是纯静态文件,可直接上传使用
- Nginx 配置是核心
- 前端静态文件服务
- 后端 API 反向代理
- SPA 路由支持(try_files)
- 自动化脚本很重要
- 减少手动操作
- 避免遗漏步骤
- 提高部署效率
- 日志是排查问题的关键
- 实时查看日志:
tail -f backend.log - 搜索错误:
grep -i "error" backend.log
- 实时查看日志:
10.2 优化建议
生产环境建议:
- 使用域名nginx
server_name yourdomain.com; - 启用 HTTPSnginx
listen 443 ssl; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; - 使用 MySQL 替代 H2yaml
spring: datasource: url: jdbc:mysql://localhost:3306/restaurant username: root password: your_password - 配置防火墙bash
firewall-cmd --permanent --add-service=http firewall-cmd --permanent --add-service=https firewall-cmd --reload - 设置进程守护
- 使用 systemd 或 supervisor 管理后端进程
- 进程意外退出时自动重启
10.3 经验总结
- 遇到问题不要慌,先查日志
- 文件名、路径要仔细核对
- 环境变量配置后要 source 生效
- 本地能跑通,服务器就能跑通(环境一致的前提下)
- 写好脚本,一键部署才是王道
附录:快速部署检查清单
□ 服务器准备
□ Java 17 安装并配置环境变量
□ Maven 安装并配置镜像
□ Nginx 安装
□ 后端部署
□ 代码上传到服务器
□ mvn clean package -DskipTests
□ 确认 jar 文件存在
□ 前端部署
□ 本地 npm run build
□ 上传 dist 目录到服务器
□ Nginx 配置
□ 创建配置文件
□ nginx -t 测试
□ 重启 Nginx
□ 启动服务
□ 创建启动脚本
□ 执行启动脚本
□ 检查进程是否运行
□ 验证
□ 浏览器访问 IP
□ 测试登录功能
□ 查看后端日志