Linux: редактирование systemd юнит-файлов, рестарт сервиса при падении и уведомление на почту

Автор: | 01/03/2019
 

Имеется RabbitMQ сервис, который изредка может упасть.

Требуется:

  1. перезапускать его, если процесс был завершён с ошибкой
  2. отправлять почтовое уведомление об этом событии

Настроим это всё через systemd-сервис файл самого RabbitMQ (хотя есть и другие варианты, например – с помощью monit, см. Monit: мониторинг и перезапуск NGINX).

Нас интересуют две опции:

  • RestartSec=: задержка при рестарте сервиса
  • Restart=: условие, при которому будет выполняться перезапуск

Допустимые значения для Restart – в таблице:

Table 2. Exit causes and the effect of the Restart= settings on them

Restart settings/Exit causes no always on-success on-failure on-abnormal on-abort on-watchdog
Clean exit code or signal X X
Unclean exit code X X
Unclean signal X X X X
Timeout X X X
Watchdog X X X X

Редактирование systemd-юнит файлов

Дефолтный юнит-файл RabbitMQ создаётся в /lib/systemd/system/rabbitmq-server.service.

Просмотреть его можно с помощью systemctl cat:

[simterm]

admin@bttrm-production-console:~$ systemctl cat rabbitmq-server.service 
# /lib/systemd/system/rabbitmq-server.service
[Unit]
Description=RabbitMQ Messaging Server
After=network.target

[Service]
Type=simple
User=rabbitmq
SyslogIdentifier=rabbitmq
LimitNOFILE=65536
ExecStart=/usr/sbin/rabbitmq-server
ExecStartPost=/usr/lib/rabbitmq/bin/rabbitmq-server-wait
ExecStop=/usr/sbin/rabbitmqctl stop

[Install]
WantedBy=multi-user.target

[/simterm]

Редактировать его, как и любой другой файл в /lib/systemd/system/, напрямую нельзя, т.к. при обновлении пакета файл будет перезаписан.

Для того, что бы изменить поведение сервиса – файлы для него должны создаваться в /etc/systemd/system.

Для редактирования существующего сервиса – используем systemctl edit foo.service с опцией --full:

[simterm]

root@bttrm-dev-console:/home/admin# systemctl edit --full rabbitmq-server.service

[/simterm]

Откроется временный файл /etc/systemd/system/rabbitmq-server.service.d/.#override.conf6a0bfbaa5ed8b8d8, с содержимым текущего /lib/systemd/system/rabbitmq-server.service, в который вносим новое описание сервиса.

Рестарт сервиса при падении

Задаём обе опции – Restart=on-failure и RestartSec=60s:

[Unit]
Description=RabbitMQ Messaging Server
After=network.target

[Service]
Type=simple
User=rabbitmq
SyslogIdentifier=rabbitmq
LimitNOFILE=65536
ExecStart=/usr/sbin/rabbitmq-server
ExecStartPost=/usr/lib/rabbitmq/bin/rabbitmq-server-wait
ExecStop=/usr/sbin/rabbitmqctl stop

Restart=on-failure
RestartSec=60s

[Install]
WantedBy=multi-user.target

Перечитываем конфиги systemd:

[simterm]

root@bttrm-dev-console:/home/admin# systemctl daemon-reload

[/simterm]

systemd создаст файл /etc/systemd/system/rabbitmq-server.service с новым содержимым.

Получаем PID RabbitMQ сервера:

[simterm]

root@bttrm-dev-console:/home/admin# systemctl status rabbitmq-server.service | grep PID
 Main PID: 14668 (rabbitmq-server)

[/simterm]

Убиваем его сигналом SIGKILL (см. Linux&FreeBSD: команды kill, nohup — сигналы и управление процессами), что бы сработало условие on-failure:

[simterm]

root@bttrm-dev-console:/home/admin# kill -9 14668

[/simterm]

Проверяем статус:

[simterm]

root@bttrm-dev-console:/home/admin# systemctl status rabbitmq-server.service
● rabbitmq-server.service - RabbitMQ Messaging Server
   Loaded: loaded (/lib/systemd/system/rabbitmq-server.service; enabled; vendor preset: enabled)
   Active: activating (auto-restart) (Result: signal) since Thu 2019-02-28 12:08:32 EET; 4s ago
  Process: 7093 ExecStop=/usr/sbin/rabbitmqctl stop (code=exited, status=0/SUCCESS)
 Main PID: 14668 (code=killed, signal=KILL)

[/simterm]

Логи:

[simterm]

