如何配置systemd服务定期重启?

122
我有一个简单的systemd服务,需要定期重新启动以防止其进程出现故障。是否有systemd服务的配置选项来定期重新启动它们?所有Restart*选项似乎都与在服务退出时重新启动服务有关。

1
由于此问题涉及到管理服务器的工具(不是通用工具,而是生产工具),也许将该问题发布在Server Fault上会更加符合主题。您认为呢?https://serverfault.com/help/on-topic - Valerio Bozz
1
我无法评论2015年的情况,但是现在systemd标签表示编写单元文件的问题最好在unix.SE上提问。 - wes
1
好的,但是服务本身存在漏洞并需要修复这一事实更适合在服务器故障网站上讨论。墨西哥僵局? - Valerio Bozz
./我希望有一个trashcan.software.se,因为这个问题不仅仅是serverfault,也不是unix.se,因为你基本上是在尝试编写一个流体系统,而不是管理或配置(这将意味着服务器/Unix),但这只是纠结于细节。 - Hvisage
7个回答

242

对于systemd版本>= 229,有一个名为RuntimeMaxSec的选项,它会在服务运行一定时间后终止该服务。

例如,每7天重新启动:

[Service]
Restart=always
RuntimeMaxSec=7d

在我看来,这比滥用Type=notifyWatchdogSec要更加优雅。

systemd提供了一种干净的方法来添加和覆盖供应商提供的systemd单元文件中的指令。Drop-In Units在man systemd.unit中有描述。例如,如果您想定期重新启动由一个软件包提供的foo服务,您可以创建一个名为/etc/systemd/system/foo.service.d/periodic-restart.conf的文件。其内容如上所示,然后:

 systemctl daemon-reload
 systemctl restart foo

您可以确认 Drop-In 单元已加载,因为它将在状态输出中报告:

 systemctl status

最后,您可以通过搜索 systemctl show 的输出来确认指令已包含:

 systemctl show foo.service | grep RuntimeMax
systemctl show 报告的指令将是 "RuntimeMaxUSec"。

27
只有在systemd版本大于等于229时才可用。 运行systemctl --version查看您的版本。 - Hilikus
10
时间字段中也可以使用单位:RuntimeMaxSec=7d - Cameron Tacklind
1
这会停止该进程,如何在超时后自动重新启动? - Maverick
1
我尝试将@Hilikus的有用评论整合到答案中,但队列现在已满。 - Valerio Bozz
1
我认为 systemctl restart foo 不是必要的。当我运行 daemon-reload 时,它已经自动重新启动了,因为进程的运行时间已经超过了最大值。 - Chad
显示剩余5条评论

67

如何使用crontab。示例:

30 3 * * sun /bin/systemctl restart yourService

这将会在每个星期天的凌晨3点30分重启名为yourService的服务。

如果您希望在任何类Unix服务器上都能正常工作(例如,您不想担心特定的systemd版本等),您可能会喜欢这个解决方案。


6
我简直不敢相信我没想到这个!这绝对是最简单的解决方案。 - jlh
2
@jlh 这有什么比已接受答案更好的吗? - Ciasto piekarz
5
请看已接受答案下的评论,了解为什么那种方案存在问题。此外,使用cron可以在特定时间重启,而其他解决方案基于时间段,可能会随着时间漂移。 - jlh
2
如果服务被禁用或停止了会发生什么?这会重新启动任何手动停止的服务吗? - Louis Loudog Trottier
10
这将启动一个已停止或禁用的服务。如果你只希望应用于正在运行的服务,则可以使用try-restart命令。请参阅https://www.man7.org/linux/man-pages/man1/systemctl.1.html。 - JN01
显示剩余2条评论

60

我看到了一个解决方案在这里,它似乎很优雅,如果有些绕。关键的想法是创建一个由计时器触发的一次性服务,该服务重新启动另一个服务。

对于计时器:

[Unit]
Description=Do something daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

针对一次性服务:

[Unit]
Description=Restart service

