关于Linux中作业调度 crond 和 systemd.timer 使用场景的一些笔记

我们承受所有的不幸,皆因我们无法独处 ——叔本华

写在前面


  • 分享一些 systemd.timer 相关的笔记
  • 博文内容涉及:
    • systemd.timer 的一些介绍
    • cron VS systemd.timer 区别
    • 如何创建 systemd.timer 作业调度
  • 理解不足小伙伴帮忙指正

我们承受所有的不幸,皆因我们无法独处 ——叔本华


关于 systemd.timer 的一些介绍

crond 这里不多介绍,小伙伴们应该都接触过。所以直接来看 systemd.timer

systemd 的 timer 单元。 用于封装一个基于时间触发的动作。它取代了传统的 atd, crond 等任务计划服务。参见 systemd.timer(5) 手册。, 换句话讲,就是对crond的任务进行了细粒度的处理,以前我们通过 bash 脚本处理的一些调度策略,现在可以通过 systemd.timer 来实现。

systemctl list-unit-files -t timer 命令可以查看当前系统的 timer 单元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$systemctl list-unit-files -t timer
UNIT FILE STATE
chrony-dnssrv@.timer disabled
fstrim.timer disabled
pmie_check.timer disabled
pmie_daily.timer disabled
pmlogger_check.timer disabled
pmlogger_daily-poll.timer disabled
pmlogger_daily.timer disabled
systemd-readahead-done.timer indirect
systemd-tmpfiles-clean.timer static

9 unit files listed.

systemctl status systemd-tmpfiles-clean.timer 可以查看具体的状态信息

1
2
3
4
5
6
7
8
9
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$systemctl status systemd-tmpfiles-clean.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 日 2022-10-23 01:07:13 CST; 4 days ago
Docs: man:tmpfiles.d(5)
man:systemd-tmpfiles(8)

10月 23 01:07:13 liruilongs.github.io systemd[1]: Started Daily Cleanup of Temporary Directories.

systemctl cat systemd-tmpfiles-clean.timer 命令可以查看相关配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$systemctl cat systemd-tmpfiles-clean.timer
# /usr/lib/systemd/system/systemd-tmpfiles-clean.timer
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.

[Unit]
Description=Daily Cleanup of Temporary Directories
Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)

[Timer]
OnBootSec=15min
OnUnitActiveSec=1d
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$

上面的配置中,OnBootSec=15min 表示系统启动后15分钟执行,OnUnitActiveSec=1d 表示间隔多长时间执行下一次。

那么这里执行什么,如果没有显示的定义 unit 字段,默认是和 timer 同名 service 单元。

1
2
3
4
5
6
7
8
9
10
11
12
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$systemctl status systemd-tmpfiles-clean.service
● systemd-tmpfiles-clean.service - Cleanup of Temporary Directories
Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-clean.service; static; vendor preset: disabled)
Active: inactive (dead) since 四 2022-10-27 15:36:56 CST; 5h 58min ago
Docs: man:tmpfiles.d(5)
man:systemd-tmpfiles(8)
Process: 9783 ExecStart=/usr/bin/systemd-tmpfiles --clean (code=exited, status=0/SUCCESS)
Main PID: 9783 (code=exited, status=0/SUCCESS)

10月 27 15:36:56 liruilongs.github.io systemd[1]: Starting Cleanup of Temporary Directories...
10月 27 15:36:56 liruilongs.github.io systemd[1]: Started Cleanup of Temporary Directories.

需要注意的是,如果当前Service unitactive 状态,那么定时任务是不会执行的,所以这里和 crond有一定的区别,如果是crond只能通过 bash 任务内部判断.

对应timerservice 的单元文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$systemctl cat systemd-tmpfiles-clean.service
# /usr/lib/systemd/system/systemd-tmpfiles-clean.service
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.

[Unit]
Description=Cleanup of Temporary Directories
Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-readahead-collect.service systemd-readahead-replay.service local-fs.target time-sync.targ
Before=shutdown.target

[Service]
Type=oneshot
ExecStart=/usr/bin/systemd-tmpfiles --clean
IOSchedulingClass=idle
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$

