如何确保在 Systemd 中启动服务之前存在延迟?

我有一个服务,这取决于卡珊德拉优雅地上来,集群正在上升和准备。

为了确保依赖顺序得到满足,我有以下单元文件

[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

如何确保 bringup-app 进程在尝试启动前等待30秒?目前,虽然它是在 Cassandra 之后启动的,但是我注意到 Cassandra 集群还没有启动,因此,在启动过程中,任何连接到 Cassandra 的 bringup-app 尝试都会失败。

因此,我想添加一个延迟。可以通过单元文件吗?

207666 次浏览

You can run the sleep command before your ExecStart with ExecStartPre :

[Service]
ExecStartPre=/bin/sleep 30

This answer on super user I think is a better answer. From https://superuser.com/a/573761/67952

"But since you asked for a way without using Before and After, you can use:

Type=idle

which as man systemd.service explains

Behavior of idle is very similar to simple; however, actual execution of the service program is delayed until all active jobs are dispatched. This may be used to avoid interleaving of output of shell services with the status output on the console. Note that this type is useful only to improve console output, it is not useful as a general unit ordering tool, and the effect of this service type is subject to a 5s time-out, after which the service program is invoked anyway. "

You can create a .timer systemd unit file to control the execution of your .service unit file.

So for example, to wait for 1 minute after boot-up before starting your foo.service, create a foo.timer file in the same directory with the contents:

[Timer]
OnBootSec=1min

It is important that the service is disabled (so it doesn't start at boot), and the timer enabled, for all this to work (thanks to user tride for this):

systemctl disable foo.service
systemctl enable foo.timer

You can find quite a few more options and all information needed here: https://wiki.archlinux.org/index.php/Systemd/Timers

Instead of editing the bringup service, add a post-start delay to the service which it depends on. Edit cassandra.service like so:

ExecStartPost=/bin/sleep 30

This way the added sleep shouldn't slow down restarts of starting services that depend on it (though does slow down its own start, maybe that's desirable?).

The systemd way to do this is to have the process "talk back" when it's setup somehow, like by opening a socket or sending a notification (or a parent script exiting). Which is of course not always straight-forward especially with third party stuff :|

You might be able to do something inline like

ExecStart=/bin/bash -c '/bin/start_cassandra &; do_bash_loop_waiting_for_it_to_come_up_here'

or a script that does the same. Or put do_bash_loop_waiting_for_it_to_come_up_here in an ExecStartPost

Or create a helper .service that waits for it to come up, so the helper service depends on cassandra, and waits for it to come up, then your other process can depend on the helper service.

(May want to increase TimeoutStartSec from the default 90s as well)

Combining the answers from @Ortomala Lokni and @rogerdpack, another alternative is to have the dependent service monitor when the first one has started / done the thing you're waiting for.

For example, here's how I am making the fail2ban service wait for Docker to open port 443 (so that fail2ban's iptables entries take priority over Docker's):

[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'

Simply replace nc -z -v -w1 localhost 443 with a command that fails (non-zero exit code) while the first service is starting and succeeds once it is up.

For the Cassandra case, the ideal would be a command that only returns 0 when the cluster is available.

(May want to increase TimeoutStartSec from the default 90s as well, or set TimeoutStartSec=0 to disable startup timeouts)

I have used systemd timer for delay a service and it work very well

cat /lib/systemd/system/foo.timer
[Unit]
Description=Wait some second before run foo


[Timer]
OnActiveSec=5sec
AccuracySec=1s


[Install]
WantedBy=timers.target

see timers : systemctl list-timers

log:

journalctl -f -u foo.timer
journalctl -f -u foo