Ожидайте, пока служба корректно завершит работу перед выключением/перезагрузкой машины.

Вопрос или проблема

Как написать сервис systemd, который корректно завершает работу при выключении или перезагрузке машины? В частности, он должен задерживать выключение машины, пока не завершит свою работу.

У меня есть сервис, который выключается за 10 секунд: /usr/local/bin/shutdowntest.sh:

#!/bin/bash

SHUTDOWN=0
SHUTDOWN_TIME=10
TRAPPED_SIGNAL=

function onexit() {
  TRAPPED_SIGNAL=$1
  SHUTDOWN=1
}

for SIGNAL in SIGINT SIGTERM SIGHUP SIGPIPE SIGALRM SIGUSR1 SIGUSR2; do
  trap "onexit $SIGNAL" $SIGNAL
done

echo >&2 "shutdowntest running"
while ((!SHUTDOWN || SHUTDOWN_TIME>0)); do
  if [[ -n "$TRAPPED_SIGNAL" ]]; then
    echo >&2 "shutdowntest received signal $TRAPPED_SIGNAL"
    TRAPPED_SIGNAL=
  elif ((SHUTDOWN)); then
    echo >&2 "shutdowntest Shutting down: $SHUTDOWN_TIME more sleeps"
    SHUTDOWN_TIME=$((SHUTDOWN_TIME-1))
    sleep 1
  else
    sleep 10
  fi
done
echo >&2 "shutdowntest Finished shutting down; quitting"

Я установил TimeoutStopSec в 15s в /etc/systemd/system/shutdowntest.service:

[Service]
ExecStart=/usr/local/bin/shutdowntest.sh
TimeoutStopSec=15

[Install]
WantedBy=multi-user.target

Когда я запускаю sudo systemctl stop shutdowntest.service, сервис корректно завершает работу, о чем свидетельствует /var/log/syslog:

00:57:11 shutdowntest.sh[1980]: shutdowntest received signal SIGTERM
00:57:11 shutdowntest.sh[1980]: shutdowntest Shutting down: 10 more sleeps
00:57:11 systemd[1]: Stopping shutdowntest.service...
00:57:11 shutdowntest.sh[1980]: Terminated
00:57:11 shutdowntest.sh[1980]: shutdowntest Shutting down: 9 more sleeps
00:57:12 shutdowntest.sh[1980]: shutdowntest Shutting down: 8 more sleeps
00:57:13 shutdowntest.sh[1980]: shutdowntest Shutting down: 7 more sleeps
00:57:14 shutdowntest.sh[1980]: shutdowntest Shutting down: 6 more sleeps
00:57:15 shutdowntest.sh[1980]: shutdowntest Shutting down: 5 more sleeps
00:57:16 shutdowntest.sh[1980]: shutdowntest Shutting down: 4 more sleeps
00:57:17 shutdowntest.sh[1980]: shutdowntest Shutting down: 3 more sleeps
00:57:18 shutdowntest.sh[1980]: shutdowntest Shutting down: 2 more sleeps
00:57:19 shutdowntest.sh[1980]: shutdowntest Shutting down: 1 more sleeps
00:57:20 shutdowntest.sh[1980]: shutdowntest Finished shutting down; quitting
00:57:20 systemd[1]: Stopped shutdowntest.service.

Но когда я sudo reboot или sudo shutdown now машину, сервис убивается без достаточного времени для корректного завершения, и /var/log/syslog заканчивается через 1 секунду.

00:59:30 shutdowntest.sh[2014]: Terminated
00:59:30 shutdowntest.sh[2014]: shutdowntest received signal SIGTERM
00:59:30 shutdowntest.sh[2014]: shutdowntest Shutting down: 10 more sleeps
00:59:30 systemd[1]: Stopping shutdowntest.service...

Как гарантировать, что сервис получает время (TimeoutSec или TimeoutStopSec) для завершения работы при выключении или перезагрузке машины?

Комментарий от Bigon правильный. Я смотрел в /var/log/syslog, но это записывается с помощью rsyslog.service, который systemd останавливает достаточно рано в процессе завершения (как указано ниже “Stopped System Logging Service”).

После того как я включил постоянное ведение журнала journald (Storage=persistent в /etc/systemd/journald.conf и systemctl restart systemd-journald), journalctl -b-1 -u shutdowntest.service показывает, что мой сервис действительно получает достаточно времени для завершения работы после reboot, shutdown или после нажатия кнопки питания (ACPI G2 Soft Off).

