如何确保在 systemd 中启动服务之前存在延迟?
Posted
技术标签:
【中文标题】如何确保在 systemd 中启动服务之前存在延迟?【英文标题】:How to ensure that there is a delay before a service is started in systemd? 【发布时间】:2017-08-17 11:50:45 【问题描述】:我有一项服务,该服务依赖于 Cassandra 正常启动并且集群已启动并准备就绪。
为确保满足依赖顺序,我有以下单元文件
[Unit]
Requires=cassandra.service
After=cassandra.service
[Service]
Environment=JAVA_HOME=/usr/java/jre
ExecStart=@bringup.instance.path@/webapps/bringup-app/bin/bringup
TimeoutStartSec=0
ExecStop=
PIDFile=@bringup.instance.path@/logs/bringup.pid
Restart=always
[Install]
WantedBy=multi-user.target
如何确保启动应用程序在尝试启动前等待 30 秒?目前,虽然它是在 Cassandra 之后启动的,但我注意到 Cassandra 集群尚未启动,因此从启动应用程序连接到 Cassandra 作为启动的一部分的任何尝试都失败了。
因此我想添加一个延迟。可以通过单元文件实现吗?
【问题讨论】:
cassandra-service 应该只在完全启动后返回。也就是说,启动器应该等到服务准备好,然后退出。此外,cassandra-service 可以使用套接字激活。 【参考方案1】:您可以在 ExecStart 和 ExecStartPre 之前运行 sleep 命令:
[Service]
ExecStartPre=/bin/sleep 30
【讨论】:
如何在服务重启时不休眠的情况下解决这个问题? 这在服务重启时有效,这对我的用例来说是一个奖励 不知道为什么,但它为服务创建了一个 30 秒的循环,ExecStart
不会启动。
我觉得 systemd 真的不希望我这样做。延迟较长会导致超时,例如systemctl start x
在睡眠完成之前不会返回
当使用上述方法延迟超过90秒时,服务的启动超时将不得不增加(例如TimeoutStartSec=120
)【参考方案2】:
我认为这个关于超级用户的答案是一个更好的答案。 来自https://superuser.com/a/573761/67952
“但是既然你问了一个没有使用Before和After的方法,你可以使用:
Type=idle
正如man systemd.service
解释的那样
idle 的行为与 simple 非常相似;但是,服务程序的实际执行会延迟,直到所有活动作业都被调度。这可以用来避免输出的交错 shell 服务与控制台上的状态输出。注意这个类型只对提高控制台输出有用,作为通用的单元排序工具没有用,这个效果 服务类型会受到 5 秒的超时,之后无论如何都会调用服务程序。 "
【讨论】:
它也与 oneshot 非常相似,对于像 vncservers 一样执行后退出 shell 的命令,simple 不会运行 ExecStop(当然可以调整为不在后台运行)但我的意思是它运行也适用于后台进程。 您很方便地从引用中省略了最重要的部分:“这种类型仅用于改善控制台输出,作为通用单元订购工具没有用,这种服务类型的效果是在 5 秒超时后,无论如何都会调用服务程序 [...] 通常不建议对长时间运行的服务使用空闲或 oneshot。” @bviktor,文档已更新。你是对的,它没有正确解释它的目的文档确实说however, actual execution of the service program is delayed until all active jobs are dispatched
。文档自相矛盾。
如果您的服务启动然后启动序列立即“结束并进入空闲”,这可能还不够,可能没有足够长的延迟......:|【参考方案3】:
您可以创建一个.timer
systemd 单元文件来控制您的.service
单元文件的执行。
例如,要在启动后等待 1 分钟再启动 foo.service
,请在同一目录中创建一个 foo.timer
文件,其内容为:
[Timer]
OnBootSec=1min
禁用该服务(因此它不会在启动时启动)并启用计时器非常重要,这样所有这些都可以正常工作(感谢用户tride):
systemctl disable foo.service
systemctl enable foo.timer
您可以在此处找到更多选项和所需的所有信息:https://wiki.archlinux.org/index.php/Systemd/Timers
【讨论】:
虽然这会起作用,但对于简单的延迟来说,这有点过头了。对于简单的延迟,请使用 ExecStartPre,对于更复杂的计划,请使用计时器。 OnBootSec 显然是相对于框“首次启动”的时间,所以如果您尝试“启动之后”的服务没有在 1m 内启动,它仍然可能启动得太早,我想知道? freedesktop.org/software/systemd/man/systemd.timer.html 但我想如果你设置一个足够长的数字它可以工作...... @rogerdpack 这是真的。然而,对于 Cassandra,即使在服务启动后,也无法立即启动会话(使用 cqlsh 或其他方式),这就是为什么这种方法对于这种特定情况更方便的原因。但是,是的,您基本上是在“猜测”它可用所需的时间。 IIRC 你还需要启动计时器systemctl start foo.timer
。
IMO 这是最好的答案,因为 systemd 在执行 ExecStartPre 指令时认为一个单元正在“启动”。【参考方案4】:
不要编辑启动服务,而是向它所依赖的服务添加启动后延迟。像这样编辑cassandra.service
:
ExecStartPost=/bin/sleep 30
这样添加的睡眠不应该减慢依赖它的启动服务的重新启动(尽管它会减慢它自己的启动,也许这是可取的?)。
【讨论】:
【参考方案5】:systemd
执行此操作的方法是让进程在以某种方式设置时“回话”,例如通过打开套接字或发送 notification(或退出父脚本)。当然,这并不总是直截了当的,尤其是对于第三方的东西:|
你也许可以做一些内联的事情,比如
ExecStart=/bin/bash -c '/bin/start_cassandra &; do_bash_loop_waiting_for_it_to_come_up_here'
或执行相同操作的脚本。或者将 do_bash_loop_waiting_for_it_to_come_up_here
放入 ExecStartPost
或者创建一个 helper .service 来等待它出现,所以 helper 服务依赖于 cassandra,并等待它出现,然后你的其他进程可以依赖于 helper 服务。
(可能还想将TimeoutStartSec 从默认的 90s 增加)
【讨论】:
【参考方案6】:结合@Ortomala Lokni 和@rogerdpack 的答案,另一种选择是在第一个启动/完成您正在等待的事情时让依赖服务监视器。
例如,下面是我如何让 fail2ban 服务等待 Docker 打开端口 443(这样 fail2ban 的 iptables 条目优先于 Docker):
[Service]
ExecStartPre=/bin/bash -c '(while ! nc -z -v -w1 localhost 443 2>/dev/null; do echo "Waiting for port 443 to open..."; sleep 2; done); sleep 2'
只需将nc -z -v -w1 localhost 443
替换为在第一个服务启动时失败(非零退出代码)的命令,并在启动后成功。
对于 Cassandra 情况,理想的情况是只在集群可用时返回 0 的命令。
(可能还想将TimeoutStartSec 从默认的 90s 增加,或者设置 TimeoutStartSec=0
以禁用启动超时)
【讨论】:
最佳答案在这里,完全被低估了!我在rogerdpack的回答中添加了关于增加TimeoutStartSec的注释,或者您可以将其设置为0以禁用启动超时。 谢谢!这是我一直在寻找的东西,最佳答案 +1。【参考方案7】:我使用 systemd timer 来延迟服务,效果很好
cat /lib/systemd/system/foo.timer
[Unit]
Description=Wait some second before run foo
[Timer]
OnActiveSec=5sec
AccuracySec=1s
[Install]
WantedBy=timers.target
查看计时器:systemctl list-timers
日志:
journalctl -f -u foo.timer
journalctl -f -u foo
【讨论】:
以上是关于如何确保在 systemd 中启动服务之前存在延迟?的主要内容,如果未能解决你的问题,请参考以下文章