文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

使用 systemd 定时器代替 cron 作业

2024-12-03 06:39

关注

定时器提供了比 cron 作业更为细粒度的事件控制。

我正在致力于将我的 cron 作业迁移到 systemd 定时器上。我已经使用定时器多年了,但通常来说,我的学识只足以支撑我当前的工作。但在我研究 systemd 系列 的过程中,我发现 systemd 定时器有一些非常有意思的能力。

与 cron 作业类似,systemd 定时器可以在特定的时间间隔触发事件(shell 脚本和程序),例如每天一次或在一个月中的特定某一天(或许只有在周一生效),或在从上午 8 点到下午 6 点的工作时间内每隔 15 分钟一次。定时器也可以做到 cron 作业无法做到的一些事情。举个例子,定时器可以在特定事件发生后的一段时间后触发一段脚本或者程序去执行,例如开机、启动、上个任务完成,甚至于定时器调用的上个服务单元的完成的时刻。

操作系统维护的计时器

当在一个新系统上安装 Fedora 或者是任意一个基于 systemd 的发行版时,作为系统维护过程的一部分,它会在 Linux 宿主机的后台中创建多个定时器。这些定时器会触发事件来执行必要的日常维护任务,比如更新系统数据库、清理临时目录、轮换日志文件,以及更多其他事件。

作为示例,我会查看一些我的主要工作站上的定时器,通过执行 systemctl status *timer 命令来展示主机上的所有定时器。星号的作用与文件通配相同,所以这个命令会列出所有的 systemd 定时器单元。

  1. [root@testvm1 ~]# systemctl status *timer
  2. mlocate-updatedb.timer - Updates mlocate database every day
  3. Loaded: loaded (/usr/lib/systemd/system/mlocate-updatedb.timer; enabled; vendor preset: enabled)
  4. Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
  5. Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
  6. Triggers: mlocate-updatedb.service
  7.  
  8. Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Updates mlocate database every day.
  9.  
  10. logrotate.timer - Daily rotation of log files
  11. Loaded: loaded (/usr/lib/systemd/system/logrotate.timer; enabled; vendor preset: enabled)
  12. Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
  13. Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
  14. Triggers: logrotate.service
  15. Docs: man:logrotate(8)
  16. man:logrotate.conf(5)
  17.  
  18. Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily rotation of log files.
  19.  
  20. sysstat-summary.timer - Generate summary of yesterday's process accounting
  21. Loaded: loaded (/usr/lib/systemd/system/sysstat-summary.timer; enabled; vendor preset: enabled)
  22. Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
  23. Trigger: Fri 2020-06-05 00:07:00 EDT; 15h left
  24. Triggers: ● sysstat-summary.service
  25.  
  26. Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Generate summary of yesterday's process accounting.
  27.  
  28. fstrim.timer - Discard unused blocks once a week
  29. Loaded: loaded (/usr/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
  30. Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
  31. Trigger: Mon 2020-06-08 00:00:00 EDT; 3 days left
  32. Triggers: fstrim.service
  33. Docs: man:fstrim
  34.  
  35. Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Discard unused blocks once a week.
  36.  
  37. sysstat-collect.timer - Run system activity accounting tool every 10 minutes
  38. Loaded: loaded (/usr/lib/systemd/system/sysstat-collect.timer; enabled; vendor preset: enabled)
  39. Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
  40. Trigger: Thu 2020-06-04 08:50:00 EDT; 41s left
  41. Triggers: sysstat-collect.service
  42.  
  43. Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Run system activity accounting tool every 10 minutes.
  44.  
  45. dnf-makecache.timer - dnf makecache --timer
  46. Loaded: loaded (/usr/lib/systemd/system/dnf-makecache.timer; enabled; vendor preset: enabled)
  47. Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
  48. Trigger: Thu 2020-06-04 08:51:00 EDT; 1min 41s left
  49. Triggers: dnf-makecache.service
  50.  
  51. Jun 02 08:02:33 testvm1.both.org systemd[1]: Started dnf makecache timer.
  52.  
  53. systemd-tmpfiles-clean.timer - Daily Cleanup of Temporary Directories
  54. Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-clean.timer; static; vendor preset: disabled)
  55. Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
  56. Trigger: Fri 2020-06-05 08:19:00 EDT; 23h left
  57. Triggers: systemd-tmpfiles-clean.service
  58. Docs: man:tmpfiles.d(5)
  59. man:systemd-tmpfiles(8)
  60.  
  61. Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily Cleanup of Temporary Directories.

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

你也许有一些不一样的定时器,取决于你的主机。

创建一个定时器

