安装 Spring Boot 应用程序
除了直接使用 java -jar 运行 Spring Boot 应用程序外,还可以将它们作为 systemd、init.d 或 Windows 服务来运行。
作为 systemd 服务安装
systemd 是 System V init 系统的继任者,如今已被许多现代 Linux 发行版所采用。Spring Boot 应用程序可以通过 systemd 的 “service” 脚本来启动。
假设你有一个 Spring Boot 应用程序,打包为 uber jar 并位于 /var/myapp,要将其安装为 systemd 服务,请创建一个名为 myapp.service 的脚本,并将其放置在 /etc/systemd/system 目录中。以下脚本提供了一个示例:
[Unit]
Description=myapp
After=syslog.target network.target
[Service]
User=myapp
Group=myapp
Type=exec
ExecStart=/path/to/java/home/bin/java -jar /var/myapp/myapp.jar
WorkingDirectory=/var/myapp
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
记得为你的应用程序修改 Description、User、Group、ExecStart 和 WorkingDirectory 字段。
ExecStart 字段未声明脚本操作命令,这意味着默认使用 run 命令。
运行应用程序的用户、PID 文件以及控制台日志文件由 systemd 自行管理,因此必须通过在 “service” 脚本中使用适当的字段进行配置。更多详细信息请参阅 service unit 配置手册页。
要将应用程序标记为在系统启动时自动启动,请使用以下命令:
$ systemctl enable myapp.service
运行 man systemctl 以获取更多详细信息。
作为 init.d 服务安装(System V)
要将你的应用程序用作 init.d 服务,请配置其构建过程以生成一个完全可执行的 jar。
完全可执行的 JAR 文件通过在文件开头嵌入一个额外的脚本来实现。目前,某些工具不支持这种格式,因此你可能无法始终使用此技术。例如,jar -xf 在解压已被设为完全可执行的 JAR 或 WAR 文件时可能会静默失败。建议仅在你打算直接执行该 JAR 或 WAR 文件时才将其设为完全可执行;若你计划通过 java -jar 运行它或将它部署到 Servlet 容器中,则不应这样做。
无法将 zip64 格式的 jar 文件制作成完全可执行的文件。如果尝试这样做,会导致生成的 jar 文件在直接执行或通过 java -jar 执行时被报告为已损坏。而包含一个或多个 zip64 格式嵌套 jar 的标准格式 jar 文件则可以被制作成完全可执行的文件。
要使用 Maven 创建一个“完全可执行”的 jar,请使用以下插件配置:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
以下示例展示了等效的 Gradle 配置:
tasks.named('bootJar') {
launchScript()
}
然后可以将其符号链接到 init.d,以支持标准的 start、stop、restart 和 status 命令。
添加到完全可执行 jar 中的默认启动脚本支持大多数 Linux 发行版,并已在 CentOS 和 Ubuntu 上经过测试。其他平台(如 OS X 和 FreeBSD)则需要使用自定义脚本。默认脚本支持以下特性:
-
以拥有该 jar 文件的用户身份启动服务
-
通过使用
/var/run/<appname>/<appname>.pid跟踪应用程序的 PID -
将控制台日志写入
/var/log/<appname>.log
假设你已将 Spring Boot 应用程序安装在 /var/myapp,要将其作为 init.d 服务安装,请创建一个符号链接,如下所示:
$ sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp
安装完成后,你可以用常规方式启动和停止该服务。例如,在基于 Debian 的系统上,你可以使用以下命令启动它:
$ service myapp start
如果你的应用程序启动失败,请检查写入到 /var/log/<appname>.log 的日志文件以查找错误。
你也可以使用标准操作系统工具将该应用程序标记为自动启动。例如,在 Debian 上,你可以使用以下命令:
$ update-rc.d myapp defaults <priority>
保护 init.d 服务
以下是一组关于如何保护以 init.d 服务形式运行的 Spring Boot 应用程序的指南。它并非旨在详尽列出加固应用程序及其运行环境所需执行的所有操作。
当以 root 用户执行时(例如使用 root 启动 init.d 服务的情况),默认的可执行脚本会以 RUN_AS_USER 环境变量中指定的用户身份运行应用程序。如果未设置该环境变量,则会使用 JAR 文件的所有者用户。你不应该以 root 用户身份运行 Spring Boot 应用程序,因此 RUN_AS_USER 不应为 root,且你的应用程序 JAR 文件也不应由 root 拥有。相反,应创建一个专用用户来运行你的应用程序,并设置 RUN_AS_USER 环境变量,或者使用 chown 命令将该用户设为 JAR 文件的所有者,如下例所示:
$ chown bootapp:bootapp your-app.jar
在这种情况下,默认的可执行脚本以 bootapp 用户身份运行该应用程序。
为了降低应用程序用户账户被攻破的风险,你应该考虑禁止该账户使用登录 shell。例如,可以将该账户的 shell 设置为 /usr/sbin/nologin。
你还应采取措施防止应用程序的 jar 文件被修改。首先,配置其权限,使其不可写,并且只能由其所有者读取或执行,如下例所示:
$ chmod 500 your-app.jar
其次,你还应采取措施,以限制在你的应用程序或运行该程序的账户被攻破时造成的损害。如果攻击者确实获得了访问权限,他们可能会使 jar 文件可写并修改其内容。一种防范方法是使用 chattr 命令将其设为不可变,如下例所示:
$ sudo chattr +i your-app.jar
这将阻止任何用户(包括 root)修改该 jar 文件。
如果使用 root 用户来控制应用程序的服务,并且你使用 .conf 文件来自定义其启动方式,那么该 .conf 文件将由 root 用户读取和解析。因此,应相应地保护该文件的安全性。使用 chmod 设置文件权限,使其仅能被所有者读取,并使用 chown 将文件所有者设为 root,如下例所示:
$ chmod 400 your-app.conf
$ sudo chown root:root your-app.conf
自定义启动脚本
由 Maven 或 Gradle 插件编写的默认嵌入式启动脚本可以通过多种方式进行自定义。对大多数人来说,使用默认脚本并进行少量自定义通常就足够了。如果你发现无法自定义所需的内容,可以使用 embeddedLaunchScript 选项完全编写自己的脚本文件。
在编写启动脚本时自定义它
通常,在将启动脚本写入 jar 文件时,自定义脚本中的某些元素是有意义的。例如,init.d 脚本可以提供一个 “description”。由于你事先知道该描述(且无需更改),因此在生成 jar 文件时直接提供它会更好。
要自定义写入的元素,请使用 Spring Boot Maven 插件的 embeddedLaunchScriptProperties 选项,或使用 Spring Boot Gradle 插件 launchScript 的 properties 属性。
默认脚本支持以下属性替换:
| 名称 | 描述 | Gradle 默认值 | Maven 默认值 |
|---|---|---|---|
mode | 脚本模式。 | auto | auto |
initInfoProvides | “INIT INFO” 的 Provides 部分 | ${task.baseName} | ${project.artifactId} |
initInfoRequiredStart | “INIT INFO” 的 Required-Start 部分。 | $remote_fs $syslog $network | $remote_fs $syslog $network |
initInfoRequiredStop | “INIT INFO” 的 Required-Stop 部分。 | $remote_fs $syslog $network | $remote_fs $syslog $network |
initInfoDefaultStart | “INIT INFO” 的 Default-Start 部分。 | 2 3 4 5 | 2 3 4 5 |
initInfoDefaultStop | “INIT INFO” 的 Default-Stop 部分。 | 0 1 6 | 0 1 6 |
initInfoShortDescription | “INIT INFO” 的 Short-Description 部分。 | ${project.description} 的单行版本(若无则回退到 ${task.baseName}) | ${project.name} |
initInfoDescription | “INIT INFO” 的 Description 部分。 | ${project.description}(若无则回退到 ${task.baseName}) | ${project.description}(若无则回退到 ${project.name}) |
initInfoChkconfig | “INIT INFO” 的 chkconfig 部分 | 2345 99 01 | 2345 99 01 |
confFolder | CONF_FOLDER 的默认值 | 包含 jar 的文件夹 | 包含 jar 的文件夹 |
inlinedConfScript | 指向一个脚本文件的引用,该脚本应被内联到默认启动脚本中。可用于在加载任何外部配置文件之前设置环境变量(例如 JAVA_OPTS) | ||
logFolder | LOG_FOLDER 的默认值。仅对 init.d 服务有效 | ||
logFilename | LOG_FILENAME 的默认值。仅对 init.d 服务有效 | ||
pidFolder | PID_FOLDER 的默认值。仅对 init.d 服务有效 | ||
pidFilename | PID_FOLDER 中 PID 文件的默认名称。仅对 init.d 服务有效 | ||
useStartStopDaemon | 当 start-stop-daemon 命令可用时,是否应使用它来控制进程 | true | true |
stopWaitTime | STOP_WAIT_TIME 的默认值(单位:秒)。仅对 init.d 服务有效 | 60 | 60 |
在脚本运行时自定义
对于在 JAR 文件写入后仍需自定义的脚本项,你可以使用环境变量或一个 配置文件。
默认脚本支持以下环境属性:
| 变量 | 描述 | |||
|---|---|---|---|---|
MODE | 操作的“模式”。默认值取决于 jar 的构建方式,但通常是 auto(表示它会尝试通过检查是否为名为 init.d 的目录中的符号链接来猜测是否为 init 脚本)。你可以显式地将其设置为 service,以便 stop | start | status | restart 命令生效;或者设置为 run,如果你想在前台运行脚本。 | |||
RUN_AS_USER | 用于运行应用程序的用户。未设置时,将使用拥有该 jar 文件的用户。 | |||
USE_START_STOP_DAEMON | 当 start-stop-daemon 命令可用时,是否应使用它来控制进程。默认为 true。 | |||
PID_FOLDER | pid 文件夹的根目录名称(默认为 /var/run)。 | |||
LOG_FOLDER | 存放日志文件的文件夹名称(默认为 /var/log)。 | |||
CONF_FOLDER | 读取 .conf 文件的文件夹名称(默认与 jar 文件位于同一目录)。 | |||
LOG_FILENAME | LOG_FOLDER 中日志文件的名称(默认为 <appname>.log)。 | |||
APP_NAME | 应用程序的名称。如果从符号链接运行 jar,脚本会猜测应用名称。如果不是符号链接,或者你想显式设置应用名称,则此变量很有用。 | |||
RUN_ARGS | 传递给程序(Spring Boot 应用)的参数。 | |||
JAVA_HOME | java 可执行文件的位置默认通过 PATH 发现,但如果 $JAVA_HOME/bin/java 处存在可执行文件,你可以显式设置它。 | |||
JAVA_OPTS | 启动 JVM 时传递给它的选项。 | |||
JARFILE | jar 文件的显式位置,适用于脚本用于启动一个并未内嵌于其中的 jar 文件的情况。 | |||
DEBUG | 如果非空,则在 shell 进程上设置 -x 标志,使你可以看到脚本中的逻辑。 | |||
STOP_WAIT_TIME | 在强制关闭之前停止应用程序时等待的时间(以秒为单位,默认为 60)。 |
PID_FOLDER、LOG_FOLDER 和 LOG_FILENAME 变量仅对 init.d 服务有效。对于 systemd,等效的自定义配置需通过使用 ‘service’ 脚本来实现。更多详情请参见 service unit configuration man page。
使用 Conf 文件
除了 JARFILE 和 APP_NAME 之外,前一节中列出的设置可以通过使用 .conf 文件进行配置。该文件应与 JAR 文件位于同一目录下,并且具有相同的名称,但后缀为 .conf 而不是 .jar。例如,名为 /var/myapp/myapp.jar 的 JAR 文件会使用名为 /var/myapp/myapp.conf 的配置文件,如下例所示:
JAVA_OPTS=-Xmx1024M
LOG_FOLDER=/custom/log/folder
如果你不喜欢将配置文件放在 jar 文件旁边,可以设置 CONF_FOLDER 环境变量来自定义配置文件的位置。
要了解如何恰当地保护此文件,请参阅 保护 init.d 服务的指南。
Microsoft Windows Services
可以使用 winsw 将 Spring Boot 应用程序作为 Windows 服务启动。
一个(单独维护的示例)逐步描述了如何为你的 Spring Boot 应用程序创建 Windows 服务。