使用 systemd 定时器代替 cron 任务

与 cron 任务相比,定时器提供对事件更精细的控制。
193 位读者喜欢这篇文章。
Team checklist

我正在将我的 cron 任务转换为 systemd 定时器。我已经使用定时器几年了,但通常,我只是学习足够的知识来执行我正在处理的任务。在为这个 systemd 系列 进行研究时,我了解到 systemd 定时器有一些非常有趣的功能。

与 cron 任务一样,systemd 定时器可以在指定的时间间隔触发事件——shell 脚本和程序——例如每天一次,在每月的特定日期(可能仅在星期一),或在工作日的上午 8 点到下午 6 点之间每 15 分钟一次。定时器还可以做一些 cron 任务无法完成的事情。例如,定时器可以触发脚本或程序在事件发生后的一段时间运行,例如启动、启动、完成之前的任务,甚至是由定时器调用的服务单元的前一次完成。

系统维护定时器

当在新的系统上安装 Fedora 或任何基于 systemd 的发行版时,它会创建几个定时器,这些定时器是任何 Linux 主机后台发生的系统维护过程的一部分。这些定时器触发对于常见维护任务必要的事件,例如更新系统数据库、清理临时目录、轮换日志文件等等。

作为一个例子,我将使用 systemctl status *timer 命令查看我的主要工作站上的一些定时器,以列出我的主机上的所有定时器。星号符号的工作方式与文件通配符相同,因此此命令列出所有 systemd 定时器单元