尽管我们可以解构一个或多个现有的计时器来了解其工作原理,但让我们创建我们自己的 服务单元 和一个定时器去触发它。为了保持简单,我们将使用一个相当简单的例子。当我们完成这个实验之后,就能更容易理解其他定时器的工作原理以及发现它们正在做什么。

首先,创建一个运行基础东西的简单的服务,例如 free 命令。举个例子,你可能想定时监控空余内存。在 /etc/systemd/system 目录下创建如下的 myMonitor.server 单元文件。它不需要是可执行文件:

  1. # This service unit is for testing timer units
  2. # By David Both
  3. # Licensed under GPL V2
  4. #
  5.  
  6. [Unit]
  7. Description=Logs system statistics to the systemd journal
  8. Wants=myMonitor.timer
  9.  
  10. [Service]
  11. Type=oneshot
  12. ExecStart=/usr/bin/free
  13.  
  14. [Install]
  15. WantedBy=multi-user.target

这大概是你能创建的最简单的服务单元了。现在我们查看一下服务状态同时测试一下服务单元确保它和我们预期一样可用。

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

输出在哪里呢?默认情况下,systemd 服务单元执行程序的标准输出(STDOUT)会被发送到系统日志中,它保留了记录供现在或者之后(直到某个时间点)查看。(在本系列的后续文章中,我将介绍系统日志的记录和保留策略)。专门查看你的服务单元的日志,而且只针对今天。-S 选项,即 --since 的缩写,允许你指定 journalctl 工具搜索条目的时间段。这并不代表你不关心过往结果 —— 在这个案例中,不会有过往记录 —— 如果你的机器以及运行了很长时间且堆积了大量的日志,它可以缩短搜索时间。

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

由服务触发的任务可以是单个程序、一组程序或者是一个脚本语言写的脚本。通过在 myMonitor.service 单元文件里的 [Service] 块末尾中添加如下行可以为服务添加另一个任务:

  1. ExecStart=/usr/bin/lsblk

再次启动服务,查看日志检查结果,结果应该看上去像这样。你应该在日志中看到两条命令的结果输出:

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

现在你知道了你的服务可以按预期工作了,在 /etc/systemd/system 目录下创建 myMonitor.timer 定时器单元文件,添加如下代码:

  1. # This timer unit is for testing
  2. # By David Both
  3. # Licensed under GPL V2
  4. #
  5.  
  6. [Unit]
  7. Description=Logs some system statistics to the systemd journal
  8. Requires=myMonitor.service
  9.  
  10. [Timer]
  11. Unit=myMonitor.service
  12. OnCalendar=*-*-* *:*:00
  13.  
  14. [Install]
  15. WantedBy=timers.target

在 myMonitor.timer 文件中的 OnCalendar 时间格式,*-*-* *:*:00,应该会每分钟触发一次定时器去执行 myMonitor.service 单元。我会在文章的后面进一步探索 OnCalendar 设置。

到目前为止,在服务被计时器触发运行时观察与之有关的日志记录。你也可以跟踪计时器,跟踪服务可以让你接近实时的看到结果。执行 journalctl 时带上 -f 选项:

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

执行但是不启用该定时器,看看它运行一段时间后发生了什么:

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

