Рубрики
boot \ grub \ grub2 \ init \ systemd

systemd \ daemon \ timer \ triger \ свой демон \ daemon \ reload

ссылки:

https://systemd.io/
https://www.freedesktop.org/wiki/Software/systemd/
https://www.freedesktop.org/software/systemd/man/systemd.service.html
https://manpages.ubuntu.com/manpages/xenial/en/man5/systemd.service.5.html

https://habr.com/ru/post/535872/
https://habr.com/ru/post/535930/
https://habr.com/ru/post/536040/

https://t.me/ru_systemd


https://linux-notes.org/pishem-systemd-unit-fajl/
https://wiki.archlinux.org/title/Systemd_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)
https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units-ru
https://habr.com/ru/company/southbridge/blog/255845/
https://medium.com/@benmorel/creating-a-linux-service-with-systemd-611b5c8b91d6


https://habr.com/ru/post/540096/ - rastic

daemon / service

Помощь:
man systemd.unit
man systemd.service
man systemd.exec
man systemctl
man sysusers.d
man systemd-sysusers


/usr/lib/systemd/system/ – юниты из установленных пакетов RPM — всякие nginx, apache, mysql и прочее
/run/systemd/system/ — юниты, созданные в рантайме — тоже, наверное, нужная штука
/etc/systemd/system/ — юниты, созданные системным администратором — а вот сюда мы и положим свой юнит.
Юнит представляет из себя текстовый файл с форматом, похожим на файлы .ini Microsoft Windows.


/etc/systemd/system/NAME-UNIT.service - расположение юнита NAME-UNIT
/etc/sysusers.d/NAME-UNIT.conf - расположение конфига NAME-UNIT (не обязательный файл)


Пример простого юнита MyUnit:
vim  /etc/systemd/system/myunit.service
-----------------------------------------
[Unit]
#описание юнита
Description=MyUnit

#Запускать юнит после какого-либо сервиса или группы сервисов        
After=syslog.target
After=network.target
After=nginx.service
After=mysql.service

#Для запуска сервиса необходим запущенный сервис mysql
Requires=mysql.service

#Для запуска сервиса желателен запущенный сервис redis
Wants=redis.service

[Service]
#Тип сервиса (Type=simple, Type=forking)
Type=forking

#Определить PIDFile=, чтобы systemd могла отслеживать основной процесс  (не обязательный)
PIDFile=/work/www/myunit/shared/tmp/pids/service.pid

#Рабочий каталог, он делается текущим перед запуском стартап команд  (не обязательный)
WorkingDirectory=/work/www/myunit/current

#Пользователь и группа, под которым надо стартовать сервис:
User=myunit
Group=myunit

#Переменные окружения (не обязательный)
Environment=RACK_ENV=production

#логи
SyslogIdentifier=MyUnit
SyslogFacility=daemon

#Запрет на убийство сервиса вследствие нехватки памяти и срабатывания механизма OOM
OOMScoreAdjust=-1000

#Команды на старт/стоп и перезапуск сервиса
ExecStart=/usr/local/bin/bundle exec service -C /work/www/myunit/shared/config/service.rb --daemon
ExecStop=/usr/local/bin/bundle exec service -S /work/www/myunit/shared/tmp/pids/service.state stop
ExecReload=/usr/local/bin/bundle exec service -S /work/www/myunit/shared/tmp/pids/service.state restart

#Таймаут в секундах, сколько ждать system отработки старт/стоп команд
TimeoutSec=300

#Попросим systemd автоматически рестартовать наш сервис, если он вдруг перестанет работать
Restart=always
RestartSec=5
Restart=on-failure


[Install]
#В секции [Install] опишем, в каком уровне запуска должен стартовать сервис
WantedBy=multi-user.target 
-----------------------------------------


Директива Type= может быть одной из следующих:
Type=simple 
Основной процесс службы указан в стартовой строке. 
Это значение по умолчанию, если директивы Type= и Busname= не установлены, но установлен ExecStart=. 
Любое сообщение должно обрабатываться вне устройства через второй блок соответствующего типа (например, через блок .socket, если эта служба должна обмениваться данными с помощью сокетов).

Type=forking 
Этот тип службы используется, когда служба форкает дочерние процессы при мгновенном покидании родительского процесса.
Это сообщает systemd, что процесс все еще работает, даже несмотря на то, что родитель завершил сеанс.
Хорошо подходит, например для запуска php-fpm, nginx, tomcat.

Type=oneshot
Этот тип указывает, что процесс будет недолговечным и система должна ждать завершения процесса, прежде чем продолжить работу с другими устройствами.
Значение по умолчанию для Type= и ExecStart= не установлены. 
Он используется для одноразовых задач.

Type=dbus
Это означает, что устройство будет использовать имя от D-Bus шины.
Когда это произойдет, systemd продолжит обработку следующего блока.

Type=notify
Это указывает на то, что служба отправит уведомление, когда закончит запуск. 
Процесс systemd будет ждать, пока это произойдет, прежде чем переходить к другим устройствам.

Type=idle
Это означает, что служба не будет запущена до тех пор, пока не будут отправлены все задания.


multi-user.target или runlevel3.target соответствует нашему привычному runlevel=3"Многопользовательский режим без графики". 
Пользователи, как правило, входят в систему при помощи множества консолей или через сеть

 



