Перезапуск службы systemd при сбое зависимости


26

Какой правильный подход для обработки перезапуска службы в случае сбоя одной из его зависимостей при запуске (но успешной после повторной попытки).

Вот искусственное воспроизведение, чтобы прояснить проблему.

a.service (имитирует неудачу с первой попытки и успех с второй попытки)

[Unit]
Description=A

[Service]
ExecStartPre=/bin/sh -x -c "[ -f /tmp/success ] || (touch /tmp/success && sleep 10)"
ExecStart=/bin/true
TimeoutStartSec=5
Restart=on-failure
RestartSec=5
RemainAfterExit=yes

b.service (тривиально успешно после запуска A)

[Unit]
Description=B
After=a.service
Requires=a.service

[Service]
ExecStart=/bin/true
RemainAfterExit=yes
Restart=on-failure
RestartSec=5

Давайте начнем б:

# systemctl start b
A dependency job for b.service failed. See 'journalctl -xe' for details.

Журналы:

Jun 30 21:34:54 debug systemd[1]: Starting A...
Jun 30 21:34:54 debug sh[1308]: + '[' -f /tmp/success ']'
Jun 30 21:34:54 debug sh[1308]: + touch /tmp/success
Jun 30 21:34:54 debug sh[1308]: + sleep 10
Jun 30 21:34:59 debug systemd[1]: a.service start-pre operation timed out. Terminating.
Jun 30 21:34:59 debug systemd[1]: Failed to start A.
Jun 30 21:34:59 debug systemd[1]: Dependency failed for B.
Jun 30 21:34:59 debug systemd[1]: Job b.service/start failed with result 'dependency'.
Jun 30 21:34:59 debug systemd[1]: Unit a.service entered failed state.
Jun 30 21:34:59 debug systemd[1]: a.service failed.
Jun 30 21:35:04 debug systemd[1]: a.service holdoff time over, scheduling restart.
Jun 30 21:35:04 debug systemd[1]: Starting A...
Jun 30 21:35:04 debug systemd[1]: Started A.
Jun 30 21:35:04 debug sh[1314]: + '[' -f /tmp/success ']'

A был успешно запущен, но B оставлен в неисправном состоянии и не будет повторять попытки.

РЕДАКТИРОВАТЬ

Я добавил следующее к обеим службам, и теперь B успешно запускается при запуске A, но я не могу объяснить, почему.

[Install]
WantedBy=multi-user.target

Почему это повлияет на отношения между А и В?

EDIT2

Выше «исправление» не работает в systemd 220.

журналы отладки systemd 219

