使用环境设置的可执行路径编写 systemd 单元文件

使用环境设置的可执行路径编写 systemd 单元文件

我正在为 Java 应用程序编写一个 systemd 单元文件,并且我想控制用于启动它的 Java 版本。我的(简化的)服务文件是

[Service]
Type=simple
EnvironmentFile=%h/Documents/apps/app/app-%i/app.cfg
ExecStart=${JAVA_HOME}/bin/java ${JAVA_OPTS} -jar %h/Documents/apps/app/app-%i/myapp.jar
SuccessExitStatus=143

当尝试启动它时,我收到错误消息

Apr 28 12:43:37 rombert systemd[1613]: [/home/robert/.config/systemd/user/[email protected]:7] Executable path is not absolute, ignoring: ${JAVA_HOME}/bin/java ${JAVA_OPT
Apr 28 12:43:37 rombert systemd[1613]: [email protected] lacks both ExecStart= and ExecStop= setting. Refusing.

我知道JAVA_HOME设置正确;如果我更改行ExecStart的开头/usr/bin/java,然后添加类似的内容,-DsomeOption=${JAVA_HOME}我就可以看到它了。

明显的解决方法是创建一个包装器脚本,但我觉得它违背了使用服务文件的目的。

如何使用单元文件为我的 Java 应用程序设置 JAVA_HOME?

答案1

来自 systemd.service(5) 中的“命令行”部分:

请注意,第一个参数(即要执行的程序)可能不是变量。

我本来建议使用实例说明符%i(您可以在 systemd.unit(5) 中阅读更多相关信息),但是(现在我们回到了 systemd.service(5)):

命令行的第一个参数(即要执行的程序)可能不包含说明符。

我认为此时最好的选择确实是创建一个 shell 脚本,按照 Warren Young 的建议包装 java 二进制文件的执行,或者您可以直接 ExecStart 一个 shell,就像“命令行”部分中的 shell 命令行示例一样。 systemd.service(5) 具有以下示例:

ExecStart=/bin/sh -c 'dmesg | tac'

所以你可以这样做(未经测试):

ExecStart=/bin/sh -c '${JAVA_HOME}....'

答案2

另一个类似的选项是使用/usr/bin/env

ExecStart=/usr/bin/env "${JAVA_HOME}/bin/java" -jar ...

这样,您可以省略'整个命令周围的引号,这在您需要嵌套引号内容时很有用。

附言。附带说明一下,在 Systemd 文件中将变量名称括在{大括号中非常重要},否则将无法正确识别它们。

答案3

另一个相当不同的选项假设使用另一个系统工具:alternatives.如果您的 Java SDK 来自系统包,那么很可能您已经准备好了。如果您手动安装它们,则必须使用类似以下内容将它们添加到系统中:

alternatives --install /usr/bin/java java /path/to/your/sdk/bin/java 3

最后一个数字是优先级(越高越重要)。您可以通过发出命令来检查现有优先级alternatives --display java,以便决定为新 SDK 选择哪个优先级。

安装后,您可以/usr/bin/java在服务文件中使用并alternatives --config java在启动服务之前运行以确定您想要选择哪个版本。还没有真正尝试过,但看来你可以这样做:

alternatives --set java /path/to/your/sdk/bin/java

...并从某个脚本中选择您的 SDK。如果交互界面alternatives --config不适合您的场景,这可能是一个选项。您甚至可以使用指令在服务文件中进行设置ExecStartPre

我真的不喜欢包装脚本。它的大部分内容可能与可怕的 SysV 初始化脚本相同(至少是开始部分)。最有可能的是,以包装器脚本开头的服务文件必须有点复杂,因为正在执行的命令不是正在运行的进程。如果您能花时间浏览许多 systemd 指令,您可能会找到一种更简洁的方法来实现您想要的目标,而无需坚持使用笨重的包装脚本。

相关内容