可以通过 journalctl 来看看单元对应的日志信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$journalctl -u systemd-tmpfiles-clean.service
-- Logs begin at 日 2022-10-23 01:07:09 CST, end at 四 2022-10-27 21:55:39 CST. --
10月 23 01:22:25 liruilongs.github.io systemd[1]: Starting Cleanup of Temporary Directories...
10月 23 01:22:25 liruilongs.github.io systemd[1]: Started Cleanup of Temporary Directories.
10月 24 01:22:45 liruilongs.github.io systemd[1]: Starting Cleanup of Temporary Directories...
10月 24 01:22:45 liruilongs.github.io systemd[1]: Started Cleanup of Temporary Directories.
10月 27 15:36:56 liruilongs.github.io systemd[1]: Starting Cleanup of Temporary Directories...
10月 27 15:36:56 liruilongs.github.io systemd[1]: Started Cleanup of Temporary Directories.
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$journalctl -u systemd-tmpfiles-clean.timer
-- Logs begin at 日 2022-10-23 01:07:09 CST, end at 四 2022-10-27 21:55:39 CST. --
10月 23 01:07:13 liruilongs.github.io systemd[1]: Started Daily Cleanup of Temporary Directories.
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$

systemctl list-timers --no-pager 可以列出当前已经生效的定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──[root@liruilongs.github.io]-[~]
└─$systemctl list-timers --no-pager
NEXT LEFT LAST PASSED UNIT
ACTIVATES
五 2022-10-28 00:55:00 CST 24min left 五 2022-10-28 00:25:08 CST 5min ago pmlogger_check.timer pmlogger_check.service
五 2022-10-28 01:30:00 CST 59min left 五 2022-10-28 00:30:01 CST 47s ago pmlogger_daily-poll.timer pmlogger_daily-poll.service
五 2022-10-28 15:36:56 CST 15h left 四 2022-10-27 15:36:56 CST 8h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
六 2022-10-29 00:10:00 CST 23h left 五 2022-10-28 00:10:01 CST 20min ago pmlogger_daily.timer pmlogger_daily.service

4 timers listed.
Pass --all to see loaded but inactive timers, too.
┌──[root@liruilongs.github.io]-[~]
└─$

查看单个定时器的 详细信息

1
2
3
4
5
6
┌──[root@liruilongs.github.io]-[~]
└─$systemctl list-timers systemd-tmpfiles-clean.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES
六 2022-10-29 15:37:00 CST 15h left 五 2022-10-28 15:37:00 CST 8h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.se
1 timers listed.
Pass --all to see loaded but inactive timers, too.

关于 timer 单位文件具体的参数我们可以通过帮助文档查看 帮助手册查看 man systemd.timer

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$man systemd.timer | cat
SYSTEMD.TIMER(5) systemd.timer SYSTEMD.TIMER(5)



NAME
systemd.timer - Timer unit configuration

SYNOPSIS
timer.timer

DESCRIPTION
# 以 ".timer" 为后缀的单元文件, 封装了一个由 systemd 管理的定时器, 以支持基于定时器的启动。
A unit configuration file whose name ends in ".timer" encodes information about a timer
controlled and supervised by systemd, for timer-based activation.

# 本手册列出了 所有专用于此类单元的配置选项(亦称"配置指令"或"单元属性")。
# systemd.unit(5) 中描述了通用于所有单元类型的配置选项, 它们位于 [Unit] 与 [Install] 小节。
# 此类单元专用的配置选项位于 [Timer] 小节。
This man page lists the configuration options specific to this unit type. See systemd.unit(5)
for the common options of all unit configuration files. The common configuration items are
configured in the generic [Unit] and [Install] sections. The timer specific configuration
options are configured in the [Timer] section.

# 每个定时器单元都必须有一个与其匹配的单元, 用于在特定的时间启动。
# 匹配的单元可以通过 Unit= 选项(见下文)明确指定。
# 若未指定,则默认是与该单元名称相同的 .service 单元(不算后缀)。例如 foo.timer 默认匹配 foo.service 单元。
For each timer file, a matching unit file must exist, describing the unit to activate when
the timer elapses. By default, a service by the same name as the timer (except for the
suffix) is activated. Example: a timer file foo.timer activates a matching service
foo.service. The unit to activate may be controlled by Unit= (see below).

