[Настройка Linux, Системное администрирование, *nix] Systemd для продолжающих. Part 1 — Запуск юнитов по временным событиям

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
02-Янв-2021 22:31


Всем привет! В последнее время я вплотную занимаюсь исследованием возможностей systemd и решил поделиться результатом исследований с сообществом, в виде небольшого (или большого, как пойдёт ;-) цикла статей. Итак первым (уже нет) номером нашей программы будет запуск юнитов по различным событиям происходящим во время работы ОС. В качестве исследовательской платформы будет выступать Manjaro Linux c systemd v247.2. И... да. Некоторые события, вынудили меня написать внеочередную статью, которая «взлетела на вершину хит-парада», а опрос показал, что тема актуальна и вызывает интерес, так что погнали!ПрологSystemd — система инициализации большинства современных систем на основе ядра Linux, обладает просто безграничными возможностями и не ограничивается обычным запуском демонов. Достаточно посмотреть на объёмы штатной документации, описывающей её возможности:
pacman -Ql $(pacman -Qsq systemd|xargs)|egrep '^systemd\s|^systemd-sysvcompat\s'|egrep "man/man[1|5|8]/[[:print:]]*\.gz"|wc -l
278
И это только маны описывающие конфиги, пользовательские и администраторские утилиты systemd. Если же не ограничивать поиск практической частью, то цифра будет ещё более «устрашающей»:
pacman -Ql $(pacman -Qsq systemd|xargs)|egrep '^systemd\s|^systemd-sysvcompat\s'|wc -l
1852
Большинство администраторов и разработчиков просто не представлют какие возможности таятся в недрах той системы, в окружении которых приходится работать их сервисам. Ну что-ж, пришлось время узнать, насколько глубока кроличья нора!Disclamer: Хоть в официальной документации и манах почти не используется такое понятие как триггеры (хотя и используется «triggered by»), но все те штуки которые описаны в этой и следующей статье, по сути, именно ими и являются. Это сущности которые срабатывают по каким-либо событиям, поэтому не удивляйтесь, если я, авторским произволом, буду использовать этот термин.Часть первая, очевидная. Таймеры.Все мы знаем старый, добрый cron, во всех его проявлениях. Созданный ещё в 80-х, он, в том или ином виде, дожил до нашего времени облачных сервисов. Так-же мы все знаем его ограничения. Например одной строчкой невозможно заставить крон запускать произвольный бинарник/скрипт раз в полтора часа, начная с часа ночи, приходится описывать такое событие двумя строчками. Что-бы обойти ограничения классического крона, в systemd были придуманы такие триггеры как таймеры (юниты с окончанием *.timer) умеющие запускать произвольные сервисы или группы сервисов (*.target) периодически; по наступлении какого-либо времени; по выходу системы из спящего режима; по календарному событию (наподобие того как это делает другой ветеран Unix утилит, команда at), а так-же по другим событиям, не связанными, напрямую, со временем.Для начала… что запускаем. Возьмём, для примера, таймер man-db.timer из комплекта поставки одноимённого пакета:
$ cat /usr/lib/systemd/system/man-db.timer
[Unit]
Description=Daily man-db regeneration
Documentation=man:mandb(8)
[Timer]
OnCalendar=daily
AccuracySec=12h
Persistent=true
[Install]
WantedBy=timers.target
Простой, коротенький таймер. Но в чём-же дело, почему не указано что мы запускаем? Всё нормально! По умолчанию, если в секции [Timer] отсутствует параметр Unit=, с указанием запускаемого юнита, systemd будет искать одноимённый *.service юнит. Проверяем!
$ cat /usr/lib/systemd/system/man-db.service
[Unit]
Description=Daily man-db regeneration
Documentation=man:mandb(8)
ConditionACPower=true
[Service]
Type=oneshot
# Recover from deletion, per FHS.
ExecStart=+/usr/bin/install -d -o root -g root -m 0755 /var/cache/man
# Expunge old catman pages which have not been read in a week.
ExecStart=/usr/bin/find /var/cache/man -type f -name *.gz -atime +6 -delete
# Regenerate man database.
ExecStart=/usr/bin/mandb --quiet
User=root
Nice=19
IOSchedulingClass=idle
IOSchedulingPriority=7
Да, вот он сервис который ежедневно пересоздаёт базу данных страниц руководства. Сервис стартует начиная с 00:00 (OnCalendar=daily) , с точностью 12 часов (AccuracySec=12h), то-есть он может сработать в любой момент между полуночью и полднем, в зависимости от загрузки системы:
$ systemctl status man-db.timer
● man-db.timer - Daily man-db regeneration
     Loaded: loaded (/usr/lib/systemd/system/man-db.timer; disabled; vendor preset: disabled)
     Active: active (waiting) since Thu 2020-12-31 23:18:59 MSK; 1 day 19h ago
    Trigger: Sun 2021-01-03 00:00:00 MSK; 5h 30min left
   Triggers: ● man-db.service
       Docs: man:mandb(8)
