监控故障并快速重启systemd服务的问题排查与优化
我在Ubuntu上运行24小时不间断的YouTube直播,将ffmpeg命令封装在systemd服务中。多次遇到ffmpeg故障时,systemd的重启速度不足以维持直播在线,需要手动执行daemon-reload并重启服务。于是我写了一个bash脚本检测日志中的流结束错误,但脚本没触发生效,故障仍会发生。现提出两个问题:
- 是否有更高效的实现方式?
- 若没有,能否指出我的脚本存在什么问题?
我的脚本代码:
#!/bin/bash RESET=0 while true; do # 获取1分钟前的时间 LAST_1_MINUTE=$(date -d '1 minute ago' '+%b %e %H:%M:%S') # 检测最近1分钟内的错误日志 if journalctl --since "$LAST_1_MINUTE" | grep -qi "Error writing trailer"; then if [ $RESET -lt 1 ]; then # 触发错误后的操作 sudo systemctl daemon-reload && \ echo "Restarting master.service by monitor.sh script at $(date)" >> /var/log/monitor.log && \ sudo systemctl restart master.service RESET=2 fi else RESET=$((RESET - 1)) fi # 20秒后再次检测 sleep 20 done
一、更高效的实现方式
完全不需要额外监控脚本,直接优化systemd服务配置就能解决问题,这是最原生且可靠的方案:
- 调整服务重启策略:在
master.service配置文件的[Service]段添加/修改以下参数:[Service] Restart=on-failure RestartSec=5 # 故障后5秒自动重启,可按需调整间隔 StartLimitIntervalSec=300 StartLimitBurst=10 # 限制5分钟内最多重启10次,避免无限循环Restart=on-failure会让systemd在进程异常退出(非0退出码)时自动重启,正常重启服务不需要执行systemctl daemon-reload,只有修改服务配置文件后才需要该命令。 - 精准追踪进程状态:systemd直接监控服务主进程,比轮询日志更及时、准确,不会出现漏检或延迟问题。
- 自定义错误码处理:如果ffmpeg退出时返回特定错误码,可通过
RestartPreventExitStatus排除无需重启的情况,或RestartForceExitStatus强制重启特定错误场景。
二、你的脚本存在的问题
日志时间格式不匹配:
脚本用%b %e %H:%M:%S生成的时间中,%e会输出带前导空格的日期(比如5号是5),和journalctl的日志时间格式存在匹配误差,导致无法正确筛选目标时间段的日志。建议改用ISO-8601标准格式:LAST_1_MINUTE=$(date -d '1 minute ago' --iso-8601=seconds),确保时间参数能被journalctl正确识别。未过滤目标服务日志:
journalctl --since "$LAST_1_MINUTE"会返回系统所有服务的日志,其他服务日志中若出现Error writing trailer字符串,会导致误触发。应加上-u master.service参数,只过滤目标服务的日志:journalctl -u master.service --since "$LAST_1_MINUTE"。RESET变量逻辑缺陷:
检测到错误后设置RESET=2,后续若持续检测到错误,RESET会一直保持2,无法触发下一次重启;而如果错误反复出现,脚本可能因RESET未降到1以下而无法及时响应。权限问题导致执行卡住:
脚本中使用sudo,若运行脚本的用户没有免密sudo权限,执行时会弹出密码输入提示,导致重启操作无法自动完成。建议给该用户配置免密sudo(编辑/etc/sudoers添加username ALL=(ALL) NOPASSWD: /bin/systemctl),或直接以root身份运行脚本。检测窗口与循环间隔不匹配:
每20秒检测一次过去1分钟的日志,会导致同一个错误被重复检测到,而RESET的限制逻辑可能让脚本在故障持续时无法及时重启服务,错过恢复时机。
内容的提问来源于stack exchange,提问作者mzrt