Mar 01 13:26:00 bttrm-dev-console systemd[1]: rabbitmq-server.service: Main process exited, code=killed, status=9/KILL
Mar 01 13:26:00 bttrm-dev-console rabbitmq[27392]: Stopping and halting node 'rabbit@bttrm-dev-console' ...
Mar 01 13:26:00 bttrm-dev-console systemd[1]: rabbitmq-server.service: Unit entered failed state.
Mar 01 13:26:00 bttrm-dev-console systemd[1]: rabbitmq-server.service: Failed with result 'signal'.

[/simterm]

И через минуту:

[simterm]

root@bttrm-dev-console:/home/admin# systemctl status rabbitmq-server.service
● rabbitmq-server.service - RabbitMQ Messaging Server
   Loaded: loaded (/lib/systemd/system/rabbitmq-server.service; enabled; vendor preset: enabled)
   Active: activating (start-post) since Thu 2019-02-28 12:09:33 EET; 2s ago
...
Feb 28 12:09:33 bttrm-stage-console systemd[1]: rabbitmq-server.service: Service hold-off time over, scheduling restart.
Feb 28 12:09:33 bttrm-stage-console systemd[1]: Stopped RabbitMQ Messaging Server.
Feb 28 12:09:33 bttrm-stage-console systemd[1]: Starting RabbitMQ Messaging Server...

[/simterm]

Логи:

[simterm]

Mar 01 13:27:01 bttrm-dev-console systemd[1]: rabbitmq-server.service: Service hold-off time over, scheduling restart.
Mar 01 13:27:01 bttrm-dev-console systemd[1]: Stopped RabbitMQ Messaging Server.
Mar 01 13:27:01 bttrm-dev-console systemd[1]: Starting RabbitMQ Messaging Server...
Mar 01 13:27:01 bttrm-dev-console rabbitmq[27526]: Waiting for 'rabbit@bttrm-dev-console' ...
Mar 01 13:27:01 bttrm-dev-console rabbitmq[27526]: pid is 27533 ...
Mar 01 13:27:04 bttrm-dev-console systemd[1]: Started RabbitMQ Messaging Server.

[/simterm]

“Service hold-off time over, scheduling restart” – вот наши 60 секунд паузы.

Уведомление на почту

Добавим уведомление на email при падении сервера.

Отправляем тестовое письмо:

[simterm]

root@bttrm-dev-console:/home/admin# echo "Stage RabbitMQ restarted on failure!" | mailx -s "RabbitMQ failure notice" [email protected]

[/simterm]

Можно использовать ExecStopPost= или OnFailure=. OnFailure смотрится логичнее – используем его.

Создаём юнит-файл /etc/systemd/system/[email protected]:

[Unit]
Description=%i failure email notification

[Service]
Type=oneshot
ExecStart=/bin/bash -c '/bin/systemctl status %i | /usr/bin/mailx -s "[%i] failure notification" [email protected]'

Добавляем OnFailure в rabbitmq-server.service через systemctl edit в блок [Unit]:

[Unit]
Description=RabbitMQ Messaging Server
After=network.target
OnFailure=rabbitmq-notify-email@%i.service
...

Не забываем перечитать конфиги systemd:

[simterm]

root@bttrm-dev-console:/home/admin# systemctl daemon-reload

[/simterm]

Убиваем RabbitMQ:

[simterm]

root@bttrm-dev-console:/home/admin# kill -9 29970

[/simterm]

Проверяем логи:

[simterm]

Feb 28 13:55:33 bttrm-dev-console systemd[1]: rabbitmq-server.service: Main process exited, code=killed, status=9/KILL
Feb 28 13:55:33 bttrm-dev-console rabbitmq[30476]: Stopping and halting node 'rabbit@bttrm-dev-console' ...
Feb 28 13:55:33 bttrm-dev-console systemd[1]: rabbitmq-server.service: Unit entered failed state.
Feb 28 13:55:33 bttrm-dev-console systemd[1]: rabbitmq-server.service: Triggering OnFailure= dependencies.
Feb 28 13:55:33 bttrm-dev-console systemd[1]: rabbitmq-server.service: Failed with result 'signal'.
Feb 28 13:55:33 bttrm-dev-console systemd[1]: Starting rabbitmq-server failure email notification...
Feb 28 13:55:33 bttrm-dev-console systemd[1]: Started rabbitmq-server failure email notification.
Feb 28 13:55:33 bttrm-dev-console systemd[1]: rabbitmq-server.service: Service hold-off time over, scheduling restart.
Feb 28 13:55:33 bttrm-dev-console systemd[1]: Stopped RabbitMQ Messaging Server.
Feb 28 13:55:33 bttrm-dev-console systemd[1]: Starting RabbitMQ Messaging Server...
Feb 28 13:55:34 bttrm-dev-console rabbitmq[30619]: Waiting for 'rabbit@bttrm-dev-console' ...
Feb 28 13:55:34 bttrm-dev-console rabbitmq[30619]: pid is 30625 ...
Feb 28 13:55:37 bttrm-dev-console systemd[1]: Started RabbitMQ Messaging Server.

