Systemd Stufe 2 Fortgeschritten

Aus Xinux Wiki
Version vom 17. Oktober 2026, 15:33 Uhr von Thomas.will (Diskussion | Beiträge) (Links zu Stufe 3 und Lernpfad)
Zur Navigation springen Zur Suche springen
Stufe 1 · Basic Stufe 2 · Fortgeschritten Stufe 3 · Security
Ich verwende es Ich schreibe Units Ich härte und untersuche

Einfach erklärt

  • Eine Unit ist eine kleine Textdatei. Sie beschreibt, was gestartet wird, wann und wovon es abhängt.
  • Man schreibt kein Startskript mehr, sondern sagt systemd nur, was man haben will. Den Rest erledigt systemd.
  • Units der Pakete liegen unter /usr/lib, eigene und geänderte Units unter /etc. Was in /etc liegt, gewinnt.
  • Wants sagt, was mitgestartet wird. After sagt, was vorher fertig sein muss. Das sind zwei getrennte Dinge.

Animation

Wo Units liegen

Verzeichnis Wer schreibt dort Vorrang
/etc/systemd/system/ der Admin höchster
/run/systemd/system/ Programme zur Laufzeit, weg nach dem Neustart mittel
/usr/lib/systemd/system/ die Pakete, wird bei Updates überschrieben niedrigster
  • Liegt eine Unit mit gleichem Namen in /etc, wird die aus /usr/lib ignoriert.
  • Drop-ins ändern nur einzelne Zeilen: /etc/systemd/system/name.service.d/override.conf
  • systemctl cat name zeigt die Unit samt allen Drop-ins, so wie systemd sie wirklich sieht.

Aufbau einer Unit

  • vi /etc/systemd/system/simple-http.service
[Unit]
Description=Einfacher Webserver für Downloads
After=network.target

[Service]
Type=exec
User=kit
WorkingDirectory=/home/kit/Downloads
ExecStart=/usr/bin/python3 -m http.server 8000
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target
Zeile Bedeutung
[Unit]: Beschreibung und Abhängigkeiten
Description= Text, der bei status und im Journal erscheint
After=network.target erst starten, wenn die Netzwerkverwaltung läuft
[Service]: wie der Dienst läuft
Type=exec systemd startet das Programm und wartet, bis es wirklich ausgeführt wird
User=kit läuft als Benutzer kit, nicht als root
WorkingDirectory= in diesem Verzeichnis startet der Prozess, http.server zeigt genau dieses an
ExecStart= der Befehl, immer mit absolutem Pfad
Restart=on-failure bei Absturz oder Fehlercode neu starten
RestartSec=2 2 Sekunden warten vor dem Neustart
[Install]: wird nur von enable und disable gelesen
WantedBy=multi-user.target enable legt einen Link in multi-user.target.wants/ an

In Betrieb nehmen

  • systemctl daemon-reload
  • systemctl enable --now simple-http
  • systemctl status simple-http
  • journalctl -fu simple-http
  • sudo ss -lntp | grep 8000
  • curl -s http://localhost:8000/ | head -3
  • Achtung: http.server lauscht auf allen Netzwerkkarten. Nur lokal: --bind 127.0.0.1

Prüfen vor dem Start

  • systemd-analyze verify /etc/systemd/system/simple-http.service
  • Keine Ausgabe heißt: keine Fehler gefunden.

Wants, Requires und After

Anweisung Bedeutung Wenn die andere Unit scheitert
Wants=b b wird mitgestartet egal, a läuft trotzdem
Requires=b b wird mitgestartet a scheitert mit
After=b a startet erst, wenn b fertig ist startet nichts von selbst
Before=b a muss fertig sein, bevor b startet startet nichts von selbst
  • Wants und Requires sagen was, After und Before sagen wann. Meist braucht man beides.
  • After= allein startet nichts. Es ordnet nur, falls die andere Unit ohnehin gestartet wird.
  • In der Praxis nimmt man fast immer Wants. Requires nur, wenn ohne die andere Unit wirklich nichts geht.
  • network.target: Die Netzwerkverwaltung läuft. Für Server, die lauschen, reicht das.
  • network-online.target: Es gibt wirklich eine Verbindung. Dann braucht man Wants= und After=.

Type=: wann ein Dienst als gestartet gilt

Type Gestartet, wenn … Beispiel
simple der Prozess angelegt ist einfache Programme (Standard)
exec das Programm wirklich ausgeführt wird, meldet Tippfehler im Pfad als Fehler besser als simple
forking der Startprozess sich beendet hat und ein Kind weiterläuft alte Daemons
oneshot das Programm fertig ist Skripte, z.B. Firewall laden
notify der Dienst selbst „bereit“ meldet nginx, postgresql
  • Bei oneshot mit RemainAfterExit=yes bleibt die Unit nach dem Skript „active“. So kann ein späteres stop noch ExecStop= ausführen.

