Перезапуск системної служби в разі відмови залежності


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

Почнемо з b:

# 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 залишається в невдалому стані і не намагатиметься повторно.

EDIT

Я додав наступне до обох сервісів, і тепер B успішно починається, коли A починається, але я не можу пояснити, чому.

[Install]
WantedBy=multi-user.target

Чому це вплине на відносини між А та В?

EDIT2

Вище "виправити" не працює в системному 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 ']'

Відповіді:


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>.

Дуже хакі, але кращих варіантів я не знайшов.

На мою думку, відсутність способу вирішити невдачі залежностей - це недолік системи.


Так, не кажучи вже про відсутність спроби для точок кріплення, які Леонард поетинг не хоче реалізовувати: github.com/systemd/systemd/isissue/4468
Hvisage

0

Здається, що така річ, яку можна було б легко написати і помістити в кронштейн. Основна логіка виходила б приблизно так

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

Після встановлення сценарію cron є хорошим місцем для його перевірки, якщо cron неефективний, сценарій буде хорошим початковим місцем для спроби написати системний сервіс низького рівня, який може перевірити стан деяких інших служб та перезапустити їх за необхідності. Залежно від обсягу зусиль, які ви хочете інвестувати, сценарій, можливо, навіть може бути налаштований на електронну пошту, ви базуєтесь на результатах (якщо, звичайно, послуги, що стосуються питань, є мережевими послугами).


Цю роботу слід скоріше робити в менеджері процесів / сервісів, інакше ви повернетесь до методів SVR4, які системні спроби не робити ...
Hvisage,

0

Afterі Beforeлише встановіть порядок запуску послуг, ваші сервісні файли кажуть "Якщо A і B будуть запущені, тоді A потрібно запустити перед B".

Requires означає, що якщо цю послугу потрібно запустити, її потрібно запустити спочатку, у вашому прикладі "Якщо B запускається, а A не працює, запускайте A"

Коли ви додаєте, WantedBy=multi-user.targetви зараз повідомляєте системі, що послуги потрібно запускати при ініціалізації системи multi-user.target, імовірно, це означає, що після додавання її ви дозволяли системі запускати служби, а не запускати їх вручну?

Я не впевнений, чому це не працює у версії 220, можливо, варто спробувати 222. Я викопаю машину VM і спробую ваші послуги, коли отримаю можливість.


1
Я запитав у systemd-devel, що він працював у 219 році - це помилка. Задумана поведінка полягає в тому, що невдалі залежності НЕ перезапускаються.
Вадим

0

Я витрачав кілька днів на це, намагаючись змусити його працювати «систематизованим» способом, але відмовився від розчарування і написав обгортковий сценарій для управління залежностями та невдачами. Кожна служба для дітей - це звичайна системна послуга, без "Потрібно" або "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="$@"isnly buggy code - ви згортаєте масив у рядок, відкидаєте початкові межі між елементами, тому аргумент, створений, тобто. 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/


Будь ласка, прочитайте питання уважніше. Йдеться не про перезапуск однієї нездорової служби, а про те, як поводиться система, коли служба відповідача не працює.
Вадим
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.