[root@testvm1 ~]# systemctl status *timer 
● mlocate-updatedb.timer - Updates mlocate database every day
     Loaded: loaded (/usr/lib/systemd/system/mlocate-updatedb.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
   Triggers: ● mlocate-updatedb.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Updates mlocate database every day.

● logrotate.timer - Daily rotation of log files
     Loaded: loaded (/usr/lib/systemd/system/logrotate.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
   Triggers: ● logrotate.service
       Docs: man:logrotate(8)
             man:logrotate.conf(5)

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily rotation of log files.

● sysstat-summary.timer - Generate summary of yesterday's process accounting
     Loaded: loaded (/usr/lib/systemd/system/sysstat-summary.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:07:00 EDT; 15h left
   Triggers: ● sysstat-summary.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Generate summary of yesterday's process accounting.

● fstrim.timer - Discard unused blocks once a week
     Loaded: loaded (/usr/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Mon 2020-06-08 00:00:00 EDT; 3 days left
   Triggers: ● fstrim.service
       Docs: man:fstrim

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Discard unused blocks once a week.

● sysstat-collect.timer - Run system activity accounting tool every 10 minutes
     Loaded: loaded (/usr/lib/systemd/system/sysstat-collect.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Thu 2020-06-04 08:50:00 EDT; 41s left
   Triggers: ● sysstat-collect.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Run system activity accounting tool every 10 minutes.

● dnf-makecache.timer - dnf makecache --timer
     Loaded: loaded (/usr/lib/systemd/system/dnf-makecache.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Thu 2020-06-04 08:51:00 EDT; 1min 41s left
   Triggers: ● dnf-makecache.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started dnf makecache –timer.

● systemd-tmpfiles-clean.timer - Daily Cleanup of Temporary Directories
     Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-clean.timer; static; vendor preset: disabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 08:19:00 EDT; 23h left
   Triggers: ● systemd-tmpfiles-clean.service
       Docs: man:tmpfiles.d(5)
             man:systemd-tmpfiles(8)

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily Cleanup of Temporary Directories.

每个定时器都至少有六行相关信息

  • 第一行是定时器的文件名及其用途的简短描述。
  • 第二行显示定时器的状态,是否已加载、定时器单元文件的完整路径以及供应商预设。
  • 第三行指示其活动状态,其中包括定时器变为活动状态的日期和时间。
  • 第四行包含定时器下次触发的日期和时间以及触发发生前的大概时间。
  • 第五行显示由定时器触发的事件或服务的名称。
  • 一些(但不是全部)systemd 单元文件具有指向相关文档的指针。我的虚拟机输出中的三个定时器具有指向文档的指针。这是一个很好的(但可选的)数据位。
  • 最后一行是由定时器触发的服务的最新实例的日志条目。

根据您的主机,您可能会有一组不同的定时器。

创建定时器

虽然我们可以解构一个或多个现有定时器来了解它们的工作原理,但让我们创建我们自己的 服务单元 和一个定时器单元来触发它。我们将使用一个相当简单的示例,以便保持简单。完成此操作后,将更容易理解其他定时器的工作原理并确定它们在做什么。

首先,创建一个简单的服务,它将运行一些基本的东西,例如 free 命令。例如,您可能希望定期监视可用内存。在 /etc/systemd/system 目录中创建以下 myMonitor.service 单元文件。它不需要是可执行的

# This service unit is for testing timer units 
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=Logs system statistics to the systemd journal
Wants=myMonitor.timer

[Service]
Type=oneshot
ExecStart=/usr/bin/free

[Install]
WantedBy=multi-user.target

现在让我们看看状态并测试我们的服务单元,以确保它按我们的预期工作。

[root@testvm1 system]# systemctl status myMonitor.service 
● myMonitor.service - Logs system statistics to the systemd journal
     Loaded: loaded (/etc/systemd/system/myMonitor.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
[root@testvm1 system]# systemctl start myMonitor.service 
[root@testvm1 system]#

输出在哪里?默认情况下,来自 systemd 服务单元运行的程序的标准输出 (STDOUT) 被发送到 systemd 日志,这会留下您可以现在或以后查看的记录——直到某个时间点。(我将在本系列的未来文章中查看 systemd 日志记录和保留策略。)专门查看您的服务单元的日志,并且仅查看今天的日志。-S 选项是 --since 的缩写版本,允许您指定 journalctl 工具应搜索条目的时间段。这不是因为您不关心以前的结果——在这种情况下,不会有任何结果——而是为了缩短搜索时间,如果您的主机已经运行了很长时间并且在日志中累积了大量条目

[root@testvm1 system]# journalctl -S today -u myMonitor.service 
-- Logs begin at Mon 2020-06-08 07:47:20 EDT, end at Thu 2020-06-11 09:40:47 EDT. --
Jun 11 09:12:09 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 11 09:12:09 testvm1.both.org free[377966]:               total        used        free      shared  buff/cache   available
Jun 11 09:12:09 testvm1.both.org free[377966]: Mem:       12635740      522868    11032860        8016     1080012    11821508
Jun 11 09:12:09 testvm1.both.org free[377966]: Swap:       8388604           0     8388604
Jun 11 09:12:09 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
[root@testvm1 system]#

由服务触发的任务可以是单个程序、一系列程序或用任何脚本语言编写的脚本。通过将以下行添加到 myMonitor.service 单元文件的 [Service] 部分的末尾,向服务添加另一个任务

ExecStart=/usr/bin/lsblk

再次启动服务并检查日志中的结果,结果应如下所示。您应该在日志中看到来自两个命令的结果

Jun 11 15:42:18 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 11 15:42:18 testvm1.both.org free[379961]:               total        used        free      shared  buff/cache   available
Jun 11 15:42:18 testvm1.both.org free[379961]: Mem:       12635740      531788    11019540        8024     1084412    11812272
Jun 11 15:42:18 testvm1.both.org free[379961]: Swap:       8388604           0     8388604
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: sda             8:0    0  120G  0 disk
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: ├─sda1          8:1    0    4G  0 part /boot
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: └─sda2          8:2    0  116G  0 part
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: sr0            11:0    1 1024M  0 rom
Jun 11 15:42:18 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 11 15:42:18 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.

现在您知道您的服务按预期工作,在 /etc/systemd/system 中创建定时器单元文件 myMonitor.timer,并添加以下内容

# This timer unit is for testing
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=Logs some system statistics to the systemd journal
Requires=myMonitor.service

[Timer]
Unit=myMonitor.service
OnCalendar=*-*-* *:*:00

[Install]
WantedBy=timers.target

myMonitor.timer 文件 中的 OnCalendar 时间规范 *-*-* *:*:00 应该触发定时器每分钟执行一次 myMonitor.service 单元。稍后我将在本文中探讨 OnCalendar 设置。

现在,观察任何与运行由定时器触发的服务相关的日志条目。您也可以跟踪定时器,但跟踪服务可以让您近乎实时地查看结果。使用 -f(跟踪)选项运行 journalctl

[root@testvm1 system]# journalctl -S today -f -u myMonitor.service
-- Logs begin at Mon 2020-06-08 07:47:20 EDT. --

启动但不启用定时器,看看运行一段时间后会发生什么

[root@testvm1 ~]# systemctl start myMonitor.service
[root@testvm1 ~]#

一个结果立即显示出来,下一个结果大约在一分钟间隔出现。观察日志几分钟,看看您是否注意到我注意到的相同内容

[root@testvm1 system]# journalctl -S today -f -u myMonitor.service
-- Logs begin at Mon 2020-06-08 07:47:20 EDT. --
Jun 13 08:39:18 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:39:18 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:39:19 testvm1.both.org free[630566]:               total        used        free      shared  buff/cache   available
Jun 13 08:39:19 testvm1.both.org free[630566]: Mem:       12635740      556604    10965516        8036     1113620    11785628
Jun 13 08:39:19 testvm1.both.org free[630566]: Swap:       8388604           0     8388604
Jun 13 08:39:18 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sda             8:0    0  120G  0 disk
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─sda1          8:1    0    4G  0 part /boot
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: └─sda2          8:2    0  116G  0 part
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sr0            11:0    1 1024M  0 rom
Jun 13 08:40:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:40:46 testvm1.both.org free[630572]:               total        used        free      shared  buff/cache   available
Jun 13 08:40:46 testvm1.both.org free[630572]: Mem:       12635740      555228    10966836        8036     1113676    11786996
Jun 13 08:40:46 testvm1.both.org free[630572]: Swap:       8388604           0     8388604
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sda             8:0    0  120G  0 disk
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─sda1          8:1    0    4G  0 part /boot
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: └─sda2          8:2    0  116G  0 part
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sr0            11:0    1 1024M  0 rom
Jun 13 08:40:46 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:40:46 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
Jun 13 08:41:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:41:46 testvm1.both.org free[630580]:               total        used        free      shared  buff/cache   available
Jun 13 08:41:46 testvm1.both.org free[630580]: Mem:       12635740      553488    10968564        8036     1113688    11788744
Jun 13 08:41:46 testvm1.both.org free[630580]: Swap:       8388604           0     8388604
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sda             8:0    0  120G  0 disk
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─sda1          8:1    0    4G  0 part /boot
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: └─sda2          8:2    0  116G  0 part
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   ├─VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sr0            11:0    1 1024M  0 rom
Jun 13 08:41:47 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:41:47 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.

务必检查定时器和服务两者的状态。

您可能至少在日志中注意到了两件事。首先,您不需要做任何特殊的事情来使 myMonitor.service 单元中 ExecStart 触发器的 STDOUT 存储在日志中。这都是使用 systemd 运行服务的一部分。但是,这确实意味着您可能需要小心从服务单元运行脚本以及它们生成多少 STDOUT

第二件事是定时器不会在 :00 秒的整分钟或甚至正好在前一个实例的一分钟触发。这是故意的,但如果必要(或者如果它只是冒犯了您的系统管理员的敏感性),则可以覆盖它。

此行为的原因是为了防止多个服务在完全相同的时间触发。例如,您可以使用诸如 Weekly、Daily 等时间规范。这些快捷方式都定义为在触发日的 00:00:00 小时触发。当以这种方式指定多个定时器时,它们很可能尝试同时启动。

systemd 定时器被有意设计为在指定时间附近以某种随机方式触发,以尝试防止同时触发。它们在时间窗口内半随机地触发,该时间窗口从指定的触发时间开始,到指定时间加一分钟结束。根据 systemd.timer 手册页,此触发时间相对于所有其他定义的定时器单元保持在稳定的位置。您可以在上面的日志条目中看到,定时器在启动时立即触发,然后在每分钟后约 46 或 47 秒触发。

在大多数情况下,这种概率触发时间是可以接受的。在安排备份等任务运行时,只要它们在非工作时间运行,就不会有问题。系统管理员可以选择确定性的开始时间,例如典型 cron 任务规范中的 01:05:00,以避免与其他任务冲突,但是有很大范围的时间值可以完成该任务。开始时间的一分钟随机性通常是无关紧要的。

但是,对于某些任务,精确的触发时间是绝对必要的。对于这些任务,您可以通过在定时器单元文件的 Timer 部分添加如下语句来指定更高的触发时间跨度精度(在微秒内)

AccuracySec=1us

时间跨度可用于指定所需的精度,以及定义重复或一次性事件的时间跨度。它识别以下单位

  • usec, us, µs
  • msec, ms
  • seconds, second, sec, s
  • minutes, minute, min, m
  • hours, hour, hr, h
  • days, day, d
  • weeks, week, w
  • months, month, M(定义为 30.44 天)
  • years, year, y(定义为 365.25 天)

/usr/lib/systemd/system 中的所有默认定时器都指定了更大的精度范围,因为精确时间并不重要。查看系统创建的定时器中的一些规范

[root@testvm1 system]# grep Accur /usr/lib/systemd/system/*timer
/usr/lib/systemd/system/fstrim.timer:AccuracySec=1h
/usr/lib/systemd/system/logrotate.timer:AccuracySec=1h
/usr/lib/systemd/system/logwatch.timer:AccuracySec=12h
/usr/lib/systemd/system/mlocate-updatedb.timer:AccuracySec=24h
/usr/lib/systemd/system/raid-check.timer:AccuracySec=24h
/usr/lib/systemd/system/unbound-anchor.timer:AccuracySec=24h
[root@testvm1 system]#

查看 /usr/lib/systemd/system 目录中的一些定时器单元文件的完整内容,以了解它们的构造方式。

您不必在此实验中启用定时器以在启动时激活它,但要执行此操作的命令将是

# systemctl enable myMonitor.timer

您创建的单元文件不需要是可执行的。您也没有启用服务单元,因为它是由定时器触发的。如果您愿意,您仍然可以从命令行手动触发服务单元。尝试一下并观察日志。

有关定时器精度、事件时间规范和触发事件的更多信息,请参阅 systemd.timersystemd.time 的手册页。

定时器类型

systemd 定时器具有 cron 中没有的其他功能,cron 仅在特定的、重复的、实时日期和时间触发。systemd 定时器可以配置为基于其他 systemd 单元中的状态更改触发。例如,定时器可以配置为在系统启动后、启动后或定义的服务器单元激活后的特定经过时间后触发。这些称为单调定时器。单调是指不断增加的计数或序列。这些定时器不是持久性的,因为它们在每次启动后都会重置。

表 1 列出了单调定时器以及每个定时器的简短定义,以及 OnCalendar 定时器,它不是单调的,用于指定可能重复也可能不重复的未来时间。此信息来自 systemd.timer 手册页,并进行了一些细微的更改。

定时器 单调 定义
OnActiveSec= X 这定义了一个相对于定时器激活时刻的定时器。
OnBootSec= X 这定义了一个相对于机器启动时间的定时器。
OnStartupSec= X 这定义了一个相对于服务管理器首次启动时间的定时器。对于系统定时器单元,这与 OnBootSec= 非常相似,因为系统服务管理器通常在启动时很早就启动。当在每个用户服务管理器中运行的单元中配置时,它主要有用,因为用户服务管理器通常仅在首次登录时启动,而不是在启动期间启动。
OnUnitActiveSec= X 这定义了一个相对于要激活的定时器上次激活时间的定时器。
OnUnitInactiveSec= X 这定义了一个相对于要激活的定时器上次停用时间的定时器。
OnCalendar=   这使用日历事件表达式定义实时(即,挂钟)定时器。有关日历事件表达式语法的更多信息,请参阅 systemd.time(7)。否则,语义类似于 OnActiveSec= 和相关设置。此定时器是最类似于 cron 服务中使用的定时器。

表 1:systemd 定时器定义

单调定时器可以使用与其之前提到的 AccuracySec 语句相同的时间跨度快捷方式名称,但 systemd 将这些名称标准化为秒。例如,您可能想要指定一个定时器,该定时器在系统启动后五天触发一次事件;它可能看起来像:OnBootSec=5d。如果主机在 2020-06-15 09:45:27 启动,则定时器将在 2020-06-20 09:45:27 或之后一分钟内触发。

日历事件规范

日历事件规范是在所需的重复时间触发定时器的关键部分。首先查看 OnCalendar 设置中使用的一些规范。

systemd 及其定时器使用与 crontab 中使用的格式不同的时间和日期规范样式。它比 crontab 更灵活,并允许以 at 命令的方式使用模糊的日期和时间。它也应该足够熟悉,以便易于理解。

使用 OnCalendar= 的 systemd 定时器的基本格式是 DOW YYYY-MM-DD HH:MM:SS。DOW(星期几)是可选的,其他字段可以使用星号 (*) 来匹配该位置的任何值。所有日历时间形式都转换为规范化形式。如果未指定时间,则假定为 00:00:00。如果未指定日期但指定了时间,则下一个匹配项可能是今天或明天,具体取决于当前时间。名称或数字可用于月份和星期几。可以指定每个单元的逗号分隔列表。可以使用 .. 在开始值和结束值之间指定单元范围。

有几个有趣的选项可用于指定日期。波浪号 (~) 可用于指定当月的最后一天或当月最后一天之前的指定天数。“/” 可用于将星期几指定为修饰符。

以下是一些在 OnCalendar 语句中使用的一些典型时间规范示例。

日历事件规范 描述
DOW YYYY-MM-DD HH:MM:SS  
*-*-* 00:15:30 每年每月每天午夜 15 分 30 秒
Weekly 每周一 00:00:00
Mon *-*-* 00:00:00 与 weekly 相同
Mon 与 weekly 相同
Wed 2020-*-* 2020 年的每个星期三 00:00:00
Mon..Fri 2021-*-* 2021 年的每个工作日 00:00:00
2022-6,7,8-1,15 01:15:00 2022 年 6 月、7 月和 8 月的 1 日和 15 日凌晨 01:15:00
Mon *-05~03 每年 5 月的下一个星期一,也是该月的倒数第 3 天。
Mon..Fri *-08~04 任何年份中 8 月倒数第 4 天,并且也是工作日。
*-05~03/2 5 月的倒数第 3 天,然后在两天后再次出现。每年重复。请注意,此表达式使用波浪号 (~)。
*-05-03/2 5 月的第三天,然后在 5 月剩余的每一天每隔一天。每年重复。请注意,此表达式使用短划线 (-)。

表 2:OnCalendar 事件规范示例

测试日历规范

systemd 提供了一个出色的工具,用于验证和检查定时器中的日历时间事件规范。systemd-analyze calendar 工具解析日历时间事件规范,并提供规范化形式以及其他有趣的信息,例如下一个“经过”的日期和时间,即匹配,以及到达触发时间之前的大概时间量。

首先,查看未来没有日期的日期(请注意,Next elapseUTC 的时间将根据您的本地时区而有所不同)

[student@studentvm1 ~]$ systemd-analyze calendar 2030-06-17
  Original form: 2030-06-17                 
Normalized form: 2030-06-17 00:00:00        
    Next elapse: Mon 2030-06-17 00:00:00 EDT
       (in UTC): Mon 2030-06-17 04:00:00 UTC
       From now: 10 years 0 months left     
[root@testvm1 system]#

现在添加时间。在此示例中,日期和时间被分析为不相关的实体

[root@testvm1 system]# systemd-analyze calendar 2030-06-17 15:21:16
  Original form: 2030-06-17                 
Normalized form: 2030-06-17 00:00:00        
    Next elapse: Mon 2030-06-17 00:00:00 EDT
       (in UTC): Mon 2030-06-17 04:00:00 UTC
       From now: 10 years 0 months left     

  Original form: 15:21:16                   
Normalized form: *-*-* 15:21:16             
    Next elapse: Mon 2020-06-15 15:21:16 EDT
       (in UTC): Mon 2020-06-15 19:21:16 UTC
       From now: 3h 55min left              
[root@testvm1 system]#

要将日期和时间作为一个单元分析,请将它们一起用引号括起来。确保在使用它们在定时器单元的 OnCalendar= 事件规范中时删除引号,否则会出错

[root@testvm1 system]# systemd-analyze calendar "2030-06-17 15:21:16"
Normalized form: 2030-06-17 15:21:16        
    Next elapse: Mon 2030-06-17 15:21:16 EDT
       (in UTC): Mon 2030-06-17 19:21:16 UTC
       From now: 10 years 0 months left     
[root@testvm1 system]#

现在测试表 2 中的条目。我特别喜欢最后一个

[root@testvm1 system]# systemd-analyze calendar "2022-6,7,8-1,15 01:15:00"
  Original form: 2022-6,7,8-1,15 01:15:00
Normalized form: 2022-06,07,08-01,15 01:15:00
    Next elapse: Wed 2022-06-01 01:15:00 EDT 
       (in UTC): Wed 2022-06-01 05:15:00 UTC
       From now: 1 years 11 months left
[root@testvm1 system]#

让我们看一个例子,其中我们列出时间戳表达式的下五个经过时间。

[root@testvm1 ~]# systemd-analyze calendar --iterations=5 "Mon *-05~3"
  Original form: Mon *-05~3                 
Normalized form: Mon *-05~03 00:00:00       
    Next elapse: Mon 2023-05-29 00:00:00 EDT
       (in UTC): Mon 2023-05-29 04:00:00 UTC
       From now: 2 years 11 months left     
       Iter. #2: Mon 2028-05-29 00:00:00 EDT
       (in UTC): Mon 2028-05-29 04:00:00 UTC
       From now: 7 years 11 months left     
       Iter. #3: Mon 2034-05-29 00:00:00 EDT
       (in UTC): Mon 2034-05-29 04:00:00 UTC
       From now: 13 years 11 months left    
       Iter. #4: Mon 2045-05-29 00:00:00 EDT
       (in UTC): Mon 2045-05-29 04:00:00 UTC
       From now: 24 years 11 months left    
       Iter. #5: Mon 2051-05-29 00:00:00 EDT
       (in UTC): Mon 2051-05-29 04:00:00 UTC
       From now: 30 years 11 months left    
[root@testvm1 ~]#

这应该为您提供足够的信息来开始测试您的 OnCalendar 时间规范。systemd-analyze 工具可用于其他有趣的分析,我将在本系列的下一篇文章中开始探讨。

摘要

systemd 定时器可以用于执行与 cron 工具相同类型的任务,但在日历和单调时间规范方面提供了更大的灵活性来触发事件。

即使您为此实验创建的服务单元通常由定时器触发,您也可以使用 systemctl start myMonitor.service 命令随时触发它。多个维护任务可以在单个定时器中编写脚本;这些可以是 Bash 脚本或 Linux 实用程序程序。您可以运行由定时器触发的服务来运行所有脚本,也可以根据需要运行单个脚本。

我将在下一篇文章中更详细地探讨 systemd 对时间和时间规范的使用。

我还没有看到任何迹象表明 cronat 将被弃用。我希望这不会发生,因为至少 at 比 systemd 定时器更容易用于一次性任务调度。

资源

互联网上有大量关于 systemd 的信息,但其中许多信息简洁、晦涩甚至具有误导性。除了本文中提到的资源外,以下网页还提供了关于 systemd 启动的更详细和可靠的信息。

  • Fedora 项目有一个很好的、实用的 systemd 指南。它几乎包含了您需要了解的所有内容,以便使用 systemd 配置、管理和维护 Fedora 计算机。
  • Fedora 项目还有一个很好的 速查表,它将旧的 SystemV 命令交叉引用到可比较的 systemd 命令。
  • 有关 systemd 的详细技术信息以及创建 systemd 的原因,请查看 Freedesktop.orgsystemd 描述
  • Linux.com 的“更多 systemd 乐趣”提供了更高级的 systemd 信息和技巧

Lennart Poettering 是 systemd 的设计者和主要开发者,他也为 Linux 系统管理员编写了一系列深入的技术文章。这些文章写于 2010 年 4 月至 2011 年 9 月之间,但它们现在与当时一样相关。关于 systemd 及其生态系统的所有其他优秀著作都基于这些论文。

接下来阅读什么
David Both
David Both 是一位开源软件和 GNU/Linux 倡导者、培训师、作家和演讲者。自 1996 年以来,他一直从事 Linux 和开源软件工作,自 1969 年以来一直从事计算机工作。他是“系统管理员 Linux 哲学”的坚定支持者和传播者。

10 条评论

您提到 journald 保留策略将在未来的文章中介绍。这难道不只是简单地将 journald.conf 存储参数更改为“persistent”……为什么这需要一整篇文章?

那将是更全面方法的一部分,因为它仅确保存储在内存中的日志条目在主机断电时不会丢失。我很高兴您提到了它,因为我一定会将该选项包含在我的文章中。我现在正在努力,所以时间刚刚好。

回复 ,作者 Joe Schubert (未验证)

感谢这篇文章,Dave!在我整理我的 crontab 时,这让我有所思考。

一个小小的挑剔

> systemctl status *timer 命令列出我的主机上的所有定时器。星号符号的工作方式与文件通配符相同

这就是为什么它应该始终用引号引起来,以阻止 shell 扩展为意外的匹配项,或者(在 bash 的 nullglob 设置下)删除整个参数。

我也更喜欢 systemd 定时器。更高级,控制更多。但是,我确实怀念像 cron 那样轻松发送邮件警报的功能。

可以将日志条目发送到 STDOUT 以及日志本身。我没有尝试过,但我认为这可以重定向到电子邮件。如果您尝试过,请告诉我们结果如何。

谢谢!

回复 ,作者 K. de Jong (未验证)

有用的文章,谢谢。我以前没有遇到过 `systemd-analyze calendars` 或 `AccuracySec`。

也可以使用以下命令列出定时器

systemctl list-timers

是否可以拥有每个用户的定时器?根据我读到的另一篇文章,也许如果您将文件放在 ~/.config/systemd/user 下,它仍然可以工作,并且是每个用户的,而不是以 root 身份运行。

这可行吗?

很棒的文章,谢谢!

我想创建一个在每个月第一个工作日运行的定时器。这可能吗?

我想使用 systemd 定时器,但我需要一个进程每天从早上 6:00 到晚上 7:00 每 5 分钟运行一次。我不知道如何用定时器完成这个。

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.