一条结果立即就显示出来了,下一条大概在一分钟后出来。观察几分钟日志,看看你有没有跟我发现同样的事情:

  1. [root@testvm1 system]# journalctl -S today -f -u myMonitor.service
  2. -- Logs begin at Mon 2020-06-08 07:47:20 EDT. --
  3. Jun 13 08:39:18 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
  4. Jun 13 08:39:18 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
  5. Jun 13 08:39:19 testvm1.both.org free[630566]: total used free shared buff/cache available
  6. Jun 13 08:39:19 testvm1.both.org free[630566]: Mem: 12635740 556604 10965516 8036 1113620 11785628
  7. Jun 13 08:39:19 testvm1.both.org free[630566]: Swap: 8388604 0 8388604
  8. Jun 13 08:39:18 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
  9. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
  10. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sda 8:0 0 120G 0 disk
  11. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─sda1 8:1 0 4G 0 part /boot
  12. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: └─sda2 8:2 0 116G 0 part
  13. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─VG01-root 253:0 0 5G 0 lvm /
  14. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─VG01-swap 253:1 0 8G 0 lvm [SWAP]
  15. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─VG01-usr 253:2 0 30G 0 lvm /usr
  16. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─VG01-tmp 253:3 0 10G 0 lvm /tmp
  17. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: ├─VG01-var 253:4 0 20G 0 lvm /var
  18. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: └─VG01-home 253:5 0 10G 0 lvm /home
  19. Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sr0 11:0 1 1024M 0 rom
  20. Jun 13 08:40:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
  21. Jun 13 08:40:46 testvm1.both.org free[630572]: total used free shared buff/cache available
  22. Jun 13 08:40:46 testvm1.both.org free[630572]: Mem: 12635740 555228 10966836 8036 1113676 11786996
  23. Jun 13 08:40:46 testvm1.both.org free[630572]: Swap: 8388604 0 8388604
  24. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
  25. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sda 8:0 0 120G 0 disk
  26. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─sda1 8:1 0 4G 0 part /boot
  27. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: └─sda2 8:2 0 116G 0 part
  28. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─VG01-root 253:0 0 5G 0 lvm /
  29. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─VG01-swap 253:1 0 8G 0 lvm [SWAP]
  30. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─VG01-usr 253:2 0 30G 0 lvm /usr
  31. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─VG01-tmp 253:3 0 10G 0 lvm /tmp
  32. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: ├─VG01-var 253:4 0 20G 0 lvm /var
  33. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: └─VG01-home 253:5 0 10G 0 lvm /home
  34. Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sr0 11:0 1 1024M 0 rom
  35. Jun 13 08:40:46 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
  36. Jun 13 08:40:46 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
  37. Jun 13 08:41:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
  38. Jun 13 08:41:46 testvm1.both.org free[630580]: total used free shared buff/cache available
  39. Jun 13 08:41:46 testvm1.both.org free[630580]: Mem: 12635740 553488 10968564 8036 1113688 11788744
  40. Jun 13 08:41:46 testvm1.both.org free[630580]: Swap: 8388604 0 8388604
  41. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
  42. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sda 8:0 0 120G 0 disk
  43. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─sda1 8:1 0 4G 0 part /boot
  44. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: └─sda2 8:2 0 116G 0 part
  45. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─VG01-root 253:0 0 5G 0 lvm /
  46. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─VG01-swap 253:1 0 8G 0 lvm [SWAP]
  47. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─VG01-usr 253:2 0 30G 0 lvm /usr
  48. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─VG01-tmp 253:3 0 10G 0 lvm /tmp
  49. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: ├─VG01-var 253:4 0 20G 0 lvm /var
  50. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: └─VG01-home 253:5 0 10G 0 lvm /home
  51. Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sr0 11:0 1 1024M 0 rom
  52. Jun 13 08:41:47 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
  53. 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 秒触发。

大部分情况下,这种概率抖动的定时器是没事的。当调度类似执行备份的任务,只需要它们在下班时间运行,这样是没问题的。系统管理员可以选择确定的开始时间来确保不和其他任务冲突,例如 01:05:00 这样典型的 cron 作业时间,但是有很大范围的时间值可以满足这一点。在开始时间上的一个分钟级别的随机往往是无关紧要的。

然而,对某些任务来说,精确的触发时间是个硬性要求。对于这类任务,你可以向单元文件的 Timer 块中添加如下声明来指定更高的触发时间跨度精确度(精确到微秒以内):

  1. AccuracySec=1us

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

所有 /usr/lib/systemd/system 中的定时器都指定了一个更宽松的时间精度,因为精准时间没那么重要。看看这些系统创建的定时器的时间格式:

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

看下 /usr/lib/systemd/system 目录下部分定时器单元文件的完整内容,看看它们是如何构建的。

在本实验中不必让这个定时器在启动时激活,但下面这个命令可以设置开机自启:

  1. [root@testvm1 system]# systemctl enable myMonitor.timer

你创建的单元文件不需要是可执行的。你同样不需要启用服务,因为它是被定时器触发的。如果你需要的话,你仍然可以在命令行里手动触发该服务单元。尝试一下,然后观察日志。

关于定时器精度、事件时间规格和触发事件的详细信息,请参见 systemd.timer 和 systemd.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= 类似。

 

Table 1: systemd 定时器定义

单调计时器可使用同样的简写名作为它们的时间跨度,即我们之前提到的 AccuracySec 表达式,但是 systemd 将这些名字统一转换成了秒。举个例子,比如你想规定某个定时器在系统启动后五天触发一次事件;它可能看起来像 OnBootSec=5d。如果机器启动于 2020-06-15 09:45:27,这个定时器会在 2020-06-20 09:45:27 或在这之后的一分钟内触发。

日历事件格式

日历事件格式是定时器在所需的重复时间触发的关键。我们开始看下一些 OnCalendar 设置一起使用的格式。

与 crontab 中的格式相比,systemd 及其计时器使用的时间和日历格式风格不同。它比 crontab 更为灵活,而且可以使用类似 at 命令的方式允许模糊的日期和时间。它还应该足够熟悉使其易于理解。