systemd219 systemd[1]: Trying to enqueue job b.service/start/replace
systemd219 systemd[1]: Installed new job b.service/start as 3454
systemd219 systemd[1]: Installed new job a.service/start as 3455
systemd219 systemd[1]: Enqueued job b.service/start as 3454
systemd219 systemd[1]: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch oldcoreos
systemd219 systemd[1]: Forked /bin/sh as 1502
systemd219 systemd[1]: a.service changed dead -> start-pre
systemd219 systemd[1]: Starting A...
systemd219 systemd[1502]: Executing: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmpoldcoreos
systemd219 sh[1502]: + '[' -f /tmp/success ']'
systemd219 sh[1502]: + touch /tmp/success
systemd219 sh[1502]: + sleep 10
systemd219 systemd[1]: a.service start-pre operation timed out. Terminating.
systemd219 systemd[1]: a.service changed start-pre -> final-sigterm
systemd219 systemd[1]: Child 1502 belongs to a.service
systemd219 systemd[1]: a.service: control process exited, code=killed status=15
systemd219 systemd[1]: a.service got final SIGCHLD for state final-sigterm
systemd219 systemd[1]: a.service changed final-sigterm -> failed
systemd219 systemd[1]: Job a.service/start finished, result=failed
systemd219 systemd[1]: Failed to start A.
systemd219 systemd[1]: Job b.service/start finished, result=dependency
systemd219 systemd[1]: Dependency failed for B.
systemd219 systemd[1]: Job b.service/start failed with result 'dependency'.
systemd219 systemd[1]: Unit a.service entered failed state.
systemd219 systemd[1]: a.service failed.
systemd219 systemd[1]: a.service changed failed -> auto-restart
systemd219 systemd[1]: a.service: cgroup is empty
systemd219 systemd[1]: a.service: cgroup is empty
systemd219 systemd[1]: a.service holdoff time over, scheduling restart.
systemd219 systemd[1]: Trying to enqueue job a.service/restart/fail
systemd219 systemd[1]: Installed new job a.service/restart as 3718
systemd219 systemd[1]: Installed new job b.service/restart as 3803
systemd219 systemd[1]: Enqueued job a.service/restart as 3718
systemd219 systemd[1]: a.service scheduled restart job.
systemd219 systemd[1]: Job b.service/restart finished, result=done
systemd219 systemd[1]: Converting job b.service/restart -> b.service/start
systemd219 systemd[1]: a.service changed auto-restart -> dead
systemd219 systemd[1]: Job a.service/restart finished, result=done
systemd219 systemd[1]: Converting job a.service/restart -> a.service/start
systemd219 systemd[1]: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch oldcoreos
systemd219 systemd[1]: Forked /bin/sh as 1558
systemd219 systemd[1]: a.service changed dead -> start-pre
systemd219 systemd[1]: Starting A...
systemd219 systemd[1]: Child 1558 belongs to a.service
systemd219 systemd[1]: a.service: control process exited, code=exited status=0
systemd219 systemd[1]: a.service got final SIGCHLD for state start-pre
systemd219 systemd[1]: About to execute: /bin/true
systemd219 systemd[1]: Forked /bin/true as 1561
systemd219 systemd[1]: a.service changed start-pre -> running
systemd219 systemd[1]: Job a.service/start finished, result=done
systemd219 systemd[1]: Started A.
systemd219 systemd[1]: Child 1561 belongs to a.service
systemd219 systemd[1]: a.service: main process exited, code=exited, status=0/SUCCESS
systemd219 systemd[1]: a.service changed running -> exited
systemd219 systemd[1]: a.service: cgroup is empty
systemd219 systemd[1]: About to execute: /bin/true
systemd219 systemd[1]: Forked /bin/true as 1563
systemd219 systemd[1]: b.service changed dead -> running
systemd219 systemd[1]: Job b.service/start finished, result=done
systemd219 systemd[1]: Started B.
systemd219 systemd[1]: Starting B...
systemd219 systemd[1]: Child 1563 belongs to b.service
systemd219 systemd[1]: b.service: main process exited, code=exited, status=0/SUCCESS
systemd219 systemd[1]: b.service changed running -> exited
systemd219 systemd[1]: b.service: cgroup is empty
systemd219 sh[1558]: + '[' -f /tmp/success ']'

журналы отладки systemd 220