journalctl -b-1 -u shutdowntest.service -u rsyslog.service -u systemd-logind
-- Logs begin at Mon 2018-03-26 18:39:12 UTC, end at Mon 2018-03-26 20:22:34 UTC. --
…
Mar 26 18:46:46 myhost systemd-logind[1202]: Power key pressed.
Mar 26 18:46:46 myhost systemd-logind[1202]: Powering Off...
Mar 26 18:46:46 myhost systemd-logind[1202]: System is powering down.
Mar 26 18:46:46 myhost shutdowntest.sh[1237]: Terminated
Mar 26 18:46:46 myhost shutdowntest.sh[1237]: shutdowntest received signal SIGTERM
Mar 26 18:46:46 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 10 more sleeps
Mar 26 18:46:46 myhost systemd[1]: Stopping shutdowntest.service...
Mar 26 18:46:46 myhost systemd[1]: Stopping Login Service...
Mar 26 18:46:46 myhost systemd[1]: Stopped Login Service.
Mar 26 18:46:46 myhost systemd[1]: Stopping System Logging Service...
Mar 26 18:46:47 myhost systemd[1]: Stopped System Logging Service.
Mar 26 18:46:47 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 9 more sleeps
Mar 26 18:46:48 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 8 more sleeps
Mar 26 18:46:49 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 7 more sleeps
Mar 26 18:46:50 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 6 more sleeps
Mar 26 18:46:51 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 5 more sleeps
Mar 26 18:46:52 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 4 more sleeps
Mar 26 18:46:53 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 3 more sleeps
Mar 26 18:46:54 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 2 more sleeps
Mar 26 18:46:55 myhost shutdowntest.sh[1237]: shutdowntest Shutting down: 1 more sleeps
Mar 26 18:46:56 myhost shutdowntest.sh[1237]: shutdowntest Finished shutting down; quitting
Mar 26 18:46:56 myhost systemd[1]: Stopped shutdowntest.service.

Добавьте следующие строки в файл юнита Systemd в секции [Service]:

ExecStop=kill -TERM $MAINPID
ExecStop=timeout 15 tail --pid=$MAINPID -f /dev/null

Это будет ожидать завершения процесса, но не дольше 15 секунд.

Ответ или решение

Создание systemd сервиса, который корректно завершается при отключении или перезагрузке машины, требует понимания работы systemd и конфигурации сервисных файлов. В данном случае, задача состоит в том, чтобы обеспечить корректное закрытие службы shutdowntest перед полным выключением системы. Для этого требуются дополнительные настройки, которые позволят сервису завершить работу за установленное время, прежде чем произойдет остановка системы.

Теория

systemd служба должна иметь возможность обработать поступающие сигналы о завершении работы таким образом, чтобы главному процессу службы было достаточно времени для корректного завершения своих задач. Для этого предоставляются возможности настройки в файлах unit systemd, такие как TimeoutStopSec, который устанавливает время ожидания для завершения работы службы. Однако кроме этого, стоит учитывать порядок остановки служб и порядок их запуска при выключении системы.

Пример

На основании предоставленного скрипта и его поведения можно заметить следующее:

  1. Скрипт принимает различные сигналы завершения и начинает процесс "корректного" закрытия сервиса в течение 10 секунд.
  2. В ходе нормальной остановки службы (sudo systemctl stop shutdowntest.service) эта процедура проходит успешно при наличии достаточного времени.
  3. Однако при выключении или перезагрузке весь процесс видимо не успевает завершиться, так как syslog отключается рано, и журналирование ведется не полностью.

Применение

Для того чтобы гарантировать корректное завершение службы перед выключением, выполните следующие действия:

  1. Настройка Persist для journald: Как было замечено, переключение на настройку Storage=persistent в journald.conf и перезапуск journald (systemctl restart systemd-journald) позволяет корректно сохранять логи даже после перезагрузки или выключения. Это позволяет удостовериться, что служба действительно завершает свою работу вовремя.

  2. Изменения в systemd unit файле: Внесите изменения в ваш файл службы, добавив в секцию [Service] следующие строки:

    ExecStop=kill -TERM $MAINPID
    ExecStop=timeout 15 tail --pid=$MAINPID -f /dev/null

    Эти строки заставляют systemd отправлять сигнал завершения процессу и удерживать систему в состоянии ожидания до полного завершения главного процесса (но не более 15 секунд).

  3. Тестирование: Проверьте конфигурацию, наблюдая за логами с помощью journalctl -b-1 -u shutdowntest.service, чтобы убедиться, что сервис завершается корректно в установленные сроки при выключении системы.

Эти шаги позволят обеспечить безопасное завершение вашего сервиса перед выключением машины, корректно сохраняя данные и избегая внезапного прерывания работы.

Оцените материал
Добавить комментарий

Капча загружается...