Restart layanan systemd pada kegagalan ketergantungan


26

Apa pendekatan yang tepat untuk menangani memulai kembali suatu layanan jika salah satu dependensinya gagal pada startup (tetapi berhasil setelah coba lagi).

Inilah repro yang dibuat-buat untuk membuat masalahnya lebih jelas.

a.service (mensimulasikan kegagalan pada percobaan pertama dan keberhasilan pada percobaan kedua)

[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 (secara sepele berhasil setelah A dimulai)

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

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

Mari kita mulai b:

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

Log:

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 telah berhasil dimulai tetapi B dibiarkan dalam keadaan gagal dan tidak akan coba lagi.

EDIT

Saya menambahkan berikut ini untuk kedua layanan dan sekarang B berhasil dimulai ketika A dimulai, tetapi saya tidak bisa menjelaskan mengapa.

[Install]
WantedBy=multi-user.target

Mengapa hal ini memengaruhi hubungan antara A dan B?

EDIT2

Di atas "fix" tidak berfungsi di systemd 220.

systemd 219 log debug

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 log debug

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
Ada masalah upstream systemd yang melacak ini: github.com/systemd/systemd/issues/1312
JKnight

Jawaban:


31

Saya akan mencoba merangkum temuan saya untuk masalah ini jika seseorang menemukan ini karena informasi tentang topik ini sangat sedikit.

  • Restart=on-failure hanya berlaku untuk kegagalan proses (tidak berlaku untuk kegagalan karena kegagalan ketergantungan)
  • Fakta bahwa unit-unit gagal yang bergantung bisa dimulai kembali dalam kondisi tertentu ketika dependensi berhasil dimulai kembali adalah bug di systemd <220: http://lists.freedesktop.org/archives/systemd-devel/2015-July/033513.html
  • Jika ada kemungkinan kecil bahwa dependensi mungkin gagal pada awal dan Anda peduli tentang ketahanan, jangan gunakan Before/ Afterdan sebaliknya lakukan pemeriksaan pada beberapa artefak yang dihasilkan dependensi

misalnya

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

Anda bahkan bisa menggunakannya systemctl is-active <dependecy>.

Sangat peretasan, tapi saya belum menemukan opsi yang lebih baik.

Menurut pendapat saya, tidak memiliki cara untuk menangani kegagalan ketergantungan adalah cacat dalam systemd.


Ya, belum lagi tidak mencoba lagi untuk poin mount yang tidak ingin diimplementasikan oleh Leonard poetring: github.com/systemd/systemd/issues/4468
Hvisage

0

Sepertinya hal-hal yang bisa dituliskan dan dimasukkan ke dalam cronjob dengan mudah. Logika dasar akan seperti ini

  1. periksa untuk melihat apakah kedua layanan a dan b serta dependensi berjalan / dalam keadaan yang valid. Anda akan tahu cara terbaik untuk memeriksa apakah semuanya berfungsi dengan benar
  2. Jika semuanya berfungsi dengan benar, jangan lakukan apa-apa atau catat bahwa semuanya berfungsi. Penebangan memiliki keuntungan memungkinkan Anda untuk mencari entri log sebelumnya.
  3. Jika ada sesuatu yang rusak restart layanan dan melompat kembali ke awal skrip di mana layanan dan status cek ketergantungan terjadi. Lompatan hanya akan terjadi jika Anda yakin akan me-restart layanan dan dependensi akan memiliki kemungkinan besar untuk bekerja, jika tidak ada potensi untuk loop.
  4. Biarkan cron menjalankan skrip lagi dalam beberapa saat

Setelah skrip ditetapkan, cron adalah tempat yang baik untuk mengujinya, jika cron tidak efisien skrip akan menjadi tempat awal yang baik untuk mencoba menulis layanan sistem tingkat rendah yang dapat memeriksa status beberapa layanan lain dan memulai kembali jika diperlukan. Bergantung pada jumlah usaha yang ingin Anda investasikan, skrip mungkin bahkan dapat disetel ke email Anda berdasarkan hasil (kecuali tentu saja layanan yang dimaksud adalah layanan jaringan).


Tugas ini harus dilakukan dalam manajer proses / layanan, jika tidak Anda akan kembali ke metode SVR4, yang tidak akan dilakukan oleh systemd ...
Hvisage

0

Afterdan Beforehanya mengatur urutan layanan akan dimulai, file layanan Anda mengatakan "Jika A dan B akan dimulai maka A harus dimulai sebelum B".

Requires berarti jika layanan ini akan dimulai, layanan itu harus dimulai terlebih dahulu, dalam contoh Anda "Jika B dimulai dan A tidak berjalan, mulai A"

Ketika Anda menambahkan WantedBy=multi-user.targetAnda sekarang memberi tahu sistem bahwa layanan harus dimulai ketika sistem menginisialisasi multi-user.target, mungkin ini berarti setelah Anda menambahkannya Anda membiarkan sistem memulai layanan alih-alih memulai secara manual?

Saya tidak yakin mengapa ini tidak berfungsi dalam versi 220, mungkin patut dicoba 222. Saya akan menggali VM dan mencoba layanan Anda ketika saya mendapatkan kesempatan.


1
Saya bertanya pada systemd-devel, fakta bahwa itu berfungsi di 219 adalah bug. Perilaku yang dimaksud adalah bahwa dependensi yang gagal TIDAK bisa dimulai kembali.
Vadim

0

Saya menghabiskan waktu berhari-hari untuk hal ini, mencoba membuatnya bekerja dengan cara "systemd", tetapi menyerah dengan frustrasi dan menulis skrip pembungkus untuk mengelola dependensi dan kegagalan. Setiap layanan anak adalah layanan systemd normal, tanpa "Membutuhkan" atau "PartOf" atau kait apa pun ke layanan lain.

File layanan tingkat atas saya terlihat seperti ini:

[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

Sejauh ini baik. The top.servicekontrol berkas foo.servicedan bar.service. Mulai topdimulai foodan bar, dan berhenti topberhenti foodan bar. Bahan terakhir adalah top-service.shskrip saya yang memantau layanan untuk kegagalan:

#!/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="$@"adalah kode bawaan kereta - Anda menciutkan array menjadi string, membuang batas-batas asli antara item, sehingga argumen dibuat oleh, yaitu. set -- "argument one" "argument two"menjadi identik dengan set -- "argument" "one" "argument" "two". requires=( "$@" )akan menyimpan data asli, sehingga dapat diperluas dengan aman systemctl is-active "${requires[@]}".
Charles Duffy

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.