# 注意,如果在启动时间点到来的时候,匹配的单元已经被启动, 那么将不执行任何动作,也不会启动任何新的服务实例。
# 因此,那些设置了 RemainAfterExit=yes(当该服务的所有进程全部退出之后,依然将此服务视为处于活动状态) 的服务单元一般不适合使用基于定时器的启动。
# 因为这样的单元仅会被启动一次,然后就永远处于活动(active)状态了。

# 下列依赖关系是自动隐含的:: 定时器单元自动获得对匹配单元的 Before= 依赖。
# 除非明确设置了 DefaultDependencies=no ,否则 timer 单元将会自动添加下列依赖关系:
# Requires=sysinit.target, After=sysinit.target, Before=timers.target 依赖; 以及 Conflicts=shutdown.target, Before=shutdown.target 依赖(确保在关机前干净的停止)。 只有那些在系统启动的早期就必须启动的定时器,以及那些必须在关机动作的结尾才能停止的定时器才需要设置 DefaultDependencies=no
Unless DefaultDependencies= is set to false, all timer units will implicitly have
dependencies of type Conflicts= and Before= on shutdown.target to ensure that they are
stopped cleanly prior to system shutdown. Timer units with at least one OnCalendar= directive
will have an additional After= dependency on timer-sync.target to avoid being started before
the system clock has been correctly set. Only timer units involved with early boot or late
system shutdown should disable the DefaultDependencies= option.

OPTIONS
# 定时器单元文件中必须包含一个 [Timer] 小节, 其中包含了该单元所定义的定时器的相关信息。 这里只列出仅能用于 [Timer] 小节的选项(亦称"指令"或"属性"):
Timer files must include a [Timer] section, which carries information about the timer it
defines. The options specific to the [Timer] section of timer units are the following:
# 定义相对于特定时间点之后一段时间的 单调定时器:
# OnActiveSec= 相对于该单元自身被启动的时间点;
# OnBootSec= 相对于机器被启动的时间点, 也就是内核开始运行的时间点;
# OnStartupSec= 相对于 systemd 被首次启动的时间点,也就是内核启动init进程的时间点;
# OnUnitActiveSec= 相对于匹配单元最后一次被启动的时间点;
# OnUnitInactiveSec= 相对于匹配单元 最后一次被停止的时间点;
OnActiveSec=, OnBootSec=, OnStartupSec=, OnUnitActiveSec=, OnUnitInactiveSec=
Defines monotonic timers relative to different starting points: OnActiveSec= defines a
timer relative to the moment the timer itself is activated. OnBootSec= defines a timer
relative to when the machine was booted up. OnStartupSec= defines a timer relative to
when systemd was first started. OnUnitActiveSec= defines a timer relative to when the
unit the timer is activating was last activated. OnUnitInactiveSec= defines a timer
relative to when the unit the timer is activating was last deactivated.

# 可以组合使用这些指令(既可以将某个指令使用多次,也可以将多个指令一起使用),
# 例如,通过同时使用 OnBootSec= 与 OnUnitActiveSec= 指令, 就可以实现在系统启动后的某个时间点启动匹配单元, 并且之后每隔一段时间周期性的反复启动匹配单元。
Multiple directives may be combined of the same and of different types. For example, by
combining OnBootSec= and OnUnitActiveSec=, it is possible to define a timer that elapses
in regular intervals and activates a specific service each time.

# 指令的值是一个时间长度, 可以使用下面的时间单位后缀:us(微秒), ms(毫秒), s(秒), m(分), h(时), d(天), w(周) 。
# 如果省略了时间单位,那么表示使用默认单位"秒"。 可以同时使用多个时间单位,
# 例如 "OnBootSec=5h 30min" 表示系统启动之后的5小时30分钟。 更多有关表示时间长度的语法,参见 systemd.time(7) 手册。
The arguments to the directives are time spans configured in seconds. Example:
"OnBootSec=50" means 50s after boot-up. The argument may also include time units.
Example: "OnBootSec=5h 30min" means 5 hours and 30 minutes after boot-up. For details
about the syntax of time spans, see systemd.unit(5).

# 如果定时器单元在启动时已经超过了 OnBootSec= 或 OnStartupSec= 指定的时间, 那么将会立即启动匹配的单元。
# 但是对于使用其他指令定义的定时器, 超过了就等于错过了,不会尝试去补救。
If a timer configured with OnBootSec= or OnStartupSec= is already in the past when the
timer unit is activated, it will immediately elapse and the configured unit is started.
This is not the case for timers defined in the other directives.