После изменения\добавления файла юнита необходимо:
systemctl daemon-reload - перечитать демоны
systemctl enable  NAME-UNIT - добавить в автозагрузку
systemctl start NAME-UNIT - запустить
systemctl -l status NAME-UNIT - проверить
systemctl --state=failed - показать сбойные юниты

Приме пример демона из документации:

[Unit]
Description=Simple notifying service

[Service]
Type=notify
ExecStart=/usr/sbin/simple-notifying-service

[Install]
WantedBy=multi-user.target

timer \ замена cron

Помощь:
man systemd.timer
man systemd.time
man systemd-system.conf
man systemd-analyze
man tzselect

Пример из поставки systemd:
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  - проверяем состояне



Событийные таймеры
Таймеры привязанные к каким-либо событиям в системе.
OnBootSec=
Таймер сработает через указанное время после старта системы.

OnStartupSec= 
Для системных таймеров действие аналогично предыдущему, для пользовательских таймеров, это время после первого логина пользователя в систему.

OnActiveSec=
Через какое время, после активации таймера системным менеджером, запускать юнит

RandomizedDelaySec= 
Этакий рандомный джиттер. 
Перед срабатыванием добавляется случайный таймаут от нуля, до заданного значения. 
По умолчанию -- отключено.

FixedRandomDelay= 
Связанный с предыдущим параметром булевый параметр. 
Если включено, то при первом срабатывании таймера, джиттер запомнится (и для этого таймера станет постоянным), но запомнится хитро. 
Сама генерация рандома будет основана на имени пользователя, имени таймера, а самое главное MachineID, 
о котором будет рассказано в одной из следующих статей и который гарантированно разный, на разных хостах. 
Для чего это нужно? Например имеем сеть с кучей хостов, которые, например в начале рабочего дня, запускают таймеры, юниты которых ломятся на сервер, устраивая шторм запросов. 
Что-бы таймеры гарантированно срабатывали в разное время и следует использовать этот параметр.

OnClockChange=, OnTimezoneChange=
Булевые параметры, определяющие будет ли таймер реагировать на перевод системных часов или смену временной зоны. 
По умолчанию, оба параметра, false.

Persistent= 
Записывать-ли на диск состояние таймера сразу после запуска юнита. 
Актуально для параметра OnCalendar=.
По умолчанию — false.

WakeSystem=
Действует на монотонные таймеры. 
По умолчанию отключён. 
Логика следующая. 
При отключённом параметре все монотонные таймеры запоминают своё состояние, перед уходом системы в спящий режим и встают на паузу. 
После выхода системы из спящего режима, отсчёт продолжается с того момента с которого система "ушла в спячку". 
Если-же параметр поставить в true, то таймеры продолжают работать и в спящем режиме (должно поддерживаться и железом) 
и по наступлении события выводят систему из спячки и запускают юнит.

RemainAfterElapse= 
По умолчанию true.
Смысл этого параметра примерно следующий, после срабатывания таймера он остаётся загруженным, но если поставить false, 
то после срабатывания таймер выгружается и перестаёт отслеживать время. 
Хорошо для одноразовых юнитов (Transient Units) о которых мы поговорим в одной из следующих статей. 
Или для таймеров которые должны сработать один раз, как это делают задания старой, доброй at.


Примеры таймеров

Самый простой таймер:
---------------------
#задача запуска сервиса раз в полтора часа, начиная с часа ночи
[Unit]
Description=Test timer

[Timer]
OnCalendar=01:00
OnUnitActiveSec=1.5h
---------------------

Например мы хотим что-б наш юнит запускался каждую пятницу 13-е… 
OnCalendar=Fri *-*-13 12:00:00

Полный формат календарной формы выглядит так: 
Mon 2025-12-01 12:00:00.000000 Europe/Moscow

Камчатка уже отпраздновала Новый год: 
OnCalendar=yearly Asia/Kamchatka 

Нормализованная форма будет выглядеть так (эти строчки указывают на одно и то-же время):
OnCalendar=*-01-01 00:00:00 Asia/Kamchatka


Проверять таймстампы на валидность можно при помощи утилиты systemd-analyze
systemd-analyze calendar 'Mon *-12-01/1'
systemd-analyze timespan 1.5h
systemd-analyze timestamp 01:00:30.9999

Посмотреть все таймеры в системе: 
systemctl list-timers

triger

Помощь:
man systemd.path
man 7 inotify
man inotifywait
man inotifywatch
man systemd.automount
man systemd.mount
man systemd-mount
man 5 fstab
man systemd.time



Пример запуск скрипта с таймером:

vim /etc/systemd/system/sctipt-test.service 
-------------------------------------------
[Unit]
Description=sctipt-test
 
[Service]
Type=oneshot
ExecStart=/usr/bin/sctipt-test.sh
Restart=on-failure
 
[Install]
WantedBy=multi-user.target
-------------------------------------------

vim /etc/systemd/system/sctipt-test.timer
----------------------------------------- 
# Каждые 30 сек запуск скрипта
[Unit]
Description=sctipt-test
 
[Timer]
OnUnitActiveSec=30 

[Install]
WantedBy=timers.target
----------------------------------------- 

systemctl daemon-reload
systemctl enable sctipt-test.service
systemctl enable sctipt-test.timer
systemctl status sctipt-test.service
systemctl status sctipt-test.timer

systemctl list-timers 
systemctl list-timers sctipt-test.timer