如何通过systemd watchdog传递新参数重启daemon程序及冻结的systemd管理应用
嘿,这个需求其实挺典型的——既要让systemd监测到应用冻结时自动重启,又要让重启后的启动参数和系统初次启动时不一样。我给你拆解成两步来实现,都是systemd生态里的常规操作:
第一步:用systemd Watchdog监测应用冻结
首先得让systemd能感知到应用“挂了”,这里用systemd原生的Watchdog机制最靠谱,比自己写监控脚本省心多了。
核心配置要点:
- 在你的应用
.service文件里,加上WatchdogSec参数,比如设成30秒(意思是如果30秒收不到应用的心跳,就判定为冻结) - 开启
Restart=on-watchdog,明确告诉systemd只有在watchdog触发时才重启(当然你也可以结合其他重启条件,比如Restart=on-failure,看你需求) - 把服务类型设为
Type=notify,因为systemd需要接收应用主动发送的心跳信号
应用端配合:
你的应用代码里需要定期调用systemd的sd_notify函数发送心跳,比如每隔15秒发一次WATCHDOG=1的信号。如果是C/C++程序,直接用libsystemd库的函数就行;如果是脚本语言,也可以用systemd-notify命令来发送,比如在Python里可以调用subprocess执行这个命令。
要是你的应用实在没法改代码支持心跳,也可以用个变通办法:写个小脚本定期检查应用的健康状态,然后替它发心跳,但这种方式不如原生支持稳定。
第二步:让重启时使用不同的启动参数
默认情况下,systemd重启服务时会复用原来的ExecStart命令,所以要换参数得绕个小弯——用启动脚本做中间层,根据“是否是第一次启动”来切换参数。
编写启动脚本:
比如创建/usr/local/bin/start-myapp.sh,内容如下:
#!/bin/bash # 定义标记文件,用来判断是不是初次启动(系统重启后这个文件会消失,刚好符合需求) FLAG_FILE="/var/run/myapp_restarted.flag" if [ ! -f "$FLAG_FILE" ]; then # 系统初次启动时,用默认参数 echo "Starting with normal arguments..." /usr/bin/myapp --normal-mode --port 8080 # 启动成功后创建标记文件,下次重启就会用新参数 touch "$FLAG_FILE" else # 重启时用恢复参数 echo "Restarting with recovery arguments..." /usr/bin/myapp --recovery-mode --port 8081 fi
别忘了给脚本加执行权限:chmod +x /usr/local/bin/start-myapp.sh
更新service文件:
把原来的ExecStart指向这个脚本,还要加上ExecStopPost在服务正常停止时删掉标记文件(比如系统关机时),这样下次开机又会回到初次启动的参数:
[Unit] Description=My Frozen-Aware Application After=network.target [Service] Type=notify ExecStart=/usr/local/bin/start-myapp.sh # 30秒没心跳就判定为冻结 WatchdogSec=30s # 只有watchdog触发时才重启,也可以改成on-failure覆盖更多场景 Restart=on-watchdog # 重启间隔设5秒,避免频繁重启 RestartSec=5s User=myapp-user WorkingDirectory=/var/lib/myapp # 正常停止时删除标记文件 ExecStopPost=/bin/rm -f /var/run/myapp_restarted.flag [Install] WantedBy=multi-user.target
测试验证
配置完后,先重新加载systemd配置:systemctl daemon-reload,然后启动服务:systemctl start myapp.service。
可以手动模拟冻结:用kill -STOP <pid>来暂停应用进程,这样它就没法发心跳了,等30秒左右systemd就会重启它,这时候你看日志journalctl -u myapp.service就能看到是用恢复参数启动的。
备注:内容来源于stack exchange,提问作者Dimitri Ratz




