systemd: Boot serialisieren

systemd: Boot serialisieren

Das Booten auf meinem Ubuntu 17.04 schlägt fehl und ich möchte es debuggen.

Das Booten schlägt zufällig fehl und ich glaube, dass dies auf einen Race Condition zurückzuführen ist.

Kann ich darum bitten, systemdkeine Aufgaben zu parallelisieren, damit ich sehen kann, ob dies dazu führt, dass der Startvorgang vorhersehbar fehlschlägt?

Antwort1

Eine Problemumgehung: Dienste manuell ausführen

Ich hatte einAbsturz beim Start. In meinem Fall trat der Absturz nach dem Erreichen des basic.targetZiels, aber vor auf multi-user.target, deshalb wollte ich herausfinden, welcher der von gestarteten Dienste multi-user.targetden Absturz verursachte.

Zuerst habe ich das Booten in basic.targeteine Root-Shell eingerichtet. Das kann man dauerhaft machen (vorausgesetzt, man kann überhaupt booten!) mit

systemctl set-default basic.target
systemctl enable debug-shell

Der debug-shellDienst führt eine Root-Shell auf TTY 9 aus.

Sie können denselben Effekt erzielen, indem Sie die Parameter systemd.unit=basic.target systemd.debug-shellzur Kernel-Befehlszeile hinzufügen. In Grub bearbeiten Sie die Befehlszeile beispielsweise wie folgt:

linux /vmlinuz-4.13.0-38-generic root=/dev/mapper/crypt-root ro systemd.unit=basic.target systemd.debug-shell

Von dieser Shell aus habe ich das folgende Skript ausgeführt, um die Dienste nacheinander zu starten. Beachten Sie, dass dies weitgehend ungetestet ist (ich habe es einmal ausgeführt und es stürzte wie erwartet bei dem problematischen Dienst ab).

#!/bin/sh
wants=$(systemctl show -p Wants multi-user.target | sed 's/^Wants=//' | tr ' ' '\n' | sort)
log=/var/tmp/multi-user-steps-$(date +%Y%m%d-%H%M%S)

log () {
  echo "$* ..." | tee -a "$log"
  sync
  "$@"
  ret=$?
  echo "$* -> $ret" | tee -a "$log"
  sync
  return $ret
}

# systemd services
for service in $wants; do
  log systemctl start $service
  sleep 2
done

# upstart services
for conf in /etc/init/*.conf; do
  service=${conf##*/}; service=${service%.conf}
  log service ${service} start
  sleep 2
done

# sysvinit services
for service in /etc/rc3.d/S*; do
  log ${service} start
  sleep 2
done

Zusätzliche Abhängigkeiten hinzufügen

Das folgende Skript deklariert „Before“-Abhängigkeiten von den systemd-Einheiten, die direkte Abhängigkeiten eines bestimmten Ziels sind, um sie zu zwingen, in einer bestimmten Reihenfolge ausgeführt zu werden. Sie können es auf multi-user.targetoder ausführen basic.target.

Beachten Sie, dassDieses Skript funktioniert im Allgemeinen nichtweil es vorhandene Abhängigkeiten nicht berücksichtigt: es kann eine Abhängigkeitsschleife verursachen. Ein richtiges Skript sollte vorhandene Abhängigkeiten sammeln und eine topologische Sortierung vornehmen. Ich habe mein Problem gelöst, also habe ich nicht vor, weiter daran zu arbeiten; ich poste es für den Fall, dass jemand es an seine Bedürfnisse anpassen möchte.

Beachten Sie auch, dass dies die Upstart- und SysVinit-Dienste nicht betrifft.

Machen Sie eine Sicherungskopie davon /etc, bevor Sie dies ausführen! (Ich empfehle dringend die Verwendungusw.Keeper.)

#!/bin/sh
set -e

if [ $# -eq 0 ] || [ "$1" = "--help" ]; then
  cat <<EOF
Usage: $0 TARGET
Linearize the dependencies of a systemd target so it starts deterministically.
This scripts adds systemd unit files called linearize-for*.conf containing
extra Before= dependencies for each dependency of TARGET.
EOF
fi

service_dir=/etc/systemd/system
target=$1

wants=$(systemctl show -p Wants "$target" | sed 's/[^= ]*=//' |
                                            tr ' ' '\n' | sort)
previous=
for want in $wants; do
  [ -d "$service_dir/$want.d" ] || mkdir "$service_dir/$want.d"
  cat <<EOF >"$service_dir/$want.d/linearize-for-${target%.*}.conf"
[Unit]
Before=$previous
EOF
  previous=$want
done

verwandte Informationen