摘要: 还在用 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_usersudouseradd -r -s /bin/false app_user# 确保该用户对 jar 包有读取权限sudo chownapp_user:app_user /opt/prod/app.jar2. 编写 Service 单元文件

  Systemd 通过 .service 文件管理进程。创建文件 /etc/systemd/system/myapp.service

   [Unit]Description=My SpringBoot Enterprise App# 在网络服务启动后再启动After=syslog.target network.target[Service]# 【关键】指定运行用户,实现权限隔离User=app_userGroup=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=alwaysRestartSec=10# 日志由 Systemd 接管 (可用 journalctl 查看)StandardOutput=syslogStandardError=syslogSyslogIdentifier=myapp[Install]WantedBy=multi-user.target3. 三板斧:激活与管理

   # 1. 重载配置sudosystemctl daemon-reload# 2. 设置开机自启 (从此告别重启焦虑)sudosystemctl enablemyapp# 3. 启动服务sudosystemctl start myapp方案二:内网受限环境(无 Root 权限)

  这是很多银行、国企开发者的痛点: 只有普通账号,没有 sudo 权限,Systemd 还能用吗?

  答案是: 能!Systemd 支持用户模式(User Mode)。

1. 关键前提:开启“驻留模式”

  默认情况下,用户退出 SSH,用户下的 Systemd 进程会被杀掉。

  你需要找管理员执行 唯一 的一次操作(或者祈祷管理员已经开启了它):

   # 检查方法,如果输出 Linger=no,则需要在服务器上执行一次开启操作。loginctl show-user $USER --property=Linger# 开启用户驻留,允许服务在用户注销后继续运行sudologinctl enable-linger2. 配置用户级服务

  配置文件位置变了,放在 ~/.config/systemd/user/ 下。

   mkdir-p ~/.config/systemd/uservim ~/.config/systemd/user/myapp.service

  配置差异点:

   不需要 User= Group= 字段。

   [Install] 部分改为 WantedBy=default.target

   [Unit]Description=My User Space AppAfter=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=143Restart=alwaysRestartSec=10 SyslogIdentifier=myapp-user[Install]# 【注意】这里不同于系统级配置WantedBy=default.target3. 用户级管理命令

  所有命令加上 --user 参数即可,完全不需要 sudo:

   # 启动systemctl --user start myapp# 开机自启 (随服务器启动)systemctl --user enablemyapp# 查看日志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: gracefulspring:lifecycle:timeout-per-shutdown-phase: 30s

  这能保证服务停止时,不再接收新请求,但会处理完当前正在进行的请求,避免用户操作中断。

3. 关于日志

  Systemd 使用 journalctl 管理日志是很好的选择,但如果你的应用日志量巨大,建议结合 Log4j2/Logback 的 RollingFileAppender 将日志输出到文件,并使用 Filebeat 采集到 ELK 栈中,而不是单纯依赖控制台输出。

为什么这么做?

      1. 自动保活 : Restart=always 保证服务挂了自动拉起,比写 Shell 监控脚本靠谱一万倍。

      2. 统一日志 :使用 journalctl 可以按时间、按服务检索日志,不再对着几 GB 的 nohup.out 发愁。

      3. 优雅停机 :配合 SpringBoot 的 server.shutdown=graceful ,Systemd 发送停止信号时,应用会处理完当前请求再关闭,保证业务数据完整。

  告别黑窗口,拥抱标准化的部署方式,是一个工程师走向专业的必经之路。

进阶配置:Nginx 反向代理

  不管是 Root 模式还是普通用户模式(通常只能监听 >1024 端口),你都需要 Nginx 在前面做反向代理。

  推荐配置 ( nginx.conf ):

   server{listen 80;server_name 【优化 1】解除上传文件大小限制# 默认只有 1M,生产环境建议设置为 100M 或更大client_max_body_size 100m; location/ {# 转发到本地 Systemd 跑的 Java 服务proxy_pass 【关键】透传真实 IP# 如果不加这些,Java 代码里的 request.getRemoteAddr 拿到的全是 127.0.0.1proxy_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 吗?遇到过哪些坑?欢迎在评论区留言交流!