停止用 nohup 启动生产!教你企业级 SpringBoot 部署方案(含无 Root 版)

摘要: 还在用
java -jar配合nohup跑生产?服务器重启服务就挂?日志文件乱成一团?无 Root 权限怎么做进程守护?本文手把手教你使用 Systemd + Nginx 打造稳健的 Java 应用部署架构。
在开发环境,我们习惯了随手敲下一行:
nohup java -jar my-app.jar &
这确实能跑,但在生产环境,这种做法就像是在高压线上走钢丝。
❌ 进程管理混乱:想重启?先 ps 再 kill,手一抖可能误杀其他服务。
❌ 开机无法自启:服务器因故障重启,深夜接到报警电话的痛,你懂的。
❌ 权限失控:为了省事直接用 root 跑?黑客都要谢谢你。
今天,我们来聊聊如何用 Linux 原生的 Systemd,优雅、安全地管理 SpringBoot 应用。(文末包含无 Root 权限的内网环境解决方案)
方案一:标准生产环境(拥有 Sudo/Root 权限)
这是最推荐的标准做法,适用于你有权管理服务器配置的场景。
1. 准备工作:创建专用账户
为了安全,绝对不要使用 root 用户运行业务代码。
# 创建一个没有登录权限的用户 app_user
sudo useradd -r -s /bin/false app_user
# 确保该用户对 jar 包有读取权限
sudo chown app_user:app_user /opt/prod/app.jar
2. 编写 Service 单元文件
Systemd 通过 .service 文件管理进程。创建文件 /etc/systemd/system/myapp.service:
[Unit]
Description=My SpringBoot Enterprise App
# 在网络服务启动后再启动
After=syslog.target network.target
[Service]
# 【关键】指定运行用户,实现权限隔离
User=app_user
Group=app_user
# 【核心】启动命令 (建议指定 JVM 参数)
ExecStart=/home/zml/.jdks/corretto-1.8.0_412/bin/java -Djava.ext.dirs=/home/zml/.jdks/corretto-1.8.0_412/jre/lib/ext:/home/zml/.jdks/corretto-1.8.0_412/lib/ext -Xms512m -Xmx512m -Xmn256m -Xloggc:/opt/share/app/demo/logs/sso_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dproject.home=/opt/share/app/sso -jar /opt/share/app/demo/myapp-user.jar --server.max-http-header-size=524288 --spring.config.location=file:/opt/share/app/demo/config/
# 【关键】Spring Boot 优雅关闭
# 143 代表 SIGTERM 信号,Java 应用正常退出时的状态码
SuccessExitStatus=143
# 崩溃自动重启配置
Restart=always
RestartSec=10
# 日志由 Systemd 接管 (可用 journalctl 查看)
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=myapp
[Install]
WantedBy=multi-user.target
3. 三板斧:激活与管理
# 1. 重载配置
sudo systemctl daemon-reload
# 2. 设置开机自启 (从此告别重启焦虑)
sudo systemctl enable myapp
# 3. 启动服务
sudo systemctl start myapp
方案二:内网受限环境(无 Root 权限)
这是很多银行、国企开发者的痛点:只有普通账号,没有 sudo 权限,Systemd 还能用吗?
答案是:能!Systemd 支持用户模式(User Mode)。
1. 关键前提:开启“驻留模式”
默认情况下,用户退出 SSH,用户下的 Systemd 进程会被杀掉。
你需要找管理员执行唯一的一次操作(或者祈祷管理员已经开启了它):
# 检查方法,如果输出 Linger=no,则需要在服务器上执行一次开启操作。
loginctl show-user $USER --property=Linger
# 开启用户驻留,允许服务在用户注销后继续运行
sudo loginctl enable-linger <你的用户名>
2. 配置用户级服务
配置文件位置变了,放在 ~/.config/systemd/user/ 下。
mkdir -p ~/.config/systemd/user
vim ~/.config/systemd/user/myapp.service
配置差异点:
- 不需要
User=和Group=字段。 [Install]部分改为WantedBy=default.target。
[Unit]
Description=My User Space App
After=network.target
[Service]
# 注意:使用绝对路径
ExecStart=/home/zml/.jdks/corretto-1.8.0_412/bin/java -Djava.ext.dirs=/home/zml/.jdks/corretto-1.8.0_412/jre/lib/ext:/home/zml/.jdks/corretto-1.8.0_412/lib/ext -Xms512m -Xmx512m -Xmn256m -Xloggc:/opt/share/app/demo/logs/sso_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dproject.home=/opt/share/app/sso -jar /opt/share/app/demo/myapp-user.jar --server.max-http-header-size=524288 --spring.config.location=file:/opt/share/app/demo/config/
SuccessExitStatus=143
Restart=always
RestartSec=10
SyslogIdentifier=myapp-user
[Install]
# 【注意】这里不同于系统级配置
WantedBy=default.target
3. 用户级管理命令
所有命令加上 --user 参数即可,完全不需要 sudo:
# 启动
systemctl --user start myapp
# 开机自启 (随服务器启动)
systemctl --user enable myapp
# 查看日志
journalctl --user -u myapp -f
进阶 Tips (让你的部署更专业)
1. JVM 参数别乱写
不要裸奔运行 java -jar。至少要指定堆内存大小,避免从宿主机抢占过多内存导致 OOM 被系统杀掉。
-Xms: 初始堆内存-Xmx: 最大堆内存- 生产环境建议
-Xms和-Xmx设置为相同值,避免内存抖动。
2. 优雅停机 (Graceful Shutdown)
在 Systemd 配置中我们提到了 SuccessExitStatus=143。
Spring Boot 2.3+ 支持优雅停机。在 application.yml 中配置:
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
这能保证服务停止时,不再接收新请求,但会处理完当前正在进行的请求,避免用户操作中断。
3. 关于日志
Systemd 使用 journalctl 管理日志是很好的选择,但如果你的应用日志量巨大,建议结合 Log4j2/Logback 的 RollingFileAppender 将日志输出到文件,并使用 Filebeat 采集到 ELK 栈中,而不是单纯依赖控制台输出。
为什么这么做?
- 自动保活:
Restart=always保证服务挂了自动拉起,比写 Shell 监控脚本靠谱一万倍。 - 统一日志:使用
journalctl可以按时间、按服务检索日志,不再对着几 GB 的nohup.out发愁。 - 优雅停机:配合 SpringBoot 的
server.shutdown=graceful,Systemd 发送停止信号时,应用会处理完当前请求再关闭,保证业务数据完整。
告别黑窗口,拥抱标准化的部署方式,是一个工程师走向专业的必经之路。
进阶配置:Nginx 反向代理
不管是 Root 模式还是普通用户模式(通常只能监听 >1024 端口),你都需要 Nginx 在前面做反向代理。
推荐配置 (nginx.conf):
server {
listen 80;
server_name www.your-company.com;
# 【优化 1】解除上传文件大小限制
# 默认只有 1M,生产环境建议设置为 100M 或更大
client_max_body_size 100m;
location / {
# 转发到本地 Systemd 跑的 Java 服务
proxy_pass http://localhost:8080;
# 【关键】透传真实 IP
# 如果不加这些,Java 代码里的 request.getRemoteAddr() 拿到的全是 127.0.0.1
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;
#长连接与超时设置 (解决 504 错误)
# 建立连接超时时间 (通常默认 60s 够用)
proxy_connect_timeout 60s;
# 核心:后端处理等待时间
# 如果你的 SpringBoot 有导出报表、发送邮件等长耗时任务,
# 务必将此值调大,否则 Nginx 会掐断连接
proxy_read_timeout 300s;
# 发送请求超时时间
proxy_send_timeout 300s;
}
# 动静分离:静态资源由 Nginx 直接处理,不经过 Java
location /static/ {
alias /opt/prod/static/;
expires 30d;
}
}
👇 互动话题
你们公司的生产环境还在用 nohup 吗?遇到过哪些坑?欢迎在评论区留言交流!
Q.E.D.


