systemd/定时器

出自 ArchWiki

定时器是 systemd 单元文件,其名称以 .timer 结尾,用于控制 .service 文件或事件。定时器可以作为 cron 的替代方案(阅读 #作为 cron 的替代品)。定时器内置了对日历时间事件、单调时间事件的支持,并且可以异步运行。

定时器单元

定时器是 systemd 单元文件,后缀为 .timer。定时器与其他 单元配置文件 类似,从相同的路径加载,但包含一个 [Timer] 部分,用于定义定时器何时以及如何激活。定时器被定义为两种类型之一

  • 实时定时器(又名挂钟定时器)在日历事件时激活,方式与 cronjobs 相同。选项 OnCalendar= 用于定义它们。
  • 单调定时器在相对于可变起点的时 span 后激活。如果计算机暂时挂起或关闭,它们将停止。有许多不同的单调定时器,但都具有以下形式:OnTypeSec=。常见的单调定时器包括 OnBootSecOnUnitActiveSec

有关定时器选项的完整说明,请参阅 systemd.timer(5)。日历事件和时间 span 的参数语法在 systemd.time(7) 中定义。

注意: systemd 提供了目标 timers.target,它设置所有应在启动后激活的定时器(有关详细信息,请参见 systemd.special(7))。要使用它,请将 WantedBy=timers.target 添加到定时器的 [Install] 部分,并启用定时器单元。

服务单元

对于每个 .timer 文件,都存在一个匹配的 .service 文件(例如 foo.timerfoo.service)。.timer 文件激活并控制 .service 文件。.service 不需要 [Install] 部分,因为启用的是 timer 单元。如有必要,可以使用定时器的 [Timer] 部分中的 Unit= 选项来控制不同名称的单元。

管理

要使用 timer 单元,像其他单元一样启用启动它(记住添加 .timer 后缀)。要查看所有已启动的定时器,请运行

$ systemctl list-timers
NEXT                          LEFT        LAST                          PASSED     UNIT                         ACTIVATES
Thu 2014-07-10 19:37:03 CEST  11h left    Wed 2014-07-09 19:37:03 CEST  12h ago    systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Fri 2014-07-11 00:00:00 CEST  15h left    Thu 2014-07-10 00:00:13 CEST  8h ago     logrotate.timer              logrotate.service
注意
  • 要列出所有定时器(包括非活动的),请使用 systemctl list-timers --all
  • 由定时器启动的服务的状态很可能处于非活动状态,除非它当前正在被触发。
  • 如果定时器失去同步,删除其 /var/lib/systemd/timers(或用户定时器情况下的 ~/.local/share/systemd/)中的 stamp-* 文件可能会有所帮助。这些是零长度文件,用于标记每个定时器上次运行的时间。如果删除,它们将在下次启动定时器时重建。

示例

服务单元文件可以使用定时器开箱即用地进行调度。以下示例计划运行 foo.service,并使用名为 foo.timer 的相应定时器。

单调定时器

一个定时器,它将在启动后 15 分钟启动,并在系统运行时每周再次启动。

/etc/systemd/system/foo.timer
[Unit]
Description=Run foo weekly and on boot

[Timer]
OnBootSec=15min
OnUnitActiveSec=1w

[Install]
WantedBy=timers.target

实时定时器

一个定时器,它每周启动一次(周一凌晨 12:00)。激活后,如果它错过了上次启动时间(选项 Persistent=true),例如由于系统已关机,它会立即触发服务

/etc/systemd/system/foo.timer
[Unit]
Description=Run foo weekly

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target

当需要更具体的日期和时间时,OnCalendar 事件使用以下格式

DayOfWeek Year-Month-Day Hour:Minute:Second

星号可用于指定任何值,逗号可用于列出可能的值。用 .. 分隔的两个值表示一个连续范围。

在下面的示例中,服务在每个月的前四天中午 12:00 运行,但当该天是星期一或星期二时才运行。

OnCalendar=Mon,Tue *-*-01..04 12:00:00

要在每个月的第一个星期六运行服务,请使用

OnCalendar=Sat *-*-1..7 18:00:00

当使用 DayOfWeek 部分时,必须至少指定一个工作日。如果您希望某些内容每天凌晨 4 点运行,请使用

OnCalendar=*-*-* 4:00:00

要在不同时间运行服务,可以多次指定 OnCalendar。在下面的示例中,服务在工作日的 22:30 运行,在周末的 20:00 运行。

OnCalendar=Mon..Fri 22:30
OnCalendar=Sat,Sun 20:00

您还可以在指令末尾指定时区(使用 timedatectl list-timezones 列出接受的值)

OnCalendar=*-*-* 02:00:00 Europe/Paris

更多信息请参见 systemd.time(7)

提示
  • 可以测试 OnCalendar 时间规范,以验证其有效性,并使用 systemd-analyze 实用程序的 calendar 选项在定时器单元文件上使用时,计算条件下一次经过的时间。例如,可以使用 systemd-analyze calendar weeklysystemd-analyze calendar "Mon,Tue *-*-01..04 12:00:00"。添加 --iterations=N 以请求打印更多迭代。
  • faketime 命令对于使用上述命令测试各种场景特别有用;它随 libfaketime 软件包一起提供。
  • 诸如 dailyweekly 之类的特殊事件表达式指的是特定的开始时间,因此任何共享此类日历事件的定时器都将同时启动。如果定时器的服务竞争系统资源,则共享启动事件的定时器可能会导致系统性能下降。[Timer] 部分中的 RandomizedDelaySec 选项通过随机错开每个定时器的启动时间来避免此问题。请参见 systemd.timer(5)
  • 将选项 AccuracySec=1us 添加到 [Timer] 部分,以避免 AccuracySec1m 默认值不准确。另请参见 systemd.timer(5)
  • 某些选项 (WakeSystem) 可能需要特定的系统功能,并阻止定时器启动,从而导致以下错误消息:“Failed to enter waiting state: Operation not supported” 和 “Failed with result 'resources'.”。

瞬态定时器单元

可以使用 systemd-run 创建瞬态 .timer 单元。也就是说,可以设置一个命令在指定时间运行,而无需服务文件。例如,以下命令在 30 秒后触摸一个文件

# systemd-run --on-active=30 /bin/touch /tmp/foo

还可以指定一个没有定时器文件的预先存在的服务文件。例如,以下命令在 12.5 小时后启动名为 someunit.service 的 systemd 单元

# systemd-run --on-active="12h 30m" --unit someunit.service

有关更多信息和示例,请参见 systemd-run(1)

作为 cron 的替代品

虽然 cron 可以说是最著名的作业调度程序,但 systemd 定时器可以作为替代方案。

优点

使用定时器的主要优点来自每个作业都有其自己的 systemd 服务。其中一些优点是

  • 作业可以轻松地独立于其定时器启动。这简化了调试。
  • 每个作业都可以配置为在特定环境中运行(请参见 systemd.exec(5))。
  • 作业可以附加到 cgroups
  • 作业可以设置为依赖于其他 systemd 单元。
  • 作业记录在 systemd 日志中,以便于调试。

注意事项

使用 cron 容易完成的一些事情,仅使用定时器单元很难完成

  • 创建:要使用 systemd 设置定时作业,您需要创建两个文件并运行 systemctl 命令,而添加到 crontab 只需一行。
  • 电子邮件:没有内置的等效于 cron 的 MAILTO 功能来在作业失败时发送电子邮件。请参见 systemd#通过电子邮件通知,了解使用 OnFailure= 设置类似功能的示例。

另请注意,默认情况下,用户定时器单元仅在活动用户登录会话期间运行。但是,持久化可以使服务在启动时运行,即使用户没有活动的登录会话。

使用 crontab

通过安装解析传统 crontab 以配置定时器的软件包,可以解决几个注意事项。systemd-cron-next-gitAURsystemd-cronAUR 是两个这样的软件包。这些可以提供缺少的 MAILTO 功能。

此外,与 crontab 一样,可以使用 systemctl 获得所有计划作业的统一视图。请参见 #管理

手动

除了从现有的 crontab 迁移之外,可能还需要使用与 cron 相同的周期性。为了避免为每个定期启动的服务创建定时器的繁琐任务,请使用 模板单元,例如

/etc/systemd/system/monthly@.timer
[Unit]
Description=Monthly Timer for %i service

[Timer]
OnCalendar=*-*-1 02:00:00
AccuracySec=6h
RandomizedDelaySec=1h
Persistent=true
Unit=%i.service

[Install]
WantedBy=default.target
注意: 请参见 systemd.timer(5) § OPTIONS,了解使用 RandomizedDelaySec 而不仅仅是 AccuracySec 以避免定时器启动的所有单元一次触发的重要性。

然后只需要启用/启动 monthly@unit_name.timer

提示: 模板单元可以嵌套,例如,可以启用/启动 monthly@btrfs-scrub@mnt-$(systemd-escape bbb76c63-e4ac-4e39-8897-a120c5d30686).timer

技巧和窍门

处理 “生存时间”

某些软件会跟踪自上次运行以来经过的时间,例如,如果上次下载结束时间不到 24 小时,则阻止更新数据库。

默认情况下,定时器不跟踪它们启动的任务何时结束。为了解决这个问题,我们可以使用 OnUnitInactiveSeconds

/etc/systemd/system/daily-inactive@.timer
[Unit]
Description=Launch %i service 24hours after it deactivated

[Timer]
OnUnitInactiveSec=1day1sec
Unit=%i.service
Persistent=true

[Install]
WantedBy=default.target
提示: 结合 Restart=on-failureRestartSec,可以使单元在失败和成功后根据不同的时间表重新运行,请参见 systemd.service(5) § OPTIONS

桌面通知

The systemd-timer-notifyAUR 提供自动桌面通知,帮助您注意到 systemd 服务何时由定时器触发并正在运行。通知将在服务完成后自动关闭。

这有助于理解为什么 CPU 使用率很高,或者在备份服务尚未完成时防止关机。

有关更多详细信息和配置选项,请访问 https://gitlab.com/Zesko/systemd-timer-notify

参见

https://github.com/systemd-cron/systemd-cron || systemd-cronAUR
  • systemd-cron-next — 从 crontab 和 anacrontab 文件生成定时器/服务的工具
https://github.com/systemd-cron/systemd-cron-next || systemd-cron-next-gitAUR