[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl try-restart my_program.service

对于 Ubuntu 16.04 LTS 上的一次性服务:

[Unit]
Description=Restart service

[Service]
Type=oneshot
ExecStart=/bin/systemctl try-restart my_program.service

这个解决方案让你能够利用systemd的定时器功能,包括在特定时间重启服务,而不仅是在经过一段时间后重启。


7
在这里,我对每日定时器如何调用一次性服务感到困惑。 - svandragt
6
我不确定这是否是您所询问的,但据我所知,这个一次性服务与计时器相关联,其作用是重新启动另一个服务。我相信这可以通过将服务及其关联的计时器命名为相同的名称来实现,即weeklyRestart.serviceweeklyRestart.timer。当我解决这个问题时,我查阅了很多链接。如果需要,可以私信给我,我会发送链接列表。 - matmat
6
@svandragt 这就是它的工作原理。systemd定时器必须与它启动的服务具有相同的名称。 - Michael Ambrose
3
不一定,你也可以使用Unit=指定另一个服务。 - Chris Down
3
为什么这个定时器的 Persistent=true?如果重新启动服务是这里的目的,并且由于服务器离线而定时器未运行,则服务将被重启。 - Dominik
显示剩余2条评论

55

是的,你可以通过将你的服务设置为Type=notify来定期重启它。 在你的服务文件的[Service]部分中加入这个选项,同时添加Restart=alwaysWatchdogSec=xx,其中xx是以秒为单位的重启时间间隔。在这里,systemd会在xx时间段后杀掉你的进程,并由systemd重新启动。

[Unit]
.
.

[Service]
Type=notify
.
.
WatchdogSec=10
Restart=always
.
.

[Install]
WantedBy= ....

8
聪明!顺便说一句,“type=notify”对于我这个愚蠢的传统服务来说并不必要,因为它过一段时间后就会出错。使用“Type=simple”设置“WatchdogSec”可以达到同样的效果。 - wes
33
“WatchdogSec” 存在的问题是每次重启会发出 SIGABRT 信号(导致核心转储),这不仅会使进程不干净地终止,还可能最终填满根磁盘的核心转储文件。 - bk0
3
@wes谢谢,对我来说Type=notify不能使服务启动,Type=simple很好用(实际上没有任何Type也可以正常工作,可能简单模式是默认的Type)。 - WonderLand
1
除非服务已经意识到systemd,否则不要使用Type=notify。这样的服务单元已经在使用Type=notify。对于不与systemd通信的服务使用它将导致启动失败。 - Michael Hampton
7
从版本240开始,有WatchdogSignal,因此您可以覆盖SIGABRT https://github.com/systemd/systemd/blob/master/NEWS - Gary Myers
@bk0 我猜核心转储文件的大小有限制。我运行了几分钟的程序,但只看到了固定数量的核心转储文件。 - gyuaisdfaasf

7
想要评论以下内容:
[Service]
Restart=always
RuntimeMaxSec=604800

虽然上面有答案,但是没有更多的信息不能解决问题。

我的评论是,这个解决方案将调用由OnFailure=failure_handling.service设置的故障处理。由于定期重启不是真正的故障,因此来自故障处理服务的任何日志记录、通知等都是不需要的,可能会干扰。

实际的定期重启对于systemd来说是一个明智的功能,但我不会抱太大希望。


2
根据文档:使用Restart=的服务单元仅在达到启动限制后进入失败状态。 - Robin Daugherty

5

以下是一些达到相同目标的替代方法:

  • 如果您可以控制服务实现,您可以使其在一段时间后自愿结束,例如在某些迭代次数后(如果适用)直接退出,或者使用一个带有处理程序的超时定时器来发送SIGTERM/SIGKILL信号。
  • 如果自愿终止服务不可行/不切实际,您可以编写一个小型基于cron的脚本来杀死服务进程。

1

针对systemd版本>=229的复制粘贴解决方案:

SERVICE="systemd-resolved.service"
env SYSTEMD_EDITOR=tee sudo -E systemctl edit --system ${SERVICE} <<EOF
[Service]
Restart=always
RuntimeMaxSec=7200
EOF

sudo systemctl daemon-reload && sudo systemctl restart "${SERVICE}"

检查:

systemd-delta | grep ${SERVICE}
# NOTE: property here must be requested with "U": RuntimeMaxUSec
systemctl show ${SERVICE} --property=RuntimeMaxUSec

回滚:

SERVICE="systemd-resolved.service"
sudo rm -r "/etc/systemd/system/${SERVICE}.d"
sudo systemctl daemon-reload
sudo systemctl restart ${SERVICE}

更优雅的做法是以下内容:
SERVICE="systemd-resolved.service"
sudo systemctl set-property ${SERVICE} RuntimeMaxSec=7200

但是由于某些原因它无法工作:
Failed to set unit properties on systemd-resolved.service: Cannot set property RuntimeMaxUSec, or unknown property.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接