LHIDC

从空环境部署Spring Boot应用:最少依赖、启动配置与可用性验证

介绍在基础Linux服务器上部署可执行Spring Boot JAR所需的最少依赖,涵盖Java运行时、外部配置、systemd进程托管、启动检查与健康验证,并提供常见故障排查和失败回滚步骤,适合首次部署与运维人员参考。

从空环境部署Spring Boot应用:最少依赖、启动配置与可用性验证

一台只有基础 Linux 系统的新服务器,要运行一个已经构建完成的 Spring Boot 应用,最少只需要匹配应用要求的 Java 运行时、可执行 JAR、外部配置和进程托管机制。生产服务器不必安装 Maven、Gradle 或完整开发工具链;构建工作应在开发环境或 CI 中完成,再把产物部署到服务器。

下面以使用 systemd 的 Ubuntu 22.04/24.04、Rocky Linux 9 或 AlmaLinux 9 为例。操作对象是可通过 java -jar 启动的 Spring Boot JAR,不适用于 WAR、容器镜像或 GraalVM Native Image。示例应用名为 myapp,端口为 8080,实际部署时应替换为项目自己的名称、端口和配置。

先确认最少依赖是否成立

“最少依赖”并不等于所有 Spring Boot 应用只安装 Java 就能运行。应用如果使用数据库、Redis、消息队列或第三方接口,这些仍然是启动或业务可用所需的外部依赖,只是不一定安装在当前服务器上。

部署前至少准备以下内容:

项目 是否必需 判断方法
可执行 Spring Boot JAR 是 构建环境中可以通过 java -jar 启动
匹配版本的 Java 运行时 是 查看 pom.xml、build.gradle 或构建流水线配置
应用配置 是 核对端口、环境标识及外部服务地址
systemd 建议 用于开机启动、异常重启和日志管理
curl 建议 用于 HTTP 可用性验证
Maven、Gradle、Git 否 已有构建产物时无需安装
Nginx 否 只有反向代理、TLS 或多站点需求时才需要

Spring Boot 3.x 的基础运行要求是 Java 17 或更高版本,但应用也可能明确构建为 Java 21。运行时版本应以项目声明并经过验证的版本为准,不能仅因为服务器支持就随意换成更高版本。

先核对系统和架构:

cat /etc/os-release
uname -m
systemctl --version
df -h /
free -h

如果服务器不是上述发行版,先确认包管理器、Java 软件包名称和 systemd 支持情况,不要直接复制安装命令。

安装匹配的 Java 运行时

以下示例安装 Java 17 的无图形运行环境。如果应用要求 Java 21,应安装对应的软件包,而不是继续使用 Java 17。

Ubuntu 22.04/24.04:

sudo apt update
sudo apt install -y openjdk-17-jre-headless curl

Rocky Linux 9、AlmaLinux 9:

sudo dnf install -y java-17-openjdk-headless curl

安装后检查版本和实际执行路径:

java -version
command -v java
readlink -f "$(command -v java)"

后面的 systemd 示例使用 /usr/bin/java。如果 command -v java 返回其他路径,应将服务文件中的 ExecStart 改成实际路径。

普通 Spring Boot 应用只需要 Java 运行时。只有应用在运行期间确实调用编译器、依赖 JDK 工具,或项目文档明确要求时,才需要安装完整 JDK。

创建独立用户并放置应用文件

不要长期使用 root 运行 Spring Boot 应用。独立用户可以限制文件访问范围,也能避免应用漏洞直接获得系统最高权限。

创建系统用户和部署目录:

if ! id myapp >/dev/null 2>&1; then
  sudo useradd --system --user-group --home-dir /opt/myapp \
    --shell /bin/false myapp
fi

sudo install -d -o root -g myapp -m 0750 /opt/myapp
sudo install -d -o root -g myapp -m 0750 /etc/myapp

假设构建产物已经上传到 /tmp/myapp.jar。先计算摘要,并与构建端提供的摘要比对,避免文件上传不完整:

sha256sum /tmp/myapp.jar
ls -lh /tmp/myapp.jar

如果 /opt/myapp/app.jar 已存在,覆盖前先备份。下面的备份操作不会删除原文件:

if sudo test -e /opt/myapp/app.jar; then
  STAMP="$(date +%Y%m%d-%H%M%S)"
  sudo install -d -o root -g root -m 0700 "/var/backups/myapp/$STAMP"
  sudo cp -a /opt/myapp/app.jar "/var/backups/myapp/$STAMP/app.jar"
  echo "备份目录:/var/backups/myapp/$STAMP"
fi

安装新 JAR,并确保应用用户只有读取权限:

sudo install -o root -g myapp -m 0640 \
  /tmp/myapp.jar /opt/myapp/app.jar

sudo -u myapp test -r /opt/myapp/app.jar

将 JAR 设置为 root 所有,可以降低应用进程自行修改部署文件的风险。Spring Boot JAR 不需要执行权限,由 Java 负责读取和启动。