# 这几个指令定义的定时器都是基于单调时间的单调定时器(monotonic timer),所谓"单调时间"的意思是从开机那一刻(零点)起, 只要系统正在运行,该时间就不断的单调均匀递增(但在系统休眠时此时间保持不变),永远不会往后退,并且与时区也没有关系。 即使在系统运行的过程中,用户向前/向后修改系统时间,也不会对"单调时间"产生任何影响。
These are monotonic timers, independent of wall-clock time and timezones. If the computer
is temporarily suspended, the monotonic clock stops too.

# 如果给某个指令赋予一个空字符串, 则表示 撤销该指令之前已设置的所有定时器
If the empty string is assigned to any of these options, the list of timers is reset, and
all prior assignments will have no effect.

# 注意, 这些指令设置的定时器并不必然在所设置的精准时间点上启动匹配单元, 因为它们还受到下文 AccuracySec= 选项的影响。
Note that timers do not necessarily expire at the precise time configured with these
settings, as they are subject to the AccuracySec= setting below.

OnCalendar=
# 定义基于挂钟时间(wallclock)的日历定时器,是与传统cron任务类似的定时器。详见 systemd.time(7) 手册以了解日历事件表达式的语法
Defines realtime (i.e. wallclock) timers with calendar event expressions. See
systemd.time(7) for more information on the syntax of calendar event expressions.
Otherwise, the semantics are similar to OnActiveSec= and related settings.
# 注意,该指令设置的定时器 并不必然在所设置的精准时间点上启动匹配单元, 因为它们还受到下文 AccuracySec= 选项的影响
Note that timers do not necessarily expire at the precise time configured with this
setting, as it is subject to the AccuracySec= setting below.

AccuracySec=
# 设置定时器的触发精度。默认值是一分钟。定时器并不必然在所设置的精准时间点上启动匹配单元, 而是在所设置的精准时
# 间点为起点的一小段时间窗口范围内的某个时间点上启动匹配单元, 这个时间窗口的起点由 OnCalendar=,
# OnActiveSec=, OnBootSec=, OnStartupSec=, OnUnitActiveSec=, OnUnitInactiveSec= 决定, 而这个时间窗口的长
# 度则由该指令决定。 在这个时间窗口内,触发点的具体位置虽然无法在系统开机之前预知, 也就是说触发点的具体位置在每
# 次启动之间是随机的, 但是一旦开机之后,对于正在运行中的系统而言又是固定的, 并且对全系统范围内所有的定时器而言
# 都是固定的。 这么做的目的在于避免过多不必要的CPU唤醒,以节约电力。 若想获得最高精度,可以将此选项设为"1us"(一
# 微秒),但是耗电量就可能明显增加。 注意,定时器的精度还受到 systemd-system.conf(5) 中的 TimerSlackNSec= 选
# 项的影响(不过一般可以忽略不计,参见 prctl(2) 手册)。 为了优化耗电量, 应该将此指令设为可接收的最大值。
Specify the accuracy the timer shall elapse with. Defaults to 1min. The timer is
scheduled to elapse within a time window starting with the time specified in OnCalendar=,
OnActiveSec=, OnBootSec=, OnStartupSec=, OnUnitActiveSec= or OnUnitInactiveSec= and
ending the time configured with AccuracySec= later. Within this time window, the expiry
time will be placed at a host-specific, randomized, but stable position that is
synchronized between all local timer units. This is done in order to optimize power
consumption to suppress unnecessary CPU wake-ups. To get best accuracy, set this option
to 1us. Note that the timer is still subject to the timer slack configured via systemd-
system.conf(5)'s TimerSlackNSec= setting. See prctl(2) for details. To optimize power
consumption, make sure to set this value as high as possible and as low as necessary.'


