Сейчас я занимаюсь заменой моих cron-заданий на таймеры systemd. Я пользовался таймерами несколько лет, но обычно в тонкости их применения особо не углублялся, разбираясь лишь с тем, что нужно было для выполнения интересующей меня задачи. Недавно я работал над серией материалов про systemd и узнал о том, что systemd-таймеры обладают некоторыми очень интересными возможностями.
Эти таймеры, как и задания cron, могут, в заданное время, вызывать выполнение различных действий в системе. Например — запуск скриптов командной оболочки или программ. Таймеры могут срабатывать, например, раз в день, причём — только по понедельникам. Ещё один пример — срабатывание таймера каждые 15 минут в рабочее время (с 8 утра до 6 вечера). Но таймеры systemd могут кое-что такое, что недоступно заданиям cron. Например, таймер может вызвать скрипт или программу через заданное время после некоего события. Таким событием может быть загрузка системы или запуск systemd, завершение предыдущей задачи или даже завершение работы сервиса, вызванного ранее по таймеру.
Таймеры, используемые для обслуживания системы
Когда Fedora или другой дистрибутив Linux, основанный на systemd, устанавливается на компьютер, создаётся несколько таймеров, являющихся частью процедур обслуживания системы. Эти процедуры автоматически выполняются в любых Linux-системах. Соответствующие таймеры вызывают различные служебные задачи, вроде обновления системных баз данных, очистки временных директорий, ротации лог-файлов и так далее.
В качестве примера приведу здесь сведения о таймерах, которые имеются на виртуальной машине, использованной мной для экспериментов. Здесь, для получения списка всех таймеров, я использовал команду systemctl status *timer
. Подстановочный символ в виде звёздочки играет здесь ту же роль, которую он играет в других подобных командах. А именно, он сообщает системе о том, что нас интересуют все таймеры (timer units, их ещё называют «unit-файлы таймера» или «юниты таймера») 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.
С каждым таймером связано, по меньшей мере, шесть строк, содержащих сведения о нём:
- Первая строка содержит имя файла таймера и короткое описание цели существования этого таймера.
- Вторая строка выводит сведения о состоянии таймера. А именно, сообщает о том, загружен ли он, даёт полный путь к файлу таймера, показывает состояние vendor preset (disabled или enabled).
- В третьей строке показаны сведения об активности таймера, куда входят данные о том, когда именно таймер был активирован.
- В четвёртой строке содержатся дата и время следующего запуска таймера и примерное время, оставшееся до его запуска.
- Пятая строка сообщает имя сервиса или события, вызываемого таймером.
- Некоторые (но не все) файлы юнитов таймеров systemd содержат указатели на документацию. Такие указатели есть, в моём примере, в описаниях трёх таймеров.
- Последняя строка в описании таймера представляет собой запись журнала, которая связана с самым свежим экземпляром сервиса, вызванного таймером.
Если вы попробуете выполнить на своём компьютере команду systemctl status *timer
, то представленный ей набор таймеров вполне может отличаться от моего.
Создание таймера
Хотя мы могли бы разобраться с особенностями работы таймеров, проанализировав какие-нибудь существующие таймеры, я предлагаю создать собственный unit-файл сервиса (файл конфигурации сервиса, service unit) и файл таймера, с помощью которого будет вызываться соответствующий сервис. Мы тут, чтобы не усложнять повествование, приведём довольно тривиальный пример. Но после того как мы с ним разберёмся, вам будет легче разобраться в работе других таймеров.
Для начала создадим простой файл конфигурации сервиса, который будет запускать что-то простое, вроде команды free
. Например, это может понадобиться в том случае, если нам надо регулярно мониторить объём свободной памяти. Создадим unit-файл с именем myMonitor.service
в папке /etc/systemd/system
. Он не обязательно должен быть исполняемым.
# 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]#
А почему ничего не выводится в консоль? Дело в том, что по умолчанию стандартный вывод (stdout
) от программ, запускаемых systemd с помощью unit-файлов сервисов, перенаправляется в журнал systemd. Благодаря этому, по крайней мере, до тех пор, пока соответствующие записи существуют, эти записи можно проанализировать. Заглянем в журнал и поищем записи, относящиеся к нашему сервису и к дню, когда мы проводили испытания. Соответствующая команда будет выглядеть так: journalctl -S today -u myMonitor.service
. Ключ -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]#
Задача, которая запускается с помощью файла конфигурации сервиса, может быть представлена отдельной программой, последовательностью программ, или скриптом, написанном на любом скриптовом языке. Добавим в unit-файл 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
, unit-файл таймера, дав ему имя 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
Указатель времени OnCalendar
в этом файле, *-*-* *:*:00
, должен приводить к тому, что таймер выполняет вызов юнита myMonitor.service
каждую минуту. Подробнее об OnCalendar
мы поговорим ниже.
А пока мы можем взглянуть на записи журнала, имеющие отношение к запуску сервиса, вызываемого таймером. Мы, кроме того, можем включить режим наблюдения за таймером. Однако наблюдение за сервисом позволит видеть результаты практически в режиме реального времени. Для этого нужно запустить journalctl
с ключом -f
(follow
):
[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.timer
[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.
Обязательно проверьте и состояние таймера, и состояние сервиса.
Удалось ли вам заметить тут то, что заметил я? Возможно, вы, читая журнал, обратите внимание как минимум на две вещи.
Во-первых — на то, что вам не нужно делать что-то особенное для логирования того, что ExecStart
из myMonitor.service
пишет в stdout
. Эта возможность входит в стандартные функции systemd по запуску сервисов. Но это означает, что вам, вероятно, потребуется проявлять осторожность при запуске скриптов из файлов конфигурации сервисов, обращая внимание на то, каков объём данных, который они пишут в stdout
.
Во-вторых, вы могли обратить внимание на то, что таймер запускается не точно в :00
секунд каждой минуты, и даже не в точности через одну минуту после предыдущего запуска. Это — одна из особенностей подобных таймеров, если это нужно (или если это задевает чувства системного администратора), такое поведение таймеров можно изменить, сделав их точнее.
Причина того, что таймер не запускается в :00
секунд каждой минуты, заключается в том, что система таким образом стремится предотвратить одновременный запуск нескольких сервисов. Например, при настройке указателя времени OnCalendar
можно пользоваться такими значениями, как 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
, вы можете просмотреть их содержимое.
Вам необязательно настраивать наш учебный таймер так, чтобы он активировался бы при загрузке системы. Правда, если хотите — можете воспользоваться для этого такой командой:
[root@testvm1 system]# systemctl enable myMonitor.timer
Файлы таймеров, которые вы будете создавать, не обязательно должны быть исполняемыми. Кроме того, файлы конфигурации сервисов не нужно настраивать так, чтобы они активировались бы при загрузке, так как их вызывают таймеры. Если надо, сервис можно вызвать и вручную, из командной строки. Попробуйте сделать это и загляните в журнал systemd.
Для того чтобы узнать подробности о точности таймеров, о способах указания времени вызова событий, о вызове событий, посмотрите документацию по systemd.timer
и systemd.time
.
Типы таймеров
Таймеры systemd обладают другими возможностями, которых нет у заданий cron, «одноразовых» или повторяющихся, которые вызываются только с привязкой к реальному времени и к реальным датам. Таймеры systemd можно настроить так, чтобы они вызывались бы на основании изменения состояния других юнитов systemd. Например, таймер можно настроить так, чтобы он срабатывал бы через заданное время после загрузки системы, после входа в неё пользователя, или через заданное время после активации определённого сервиса. Такие таймеры называют монотонными (monotonic). Эти таймеры сбрасываются после каждой перезагрузки системы.
В следующей таблице приведён список монотонных таймеров с краткой характеристикой каждого из них. Там же есть и описание таймера OnCalendar
, который монотонным не является и применяется в тех случаях, когда нужно организовать единовременный или повторяющийся запуск чего-либо в будущем. Эта таблица основана на документации systemd.timer
.
Таймер | Монотонный | Описание |
|
X | Время срабатывания таймера задаётся относительно момента активации таймера. |
|
X | Время срабатывания таймера задаётся относительно момента загрузки системы. |
|
X | Время срабатывания таймера определяется относительно первого запуска менеджера служб. В случае с системными таймерами это очень похоже на OnBootSec= , так как системный менеджер служб обычно запускается при загрузке очень рано. Подобные таймеры, преимущественно, ценны при использовании их с привязкой к менеджерам служб, относящимся к отдельным пользователям, так как такие менеджеры служб обычно запускаются только при первом входе пользователя в систему, а не в процессе загрузки системы. |
|
X | Время срабатывания таймера задаётся относительно того времени, когда таймер, который должен быть активирован, был активирован в последний раз. |
|
X | Время срабатывания таймера определяется относительно того времени, когда таймер, который должен быть активирован, был в последний раз деактивирован. |
|
Такой таймер привязан к реальному времени и ориентируется на события календаря. Подробности о синтаксисе описаний событий календаря можно найти в справке по systemd.time(7) . В остальном же семантика описаний таких таймеров похожа на таймеры OnActiveSec= . Это — именно такие таймеры systemd, которые сильнее всего похожи на те механизмы, которые применяются при настройке заданий cron. |
При настройке монотонных таймеров могут использоваться те же ключевые слова, что были описаны выше при разговоре об AccuracySec
. Но надо отметить, что systemd приводит соответствующие временные промежутки к секундам. Например, вам нужно задать таймер, который срабатывает один раз, через пять дней после загрузки системы. Выглядеть его описание может так: OnBootSec=5d
. Если компьютер был загружен 2020-06-15
в 09:45:27
, то таймер сработает 2020-06-20
в 09:45:27
(или в пределах 1 минуты после этого момента времени).
Описание событий календаря
Применение событий календаря — это ключевая часть описания таймеров, вызываемых через определённые промежутки времени. Разберём некоторые особенности таких событий, используемых при настройке указателя времени OnCalendar
.
В systemd и в соответствующих таймерах используется формат описания времени и дат, отличающийся от того, который принят в crontab. Этот формат является более гибким, чем тот, что используется в crontab. Он позволяет указывать дату и время в упрощённом виде, в стиле команды at
. Тому, кто знаком с at
, будет несложно разобраться с настройками таймеров systemd.
При использовании OnCalendar=
для настройки таймеров используется следующий базовый формат для указания даты и времени:
DOW YYYY-MM-DD HH:MM:SS
DOW
(Day Of Week, день недели), это необязательная часть вышеприведённой конструкции. В других полях можно использовать символ звёздочки (*
), символизирующий любое значение, которое может находиться в занимаемой им позиции. Все формы указания даты и времени приводятся к нормализованной форме. Если время не указано, предполагается, что это — 00:00:00
. Если дата не указана, а время указано, то таймер сработает либо в день его запуска (условно говоря, «сегодня»), либо на следующий день («завтра»). Это зависит от текущего времени. Для указания месяцев и дней недели могут использоваться их названия. Здесь можно использовать списки значений, разделяемые запятой. Диапазоны значений можно разделять тремя точками (…
), которые ставят между начальным и конечным значением диапазона.
При указании дат в нашем распоряжении есть пара интересных возможностей. Так, тильда (~) может использоваться для указания последнего дня месяца, или для указания даты, на заданное количество дней предшествующей последнему дню месяца. Косая черта (/) может применяться, в виде модификатора, для указания дня недели.
В следующей таблице показано несколько типичных примеров описания времени, используемых в выражении OnCalendar
.
Пример представления события календаря DOW YYYY-MM-DD HH:MM:SS |
Описание |
|
Каждый день каждого месяца каждого года, через 15 минут 30 секунд после полуночи. |
|
Каждый понедельник в 00:00:00 . |
|
То же самое, что и Weekly . |
|
То же самое, что и Weekly . |
|
Каждую среду 2020 года в 00:00:00 . |
|
Каждый будний день в 2021 году в 00:00:00 . |
|
1 и 15 июня, июля и августа 2022 года в 01:15:00 после полуночи. |
|
В следующий раз, в любой год, когда в мае понедельник будет днём, на 3 дня предшествующим концу месяца. |
|
В любом году, в 4 будний день, предшествующий концу августа. |
|
В 3 день, предшествующий концу мая, и затем — снова, через два дня. Повторяется ежегодно. Обратите внимание на то, что тут использован знак ~ . |
|
В третий день мая, а затем — каждый второй день этого месяца. Повторяется ежегодно. Обратите внимание на то, что тут использовано тире (- ). |
Тестирование описаний времени, используемых в событиях календаря
В systemd имеется отличный инструмент для проверки и исследования спецификаций событий календаря. Речь идёт о команде systemd-analyze calendar
, которая разбирает описания событий календаря и представляет их в нормализованной форме. Эта команда даёт и другую интересную информацию, такую, как дата и время наступления следующего такого события, и приблизительное время, оставшееся до достижения этого момента.
Для начала взглянем на спецификацию, которая содержит только дату, не содержит сведений о времени (обратите внимание на то, что время в полях Next elapse
и (in UTC)
различаются, это различие зависит от местного часового пояса):
[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]#
Теперь проверим что-нибудь из предыдущей таблицы. Мне особенно нравится такое описание из неё:
2022-6,7,8-1,15 01:15:00
Проанализируем его:
[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]#
Теперь давайте взглянем на описание Mon *-05~3
, но в этот раз запросим у программы сведения о следующих 5 срабатываниях таймера, в котором использованы такие настройки:
[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 ~]#
Полагаю, мы рассмотрели достаточно примеров использования systemd-analyze calendar
, что позволит вам приступить к тестированию собственных описаний событий календаря. Учитывайте то, что средство systemd-analyze
обладает и другими интересными возможностями.
Дополнительные материалы
В интернете есть много публикаций по systemd, но они, в основном, слишком краткие, очень упрощённые, или даже содержат ошибки. В этой статье приведены некоторые хорошие источники информации по systemd. Ниже дан список ссылок ещё на некоторые качественные материалы по этой теме.
- Практическое руководство по systemd, подготовленное Fedora Project.
- Шпаргалка от Fedora Project, в которой сопоставлены команды старой системы SystemV и systemd.
- Подробности о systemd и о причинах создания этой системы.
- Материал со сведениями и советами, посвящённый systemd.
- Материалы Леннарта Поттеринга (Lennart Poettering), архитектора и основного разработчика systemd. Эти материалы предназначены для системных администраторов, они написаны между апрелем 2010 года и сентябрём 2011, но они всё ещё не потеряли актуальности. Практически все другие достойные публикации о systemd основаны на этих статьях.
- Материал об управлении датой и временем системы с помощью systemd.
- Статья об управлении запуском компьютера с использованием systemd.
Итоги
Таймеры systemd можно использовать для решения тех же задач, которые решают с помощью заданий cron. Но systemd даёт больше гибкости в плане настройки календарных и монотонных таймеров.
Даже хотя файлы конфигурации сервисов, созданные нами в ходе экспериментов, обычно вызываются с помощью таймеров, для их вызова можно, в любое время, воспользоваться командой вида systemctl start myMonitor.service
. Одним таймером может запускаться несколько задач. Это могут быть, например, и Bash-скрипты, и утилиты Linux. Файл конфигурации сервиса можно составить так, чтобы при его вызове выполнялись бы несколько скриптов. Можно сделать и так, чтобы скрипты запускались бы по отдельности.
Если говорить о сосуществовании systemd, cron и at, то хочу отметить, что я пока не видел каких-либо признаков того, чтобы cron или at были бы признаны устаревшими. Я надеюсь, что их продолжат поддерживать, так как at, по крайней мере, гораздо легче, чем systemd, использовать для планирования единовременных задач.
Чем пользуетесь вы? Таймерами systemd или заданиями cron?
Автор: ru_vds