使用外部配置控制首次启动

配置文件不要直接修改在 JAR 内部。外部配置便于升级、排障和回滚,也能避免每次修改端口都重新构建应用。

使用以下命令编辑配置:

sudoedit /etc/myapp/application.yml

写入最小配置:

server:
  address: 127.0.0.1
  port: 8080

spring:
  application:
    name: myapp

127.0.0.1 表示只接受本机连接,适合后续由本机反向代理访问,也能降低首次启动时意外暴露管理接口的风险。如果应用确实需要直接接受外部请求,可在确认认证、访问控制和网络策略后改成:

server:
  address: 0.0.0.0
  port: 8080

这只改变应用监听地址,不会自动放行操作系统防火墙或上游访问规则。

设置配置文件权限:

sudo chown root:myapp /etc/myapp/application.yml
sudo chmod 0640 /etc/myapp/application.yml

再创建 systemd 环境变量文件:

sudoedit /etc/myapp/myapp.env

写入运行环境:

SPRING_PROFILES_ACTIVE=prod

设置权限:

sudo chown root:myapp /etc/myapp/myapp.env
sudo chmod 0640 /etc/myapp/myapp.env

数据库密码、接口密钥等敏感内容不宜出现在启动命令中,否则可能被进程信息、历史记录或运维工具采集。如果必须通过环境变量传递,应写入权限受控的环境文件,且不要把真实密钥提交到代码仓库。

Spring Boot 的环境变量和命令行参数通常比配置文件具有更高优先级。遇到“配置文件明明修改了但没有生效”时,应同时检查 systemd 环境文件、启动参数和当前启用的 Profile。

交给 systemd 管理进程

创建服务文件前,如果同名文件已经存在,应先备份:

if sudo test -e /etc/systemd/system/myapp.service; then
  sudo cp -a /etc/systemd/system/myapp.service \
    "/etc/systemd/system/myapp.service.bak.$(date +%Y%m%d-%H%M%S)"
fi

编辑服务单元:

sudoedit /etc/systemd/system/myapp.service

写入以下内容:

[Unit]
Description=My Spring Boot Application
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/usr/bin/java -jar /opt/myapp/app.jar --spring.config.additional-location=file:/etc/myapp/
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
KillSignal=SIGTERM
UMask=0027

[Install]
WantedBy=multi-user.target

spring.config.additional-location 指向目录时,末尾的 / 不应省略。这里没有使用 optional:,因此配置目录缺失时启动会直接失败,避免应用静默使用默认配置运行。

首次部署不建议直接复制固定的 -Xms、-Xmx 参数。JVM 堆大小需要根据服务器可用内存、应用非堆内存、线程数量和同机其他进程计算。没有明确内存预算时,盲目设置较大的堆上限反而可能触发系统内存不足。

检查服务文件语法:

sudo systemd-analyze verify /etc/systemd/system/myapp.service

如果没有与 myapp.service 相关的错误,重新加载 systemd 并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp

enable --now 会同时设置开机启动并立即启动应用。已有同名服务时,该操作可能中断当前业务,应安排维护时间后再执行。

按层次验证应用是否真正可用

进程存在不等于 Spring Boot 应用可用。至少要依次确认服务状态、端口监听、HTTP 响应和业务依赖。

检查 systemd 状态

sudo systemctl status myapp --no-pager -l
sudo systemctl is-enabled myapp
sudo systemctl is-active myapp

预期 is-enabled 返回 enabled,is-active 返回 active。但 active 只说明 Java 进程没有退出,仍需继续检查 HTTP。

查看首次启动日志

sudo journalctl -u myapp -b -n 200 --no-pager

需要持续观察时使用:

sudo journalctl -u myapp -f

正常日志通常可以看到 Spring Boot 完成启动、Web 服务器监听指定端口等信息。若日志不断重复启动,说明 Restart=on-failure 正在重试,应先停止服务再修复配置,避免错误循环刷日志:

sudo systemctl stop myapp

确认端口监听

sudo ss -lntp 'sport = :8080'

如果配置为 127.0.0.1,结果应显示端口监听在本机回环地址。若没有输出,说明应用尚未成功监听,需要回到日志定位原因。

验证 HTTP 接口

如果应用已经包含 Spring Boot Actuator,并开放了健康端点:

curl -fsS --max-time 5 http://127.0.0.1:8080/actuator/health

成功时通常会返回包含 UP 状态的 JSON。若项目没有 Actuator,应请求一个无需修改数据、能代表业务就绪状态的只读接口:

curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
  --max-time 5 http://127.0.0.1:8080/

单纯返回 404 只能说明 Web 容器能够响应,不能证明业务接口正常。更可靠的验收条件是:请求项目已知接口获得预期状态码和响应内容,并确认数据库、缓存等必要依赖已经连通。

