Erstellen und Verwalten benutzerdefinierter Systemd-Unit-Dateien mit Effektivität
Moderne Linux-Distributionen verwenden überwiegend systemd als ihr Initialisierungssystem und Diensteverwalter. Das Verständnis von systemd ist für jeden Linux-Systemadministrator oder Entwickler, der Anwendungen zuverlässig bereitstellen und verwalten muss, von entscheidender Bedeutung. Obwohl viele Anwendungen mit vorinstallierten systemd-Unit-Dateien geliefert werden, ermöglicht Ihnen die Fähigkeit, benutzerdefinierte Unit-Dateien zu schreiben, die Start-, Stopp- und allgemeine Lebenszyklusverwaltung Ihrer eigenen Anwendungen, Skripte oder beliebiger benutzerdefinierter Prozesse zu standardisieren.
Dieser Artikel führt Sie durch den Prozess der Erstellung, Konfiguration und Verwaltung benutzerdefinierter systemd .service-Unit-Dateien. Wir werden die wesentlichen Direktiven untersuchen, die definieren, wie Ihre Anwendung ausgeführt wird, Abhängigkeiten festlegen und einen robusten Betrieb gewährleisten. Am Ende werden Sie in der Lage sein, Ihre benutzerdefinierten Dienste nahtlos in das Linux-Betriebssystem zu integrieren und sicherzustellen, dass sie beim Booten automatisch starten, bei einem Fehler neu starten und einfach mit systemctl verwaltet werden können.
Die Beherrschung benutzerdefinierter systemd-Unit-Dateien ermöglicht eine granulare Kontrolle über Ihre Dienste, verbessert die Systemstabilität und vereinfacht administrative Aufgaben. Tauchen wir ein in die Kernkomponenten und praktischen Schritte, die erforderlich sind, um Ihre Anwendungen wie ein Profi zu verwalten.
Systemd Unit-Dateien verstehen
Systemd verwaltet verschiedene Systemressourcen, bekannt als Units, die durch Konfigurationsdateien definiert werden. Diese Units umfassen Dienste (.service), Mount-Punkte (.mount), Geräte (.device), Sockets (.socket) und mehr. Für die Verwaltung von Anwendungen und Hintergrundprozessen ist der Unit-Typ .service der gebräuchlichste und relevanteste.
Systemd Unit-Dateien sind einfache Textdateien, die typischerweise in bestimmten Verzeichnissen gespeichert sind. Die primären Speicherorte, in der Reihenfolge ihrer Priorität, sind:
/etc/systemd/system/: Dies ist der empfohlene Speicherort für benutzerdefinierte Unit-Dateien und Überschreibungen, da sie Vorrang vor den Systemeinstellungen haben und Systemaktualisierungen überdauern./run/systemd/system/: Wird für zur Laufzeit generierte Unit-Dateien verwendet./usr/lib/systemd/system/: Enthält Unit-Dateien, die von installierten Paketen bereitgestellt werden. Ändern Sie Dateien in diesem Verzeichnis nicht direkt.
Indem Sie Ihre benutzerdefinierten Unit-Dateien in /etc/systemd/system/ ablegen, stellen Sie sicher, dass sie von systemd ordnungsgemäß erkannt und verwaltet werden.
Anatomie einer .service Unit-Datei
Eine systemd .service Unit-Datei ist in mehrere Abschnitte gegliedert, die jeweils durch [Abschnittsname] gekennzeichnet sind und verschiedene Direktiven (Schlüssel-Wert-Paare) enthalten. Die drei Hauptabschnitte für eine Service-Unit sind [Unit], [Service] und [Install].
Lassen Sie uns die wichtigsten Direktiven aufschlüsseln, die Sie verwenden werden:
[Unit] Abschnitt
Dieser Abschnitt enthält allgemeine Optionen zur Unit, ihrer Beschreibung und Abhängigkeiten.
Description: Eine für Menschen lesbare Zeichenfolge, die den Dienst beschreibt. Diese wird in der Ausgabe vonsystemctl statusangezeigt.
ini Description=Meine Benutzerdefinierte Python-WebanwendungDocumentation: Eine URL, die auf die Dokumentation für den Dienst verweist (optional).
ini Documentation=https://example.com/docs/my-appAfter: Gibt an, dass diese Unit nach den aufgeführten Units gestartet werden soll. Dies hilft bei der Verwaltung der Startreihenfolge. Für Webanwendungen möchten Sie möglicherweise sicherstellen, dass das Netzwerk aktiv ist.
ini After=network.targetRequires: Ähnlich wieAfter, impliziert jedoch eine stärkere Abhängigkeit. Wenn die erforderliche Unit fehlschlägt, wird diese Unit nicht gestartet oder gestoppt.
ini Requires=docker.serviceWants: Eine schwächere Form vonRequires. Wenn die gewünschte Unit fehlschlägt oder nicht gefunden wird, wird diese Unit trotzdem versuchen zu starten. Dies wird im Allgemeinen gegenüberRequiresfür nicht kritische Abhängigkeiten bevorzugt.
ini Wants=syslog.target
[Service] Abschnitt
Dieser Abschnitt definiert die Ausführungsparameter für Ihren Dienst, einschließlich wie er gestartet, gestoppt und sich verhält.
-
Type: Definiert den Prozessstarttyp. Entscheidend dafür, wie systemd Ihren Dienst überwacht.simple(Standard): Der BefehlExecStartist der Hauptprozess des Dienstes. Systemd betrachtet den Dienst als sofort gestartet, nachdemExecStartaufgerufen wurde. Es wird erwartet, dass der Prozess unbegrenzt im Vordergrund läuft.forking: Der BefehlExecStartforkt einen untergeordneten Prozess, und der Elternprozess wird beendet. Systemd betrachtet den Dienst als gestartet, sobald der Elternprozess beendet ist. Verwenden Sie dies, wenn sich Ihre Anwendung selbst in den Hintergrund versetzt (daemonisiert).oneshot: Der BefehlExecStartist ein einmaliger Prozess, der beendet wird, wenn er seine Aufgabe erledigt hat. Nützlich für Skripte, die eine Aufgabe ausführen und beendet werden (z. B. ein Backup-Skript).notify: Ähnlich wiesimple, aber der Dienst sendet eine Benachrichtigung an systemd, wenn er bereit ist. Erfordertlibsystemd-devund spezifischen Code in Ihrer Anwendung.idle: Der BefehlExecStartwird erst ausgeführt, wenn alle Jobs abgeschlossen sind, wodurch die Ausführung verzögert wird, bis das System größtenteils im Leerlauf ist.
ini Type=simple -
ExecStart: Der Befehl, der ausgeführt wird, wenn der Dienst gestartet wird. Dies ist die wichtigste Direktive in diesem Abschnitt. Verwenden Sie immer den absoluten Pfad zu Ihrer ausführbaren Datei oder Ihrem Skript.
ini ExecStart=/usr/bin/python3 /opt/my_app/app.py ExecStop: Der Befehl, der ausgeführt wird, wenn der Dienst gestoppt wird (optional). Wenn nicht angegeben, sendet systemdSIGTERMan die Prozesse.
ini ExecStop=/usr/bin/pkill -f 'my_app/app.py'ExecReload: Der Befehl, der ausgeführt wird, um die Konfiguration des Dienstes neu zu laden (optional).
ini ExecReload=/bin/kill -HUP $MAINPIDUser: Das Benutzerkonto, unter dem die Prozesse des Dienstes ausgeführt werden. Wichtig für die Sicherheit; vermeiden Sieroot, es sei denn, es ist unbedingt erforderlich.
ini User=myappuserGroup: Die Gruppe, unter der die Prozesse des Dienstes ausgeführt werden.
ini Group=myappgroupWorkingDirectory: Das Arbeitsverzeichnis für die ausgeführten Befehle.
ini WorkingDirectory=/opt/my_appRestart: Definiert, wann der Dienst automatisch neu gestartet werden soll.no(Standard): Niemals neu starten.on-success: Nur neu starten, wenn der Dienst sauber beendet wird.on-failure: Nur neu starten, wenn der Dienst mit einem Statuscode ungleich Null beendet wird oder durch ein Signal beendet wird.always: Den Dienst immer neu starten, unabhängig vom Exit-Status.
ini Restart=on-failure
RestartSec: Wie lange gewartet werden soll, bevor der Dienst neu gestartet wird (z. B.5sfür 5 Sekunden).
ini RestartSec=5sEnvironment: Legt Umgebungsvariablen für die ausgeführten Befehle fest.
ini Environment="APP_ENV=production" "DEBUG=false"EnvironmentFile: Liest Umgebungsvariablen aus einer Datei. Jede Zeile sollteKEY=VALUEsein.
ini EnvironmentFile=/etc/default/my_appLimitNOFILE: Legt die maximale Anzahl von geöffneten Dateideskriptoren fest, die dem Dienst erlaubt sind (z. B.100000). Wichtig für Anwendungen mit hoher Nebenläufigkeit.
ini LimitNOFILE=65536
[Install] Abschnitt
Dieser Abschnitt definiert, wie der Dienst beim Systemstart automatisch gestartet werden soll (aktiviert wird).
WantedBy: Gibt die Ziel-Unit an, die diesen Dienst "wünscht". Wenn die Ziel-Unit aktiviert ist, wird dieser Dienst in deren.wants-Verzeichnis symbolisch verknüpft, wodurch er effektiv mit dem Ziel gestartet wird.multi-user.target: Das Standardziel für die meisten Serverdienste, das ein System mit nicht-grafischen Multi-User-Anmeldungen anzeigt.graphical.target: Für Dienste, die eine grafische Umgebung benötigen.
ini WantedBy=multi-user.target
RequiredBy: Ähnlich wieWantedBy, aber eine stärkere Abhängigkeit. Wenn das Ziel aktiviert ist, wird auch diese Unit aktiviert, und wenn diese Unit fehlschlägt, wird auch das Ziel fehlschlagen.
Tipp: Für die meisten benutzerdefinierten Dienste, die im Hintergrund auf einem Server laufen sollen, sind
Type=simpleundWantedBy=multi-user.targetdie gängigsten und am besten geeigneten Optionen.
Schritt-für-Schritt: Erstellen und Verwalten eines benutzerdefinierten Systemd-Dienstes
Erstellen wir ein praktisches Beispiel: einen einfachen Python-HTTP-Server, der Dateien aus einem bestimmten Verzeichnis bereitstellt. Wir werden ihn als systemd-Dienst einrichten.
Schritt 1: Vorbereiten Ihrer Anwendung/Ihres Skripts
Erstellen Sie zuerst das Anwendungsskript. Für dieses Beispiel verwenden wir einen einfachen Python-HTTP-Server. Erstellen Sie ein Verzeichnis für Ihre Anwendung, z. B. /opt/my_app, und legen Sie app.py darin ab.
# /opt/my_app/app.py
import http.server
import socketserver
import os
PORT = int(os.environ.get("PORT", 8000))
DIRECTORY = os.environ.get("DIRECTORY", os.getcwd())
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs)
print(f"Serving directory {DIRECTORY} on port {PORT}")
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("Server started.")
httpd.serve_forever()
Erstellen Sie das Verzeichnis und die Datei:
sudo mkdir -p /opt/my_app
sudo nano /opt/my_app/app.py
(Fügen Sie den Python-Code ein)
Stellen Sie sicher, dass das Skript ausführbar ist (optional für den Befehl python3, aber eine gute Vorgehensweise):
sudo chmod +x /opt/my_app/app.py
Ziehen Sie aus Sicherheitsgründen in Betracht, einen dedizierten Benutzer für Ihren Dienst zu erstellen:
sudo useradd --system --no-create-home myappuser
Legen Sie die entsprechende Berechtigung für Ihr Anwendungsverzeichnis fest:
sudo chown -R myappuser:myappuser /opt/my_app
Schritt 2: Die Unit-Datei erstellen
Erstellen Sie nun die systemd-Unit-Datei für unsere Python-Anwendung. Wir werden sie my_app.service nennen.
sudo nano /etc/systemd/system/my_app.service
Fügen Sie den folgenden Inhalt ein:
# /etc/systemd/system/my_app.service
[Unit]
Description=Mein Benutzerdefinierter Python HTTP Server
Documentation=https://github.com/example/my_app
After=network.target
[Service]
Type=simple
User=myappuser
Group=myappuser
WorkingDirectory=/opt/my_app
Environment="PORT=8080" "DIRECTORY=/var/www/html"
ExecStart=/usr/bin/python3 /opt/my_app/app.py
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Hinweis: Wir haben
StandardOutput=journalundStandardError=journalfestgelegt, um die Ausgabe des Dienstes an das systemd-Journal weiterzuleiten, sodass Protokolle einfach mitjournalctlangezeigt werden können.
Schritt 3: Die Unit-Datei platzieren
Wie angewiesen, haben wir die Unit-Datei in /etc/systemd/system/ platziert. Hier sollten benutzerdefinierte Unit-Dateien abgelegt werden.
Schritt 4: Systemd-Daemon neu laden
Nach der Erstellung oder Änderung einer Unit-Datei muss systemd über die Änderungen informiert werden. Dies geschieht durch das Neuladen des systemd-Daemons:
sudo systemctl daemon-reload
Schritt 5: Den Dienst starten
Nun können Sie Ihren Dienst starten:
sudo systemctl start my_app.service
Schritt 6: Dienststatus und Protokolle überprüfen
Überprüfen Sie, ob Ihr Dienst korrekt läuft:
systemctl status my_app.service
Beispielausgabe (gekürzt):
● my_app.service - Mein Benutzerdefinierter Python HTTP Server
Loaded: loaded (/etc/systemd/system/my_app.service; disabled; vendor preset: enabled)
Active: active (running) since Tue 2023-10-26 10:30:00 UTC; 5s ago
Docs: https://github.com/example/my_app
Main PID: 12345 (python3)
Tasks: 1 (limit: 1100)
Memory: 6.5M
CPU: 45ms
CGroup: /system.slice/my_app.service
└─12345 /usr/bin/python3 /opt/my_app/app.py
Oct 26 10:30:00 yourhostname python3[12345]: Serving directory /var/www/html on port 8080
Oct 26 10:30:00 yourhostname python3[12345]: Server started.
Um die Protokolle des Dienstes anzuzeigen, verwenden Sie journalctl:
journalctl -u my_app.service -f
Dieser Befehl zeigt Protokolle für my_app.service an, und -f (follow) zeigt neue Protokolle in Echtzeit an.
Sie können den Server auch über Ihren Browser oder curl unter http://localhost:8080 testen (vorausgesetzt, /var/www/html existiert und enthält einige Dateien).
Schritt 7: Den Dienst für den automatischen Start aktivieren
Damit Ihr Dienst bei jedem Systemstart automatisch startet, müssen Sie ihn aktivieren:
sudo systemctl enable my_app.service
Dieser Befehl erstellt einen symbolischen Link von /etc/systemd/system/multi-user.target.wants/my_app.service zu /etc/systemd/system/my_app.service.
Schritt 8: Den Dienst stoppen und deaktivieren
Um einen laufenden Dienst zu stoppen:
sudo systemctl stop my_app.service
Um zu verhindern, dass ein Dienst beim Booten automatisch startet (während er manuell startbar bleibt):
sudo systemctl disable my_app.service
Wenn Sie den Dienst vollständig entfernen möchten, deaktivieren Sie ihn zuerst (disable), stoppen Sie ihn dann (stop) und löschen Sie schließlich die .service-Datei aus /etc/systemd/system/ und führen Sie sudo systemctl daemon-reload aus.
Schritt 9: Einen Dienst aktualisieren
Wenn Sie Ihr app.py-Skript oder die Unit-Datei my_app.service ändern, müssen Sie systemd aktualisieren und den Dienst neu starten:
- Bearbeiten Sie
/opt/my_app/app.pyoder/etc/systemd/system/my_app.service. - Wenn Sie die Unit-Datei geändert haben, führen Sie
sudo systemctl daemon-reloadaus. - Starten Sie den Dienst neu:
sudo systemctl restart my_app.service.
Best Practices und Fehlerbehebung
- Absolute Pfade: Verwenden Sie immer absolute Pfade für
ExecStart,WorkingDirectoryund alle anderen Dateipfade in Ihrer Unit-Datei. Relative Pfade können zu unerwartetem Verhalten führen. - Dedizierte Benutzer: Führen Sie Dienste unter nicht-privilegierten, dedizierten Benutzerkonten aus (z. B.
myappuser), um die Sicherheit zu erhöhen und potenziellen Schaden im Falle einer Kompromittierung zu begrenzen. - Klare Protokollierung: Nutzen Sie
StandardOutput=journalundStandardError=journal, um die Dienstausgabe an das systemd-Journal weiterzuleiten. Verwenden Siejournalctl -u <service_name>, um Protokolle anzuzeigen. - Abhängigkeiten: Berücksichtigen Sie sorgfältig
After,WantsundRequires, um sicherzustellen, dass Ihr Dienst in der richtigen Reihenfolge in Bezug auf seine Abhängigkeiten (z. B. Netzwerk, Datenbanken) startet. - Änderungen testen: Bevor Sie einen Dienst für den automatischen Start beim Booten aktivieren, testen Sie ihn gründlich, indem Sie ihn manuell starten und stoppen. Überprüfen Sie seinen Status und seine Protokolle.
- Ressourcenlimits: Verwenden Sie Direktiven wie
LimitNOFILE,LimitNPROC,MemoryLimitusw., um zu verhindern, dass außer Kontrolle geratene Dienste alle Systemressourcen verbrauchen. - Umgebungsvariablen: Verwenden Sie
Environment=oderEnvironmentFile=, um Konfigurationswerte festzulegen, die sich ändern oder zwischen Umgebungen variieren können, anstatt sie fest im Code der Unit-Datei oder des Skripts zu verankern. - Fehlerbehandlung in Skripten: Stellen Sie sicher, dass Ihre Anwendungsskripte Fehler sauber behandeln. Ein Exit-Code ungleich Null löst
Restart=on-failureaus.
Warnung: Vermeiden Sie es, Unit-Dateien direkt in
/usr/lib/systemd/system/zu ändern. Alle Änderungen werden wahrscheinlich durch Paketaktualisierungen überschrieben. Verwenden Sie/etc/systemd/system/für benutzerdefinierte Units oder Überschreibungen.
Fazit
Systemd Unit-Dateien sind ein leistungsstarkes und flexibles Werkzeug für die Verwaltung von Prozessen und Anwendungen auf Linux-Systemen. Indem Sie deren Struktur und Schlüssel-Direktiven verstehen, können Sie den Start, Stopp und die Überwachung Ihrer benutzerdefinierten Dienste effektiv standardisieren, was die Systemstabilität erhöht und die Verwaltung vereinfacht. Von der Definition von Startbefehlen mit ExecStart über die Verwaltung von Abhängigkeiten mit After bis hin zur Aktivierung des automatischen Starts mit WantedBy verfügen Sie nun über die Werkzeuge, um Ihre Anwendungen nahtlos in das systemd-Ökosystem zu integrieren. Diese grundlegende Fähigkeit ist von unschätzbarem Wert für die Wartung robuster und zuverlässiger Linux-Bereitstellungen.
Erkunden Sie weiterhin erweiterte systemd-Funktionen wie Timer (.timer), Socket-Aktivierung (.socket) und cgroups für eine anspruchsvollere Szenarioverwaltung von Diensten.