дек 31 23:18:59 dell-lnx systemd[1]: Started Daily man-db regeneration.
Минимальная точность у параметра AccuracySec= — 1us! Чем больше этот параметр, тем меньше нагрузка на систему. Если параметр отсутствует, то по умолчанию (указано в /etc/systemd/system.conf: DefaultTimerAccuracySec=) он равен одной минуте. Ладно, это всё лирика, давайте быстренько пробежимся по другим возможным параметрам секции [Timer], а на сладкое оставим параметры задания времени в OnCalendar= и других «временнЫх» параметрах. Монотонные таймеры, для периодических событий
  • OnBootSec= Таймер сработает через указанное время после старта системы.
  • OnStartupSec= Для системных таймеров действие аналогично предыдущему, для пользовательских таймеров, это время после первого логина пользователя в систему.
  • OnActiveSec= Один из параметров для периодического запуска. Через какое время, после реального времени срабатывания таймера, запускать юнит.
  • OnUnitActiveSec= Триггер будет ориентироваться на время последнего запуска целевого юнита.
  • OnUnitInactiveSec= Триггер будет ориентироваться на последнее время завершения работы целевого юнита. Хорошо для долгоиграющих сервисов. Бэкапы и вот это вот всё. Все эти таймеры можно комбинировать между собой и с таймером OnCalendar=.
Прочие параметры
  • RandomizedDelaySec= Этакий рандомный джиттер. Перед срабатыванием добавляется случайный таймаут от нуля, до заданного значения. По умолчанию -- отключено.
  • OnClockChange=, OnTimezoneChange= Булевые параметры, определяющие будет-ли таймер реагировать на перевод системных часов или смену временной зоны. По умолчанию, оба параметра, false.
  • Persistent= Записывать-ли на диск состояние таймера сразу после запуска юнита. Актуально для параметра OnCalendar=. По умолчанию — false.
  • WakeSystem= Ещё один логический параметр. Действует на монотонные таймеры. По умолчанию отключён. Логика следующая. При отключённом параметре все монотонные таймеры запоминают своё состояние, перед уходом системы в спящий режим и встают на паузу. После выхода системы из спящего режима, отсчёт продолжается с того момента с которого система «ушла в спячку». Если-же параметр поставить в true, то таймеры продолжают работать и в спящем режиме (должно поддерживаться и железом) и по наступлении события выводят систему из спячки и запускают юнит.
  • RemainAfterElapse= Последняя крутилка, по умолчанию true Смысл этого параметра примерно следующий, После срабатывания таймера он остаётся загруженным, но если поставить false, то после срабатывания таймер выгружается и перестаёт отслеживать время. Хорошо для одноразовых юнитов (Transient Units) о которых мы поговорим в одной из следующих статей. Или для таймеров которые должны сработать один раз, как это делают задания старой, доброй at.
Таймстампы, диапазоны, тестирование, примерыНу и наконец зачем это всё. Чем хороши таймеры, так это тем, что всяческие сложные временные метки задаются гораздо проще чем в cron и задача запуска сервиса раз в полтора часа, начиная с часа ночи, выглядит куда приятнее. В самом простом случае она будет выглядеть так:
[Unit]
Description=Test timer
[Timer]
OnCalendar=01:00
OnActiveSec=1.5h
Ну это слишком просто. Например мы хотим что-б наш юнит запускался каждую пятницу 13-е… OnCalendar=Fri *-*-13 12:00:00 Полный формат календарной формы выглядит так: Mon 2025-12-01 00:00:00.000000 Europe/Moscow Поэтому мы можем запускать таймер по времени другого часового пояса (по умолчанию текущий) Например хотим что-б таймер прислал нам уведомление, что Камчатка уже отпраздновала Новый год: OnCalendar=yearly Asia/Kamchatka Нормализованная форма будет выглядеть так(эти строчки указывают на одно и то-же время):
OnCalendar=*-01-01 00:00:00 Asia/Kamchatka Алиасы (и их эквиваленты в нормализованной форме) могут быть такими:
minutely → *-*-* *:*:00
                         hourly → *-*-* *:00:00
                          daily → *-*-* 00:00:00
                        monthly → *-*-01 00:00:00
                         weekly → Mon *-*-* 00:00:00
                         yearly → *-01-01 00:00:00
                      quarterly → *-01,04,07,10-01 00:00:00
                   semiannually → *-01,07-01 00:00:00