[/simterm]

  1. Triggering OnFailure= dependencies.
  2. Started rabbitmq-server failure email notification.

Окей – работает.

Логи почты:

[simterm]

root@bttrm-dev-console:/home/admin# tail /var/log/exim4/mainlog
2019-02-28 13:48:58 1gzK7S-0007Td-Bt H=alt2.aspmx.l.google.com [2a00:1450:400b:c01::1b] Network is unreachable
2019-02-28 13:51:09 1gzK7S-0007Td-Bt H=alt1.aspmx.l.google.com [172.217.192.27] Connection timed out
2019-02-28 13:51:42 1gzK7S-0007Td-Bt => [email protected] R=dnslookup T=remote_smtp H=alt2.aspmx.l.google.com [74.125.193.27] X=TLS1.2:ECDHE_RSA_CHACHA20_POLY1305:256 CV=yes DN="C=US,ST=California,L=Mountain View,O=Google LLC,CN=mx.google.com" C="250 2.0.0 OK  1551354702 x34si4667116edb.147 - gsmtp"
2019-02-28 13:51:42 1gzK7S-0007Td-Bt Completed
2019-02-28 13:53:53 1gzK16-0006pp-NU H=alt2.aspmx.l.google.com [74.125.193.27] Connection timed out
2019-02-28 13:53:53 1gzK16-0006pp-NU H=aspmx2.googlemail.com [2800:3f0:4003:c02::1a] Network is unreachable
2019-02-28 13:54:59 1gzK16-0006pp-NU => [email protected] R=dnslookup T=remote_smtp H=aspmx3.googlemail.com [74.125.193.26] X=TLS1.2:ECDHE_RSA_CHACHA20_POLY1305:256 CV=yes DN="C=US,ST=California,L=Mountain View,O=Google LLC,CN=mx.google.com" C="250 2.0.0 OK  1551354899 s45si1200185edm.357 - gsmtp"
2019-02-28 13:54:59 1gzK16-0006pp-NU Completed
2019-02-28 13:54:59 End queue run: pid=29201
2019-02-28 13:55:33 1gzKHl-0007xl-Lm <= [email protected] U=root P=local S=1331

[/simterm]

Если письмо не приходит – проверяем очередь exim:

[simterm]

root@bttrm-dev-console:/home/admin# exim -bp
 0m  1.2K 1gzL3R-0000dn-5h <[email protected]>
          [email protected]

[/simterm]

Зависло тут.

Запускаем отправку вручную:

[simterm]

root@bttrm-dev-console:/home/admin# runq

[/simterm]

Проверяем лог:

[simterm]

root@bttrm-dev-console:/home/admin# cat /var/log/exim4/mainlog | grep 1gzL3R-0000dn-5h
2019-02-28 14:44:49 1gzL3R-0000dn-5h <= [email protected] U=root P=local S=1241
2019-02-28 14:46:48 1gzL3R-0000dn-5h H=aspmx.l.google.com [2607:f8b0:400d:c0f::1a] Network is unreachable
2019-02-28 14:46:49 1gzL3R-0000dn-5h => [email protected] R=dnslookup T=remote_smtp H=aspmx.l.google.com [173.194.68.26] X=TLS1.2:ECDHE_RSA_CHACHA20_POLY1305:256 CV=yes DN="C=US,ST=California,L=Mountain View,O=Google LLC,CN=mx.google.com" C="250 2.0.0 OK  1551358009 w11si208223qvc.68 - gsmtp"
2019-02-28 14:46:49 1gzL3R-0000dn-5h Completed

[/simterm]

И само письмо:

Что бы решить проблему с отправкой (не знаю, почему exim просто в очередь сохраняет, но не отправялет) – добавим костыль в /etc/systemd/system/[email protected] в виде ExecStartPost:

...
ExecStart=/bin/bash -c '/bin/systemctl status %i | /usr/bin/mailx -s "[%i] failure notification" [email protected]'
ExecStartPost=runq
...

Если надо удалить старые письма – используем их ID:

[simterm]

root@bttrm-dev-console:/home/admin# exim -Mrm 1gzVar-0003oO-Rf
Message 1gzVar-0003oO-Rf has been removed

[/simterm]

Готово.