代码写完了,接口调通了,接下来要做的是把项目部署到服务器上,让其他人也能访问。
如果你只在本地跑过 Spring Boot 项目,那你可能对部署这件事还有一些陌生------打包命令是什么?服务器上怎么装 Java?怎么让项目在后台一直运行,关掉终端也不会停?
这一篇我们用一篇完整的实战指南,把这些问题全部解决。从 Linux 基础命令到云服务器选购,从环境安装到项目部署,完整走一遍。
学习目标
- 掌握 Linux 常用命令(文件操作、进程管理、网络查看)
- 掌握在 Ubuntu 上安装 JDK 和 MySQL
- 掌握 Spring Boot 项目的打包(
mvn package)与部署(java -jar) - 掌握多环境配置 (
application-dev.yml/application-prod.yml) - 掌握
nohup后台运行和kill停止进程
正文
一、Linux 基础命令速通
在开始部署之前,先熟悉一组最常用的 Linux 命令。这些命令覆盖了 90% 的日常操作场景。
文件操作类:
| 命令 | 用法 | 说明 |
|---|---|---|
ls |
ls -la |
列出当前目录下的所有文件(含隐藏文件),显示详细信息 |
cd |
cd /opt |
切换目录 |
pwd |
pwd |
显示当前所在目录的完整路径 |
mkdir |
mkdir -p app/logs |
创建目录,-p 可以递归创建父目录 |
rm |
rm -rf app/ |
删除目录或文件,-r 递归删除,-f 强制不确认 |
cp |
cp source.txt target/ |
复制文件 |
mv |
mv old.txt new.txt |
移动文件或重命名 |
tail |
tail -f app.log |
查看文件末尾,-f 实时跟踪文件变化,最常用于查看日志 |
进程管理类:
| 命令 | 用法 | 说明 |
|---|---|---|
ps |
`ps -ef | grep java` |
kill |
kill -9 12345 |
终止进程,-9 强制终止,-15 优雅终止 |
netstat |
netstat -tlnp |
查看端口占用情况,-tlnp 显示 TCP 端口和对应的进程 |
grep 是文本搜索工具,常与其他命令配合使用。| 是管道操作符,把前一个命令的输出传给后一个命令作为输入。 这两个符号是 Linux 命令行的基本组合方式,几乎所有排查场景都会用到。
二、云服务器选购与连接
如果你是第一次买云服务器,不用纠结配置。
规格推荐:
- 入门级(个人项目):1 核 2G 内存 + 40G 硬盘,约 50-80 元/月
- 标准级(小型应用):2 核 4G 内存 + 60G 硬盘,约 150-250 元/月
- 生产级(线上正式服务):4 核 8G 内存 + 100G 硬盘,约 400-600 元/月
操作系统 :推荐 Ubuntu 22.04 LTS。长期支持版本,软件源稳定,国内云厂商均有镜像。
连接方式:
- 在云厂商控制台重置 root 密码
- 使用 SSH 客户端连接(命令行或 XShell / FinalShell / Termius)
- 命令格式:
ssh root@你的公网IP -p 22
安全组配置:
这是最容易忽略的步骤。服务器默认只开放 22 端口(SSH),你需要手动开放:
| 端口 | 用途 |
|---|---|
| 22 | SSH 远程连接 |
| 80 | HTTP 服务(Nginx) |
| 443 | HTTPS 服务 |
| 8080 | Spring Boot 应用直接访问 |
| 3306 | MySQL(建议仅内网开放) |
在云厂商控制台的"安全组"或"防火墙"中添加入站规则,允许对应端口的访问。
三、Java 环境安装
Ubuntu 22.04 可以直接用 apt 安装 OpenJDK 17:
bash
# 更新软件包索引
sudo apt update
# 安装 OpenJDK 17
sudo apt install -y openjdk-17-jdk
# 验证安装
java -version
输出类似:
openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment (build 17.0.9+9-Ubuntu-122.04)
OpenJDK 64-Bit Server VM (build 17.0.9+9-Ubuntu-122.04, mixed mode, sharing)
四、MySQL 安装与配置
bash
# 安装 MySQL 8.0
sudo apt install -y mysql-server
# 启动 MySQL 服务
sudo systemctl start mysql
sudo systemctl enable mysql # 设置开机自启
# 查看 MySQL 状态
sudo systemctl status mysql
安全配置:
MySQL 8.0 默认 root 用户使用 auth_socket 认证插件,需要用 sudo mysql 才能登录。为了方便外部连接,需要改为密码认证:
bash
# 用 root 登录 MySQL(无密码)
sudo mysql
# 切换到 mysql 数据库
USE mysql;
# 修改 root 密码
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的新密码';
# 允许外部 IP 连接(生产环境建议仅限内网)
CREATE USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的新密码';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
# 刷新权限
FLUSH PRIVILEGES;
# 退出
EXIT;
测试连接:
bash
# 用新密码登录测试
mysql -u root -p
如果连接成功,说明配置完成。
五、多环境配置
在生产环境中,开发环境的配置(如本地 MySQL、调试日志)和生产环境完全不同。Spring Boot 通过 Profile 来隔离多环境配置。
配置结构:
src/main/resources/
├── application.yml # 公共配置
├── application-dev.yml # 开发环境
└── application-prod.yml # 生产环境
公共配置(application.yml) :
yaml
spring:
profiles:
active: dev # 默认使用 dev 环境
jackson:
time-zone: Asia/Shanghai
date-format: yyyy-MM-dd HH:mm:ss
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
server:
port: 8080
开发环境(application-dev.yml) :
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/blog_db?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
logging:
level:
com.example.blog: DEBUG
生产环境(application-prod.yml) :
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/blog_db?useSSL=true&serverTimezone=Asia/Shanghai
username: blog_user
password: 生产环境强密码
logging:
level:
com.example.blog: INFO
server:
port: 8080 # 生产环境通常用 Nginx 转发,这里保持 8080
切换环境:
在打包或启动时通过 spring.profiles.active 指定环境:
bash
# 方式一:启动时指定
java -jar blog.jar --spring.profiles.active=prod
# 方式二:通过环境变量
export SPRING_PROFILES_ACTIVE=prod
java -jar blog.jar
# 方式三:在配置文件中设置(不推荐)
# application.yml 中写 spring.profiles.active: prod
六、项目打包与上传
打包:
bash
# 在项目根目录执行(跳过测试,加快打包速度)
mvn clean package -DskipTests
# 打包完成后,target 目录下会生成 blog-backend-1.0.0.jar
上传到服务器:
使用 scp 命令上传:
bash
# 从本地上传到服务器
scp target/blog-backend-1.0.0.jar root@你的服务器IP:/opt/blog/
如果服务器没有 /opt/blog/ 目录,先用 mkdir -p /opt/blog 创建。
在服务器上验证:
bash
# 切换到部署目录
cd /opt/blog
# 查看文件
ls -lh
# 输出:-rw-r--r-- 1 root root 45M Jan 15 10:30 blog-backend-1.0.0.jar
七、后台运行与日志查看
前台运行(仅用于测试,关掉终端就停了) :
bash
java -jar blog-backend-1.0.0.jar --spring.profiles.active=prod
后台运行(生产环境推荐) :
bash
nohup java -jar blog-backend-1.0.0.jar \
--spring.profiles.active=prod \
> /opt/blog/logs/app.log 2>&1 &
这个命令拆开看:
nohup:忽略 SIGHUP 信号,终端关闭后进程继续运行>:重定向标准输出(stdout)到日志文件2>&1:把标准错误(stderr)也合并到标准输出,统一写入日志&:在后台运行,不占用终端
查看日志:
bash
# 实时跟踪日志(最常用)
tail -f /opt/blog/logs/app.log
# 查看最近 100 行
tail -n 100 /opt/blog/logs/app.log
# 查看全部日志
cat /opt/blog/logs/app.log
停止进程:
bash
# 查找 Java 进程的 PID
ps -ef | grep blog-backend-1.0.0.jar
# 输出示例:
# root 12345 1 0 10:30 ? 00:00:30 java -jar blog-backend-1.0.0.jar
# 优雅停止
kill 12345
# 如果优雅停止无效,强制终止
kill -9 12345
查看进程是否还在运行:
bash
ps -ef | grep java | grep blog
一键启动和停止脚本:
把以下脚本保存为 /opt/blog/start.sh:
bash
#!/bin/bash
JAR_NAME="blog-backend-1.0.0.jar"
LOG_DIR="/opt/blog/logs"
LOG_FILE="$LOG_DIR/app.log"
# 创建日志目录
mkdir -p $LOG_DIR
# 检查进程是否已经在运行
PID=$(ps -ef | grep $JAR_NAME | grep -v grep | awk '{print $2}')
if [ -n "$PID" ]; then
echo "应用已在运行,PID: $PID"
exit 1
fi
# 启动应用
nohup java -jar $JAR_NAME --spring.profiles.active=prod > $LOG_FILE 2>&1 &
echo "应用启动成功,日志文件: $LOG_FILE"
停止脚本 /opt/blog/stop.sh:
bash
#!/bin/bash
JAR_NAME="blog-backend-1.0.0.jar"
PID=$(ps -ef | grep $JAR_NAME | grep -v grep | awk '{print $2}')
if [ -z "$PID" ]; then
echo "应用未运行"
exit 1
fi
kill $PID
echo "应用已停止,PID: $PID"
代码示例
示例一:多环境配置切换
第一步:确认配置文件存在
src/main/resources/
├── application.yml
├── application-dev.yml
└── application-prod.yml
第二步:在 application.yml 中指定默认环境:
yaml
spring:
profiles:
active: dev # 开发时默认用 dev
第三步:打包时指定环境:
bash
# 开发环境打包
mvn clean package -DskipTests
# 启动时覆盖环境
java -jar target/blog.jar --spring.profiles.active=prod
第四步:验证环境是否生效:
启动后查看日志,找到类似以下输出:
... Initializing Spring Boot Application (prod)
... The following 1 profile is active: "prod"
示例二:项目部署完整流程(操作清单)
这是一份完整的部署操作清单,你可以按顺序执行:
服务器端:
bash
# 1. 安装 Java
sudo apt update && sudo apt install -y openjdk-17-jdk
java -version
# 2. 安装 MySQL
sudo apt install -y mysql-server
sudo systemctl start mysql
sudo systemctl enable mysql
# 3. 创建数据库
mysql -u root -p
CREATE DATABASE blog_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
EXIT;
# 4. 导入数据(如果有 SQL 文件)
mysql -u root -p blog_db < /opt/blog/blog.sql
# 5. 创建部署目录
mkdir -p /opt/blog/logs
# 6. 上传 JAR 包(在本地执行)
scp target/blog-backend-1.0.0.jar root@你的IP:/opt/blog/
# 7. 修改生产环境配置
vi /opt/blog/application-prod.yml
# 修改数据库密码,改为生产环境的强密码
# 8. 启动项目
cd /opt/blog
nohup java -jar blog-backend-1.0.0.jar \
--spring.profiles.active=prod \
> logs/app.log 2>&1 &
# 9. 查看启动日志
tail -f logs/app.log
# 10. 测试接口
curl http://localhost:8080/api/posts
前端联调测试:
项目启动后,使用 Postman 或 curl 测试接口:
bash
# 健康检查(如果写了 /health 接口)
curl http://你的IP:8080/api/health
# 获取文章列表
curl http://你的IP:8080/api/posts
如果访问失败,检查安全组是否开放了 8080 端口。
新手错误 vs 正确姿势
| 错误表象 | 根本原因 | 正确姿势 |
|---|---|---|
| 外网无法访问项目,浏览器一直转圈 | 云服务器防火墙/安全组未开放端口 | 在云厂商控制台 → 安全组 → 添加入站规则,开放 8080 端口(或其他应用端口) |
项目启动失败,报 Communications link failure |
数据库连接配置错误,或 MySQL 未启动 | 检查 application-prod.yml 中的数据库 URL、用户名、密码;用 systemctl status mysql 确认 MySQL 正在运行 |
nohup 启动后项目又停了,没有任何日志 |
nohup.out 或指定日志文件权限不足,或 JAR 包路径错误 |
确保日志目录有写入权限:chmod 755 /opt/blog/logs |
| 关闭终端后项目停止运行 | 使用了 java -jar 而非 nohup ... & |
用 nohup + & 启动,或用 screen / tmux 替代 |
| `ps -ef | grep java` 看到多个 Java 进程,不知道停哪个 | 服务器上运行了多个 Java 应用 |
疑难深度追问
Q1:为什么 nohup 启动的程序关闭终端后仍在运行?
nohup 命令让进程忽略 SIGHUP 信号 (终端关闭时发送的信号),因此终端关闭后进程不会退出。& 让进程在后台运行,不占用终端。两者结合就实现了"关掉终端,进程继续跑"的效果。
Q2:如果部署后发现代码有 bug,如何快速更新?
流程是:本地修改代码 → mvn clean package -DskipTests → scp 上传新的 JAR 包 → ps -ef | grep blog.jar 找到旧进程的 PID → kill 停止旧进程 → nohup 启动新进程。如果使用蓝绿部署,可以先启动新版本(用不同的端口),验证通过后再切换流量。
Q3:服务器重启后,Java 进程会自动启动吗?
不会。 nohup 启动的进程不会自动重启。要实现开机自启,有以下几种方案:
| 方案 | 复杂度 | 适用场景 |
|---|---|---|
| systemd 服务 | 中 | 生产环境推荐 |
| crontab @reboot | 低 | 简单场景 |
| 手动重启 | 最低 | 临时部署、验证环境 |
systemd 方案(生产环境推荐) :
创建 /etc/systemd/system/blog.service:
[Unit]
Description=Blog Backend Service
After=network.target mysql.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/blog
ExecStart=/usr/bin/java -jar /opt/blog/blog-backend-1.0.0.jar --spring.profiles.active=prod
StandardOutput=append:/opt/blog/logs/app.log
StandardError=append:/opt/blog/logs/app-error.log
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
启用服务:
bash
sudo systemctl daemon-reload
sudo systemctl enable blog
sudo systemctl start blog
sudo systemctl status blog
这样服务器重启后,服务会自动启动。
思考与延伸
-
动手验证 :按照完整操作清单,在自己的云服务器上部署一遍博客系统。从
apt install到nohup java -jar完整走一遍流程。 -
思考题 :如果部署后发现数据库连接报错,但
application-prod.yml的配置看起来没问题,你会怎么排查?(提示:检查 MySQL 是否允许外部连接、防火墙是否放行 3306 端口、MySQL 服务是否在运行、密码是否包含特殊字符导致 YAML 解析错误) -
延伸阅读 :systemd 的官方文档对
ExecStart、Restart等参数有详细的说明。另外,crontab -e配合@reboot可以实现简单的开机自启。
参考与延伸阅读
- Ubuntu. OpenJDK Installation Guide. Ubuntu Documentation
- MySQL. MySQL 8.0 Installation Guide. MySQL Official Documentation
- Spring Boot. Deploying Spring Boot Applications. Spring Boot Documentation
- Baeldung. Deploy a Spring Boot Application on Linux. Baeldung, 2024
- 阿里云. Spring Boot 项目部署到云服务器完整流程. 2024-03
- 腾讯云. 使用 systemd 管理 Spring Boot 应用. 2024-05