Примеры валидных таймстампов:таймстамп с @ — epoch time
Fri 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13
            2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13
        2012-11-23 11:12:13 UTC → Fri 2012-11-23 19:12:13
                     2012-11-23 → Fri 2012-11-23 00:00:00
                       12-11-23 → Fri 2012-11-23 00:00:00
                       11:12:13 → Fri 2012-11-23 11:12:13
                          11:12 → Fri 2012-11-23 11:12:00
                            now → Fri 2012-11-23 18:15:22
                          today → Fri 2012-11-23 00:00:00
                      today UTC → Fri 2012-11-23 16:00:00
                      yesterday → Fri 2012-11-22 00:00:00
                       tomorrow → Fri 2012-11-24 00:00:00
      tomorrow Pacific/Auckland → Thu 2012-11-23 19:00:00
                       +3h30min → Fri 2012-11-23 21:45:22
                            -5s → Fri 2012-11-23 18:15:17
                      11min ago → Fri 2012-11-23 18:04:22
                    @1395716396 → Tue 2014-03-25 03:59:56
Здесь представлены таймстампы как для OnCalendar=, так и для монотонных таймеров.Перечисления и диапазоны:Боольшой список примеров
Sat,Thu,Mon..Wed,Sat..Sun → Mon..Thu,Sat,Sun *-*-* 00:00:00
          Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00
                        Wed *-1 → Wed *-*-01 00:00:00
               Wed..Wed,Wed *-1 → Wed *-*-01 00:00:00
                     Wed, 17:48 → Wed *-*-* 17:48:00
    Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03
                    *-*-7 0:0:0 → *-*-07 00:00:00
                          10-15 → *-10-15 00:00:00
            monday *-12-* 17:00 → Mon *-12-* 17:00:00
      Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45
           12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00
                12..14:10,20,30 → *-*-* 12..14:10,20,30:00
      mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45
                 03-05 08:05:40 → *-03-05 08:05:40
                       08:05:40 → *-*-* 08:05:40
                          05:40 → *-*-* 05:40:00
         Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40
               Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40
               2003-03-05 05:40 → 2003-03-05 05:40:00
     05:40:23.4200004/3.1700005 → *-*-* 05:40:23.420000/3.170001
                 2003-02..04-05 → 2003-02..04-05 00:00:00
           2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
                     2003-03-05 → 2003-03-05 00:00:00
                          03-05 → *-03-05 00:00:00
                         hourly → *-*-* *:00:00
                          daily → *-*-* 00:00:00
                      daily UTC → *-*-* 00:00:00 UTC
                        monthly → *-*-01 00:00:00
                         weekly → Mon *-*-* 00:00:00
        weekly Pacific/Auckland → Mon *-*-* 00:00:00 Pacific/Auckland
                         yearly → *-01-01 00:00:00
                       annually → *-01-01 00:00:00
                          *:2/3 → *-*-* *:02/3:00
Да. Микро и наносекунды тоже поддерживаются, а ещё очень удобная функция конца месяца и счётчик:
  • *-*~01 — Первый день с конца каждого месяца (он-же последний день месяца).
  • *-05~05 — 27-e мая каждого года (31-5).
  • Mon *-12~07/1 — Последний понедельник декабря.
  • Mon *-12-01/3 — Третий понедельник декабря.
Проверять таймстампы на валидность можно при помощи утилиты systemd-analyze:
$ systemd-analyze calendar 'Mon *-12-01/1'
  Original form: Mon *-12-01/1
Normalized form: Mon *-12-01/1 00:00:00
    Next elapse: Mon 2021-12-06 00:00:00 MSK
       (in UTC): Sun 2021-12-05 21:00:00 UTC
       From now: 11 months 2 days left
$ systemd-analyze timespan 1.5h
Original: 1.5h
      μs: 5400000000
   Human: 1h 30min
$ systemd-analyze timestamp 01:00:30.9999
  Original form: 01:00:30.9999
Normalized form: Sat 2021-01-02 01:00:30 MSK
       (in UTC): Fri 2021-01-01 22:00:30 UTC
   UNIX seconds: @1609538430.999900
       From now: 18h ago
Вот так, в принципе, всё просто, логично и красиво. И разумеется напочитать:
man systemd.timer
man systemd.time
man systemd-system.conf
man systemd-analyze
man tzselect
Список статей серии
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_nastrojka_linux (Настройка Linux), #_sistemnoe_administrirovanie (Системное администрирование), #_*nix, #_linux, #_systemd, #_timer, #_nastrojka_linux (
Настройка Linux
)
, #_sistemnoe_administrirovanie (
Системное администрирование
)
, #_*nix
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 14:51
Часовой пояс: UTC + 5