RandomizedDelaySec=
# 将此单元的定时器随机延迟一小段时间, 这一小段时间的长度 介于零到该指令设置的时间长度之间, 以均匀概率分布。 默
# 认值是零,表示不延迟。 定时器单元在每次迭代前确定这个随机值的大小, 并简单的把它加到下一次启动匹配单元的时间点
# 上, 此选项可以将大量在同一时间点触发的定时器 均匀的分摊到一小段时间范围内触发, 以避免造成资源争抢。
# AccuracySec= 允许将一段时间内分散的定时器事件集中到一点,以减少CPU唤醒。 而本指令正好相反, 允许将集中在一点
# 的定时器事件分散到一小段时间范围内, 以避免造成资源争抢。如果同时使用 RandomizedDelaySec= 与 AccuracySec=
# 选项, 那么首先添加一个随机的时间, 然后结果可能进一步被平移, 以和系统上其他定时器事件合并。 因为默认设置
# AccuracySec=1min, RandomizedDelaySec=0 鼓励将多个定时器事件聚合在一起。 所以如果希望尽可能均匀分散定时器事
# 件,那么应该将 RandomizedDelaySec= 设为一个较大的值,同时设置 AccuracySec=1us

Delay the timer by a randomly selected, evenly distributed amount of time between 0 and
the specified time value. Defaults to 0, indicating that no randomized delay shall be
applied. Each timer unit will determine this delay randomly each time it is started, and
the delay will simply be added on top of the next determined elapsing time. This is
useful to stretch dispatching of similarly configured timer events over a certain amount
time, to avoid that they all fire at the same time, possibly resulting in resource
congestion. Note the relation to AccuracySec= above: the latter allows the service
manager to coalesce timer events within a specified time range in order to minimize
wakeups, the former does the opposite: it stretches timer events over a time range, to
make it unlikely that they fire simultaneously. If RandomizedDelaySec= and AccuracySec=
are used in conjunction, first the a randomized time is added, and the result is then
possibly shifted further to coalesce it with other timer events possibly happening on the
system. As mentioned above AccuracySec= defaults to 1min and RandomizedDelaySec= to 0,
thus encouraging coalescing of timer events. In order to optimally stretch timer events
over a certain range of time, make sure to set RandomizedDelaySec= to a higher value, and
AccuracySec=1us.

Unit=
# 该定时器单元的匹配单元, 也就是要被该定时器启动的单元。参数是一个不以 ".timer" 结尾的单元名。
# 默认值是 与此定时器单元同名的服务单元(见上文)。
# 建议将定时器单元的名字 与被该定时器启动的匹配单元的名字保持一致 (也就是仅单元后缀名不同)。
The unit to activate when this timer elapses. The argument is a unit name, whose suffix
is not ".timer". If not specified, this value defaults to a service that has the same
name as the timer unit, except for the suffix. (See above.) It is recommended that the
unit name that is activated and the unit name of the timer unit are named identically,
except for the suffix.

Persistent=
# 此选项仅对 OnCalendar= 指令定义的日历定时器有意义。 若设为"yes",则表示将匹配单元的上次触发时间永久保存在磁盘
# 上。 这样,当定时器单元再次被启动时, 如果匹配单元本应该在定时器单元停止期间至少被启动一次, 那么将立即启动匹配
# 单元。 这样就不会因为关机而错过必须执行的任务。 默认值为 no
Takes a boolean argument. If true, the time when the service unit was last triggered is
stored on disk. When the timer is activated, the service unit is triggered immediately if
it would have been triggered at least once during the time when the timer was inactive.
This is useful to catch up on missed runs of the service when the machine was off. Note
that this setting only has an effect on timers configured with OnCalendar=.

WakeSystem=
# 若设为"yes", 则表示当某个定时器到达触发时间点时, 唤醒正在休眠的系统并阻止系统进入休眠状态。
# 注意, 此选项仅确保唤醒系统, 而不关心任务执行完成之后是否需要再次休眠系统。 默认值为 no
Takes a boolean argument. If true, an elapsing timer will cause the system to resume from
suspend, should it be suspended and if the system supports this. Note that this option
will only make sure the system resumes on the appropriate times, it will not take care of
suspending it again after any work that is to be done is finished. Defaults to false.

SEE ALSO
systemd(1), systemctl(1), systemd.unit(5), systemd.service(5), systemd.time(7),
systemd.directives(7), systemd-system.conf(5), prctl(2)



systemd 219 SYSTEMD.TIMER(5)
┌──[root@liruilongs.github.io]-[/etc/tmpfiles.d]
└─$

crond 于 systemd.timer 的区别

