systemd reload 和 hypnotoad reload 需要一个包装脚本才能正常工作
注意:此解决方法适用于 Mojolicious 6.x。SystemD 兼容性已在 Mojolicious 7.x 中修复
如果您想在 systemd 下重新加载,那么标准的 hypnotoad 重新加载(即hypnotoad myapp.pl
)或者kill -USR $MAINPID
是不可取的。SystemD 假设一旦重新加载 hypnotoad 服务器的命令返回,重新加载就完成了。但是,相反,这两个选项都会立即退出,并且不要等待重新加载完成。
当我尝试了这些标准的重新加载选项时,systemd 完成了重新加载命令的执行,然后,当 hypnotoad 执行“开始零停机时间软件升级 > 升级成功”时,监视 pid 文件被删除和替换。
如果您已将 systemd 配置为始终运行 hypnotoad 应用程序,则 systemd 将在 hypnotoad 正常运行之后再次重新启动(停止并启动)应用程序。
这是发生的事情:
- systemd 重新加载 hypnotoad-app (ExecReload=hypnotoad myapp.pl)
- hypnotoad-app 在被告知重新加载后退出
- hypnotoad 使用最新的 myapp.pl 启动一个新进程
- systemd 认为 reload 刚刚完成,并且看到 PIDFile 中的 pid 没有改变
- hypnotoad 看到旧进程没有负载,杀死它们,并在“升级[is]成功”时删除并使用新进程的 PID 创建一个新的 PIDFile(这是 hypnotoad 的“零停机时间”重新加载过程)
- systemd 看到 pid 文件被删除(它忽略了它被重新创建,可能是因为在设计上,systemd 想要控制分叉)
- systemd 等待 HOLDOFF (
RestartSec=<seconds>
) 如果已配置
- systemd 对 hypnotoad-app 进行完整的单元启动
systemd 文档特别指出,当ExecReload=
命令返回时,systemd 期望进程已经完成重新加载,并且 pid 文件具有重新加载进程的新 PID。但是 Hypnotoad 不能像那样同步工作。所以 systemd 和 hypnotoad 不能很好地协同工作。
https://www.freedesktop.org/software/systemd/man/systemd.service.html
但是请注意,通过发送信号(如上面的示例行)来重新加载守护程序通常不是一个好的选择,因为这是一个异步操作,因此不适合命令多个服务相互重新加载。强烈建议将 ExecReload= 设置为不仅会触发守护程序的配置重新加载,而且还会同步等待它完成的命令。
解决方案是围绕重新加载过程编写一个包装器。
#!/bin/bash
SERVER="/path/to/myapp.pl"
HYPNOTOAD="/usr/bin/hypnotoad"
PIDFILE="/var/run/myapp.pid"
# Timeout == LOOPSAFE x SLEEPTIME
SLEEPTIME="0.5"
LOOPSAFE=20
LOOPCOUNT=0
#
if [ ! -f "${PIDFILE}" ]; then
# The PID files does not exist, maybe $SERVER is not running
exit 1
fi
OLDPID=$(cat ${PIDFILE})
NEWPID=$OLDPID
# Reload the application
${HYPNOTOAD} ${SERVER}
while (( $LOOPCOUNT <= $LOOPSAFE )); do
let LOOPCOUNT++
if [ -f ${PIDFILE} ]; then
NEWPID=$(cat ${PIDFILE})
if (( $NEWPID > 1 )) && (( $NEWPID != $OLDPID )); then
exit 0
fi
fi
sleep ${SLEEPTIME}
done
exit 1
这是 Hypnotoad 所需的 systemd 单元文件
[Unit]
Description=Hypnotoad-app
Requires=network.target
After=network.target
[Service]
Type=simple
SyslogIdentifier=hypnotoad-app
PIDFile=/var/run/myapp.pid
EnvironmentFile=-/etc/sysconfig/myapp
ExecStart=/usr/bin/hypnotoad --foreground /path/to/myapp.pl
ExecStop=/usr/bin/hypnotoad --stop /path/to/myapp.pl
ExecReload=/path/to/reload-myapp.sh
KillMode=process
Restart=always
RestartSec=5
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
这是重新加载过程现在的工作方式
- systemd 重新加载 hypnotoad-app
- systemd 执行重载脚本
- reload 脚本会重新加载 hypnotoad-app,并等待“成功升级”——本质上是休眠,直到 PID 文件具有新重新加载的 myapp.pl 进程的 PID
- systemd 现在使用新的 PID 监视 pid 文件,该 PID 不再更改 - 所以现在 systemd 不会尝试重新启动它