# systemctl 针对 timer 的配置文件

某些时候需要定期执行某项工作,或则是开机后执行,或则是某服务启动多久后执行等等的需求。在以前可以使用 crond 来定期处理,如今有 systemd 这个长期驻留在内存中好用的服务,另外还提供了一个协力服务 timers.target ,它可以协助定期处理各种任务

# systemd.timer 的优势

在 archlinux 的官网 wiki 上有提到,为啥要用 systemd.timer ?

  • 由于所有的 systemd 的服务产生的信息都会被记录(log),因此比 crond 在 debug 上要更清楚方便
  • 各项 timer 的工作可以跟 systemd 的服务相结合
  • 各项 timer 的工作可以跟 control group(cgroup 用来取代 /etc/secure/limit.conf 的功能)结合,来限制该工作的资源利用
  • 时间安排可以精确到毫秒的单位

弱点就是:没有 email 通知功能(除非自己实现 email 通知),也没有类似 anacron 一段时间内的随机取样功能(random_delay)

# 任务需求

想要用 systemd 的 timer 功能,必须有如下条件:

  • 系统的 timer.target 必须启动
  • 要有 sname.service 的服务存在:sname 是我们自定义的名称
  • 要有 sname.timer 的时间启动服务存在

使用前一小节写好的 backup.service 来测试

# sname.timer 的设置

[Timer] 部分

  • OnActiveSec:当 timers.target 启动多久后才执行该 unit

  • OnBootSec:当开机后多久之后才执行

  • OnStartupSec:当 systemd 第一次启动后多久才执行

  • OnUnitActiveSec:这个 timer 配置文件所管理的那个 unit 服务在最后一次启动后,相隔多久后再执行一次

  • OnUnitInactiveSec:这个 timer 配置文件所管理的那个 unit 服务在最后一次停止后,隔多久再执行一次

  • OnCalendar:使用实际时间(非循环时间)的方式来启动服务。时间格式后续讲解

  • Unit:

    一般来说不太需要设置,当你的 sname.service 与 sname.timer 中的 sname 不一致时,这里指向的 service unit

  • Persistent

    当使用 OnCalendar 的设置时,指定该功能要不要持续进行。通常设置为 yes,比较能满足类似 anacron 的功能

# 使用 OnCalendar 的时间

想要从 crontab 转成 timer 的功能,对于时间格式需要了解,基本上的格式如下

语法:英文周名 YYYY-MM-DD HH:MM:SS
范例:Thu	  2020-03-20 10:00:00
1
2

也可以使用时间间隔时间来处理,常用的时间间隔单位有:

  • us 或 usec:微秒
  • ms 或 msec:毫秒
  • s、sec、second、seconds
  • m、min、minute、minutes
  • h、hr、hour、hours
  • d、day、days
  • w、week、weeks
  • month、months
  • y、year、years

常见的范例有

3 小时:				3h 或 3hr 或 3hours
隔 300 分钟过 10 秒:	   10s 300m	
隔 5 天又 100 分钟:	   100m 5day
# 通常英文的写法:小单位写在前面,大单位写后面、先秒、分、小时、天等
1
2
3
4

此外,还可以使用英文常用的口语化日期代表,例如 today、tomorrow 等,假设今天是 2015-08-13 13:50:00 那么

  • now:Thu 2015-08-13 13:50:00
  • today:Thu 2015-08-13 00:00:00
  • tomorrow:Thu 2015-08-14 00:00:00
  • hourly:*-*-* *:00:00
  • daily:*-*-* 00:00:00
  • weekly:Mon *-*-* 00:00:00
  • monthly:*-*-01 00:00:00
  • +3h10m:Thu 2015-08-13 17:00:00
  • 2015-08-16:Sun 2015-08-16 00:00:00

# 一个循环时间运行的案例

需求如下:

  • 开机后 2 小时开始执行一次 backup.service
  • 自从第一次执行后,未来每两天执行一次 backup.service
[root@study ~]# vim /etc/systemd/system/backup.timer
[Unit]
Description=backup my server timer

[Timer]
OnBootSec=2hrs
OnUnitActiveSec=2days

[Install]
WantedBy=multi-user.target

[root@study ~]# systemctl daemon-reload 
[root@study ~]# systemctl enable backup.timer
Created symlink from /etc/systemd/system/multi-user.target.wants/backup.timer to /etc/systemd/system/backup.timer.
[root@study ~]# systemctl restart backup.timer    
[root@study ~]# systemctl list-unit-files | grep backup
backup.service                                disabled		# 只需要 timer 启动就 ok
backup.timer                                  enabled

[root@study ~]# systemctl show timers.target 
ConditionTimestamp=Tue 2020-03-17 10:49:38 CST		# timer 这个 unit 启动的时间

[root@study ~]# systemctl show backup.service | grep ExecMainStartTimestamp
ExecMainStartTimestamp=Fri 2020-03-20 09:38:19 CST	# backup.service 上一次执行的时间

[root@study ~]# systemctl show backup.timer | grep NextElapseUSecMonotonic 
NextElapseUSecMonotonic=4d 22h 48min 56.756775s		# 下一次执行距离 timers.target 的时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

关于这个 NextElapseUSecMonotonic 值得说明,上次执行 backup.service 的时间在 2020-03-20 09:38:19,由于 2 天执行一次,下一次执行时间应该是 2020-03-22 09:38:19 才对,但是由于 timer 是由 timers.target 这个 unit 所管理的,而这个 timers.target 启动时间是在 2020-03-17 10:49:38,注意的是,这个 NextElapseUSecMonotonic 记录的下次运行的时间,其实是与 timers.target 所记录的时间差,因此是 2020-03-22 09:38:19 - 2020-03-17 10:49:38` 结果就是相差 4d 22h 48min;

计算公式:NextElapseUSecMonotonic = 实际下一次运行的时间 - timers.target 的启动时间

# 一个固定日期运行的案例

[root@study ~]# vim /etc/systemd/system/backup2.timer
[Unit]
Description=backup my server timer2

[Timer]
OnCalendar=Sun *-*-* 02:00:00
Persistent=true
Unit=backup.service		# 这里的 timer 名称与原来的 service 不一致,这里指定下

[Install]
WantedBy=multi-user.target

[root@study ~]# systemctl daemon-reload
[root@study ~]# systemctl enable backup2.timer
Created symlink from /etc/systemd/system/multi-user.target.wants/backup2.timer to /etc/systemd/system/backup2.timer.
[root@study ~]# systemctl restart backup2.timer 
[root@study ~]# systemctl list-unit-files | grep backup
backup.service                                disabled
backup.timer                                  enabled 
backup2.timer                                 enabled 
[root@study ~]# systemctl show timers.target | grep ConditionTimestamp
ConditionTimestamp=Tue 2020-03-17 10:49:38 CST
ConditionTimestampMonotonic=15862087
[root@study ~]#  systemctl show backup.service | grep ExecMainStartTimestamp
ExecMainStartTimestamp=Fri 2020-03-20 09:38:19 CST
ExecMainStartTimestampMonotonic=254936756737
[root@study ~]# systemctl show backup2.timer | grep NextElapseUSecRealtime
NextElapseUSecRealtime=50y 2month 2w 5d 9h
# 由于只有一次运行,所以没有 NextElapseUSecMonotonic 值了。
# 这里的时间是 Unix 标准时间,也就是是 1970-01-01 00:00:00 去比较的
# 这里是 50 年 2个月 2周 5天 9小时才会执行,这个是对比的日历时间(1970-01-01 00:00:00)
# 时间的基准值不一样。一定要注意
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32