虽然 systemd 定时器单元可以用来安排类似 cron 的工作,但两者之间有明显的优缺点。systemd.timer 更独立,但是配置略麻烦,crond配置简单,所以相对功能简单,当前用户的所有任务在一起配置

Systemd 计时器单元基本上是 systemd 服务,随之而来的是所有功能和优势,包括但不限于:

  • 作业可以有依赖项(可以依赖于其他 systemd 服务),可以设置前置后置依赖,可以运用于一些调度场景,这一点事
  • 定时器单元被记录到 systemd 日志中,可以通过 journalctl -u 来查看日志信息
  • 可以轻松地运行一个独立的定时器的作业。可以作为一个工具存在,crond,at 的所有作业是依赖于crond等服务的
  • 计时器单元可以有一个很好的值,也可以使用cgroups来管理资源。
  • Systemd 计时器单元可以由启动和硬件更改等事件触发。
  • 可以使用 systemctl 轻松管理,禁用或启用。
  • 定时器单元可以使用实时单调时间

systemd 计时器相比,Cron 有几个明显的好处。

  • 配置 cron 作业是一个简单的过程。
  • Cron 能够使用 MAILTO 功能发送电子邮件。

创建 sustemd.timer unit

来看一个小Demo,我们希望在指定时间周期重启服务,创建 systemd.timer unit 我们需要下面两个单元:

  • 作业运行的定时器单元
  • 重启任务的服务单元

以 httpd 服务为例,当前到的服务状态为 inactive

1
2
3
4
5
6
7
8
9
┌──[root@liruilongs.github.io]-[~]
└─$systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/httpd.service.d
└─50-BlockIOAccounting.conf, 50-CPUShares.conf, 50-MemoryLimit.conf
Active: inactive (dead)
Docs: man:httpd(8)
man:apachectl(8)

创建重启服务的 Service unit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──[root@liruilongs.github.io]-[~]
└─$vim /etc/systemd/system/restart-httpd.service
┌──[root@liruilongs.github.io]-[~]
└─$cat /etc/systemd/system/restart-httpd.service
[Unit]
Description= update httpd config , restart httpd service
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl restart httpd.service

[Install]
WantedBy=multi-user.target
┌──[root@liruilongs.github.io]-[~]
└─$systemd-analyze verify /etc/systemd/system/restart-httpd.service

创建定时器的 timer units

这里我么使用 OnCalendar=*:*:10 设置每分钟10秒的时候z周期执行 Service。OnwBootSec=3s 在系统开机多久执行 ,当然也可以尝试其他的字段,具体可以看下文档

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──[root@liruilongs.github.io]-[~]
└─$systemctl cat restart-httpd.timer
# /etc/systemd/system/restart-httpd.timer
[Unit]
Description="每分钟10秒 后重启 httpd"

[Timer]
OnBootSec=3s
OnCalendar=*:*:10
Unit=restart-httpd.service

[Install]
WantedBy=multi-user.target

写完之后要做文件校验

1
2
3
4
┌──[root@liruilongs.github.io]-[~]
└─$vim /etc/systemd/system/restart-httpd.timer
┌──[root@liruilongs.github.io]-[~]
└─$systemd-analyze verify /etc/systemd/system/restart-httpd.timer

简单的介绍下 onCalendar字段表达式的写法:

* *-*-* *:*:*

  • * - 表示一周中的某一天,例如:- Sat,Thu,Mon
  • *-*-* - 表示日历日期。这意味着它分解为 year-month-date
    • 2021-10-15 是 10 月 15 日
    • *-10-15 表示每年的 10 月 15 日
    • *-01-01意味着每个新年。
  • *:*:* 是表示日历事件的时间分量 - hour:minute:second