systemd220 systemd[1]: b.service: Trying to enqueue job b.service/start/replace
systemd220 systemd[1]: a.service: Installed new job a.service/start as 4846
systemd220 systemd[1]: b.service: Installed new job b.service/start as 4761
systemd220 systemd[1]: b.service: Enqueued job b.service/start as 4761
systemd220 systemd[1]: a.service: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmp/success && sleep 10)'
systemd220 systemd[1]: a.service: Forked /bin/sh as 2032
systemd220 systemd[1]: a.service: Changed dead -> start-pre
systemd220 systemd[1]: Starting A...
systemd220 systemd[2032]: a.service: Executing: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmp/success && sleep 10)'
systemd220 sh[2032]: + '[' -f /tmp/success ']'
systemd220 sh[2032]: + touch /tmp/success
systemd220 sh[2032]: + sleep 10
systemd220 systemd[1]: a.service: Start-pre operation timed out. Terminating.
systemd220 systemd[1]: a.service: Changed start-pre -> final-sigterm
systemd220 systemd[1]: a.service: Child 2032 belongs to a.service
systemd220 systemd[1]: a.service: Control process exited, code=killed status=15
systemd220 systemd[1]: a.service: Got final SIGCHLD for state final-sigterm.
systemd220 systemd[1]: a.service: Changed final-sigterm -> failed
systemd220 systemd[1]: a.service: Job a.service/start finished, result=failed
systemd220 systemd[1]: Failed to start A.
systemd220 systemd[1]: b.service: Job b.service/start finished, result=dependency
systemd220 systemd[1]: Dependency failed for B.
systemd220 systemd[1]: b.service: Job b.service/start failed with result 'dependency'.
systemd220 systemd[1]: a.service: Unit entered failed state.
systemd220 systemd[1]: a.service: Failed with result 'timeout'.
systemd220 systemd[1]: a.service: Changed failed -> auto-restart
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: Failed to send unit change signal for a.service: Transport endpoint is not connected
systemd220 systemd[1]: a.service: Service hold-off time over, scheduling restart.
systemd220 systemd[1]: a.service: Trying to enqueue job a.service/restart/fail
systemd220 systemd[1]: a.service: Installed new job a.service/restart as 5190
systemd220 systemd[1]: a.service: Enqueued job a.service/restart as 5190
systemd220 systemd[1]: a.service: Scheduled restart job.
systemd220 systemd[1]: a.service: Changed auto-restart -> dead
systemd220 systemd[1]: a.service: Job a.service/restart finished, result=done
systemd220 systemd[1]: a.service: Converting job a.service/restart -> a.service/start
systemd220 systemd[1]: a.service: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmp/success && sleep 10)'
systemd220 systemd[1]: a.service: Forked /bin/sh as 2132
systemd220 systemd[1]: a.service: Changed dead -> start-pre
systemd220 systemd[1]: Starting A...
systemd220 systemd[1]: a.service: Child 2132 belongs to a.service
systemd220 systemd[1]: a.service: Control process exited, code=exited status=0
systemd220 systemd[1]: a.service: Got final SIGCHLD for state start-pre.
systemd220 systemd[1]: a.service: About to execute: /bin/true
systemd220 systemd[1]: a.service: Forked /bin/true as 2136
systemd220 systemd[1]: a.service: Changed start-pre -> running
systemd220 systemd[1]: a.service: Job a.service/start finished, result=done
systemd220 systemd[1]: Started A.
systemd220 systemd[1]: a.service: Child 2136 belongs to a.service
systemd220 systemd[1]: a.service: Main process exited, code=exited, status=0/SUCCESS
systemd220 systemd[1]: a.service: Changed running -> exited
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 sh[2132]: + '[' -f /tmp/success ']'

1
Существует системная проблема, которая отслеживает это: github.com/systemd/systemd/issues/1312
JKnight

Ответы:


31

Я постараюсь обобщить свои выводы по этому вопросу на случай, если кто-то столкнется с этим, поскольку информация по этой теме скудна.

  • Restart=on-failure применяется только к сбоям процесса (не относится к сбоям из-за сбоев зависимостей)
  • Тот факт, что зависимые отказавшие модули перезапускаются при определенных условиях, когда перезапуск зависимости успешно завершен, был ошибкой в ​​systemd <220: http://lists.freedesktop.org/archives/systemd-devel/2015-July/033513.html
  • Если есть даже небольшая вероятность того, что зависимость может потерпеть неудачу при запуске, и вы заботитесь об отказоустойчивости, не используйте Before/, Afterа вместо этого выполните проверку какого-либо артефакта, который создает зависимость

например

ExecStartPre=/usr/bin/test -f /some/thing
Restart=on-failure
RestartSec=5s

Вы могли бы даже использовать systemctl is-active <dependecy>.

Очень хакерский, но я не нашел лучших вариантов.

На мой взгляд, отсутствие возможности обрабатывать ошибки зависимостей является недостатком в systemd.


Да, не говоря уже об отсутствии повторной попытки для точек монтирования, которые Леонард Поэринг не хочет реализовывать: github.com/systemd/systemd/issues/4468
Hvisage

0

Похоже, что-то вроде того, что можно было бы легко написать в сценарии и поместить в cronjob. Основная логика будет выглядеть примерно так

  1. проверьте, работают ли службы a и b, а также зависимости / в правильном состоянии. Вы узнаете лучший способ проверить, все ли работает правильно
  2. Если все работает правильно, ничего не делайте или регистрируйте, что все работает. Ведение журнала имеет то преимущество, что позволяет вам искать предыдущую запись в журнале.
  3. Если что-то не работает, перезапустите службы и вернитесь к началу сценария, где происходит проверка состояния службы и зависимостей. Переход должен происходить только в том случае, если вы уверены в перезапуске служб, и у зависимостей будет высокая вероятность работы, в противном случае существует вероятность возникновения цикла.
  4. Пусть cron снова запустит скрипт

