我有一个 systemd 单元文件(serv_unit.service):
[Unit]
Description=My service
[Service]
Type=simple
Restart=always
RestartSec=60
StartLimitInterval=400
StartLimitBurst=3
ExecStart=/etc/init.d/myscript start
[Install]
WantedBy=multi-user.target
/etc/init.d/myscript
source /etc/myfile.sh # JAVA_home & other vars are resolved through this file
mode=$1
case "$mode" in
'start')
# Start daemon
daemon --user=abc $JAVA_HOME/bin/java -cp $appClassPath $MAIN_CLASS $args &
我遇到的问题是当我运行以下命令时:
systemctl start serv_unit.service
我看不到 java 进程。但是当我删除尾随的&IE:
daemon --user=abc $JAVA_HOME/bin/java -cp $appClassPath $MAIN_CLASS $args
可以看到 Java 进程(并且该 Java 进程没有崩溃,因此排除了它在后台崩溃的可能性)
原因何在?
答案1
除了主要问题之外,从 systemd 调用 init.d 脚本有点多余,而且通常一开始就不是一个好主意。以下就是您所需的全部内容:
User=abc
EnvironmentFile=/etc/myfile.conf
ExecStart=/usr/bin/env $JAVA_HOME/bin/java -cp $appClassPath $MAIN_CLASS $args
EnvironmentFile 只能是简单的 KEY=value 赋值,但如果配置必须采用 shell 脚本语法,则也可以使用:
User=abc
ExecStart=/bin/sh -c ". /etc/myfile.sh && exec $$JAVA_HOME/bin/java -cp $$appClassPath $$MAIN_CLASS $$args"
SyslogIdentifier=
(如果服务生成标准输出消息,您也应该在两种情况下进行设置。)
服务类型
Systemd 服务应遵循特定规则。一个.service
单元只能有一个“主”守护进程,该Type=
选项会告诉 systemd 该进程如何工作。
Type=simple
表示初始进程(即从 ExecStart= 启动的进程)本身是服务的主进程。一旦主进程退出,服务就被认为已经停止,以便清理所有剩余物。
这意味着如果你使用 Type=simple,你就是告诉 systemd 该守护进程将不会进入“后台”。事实上,你还告诉 systemdinit.d 脚本– 不是daemon
– 是服务的主要流程……
因此,最好的情况是,init.d 脚本不仅不会尝试将实际的守护进程置于后台,而且还可以通过exec
如下方式启动它:
case "$mode" in
'start-foreground')
# Start daemon in foreground
exec daemon --user=abc $JAVA_HOME/bin/java -cp $appClassPath $MAIN_CLASS $args
另一方面,Type=forking
表示初始过程将要在启动过程中 fork 和 exit,并且应该从 PIDFile= 或启发式地发现主进程。
使用哪一个
使用具有内置“守护进程”模式Type=forking
具有显著的优势:systemd 服务一直处于“正在启动”状态,直到守护进程最终尝试进入后台,此时它最终进入“已启动/活动”状态。这有助于配置依赖项,例如服务 A 可以声明“After=B.service”。
但是,如果后台运行是通过外部手段完成的,比如 shell&
特性——没有任何可以报告守护进程是否已经准备好的东西——那么它就完全无用您可能应该只使用Type=simple
不带任何背景选项。