为什么我的 systemd 计时器仅在单位成为目标时触发一次?

Pau*_*aul 9 systemd systemd-timers

我有几个服务(静态站点生成器),我想从同一个 systemd 计时器定期触发它们。我找到了这个问题/答案,它准确地涵盖了我想要做的事情,并描述了一个设置,其中多个服务的.target文件Wants=由相应的计时器触发。这听起来不错,但我发现当我实际设置它时,它只会触发一次,然后自行禁用!

\n

我准备了一个最小的工作示例(这不会触发多个服务,但演示了相同的问题):

\n

test-timer.timer:

\n
[Unit]\nDescription=A test timer\n\n[Timer]\nOnCalendar=*-*-* *:*:30\nUnit=test-timer.target\n\n[Install]\nWantedBy=timers.target\n
Run Code Online (Sandbox Code Playgroud)\n

test-timer.target:

\n
[Unit]\nDescription=Target unit\nWants=test-timer.service\nAfter=test-timer.service\n\n[Install]\nWantedBy=timers.target\n
Run Code Online (Sandbox Code Playgroud)\n

test-timer.service:

\n
[Unit]\nDescription=Run test\n\n[Service]\nExecStart=/usr/bin/bash -c "date --rfc-3339=\'seconds\' >> /tmp/test-timer-output"\n\n[Install]\nAlso=test-timer.target\n
Run Code Online (Sandbox Code Playgroud)\n

启用定时器:

\n
$ sudo cp test-timer.* /etc/systemd/system/\n$ sudo systemctl enable --now test-timer.timer\nCreated symlink /etc/systemd/system/timers.target.wants/test-timer.timer \xe2\x86\x92 /etc/systemd/system/test-timer.timer.\n
Run Code Online (Sandbox Code Playgroud)\n

systemctl list-timers --all然后,当我在第一次运行之前查看 的输出时,我得到(忽略其他计时器):

\n
NEXT                        LEFT       LAST     PASSED       UNIT                ACTIVATES\nFri 2021-10-08 10:38:30 EDT 21s left   n/a      n/a          test-timer.timer    test-timer.target\n
Run Code Online (Sandbox Code Playgroud)\n

第一次运行后,NEXTLEFT已替换为n/a

\n
NEXT     LEFT    LAST                        PASSED        UNIT                  ACTIVATES\nn/a      n/a     Fri 2021-10-08 10:38:32 EDT 1min 5s ago   test-timer.timer      test-timer.target\n
Run Code Online (Sandbox Code Playgroud)\n

我还尝试添加Persistent=truetest-timer.target显式启用test-timer.target,但这些都不起作用。每当我这样做时systemctl restart test-timer.timer,它都会重新启动,但只会触发一次运行,然后再也不会运行。

\n

如果我通过更改toUnit=行来删除间接层,该服务会很高兴地按预期每分钟触发一次。test-timer.timerUnit=test-timer.service

\n

我是否缺少某些配置或安装步骤?

\n

Pau*_*aul 7

在 Twitter 上获得一些帮助后,我成功解决了这个问题。问题是 systemd计时器只会激活不活动的服务,而目标的默认行为是激活并保持活动状态,除非有什么原因导致其停机(它与单元的生命周期无关Wants=)。要在目标单元激活的任何服务变为非活动状态时强制目标单元变为非活动状态,请在上面的示例中BindsTo=使用 代替。Wants=因此,对于这个最小的例子:

test-timer.target:

[Unit]
Description=Target unit
BindsTo=test-timer.service
After=test-timer.service

[Install]
WantedBy=timers.target
Run Code Online (Sandbox Code Playgroud)

test-timer.service:

[Unit]
Description=Run test

[Service]
ExecStart=/usr/bin/bash -c "date --rfc-3339='seconds' >> /tmp/test-timer-output"

[Install]
Also=test-timer.target
Run Code Online (Sandbox Code Playgroud)

然后您可以看到,一旦test-timer.service运行完毕,test-timer.target也将变为inactive(因此计时器将能够再次激活它):

$ sudo systemctl list-units --all test-timer.target test-timer.service
  UNIT               LOAD   ACTIVE   SUB  DESCRIPTION
  test-timer.service loaded inactive dead Run test   
  test-timer.target  loaded inactive dead Target unit
Run Code Online (Sandbox Code Playgroud)

而在更改之前,目标在服务终止后仍保持活动状态:

$ sudo systemctl list-units --all test-timer.target test-timer.service
  UNIT               LOAD   ACTIVE   SUB    DESCRIPTION
  test-timer.service loaded inactive dead   Run test   
  test-timer.target  loaded active   active Target unit
Run Code Online (Sandbox Code Playgroud)

  • 特别是当你有多个服务由一个目标触发并且它们都是一次性的时,你可以使用“StopWhenUnneeded=True”。这将导致目标在工作完成后变得不活动。 (4认同)