Как только сценарий установлен, cron - это хорошее место для его проверки, а если cron неэффективен, сценарий будет хорошей отправной точкой для попыток написать системный сервис низкого уровня, который может проверять состояние некоторых других сервисов и перезапускать их по мере необходимости. В зависимости от того, сколько усилий вы хотите инвестировать, сценарий может быть даже настроен для отправки вам электронных писем на основе результатов (если, конечно, рассматриваемые службы не являются сетевыми службами).


Это занятие должно быть сделано в диспетчере процессов / сервисов, иначе вы вернетесь к методам SVR4, которые systemd пытается не делать ...
Hvisage

0

Afterи Beforeтолько установите порядок, в котором будут запускаться сервисы, ваши сервисные файлы говорят: «Если A и B будут запущены, то A должен быть запущен до B».

Requires означает, что если эта служба должна быть запущена, эта служба должна быть запущена первой, в вашем примере «Если B запущен, а A не запущен, запустите A»

Когда вы добавляете, что WantedBy=multi-user.targetвы сейчас говорите системе, что службы должны запускаться при инициализации системы multi-user.target, возможно, это означает, что после добавления вы позволяете системе запускать службы вместо того, чтобы запускать их вручную?

Я не уверен, почему это не работает в версии 220, возможно, стоит попробовать 222. Я найду виртуальную машину и попробую ваши службы, когда у меня будет такая возможность.


1
Я спросил на systemd-devel, тот факт, что он работал в 219, был ошибкой. Предполагаемое поведение состоит в том, что сбойные зависимости НЕ перезапускаются.
Вадим

0

Я потратил на это несколько дней, пытаясь заставить его работать "systemd", но разочаровался и написал скрипт-обертку для управления зависимостями и сбоями. Каждый дочерний сервис - это обычный системный сервис, без «Требуется» или «PartOf» или каких-либо подключений к другим сервисам.

Мой сервисный файл верхнего уровня выглядит так:

[Service]
Type=simple
Environment=REQUIRES=foo.service bar.service
ExecStartPre=/usr/bin/systemctl start $REQUIRES
ExecStart=@PREFIX@/bin/top-service.sh $REQUIRES
ExecStop=/usr/bin/systemctl      stop $REQUIRES

Все идет нормально. В top.serviceуправлении файлами foo.serviceи bar.service. Запуск topначинается fooи bar, и останавливается, topостанавливается fooи bar. Последним компонентом является мой top-service.shскрипт, который отслеживает службы на предмет сбоев:

#!/bin/bash

# This monitors REQUIRES services. If any service stops, all of the services are stopped and this script ends.

REQUIRES="$@"

if [ "$REQUIRES" == "" ]
then
  echo "ERROR: no services listed"
  exit 1
fi

echo "INFO: watching services: ${REQUIRES}"

end=0
while [[ $end == 0 ]]
do
  s=$(systemctl is-active ${REQUIRES} )
  if echo $s | egrep '^(active ?)+$' > /dev/null
  then
    # $s has embedded newlines, but echo $s seems to get rid of them, while echo "$s" keeps them.
    # echo INFO: All active, $s
    end=0
  else
    echo "WARN: ${REQUIRES}"
    echo WARN: $s
  fi

  if [[ $s == *"failed"* ]] || [[ $s == *"unknown"* ]]
  then
    echo "WARN: At least one service is failed or unknown, ending service"
    end=1
  else
    sleep 1
  fi
done

echo "INFO: done watching services, stopping: ${REQUIRES}"
systemctl stop ${REQUIRES}
echo "INFO: stopped: ${REQUIRES}"
exit 1

REQUIRES="$@"изначально глючный код - вы сворачиваете массив в строку, отбрасывая исходные границы между элементами, поэтому аргумент создается, т.е. set -- "argument one" "argument two"становится идентичным set -- "argument" "one" "argument" "two". requires=( "$@" )сохранит исходные данные, таким образом, будучи безопасно расширяемым как systemctl is-active "${requires[@]}".
Чарльз Даффи

-1

Не отвечай на это. Но кому-то это может понадобиться (потому что эта страница отображается в поиске):

должно быть

[Service]
 Restart=always
 RestartSec=3

https://jonarcher.info/2015/08/ensure-systemd-services-restart-on-failure/


Пожалуйста, прочитайте вопрос более внимательно. Речь идет не о перезапуске одной нездоровой службы, а о том, как systemd ведет себя при сбое службы-ответчика.
Вадим
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.