systemd 定时器使用 OnCalendar= 的基础格式是 DOW YYYY-MM-DD HH:MM:SS。DOW(星期几)是选填的,其他字段可以用一个星号(*)来匹配此位置的任意值。所有的日历时间格式会被转换成标准格式。如果时间没有指定,它会被设置为 00:00:00。如果日期没有指定但是时间指定了,那么下次匹配的时间可能是今天或者明天,取决于当前的时间。月份和星期可以使用名称或数字。每个单元都可以使用逗号分隔的列表。单元范围可以在开始值和结束值之间用 .. 指定。

指定日期有一些有趣的选项,波浪号(~)可以指定月份的最后一天或者最后一天之前的某几天。/ 可以用来指定星期几作为修饰符。

这里有几个在 OnCalendar 表达式中使用的典型时间格式例子。

日期事件格式 描述
DOW YYYY-MM-DD HH:MM:SS  
*-*-* 00:15:30 每年每月每天的 0 点 15 分 30 秒
Weekly 每个周一的 00:00:00
Mon *-*-* 00:00:00 同上
Mon 同上
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 每年五月份的下个周一同时也是月末的倒数第三天
Mon..Fri *-08~04 任何年份 8 月末的倒数第四天,同时也须是工作日
*-05~03/2 五月末的倒数第三天,然后 2 天后再来一次。每年重复一次。注意这个表达式使用了波浪号(~)。
*-05-03/2 五月的第三天,然后每两天重复一次直到 5 月底。注意这个表达式使用了破折号(-)。

Table 2: OnCalendar 事件时间格式例子

测试日历格式

systemd 提供了一个绝佳的工具用于检测和测试定时器中日历时间事件的格式。systemd-analyze calendar 工具解析一个时间事件格式,提供标准格式和其他有趣的信息,例如下次“经过”(即匹配)的日期和时间,以及距离下次触发之前大概时间。

首先,看看未来没有时间的日(注意 Next elapse 和 UTC 的时间会根据你当地时区改变):

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

现在添加一个时间,在这个例子中,日期和时间是当作无关的部分分开解析的:

  1. [root@testvm1 system]# systemd-analyze calendar 2030-06-17 15:21:16
  2.   Original form: 2030-06-17                
  3. Normalized form: 2030-06-17 00:00:00        
  4.     Next elapse: Mon 2030-06-17 00:00:00 EDT
  5.        (in UTC): Mon 2030-06-17 04:00:00 UTC
  6.        From now: 10 years 0 months left    
  7.  
  8.   Original form: 15:21:16                  
  9. Normalized form: *-*-* 15:21:16            
  10.     Next elapse: Mon 2020-06-15 15:21:16 EDT
  11.        (in UTC): Mon 2020-06-15 19:21:16 UTC
  12.        From now: 3h 55min left              
  13. [root@testvm1 system]#

为了把日期和时间当作一个单元来分析,可以把它们包在引号里。你在定时器单元里 OnCalendar= 时间格式中使用的时候记得把引号去掉,否则会报错:

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

现在我们测试下 Table2 里的例子。我尤其喜欢最后一个:

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

让我们看一个例子,这个例子里我们列出了时间表达式的五个经过时间。

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

这些应该为你提供了足够的信息去开始测试你的 OnCalendar 时间格式。systemd-analyze 工具可用于其他有趣的分析,我会在这个系列的下一篇文章来探索这些。

总结

systemd 定时器可以用于执行和 cron 工具相同的任务,但是通过按照日历和单调时间格式去触发事件的方法提供了更多的灵活性。

虽然你为此次实验创建的服务单元通常是由定时器调用的,你也可以随时使用 systemctl start myMonitor.service 命令去触发它。可以在一个定时器中编写多个维护任务的脚本;它们可以是 Bash 脚本或者其他 Linux 程序。你可以通过触发定时器来运行所有的脚本来运行服务,也可以按照需要执行单独的脚本。

我会在下篇文章中更加深入的探索 systemd 时间格式的用处。

我还没有看到任何迹象表明 cron 和 at 将被废弃。我希望这种情况不会发生,因为至少 at 在执行一次性调度任务的时候要比 systemd 定时器容易的多。

参考资料

网上有大量的关于 systemd 的参考资料,但是大部分都有点简略、晦涩甚至有误导性。除了本文中提到的资料,下列的网页提供了跟多可靠且详细的 systemd 入门信息。

此外,还有一系列深度的技术文章,是由 systemd 的设计者和主要实现者 Lennart Poettering 为 Linux 系统管理员撰写的。这些文章写于 2010 年 4 月至 2011 年 9 月间,但它们现在和当时一样具有现实意义。关于 systemd 及其生态的许多其他好文章都是基于这些文章:

 

 

来源:Linux中国内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