常见的表达式:

  • 每一分钟 *-*-* *:*:00
  • 每 5 分钟 *-*-* *:*/5:00
  • 每 60 分钟 *-*-* */1:00:00
  • 每 1 小时 *-*-* *:00:00
  • 每 3 小时 *-*-* */3:00:00
  • 每隔一小时 *-*-* */2:00:00
  • 每 12 小时 *-*-* */12:00:00
  • 在特定时间之间 *-*-* 9-17:00:00
  • 每天 *-*-* 00:00:00
  • 每天凌晨 1 点 *-*-* 01:00:00
  • 每晚午夜 *-*-* 00:00:00
  • 每周六 Sat *-*-* 00:00:00
  • 星期一到星期五 Mon...Fri *-*-* 00:00:00
  • 每周末 Sat,Sun *-*-* 00:00:00
  • 每 7 天 * *-*-* 00:00:00
  • 每个月 * *-*-01 00:00:00
  • 每个季度 * *-01,04,07,10-01 00:00:00
  • 每 6 个月 * *-01,07-01 00:00:00
  • 每年 * *-01-01 00:00:00

执行定时器

1
2
┌──[root@liruilongs.github.io]-[~]
└─$systemctl start restart-httpd.timer

通过 systemctl list-timers restart-httpd.timer 我们可以查看定时计划的详细信息,next 表示下一秒执行的时间

1
2
3
4
5
6
7
8
9
10
┌──[root@liruilongs.github.io]-[~]
└─$systemctl list-timers restart-httpd.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES
五 2022-10-28 22:49:10 CST 58s left 五 2022-10-28 22:48:10 CST 1s ago restart-httpd.timer restart-httpd.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.
┌──[root@liruilongs.github.io]-[~]
└─$date
2022年 10月 28日 星期五 22:48:30 CST

配置指定在未来的某个时间执行

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──[root@liruilongs.github.io]-[~]
└─$systemctl cat restart-httpd.timer
# /etc/systemd/system/restart-httpd.timer
[Unit]
Description="2022-10-28 23:20:00 后重启 httpd"

[Timer]
OnBootSec=3s
OnCalendar=2022-10-28 23:20:00
Unit=restart-httpd.service

[Install]
WantedBy=multi-user.target

查看执行日志

1
2
3
4
5
6
7
┌──[root@liruilongs.github.io]-[~]
└─$journalctl -u restart-httpd.service | tail -n 5
10月 28 23:19:15 liruilongs.github.io systemd[1]: Started update httpd config , restart httpd service.
10月 28 23:19:44 liruilongs.github.io systemd[1]: Starting update httpd config , restart httpd service...
10月 28 23:19:45 liruilongs.github.io systemd[1]: Started update httpd config , restart httpd service.
10月 28 23:20:01 liruilongs.github.io systemd[1]: Starting update httpd config , restart httpd service...
10月 28 23:20:02 liruilongs.github.io systemd[1]: Started update httpd config , restart httpd service.

在次查看状态,就没有下一次执行的时间了

1
2
3
4
5
6
7
8
9
┌──[root@liruilongs.github.io]-[~]
└─$systemctl list-timers restart-httpd.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES
n/a n/a 五 2022-10-28 23:20:01 CST 40min ago restart-httpd.timer restart-httpd.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.
┌──[root@liruilongs.github.io]-[~]
└─$

当然如果是一次性的,我们可以使用 systemc-run 命令来处理, systemc-run 命令可以创建一些临时的 systemd units ,比如常见的临时命令利用 Cgroup 限制资源,

1
2
3
4
┌──[root@liruilongs.github.io]-[~]
└─$systemd-run --on-active=30 systemctl start restart-httpd.service
Running timer as unit run-23043.timer.
Will run service as unit run-23043.service.

使用 systemc-run 可以创建一个在一段时间后执行的Service unit,这里的命令行会自动包装成 对应的 timer,service 单元

1
2
3
4
5
6
7
┌──[root@liruilongs.github.io]-[~]
└─$journalctl -u restart-httpd.service | tail -n 3
10月 28 23:20:02 liruilongs.github.io systemd[1]: Started update httpd config , restart httpd service.
10月 29 00:21:37 liruilongs.github.io systemd[1]: Starting update httpd config , restart httpd service...
10月 29 00:21:38 liruilongs.github.io systemd[1]: Started update httpd config , restart httpd service.
┌──[root@liruilongs.github.io]-[~]
└─$

博文引用资源

http://www.jinbuguo.com/systemd/systemd.html#

https://www.putorius.net/using-systemd-timers.html#

https://blog.csdn.net/weixin_42347739/article/details/112406369

https://silentlad.com/systemd-timers-oncalendar-(cron)-format-explained

发布于

2022-10-27

更新于

2023-06-21

许可协议

评论
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×