Bağımlılık arızasında systemd hizmetini yeniden başlatma


26

Bağımlılıklarından birinin başlangıçta başarısız olması durumunda bir hizmeti yeniden başlatmakla başa çıkmak için doğru yaklaşım nedir (ancak yeniden denemeden sonra başarılı olur).

İşte problemi daha net hale getirmek için tartışmalı bir repro.

a.service (ilk denemede başarısızlığı ve ikinci denemede başarısını simüle eder)

[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 başladıktan sonra önemsiz derecede başarılı)

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

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

B başlayalım:

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

Kayıtlar:

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 başarıyla başlatıldı, ancak B başarısız bir durumda kaldı ve yeniden denemiyor.

DÜZENLE

Aşağıdakileri her iki hizmete de ekledim ve şimdi B, A başladığında başarıyla başlıyor, ancak nedenini açıklayamıyorum.

[Install]
WantedBy=multi-user.target

Bu neden A ve B arasındaki ilişkiyi etkiler?

EDIT2

Yukarıdaki "düzeltme" 220 numaralı sistemde çalışmıyor.

systemd 219 hata ayıklama günlükleri

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 hata ayıklama günlükleri

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
Bunu izleyen bir upstream systemd sorunu var: github.com/systemd/systemd/issues/1312
JKnight

Yanıtlar:


31

Birisinin bu konuyla ilgili bilgiler yetersiz olduğu için karşılaşması durumunda bu konuyla ilgili bulgularımı özetlemeye çalışacağım.

  • Restart=on-failure sadece işlem hataları için geçerlidir (bağımlılık hataları nedeniyle başarısızlık için geçerli değildir)
  • Bağımlılık başarısız olan ünitelerin bir bağımlılık başarılı bir şekilde yeniden başlatıldığında belirli koşullar altında yeniden başlatılması, <220: http://lists.freedesktop.org/archives/systemd-devel/2015-July/033513.html sistemindeki bir hataydı.
  • Bir bağımlılığın başlangıçta başarısız olma ihtimali çok küçük bir ihtimal olsa ve esnekliği önemsiyorsanız, kullanmayın Before/ Afteryerine bağımlılığın ürettiği bazı öğeleri kontrol edin.

Örneğin

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

Kullanabilirsin bile systemctl is-active <dependecy>.

Çok hack, ama daha iyi bir seçenek bulamadım.

Benim düşünceme göre, bağımlılık başarısızlıklarını ele almanın bir yolunun olmaması sistemdeki bir kusurdur.


Evet, Leonard şairinin uygulamak istemediği montaj noktaları için bir denemeye sahip olmamaktan bahsetmiyorum: github.com/systemd/systemd/issues/4468
Hvisage

0

Bu kolayca yazılmış ve bir cronjob içine yazılabilir ve bir şey olabilir gibi görünüyor. Temel mantık böyle bir şey olur

  1. Hem a hem de b hizmetinin yanı sıra bağımlılıkların geçerli bir durumda çalışıp çalışmadığını kontrol edin. Her şeyin doğru çalışıp çalışmadığını kontrol etmenin en iyi yolunu bileceksiniz
  2. Her şey doğru çalışıyorsa, hiçbir şey yapmayın veya her şeyin çalıştığını kaydedin. Günlük kaydı, önceki günlük girişini aramanıza izin verme avantajına sahiptir.
  3. Bir şey bozulursa hizmetleri yeniden başlatın ve servis ve bağımlılık durum kontrolünün gerçekleştiği betiğin başına geri dönün. Zıplama yalnızca hizmetlerin yeniden başlatılmasından eminseniz ve bağımlılıkların çalışma olasılığının yüksek olacağından emin olmanız gerekir, aksi halde döngü potansiyeli vardır.
  4. Cron birazdan komut dosyasını tekrar çalıştırsın

Betik ayarlandıktan sonra cron test etmek için iyi bir yerdir, eğer cron yetersizse, betik bazı diğer servislerin durumunu kontrol edebilecek ve gerektiğinde yeniden başlatabilecek düşük seviyeli bir sistem servisi yazmaya çalışmak için iyi bir başlangıç ​​noktası olacaktır. Yatırım yapmak istediğiniz çabanın miktarına bağlı olarak, senaryo muhtemelen sonuçlara göre size e-posta göndermek için bile ayarlanmış olabilir (tabii ki, sorulardaki hizmetler ağ hizmetleri değilse).


Şeyler Bu cronjob ziyade aksi takdirde girişimlerini systemd SVR4 yöntemlere geri gidiyorum, süreç / hizmet yöneticisi yapılmalıdır değil do ...
Hvisage

0

Afterve Beforeyalnızca servislerin başlatılacağı sırayı ayarlayın; servis dosyalarınız "A ve B başlatılacaksa, A B'den önce başlatılmalıdır" der.

Requires Bu hizmet başlatılacaksa, önce bu hizmetin başlatılması gerektiği anlamına gelir, örneğin “B başlatıldıysa ve A çalışmıyorsa, A başlat”

Siz WantedBy=multi-user.targetşimdi eklediğinizde, sisteme sistem başlatılırken servislerin başlatılması gerektiğini söylüyorsunuz, multi-user.targetbu muhtemelen bir kez eklediğinizde sistemin manuel olarak başlatmak yerine hizmetleri başlatmasına izin verdiğiniz anlamına mı geliyor?

Bunun neden sürüm 220’de çalışmadığından emin değilim, 222 denemeye değer olabilir.


1
Systemd-devel'e sordum, 219'da çalıştığı gerçeği bir hataydı. Amaçlanan davranış başarısız bağımlılıkları yok olmasıdır DEĞİL yeniden olsun.
Vadim

0

Bunun üzerinde günler geçirdim, "systemd" yöntemiyle çalışmasını sağlamaya çalıştım, ancak hayal kırıklığı içinde pes etti ve bağımlılıkları ve başarısızlıkları yönetmek için bir sarmalayıcı betiği yazdım. Her çocuk servisi normal bir sistem hizmetidir; "Gerekir" veya "Kısım" veya başka hizmetlere kanca takılmaz.

Üst düzey servis dosyam şöyle görünüyor:

[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

Çok uzak çok iyi. top.serviceDosya kontrolleri foo.serviceve bar.service. Başlangıç topbaşlar foove bar, ve topdurur durur foove bar. Son bileşen, top-service.shhizmetleri başarısızlık için izleyen senaryom:

#!/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="$@"doğuştan gelen buggy kodudur - bir dizgeyi dizgeye sıkıştırıyorsunuz, öğeler arasındaki orijinal sınırları atıyorsunuz, yani yarattığı argüman, yani. set -- "argument one" "argument two"ile aynı hale gelir set -- "argument" "one" "argument" "two". requires=( "$@" )orijinal verileri koruyacak, böylece olduğu gibi güvenle genişletilebilecek systemctl is-active "${requires[@]}".
Charles Duffy

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.