如果接口启用了认证,返回 401 或 403 可能是正常的访问控制结果,不应直接判断为应用启动失败。此时应使用经过授权的健康检查方式验证。

首次启动失败时的检查顺序

故障处理应从低风险、最容易确认的项目开始,不要先重装 Java、删除配置或强制结束未知进程。

Java 版本不匹配

典型日志包括:

UnsupportedClassVersionError

这表示 JAR 使用了高于当前运行时支持的字节码版本。处理方式是安装项目要求的 Java 版本,或在构建端重新指定目标版本。不要只修改服务文件中的版本名称而不检查实际 java -version。

JAR 路径或权限错误

检查文件是否存在,以及服务用户能否读取:

ls -l /opt/myapp/app.jar
namei -l /opt/myapp/app.jar
sudo -u myapp test -r /opt/myapp/app.jar && echo "JAR 可读"

如果 ExecStart 路径、文件名或大小写错误,systemd 日志中通常会出现找不到文件或无法访问的信息。

端口已被占用

常见日志为:

Address already in use

确认占用进程:

sudo ss -lntp 'sport = :8080'

不要直接结束未知进程。应先确认它属于哪个服务,再决定修改 Spring Boot 端口,还是在评估影响后停止原服务。

YAML、Profile 或环境变量有误

配置缩进错误、属性名称错误或必需变量为空,可能导致 Spring 容器初始化失败。重点检查:

sudo sed -n '1,160p' /etc/myapp/application.yml
sudo systemctl cat myapp
sudo systemctl show myapp -p EnvironmentFiles

包含密码的环境文件不应直接打印到工单、聊天记录或公开日志中。排查时只确认变量是否存在,避免暴露真实值。

外部组件连接失败

如果日志停留在数据源、Redis、消息队列或配置中心初始化阶段,说明 Java 和 JAR 已经执行,但应用依赖未满足。应根据异常链最底层的 Caused by 检查:

  • 地址和端口是否填写正确;
  • DNS 是否能够解析;
  • 账号是否有权限;
  • TLS 证书或协议是否匹配;
  • 外部组件是否要求来源访问控制。

network-online.target 只表示系统网络初始化完成,不保证数据库等远端服务已经可访问。

本机可用但外部无法访问

先确认 server.address。监听在 127.0.0.1 时,外部无法直接连接属于预期行为;监听在 0.0.0.0 时,再检查服务器防火墙、上游访问规则和反向代理配置。不要为了排障直接关闭全部防火墙规则,应只核对并调整实际需要的端口范围。

失败回滚与恢复

首次部署失败且暂时不继续处理时,可以停止并取消开机启动:

sudo systemctl disable --now myapp

该操作不会删除 JAR、配置和日志,后续仍可继续排查。

如果是替换已有版本后失败,应恢复部署前记录的备份。以下操作会覆盖当前 JAR,执行前必须把 ROLLBACK 改为已经确认存在的备份目录:

ROLLBACK="20260101-120000"

sudo test -r "/var/backups/myapp/$ROLLBACK/app.jar" \
  || { echo "指定备份不存在"; exit 1; }

sudo systemctl stop myapp
sudo install -o root -g myapp -m 0640 \
  "/var/backups/myapp/$ROLLBACK/app.jar" \
  /opt/myapp/app.jar
sudo systemctl start myapp

恢复后重新检查日志、端口和健康接口:

sudo systemctl status myapp --no-pager -l
sudo journalctl -u myapp -b -n 100 --no-pager
sudo ss -lntp 'sport = :8080'
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
  --max-time 5 http://127.0.0.1:8080/

配置文件和 systemd 单元也发生过变更时,应分别恢复对应备份,再执行:

sudo systemctl daemon-reload
sudo systemctl restart myapp

重启会造成当前应用连接中断,已有业务流量时应先确认影响范围。

上线验收检查清单

  • Java 版本与应用构建要求一致,实际路径与 ExecStart 一致;
  • Spring Boot 应用由独立低权限用户运行,而不是 root;
  • JAR、配置文件和环境文件的所有者及权限正确;
  • systemd 服务已启用,停止、启动和异常退出行为符合预期;
  • 日志中没有持续重启、配置解析失败或外部依赖连接异常;
  • 端口只监听在业务需要的地址上;
  • 健康接口或只读业务接口返回预期状态和内容;
  • 数据库、缓存等必要外部组件已纳入可用性验证;
  • 新版本部署前已保留 JAR、配置和服务文件备份;
  • 回滚路径已经记录,恢复后仍会重新验证日志、端口和业务接口。
上一篇 Minecraft服务器配置修改后不生效,如何检查启动参数与面板覆盖项 下一篇 如何用c_status()命令验证饥荒联机版服务器状态与玩家连接

LHIDC 产品中心

继续查看可购买的海外服务器产品

文章用于辅助选型,最终价格、库存与配置请以产品详情页和下单页面展示为准。

查看产品 查看方案