Restart=: wann neu gestartet wird

Restart Exit-Code ≠ 0 Absturz durch Signal (kill -9, SIGSEGV) normales Ende
no – – –
on-failure ja ja –
on-abort – ja –
always ja ja ja
  • on-abort startet bei einem Python-Fehler (Exit-Code 1) nicht neu. Meist will man on-failure.
  • Startet ein Dienst mehr als 5-mal in 10 Sekunden, gibt systemd auf: Start request repeated too quickly. Danach hilft systemctl reset-failed name.

Units ändern

Befehl Wirkung
systemctl edit ssh legt einen Drop-in an, nur die Änderungen
systemctl edit --full ssh kopiert die ganze Unit nach /etc
systemctl revert ssh alle eigenen Änderungen verwerfen
systemctl daemon-reload nach Änderungen mit vi oder nano, nicht nötig nach systemctl edit
  • Besser edit als --full: Bei --full bekommt man Änderungen aus Paket-Updates nicht mehr mit.
  • Einen Wert ersetzen statt ergänzen, z.B. ExecStart: erst leeren, dann neu setzen.
[Service]
ExecStart=
ExecStart=/usr/bin/python3 -m http.server 8080

Timer statt cron

  • Ein Timer startet eine andere Unit. Beide heißen gleich: backup.timer startet backup.service.
  • /etc/systemd/system/backup.service
[Unit]
Description=Nächtliches Backup

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup.sh
  • /etc/systemd/system/backup.timer
[Unit]
Description=Backup jede Nacht um 2 Uhr

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target
  • systemctl daemon-reload
  • systemctl enable --now backup.timer
  • systemctl list-timers backup.timer
  • Persistent=true: War der Rechner um 2 Uhr aus, läuft das Backup beim nächsten Start nach.
  • Zeitangaben testen:
  • systemd-analyze calendar "Mon *-*-* 02:00"
  Original form: Mon *-*-* 02:00
Normalized form: Mon *-*-* 02:00:00
    Next elapse: Mon 2026-10-19 02:00:00 CEST

Beispiel: Firewall vor dem Netz

  • Eine Firewall muss stehen, bevor das Netz hochkommt, sonst gibt es ein Zeitfenster ohne Schutz.
  • /etc/systemd/system/firewall.service
[Unit]
Description=Firewall-Regeln laden
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/firewall start
ExecStop=/usr/local/sbin/firewall stop

[Install]
WantedBy=multi-user.target

Stolperfallen

  • daemon-reload vergessen. systemd arbeitet dann mit der alten Version weiter. status warnt: changed on disk.
  • Kein Shell. In ExecStart= gehen keine Umleitungen und Pipes. Dafür: ExecStart=/bin/bash -c "befehl >> /tmp/log"
  • Relativer Pfad. ExecStart= braucht den vollen Pfad, sonst status=203/EXEC.
  • Kein Zugriff. Darf User= nicht in WorkingDirectory=, kommt status=200/CHDIR.
  • Beim Timer den Timer aktivieren, nicht den Service.
  • Ganze Unit nach /etc kopiert. Updates des Pakets kommen nicht mehr an. Besser systemctl edit.

Aufgaben

Grundaufgaben

  1. Lassen Sie sich mit systemctl cat die Unit von cron anzeigen. Wo liegt sie, und wovon hängt sie ab?
  2. Legen Sie simple-http.service an, starten Sie den Dienst und rufen Sie die Seite mit curl auf.
  3. Beenden Sie den Prozess mit kill -9 und beobachten Sie status. Was macht systemd?
  4. Ändern Sie mit systemctl edit den Port auf 8080. Prüfen Sie mit systemctl cat, was daraus geworden ist.
  5. Bauen Sie einen Dienst für den iperf3-Server (iperf3 -s), der als Benutzer nobody läuft.
  6. Schreiben Sie einen Timer, der jede Minute das Datum an /tmp/zeit.log anhängt.

Für Schnelle

  1. Ersetzen Sie Restart=on-failure durch on-abort. Lassen Sie den Dienst einmal mit kill -9 und einmal mit einem Fehlercode enden. Was ist der Unterschied?
  2. Bauen Sie einen Dienst, der sofort mit exit 1 endet und Restart=always hat. Was passiert nach einigen Sekunden?
  3. Schreiben Sie die Firewall-Unit und prüfen Sie mit systemd-analyze critical-chain network-pre.target, dass sie vor dem Netz startet.

Weiter