Leitfaden zu Systemd-Timern: Cron-Jobs durch zuverlässige Planung ersetzen
Verwenden Sie systemd-Timer anstelle von Cron, wenn Sie Journal-Logs, Behandlung verpasster Ausführungen, Abhängigkeiten und Ressourcenkontrollen benötigen.
Leitfaden zu Systemd-Timern: Cron-Jobs durch zuverlässige Planung ersetzen
cron ist für viele Aufgaben immer noch in Ordnung. Wenn Sie jede Nacht ein Shell-Skript ausführen müssen und bereits eine funktionierende Log-Umleitung haben, gibt es keinen Preis für die Ersetzung. Der Grund, warum viele Teams geplante Linux-Arbeiten auf systemd-Timer umstellen, ist nicht Mode. Es liegt daran, dass systemd-Timer dem Job eine echte Service-Einheit, vorhersagbare Logs, Abhängigkeitsbehandlung, Verhalten bei verpassten Ausführungen und Ressourcenlimits geben.
Das ist wichtig, wenn die Aufgabe nicht nur "Befehl ausführen" ist. Ein Backup-Job benötigt möglicherweise eine eingehängte Festplatte. Ein Cache-Wärmer benötigt möglicherweise ein nutzbares Netzwerk. Ein Berichtsexport muss möglicherweise als eingeschränktes Service-Konto ausgeführt werden und lesbare Logs für die nächste Bereitschaftsperson hinterlassen. Ein systemd-Timer ermöglicht es Ihnen, diese Anforderungen an derselben Stelle zu beschreiben, an der Sie den Rest des Service-Lebenszyklus verwalten.
Systemd-Timer verstehen
systemd-Timer sind systemd-Unit-Dateien, die steuern, wann andere systemd-Units, typischerweise service-Units, aktiviert werden. Im Gegensatz zu cron, einem eigenständigen Daemon, sind systemd-Timer ein integraler Bestandteil des systemd-Init-Systems. Diese tiefe Integration bringt mehrere bedeutende Vorteile, insbesondere in Bezug auf Zuverlässigkeit, Protokollierung und Ressourcenverwaltung.
Ein systemd-Timer arbeitet immer mit einer anderen Unit zusammen, am häufigsten einer service-Unit. Die .timer-Datei definiert wann ein Ereignis eintreten soll, und die entsprechende .service-Datei definiert welche Aktion ausgeführt werden soll, wenn dieses Ereignis ausgelöst wird. Diese klare Trennung der Zuständigkeiten macht systemd-Timer sehr modular und flexibel.
Hauptvorteile von Systemd-Timern gegenüber Cron
Obwohl cron funktional ist, adressieren systemd-Timer viele seiner Einschränkungen und bieten eine robustere und funktionsreichere Planungslösung:
- Zuverlässigkeit und Persistenz: Wenn ein Kalender-Timer
Persistent=trueverwendet und das System während einer geplanten Ausführung ausgeschaltet ist, zeichnet systemd auf, dass die Ausführung verpasst wurde, und startet den zugehörigen Service nach dem nächsten Neustart. Ein einfacher Cron holt dies normalerweise nicht ohne ein separates Tool wie anacron nach. - Integration mit
systemd: Timer profitieren von der leistungsstarken Protokollierung vonsystemd(überjournalctl), der Abhängigkeitsverwaltung und der Ressourcenkontrolle (cgroups). Dies bedeutet eine bessere Überwachung, klarere Fehlerberichte und die Möglichkeit, komplexe Startsequenzen oder Ressourcenlimits für geplante Aufgaben zu definieren. - Reproduzierbarkeit und Versionskontrolle:
systemd-Unit-Dateien sind einfache Textdateien, die leicht in Versionskontrollsystemen gespeichert werden können. Dies ermöglicht reproduzierbare Bereitstellungen und eine einfachere Nachverfolgung von Änderungen an geplanten Aufgaben auf mehreren Systemen. - Ereignisbasierte Planung: Über die einfache zeitbasierte Planung hinaus können
systemd-Timer relativ zum Systemstart (OnBootSec) oder nach der letzten Aktivierung einer Unit (OnUnitActiveSec) ausgelöst werden, was dynamischere Planungsoptionen bietet. - Flexible Zeitausdrücke:
systemdbietet eine reichhaltige Menge an Kalenderereignisausdrücken, die oft lesbarer und vielseitiger sind als die Syntax voncron, einschließlich stündlich, täglich, wöchentlich und bestimmter Daten/Zeiten. - Ressourcenverwaltung und Abhängigkeiten: Von Timern gestartete
systemd-Services erben diesystemd-Umgebung, einschließlich cgroup-Einstellungen, und können Abhängigkeiten von anderensystemd-Units deklarieren (z. B. warten, bis das Netzwerk oder eine Datenbank verfügbar ist, bevor sie ausgeführt werden). - Standardausgabe-/Fehlerbehandlung:
systemderfasst automatischstdoutundstderrvon Services, die von Timern gestartet werden, und leitet sie an das System-Journal weiter, was das Debuggen und die Überprüfung viel einfacher macht als bei der E-Mail-basierten Ausgabe voncronoder der manuellen Umleitung.
Konfiguration von Systemd-Timern
Die Konfiguration eines systemd-Timers umfasst das Erstellen von zwei Unit-Dateien: einer Service-Unit (.service) und einer Timer-Unit (.timer). Diese Dateien werden normalerweise in /etc/systemd/system/ für systemweite Timer oder ~/.config/systemd/user/ für benutzerspezifische Timer abgelegt.
1. Die Service-Unit (.service-Datei)
Die Service-Unit definiert den tatsächlichen Befehl oder das Skript, das ausgeführt werden soll. Es ist eine standardmäßige systemd-Service-Datei, die jedoch oft so ausgelegt ist, dass sie nicht interaktiv ausgeführt wird und eine bestimmte Aufgabe erfüllt.
Beispiel: /etc/systemd/system/mytask.service
[Unit]
Description=Mein geplanter Aufgaben-Service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/mytask.sh
User=myuser
Group=mygroup
# Optional: Ressourcen bei neueren systemd-Versionen begrenzen
# CPUWeight=50
# MemoryMax=1G
[Install]
WantedBy=multi-user.target
Erklärung:
[Unit]: Enthält allgemeine Informationen über die Unit.Description: Eine menschenlesbare Beschreibung.
[Service]: Definiert die servicespezifische Konfiguration.Type=oneshot: Gibt an, dass der Service einen einzelnen Befehl ausführt und dann beendet wird. Dies ist üblich für geplante Aufgaben.ExecStart: Der auszuführende Befehl oder das Skript. Geben Sie den vollständigen Pfad an.User,Group: Definiert den Benutzer und die Gruppe, unter denen der Befehl ausgeführt wird. Führen Sie Aufgaben immer mit den geringsten erforderlichen Berechtigungen aus.CPUWeight,MemoryMax: Optionale cgroup-Steuerungen. Sie sind nützlich, wenn ein geplanter Job den Rest des Hosts nicht aushungern soll.
[Install]: Definiert, wie die Unit aktiviert werden soll.WantedBy=multi-user.target: Obwohl vorhanden, ist dieser Abschnitt für timer-gesteuerte Services oft weniger kritisch, da die Timer-Unit selbst normalerweise die Aktivierung bestimmt. Er kann jedoch nützlich sein, wenn der Service auch manuell aktivierbar sein oder in anderesystemd-Ziele integriert werden soll.
2. Die Timer-Unit (.timer-Datei)
Die Timer-Unit definiert, wann die entsprechende Service-Unit aktiviert werden soll. Sie muss denselben Namen wie ihr Service-Pendant haben (z. B. mytask.timer für mytask.service).
Beispiel: /etc/systemd/system/mytask.timer
[Unit]
Description=Führt mytask.service täglich aus
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=600
AccuracySec=1min
[Install]
WantedBy=timers.target
Erklärung:
[Unit]: Allgemeine Informationen.Description: Eine Beschreibung für den Timer.
[Timer]: Definiert die timerspezifische Konfiguration.OnCalendar: Die häufigste Einstellung, die ein Kalenderereignis definiert. Sie verwendet Ausdrücke wie:daily: Jeden Tag um Mitternacht.weekly: Jeden Montag um Mitternacht.monthly: Am ersten Tag jedes Monats um Mitternacht.hourly: Jede Stunde zur vollen Minute.*-*-* 03:00:00: Jeden Tag um 3:00 Uhr.Mon..Fri 08:00..17:00: Werktags zwischen 8 und 17 Uhr.Mon *-*-* 03:00:00: Jeden Montag um 3 Uhr.
OnBootSec: Aktiviert den Service nach einer bestimmten Zeit ab Systemstart. Z. B.OnBootSec=10min.OnUnitActiveSec: Aktiviert den Service nach einer bestimmten Zeit ab der letzten Aktivierung des Services. Z. B.OnUnitActiveSec=1h, um stündlich nach Abschluss der vorherigen Ausführung zu laufen.Persistent=true: Entscheidend für die Zuverlässigkeit. Wenn das System während einer geplanten Ausführung ausgeschaltet ist, wird der Service kurz nach dem nächsten Neustart ausgelöst.RandomizedDelaySec=600: Fügt eine zufällige Verzögerung von bis zu 600 Sekunden hinzu. Dies ist nützlich, wenn viele Maschinen denselben Timer verwenden und Sie nicht möchten, dass jeder Host genau zur gleichen Sekunde eine Datenbank, API oder einen Backup-Server trifft.
Eine echte Migration von Cron zu Timer
Angenommen, Sie haben derzeit diesen Root-Cron-Eintrag:
15 2 * * * /usr/local/sbin/backup-app.sh >> /var/log/backup-app.log 2>&1
Er funktioniert auf einer ruhigen Maschine, hat aber die üblichen Schwachstellen. Wenn die Backup-Festplatte nicht eingehängt ist, kann das Skript auf halbem Weg fehlschlagen. Wenn der Server um 2:15 Uhr ausgeschaltet ist, wird die Ausführung übersprungen. Wenn das Skript einen nützlichen Fehler schreibt, muss sich jemand merken, welche benutzerdefinierte Logdatei zu überprüfen ist. Wenn das Skript zu viel Speicher verbraucht, hilft Cron nicht, es zu begrenzen.
Die systemd-Version trennt den Befehl vom Zeitplan:
# /etc/systemd/system/backup-app.service
[Unit]
Description=Anwendungsdaten sichern
RequiresMountsFor=/mnt/backups
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
WorkingDirectory=/srv/app
ExecStart=/usr/local/sbin/backup-app.sh
MemoryMax=1G
CPUWeight=40
Nice=10
# /etc/systemd/system/backup-app.timer
[Unit]
Description=Anwendungs-Backup jede Nacht ausführen
[Timer]
OnCalendar=*-*-* 02:15:00
Persistent=true
RandomizedDelaySec=15min
AccuracySec=1min
Unit=backup-app.service
[Install]
WantedBy=timers.target
Es gibt einige Details, die beachtenswert sind. RequiresMountsFor=/mnt/backups teilt systemd mit, dass der Pfad eingehängt sein muss, bevor der Service startet. After=network-online.target und Wants=network-online.target sind nur nützlich, wenn Ihr Netzwerk-Manager tatsächlich einen Wait-Online-Service bereitstellt; auf vielen Distributionen ist dieser Service standardmäßig deaktiviert. Wenn das Backup nur auf eine lokale Festplatte schreibt, lassen Sie die Netzwerkabhängigkeit weg.
Type=oneshot passt für Skripte, die ihre Arbeit erledigen und beenden. Verwenden Sie es nicht für einen Daemon, der weiterläuft. WorkingDirectory= erspart Ihnen Skripte, die versehentlich davon abhängen, aus einer Shell in einem bestimmten Verzeichnis gestartet zu werden. User=backup ist normalerweise besser, als den Job als root auszuführen und zu hoffen, dass jeder Befehl im Skript vorsichtig ist.
Nach dem Speichern der Dateien:
sudo systemctl daemon-reload
sudo systemctl enable --now backup-app.timer
systemctl list-timers backup-app.timer
Um den Job sofort zu testen, starten Sie den Service, nicht den Timer:
sudo systemctl start backup-app.service
journalctl -u backup-app.service -n 100 --no-pager
Diese eine Unterscheidung verhindert viel Verwirrung. Das Starten von backup-app.timer bewaffnet den Zeitplan. Das Starten von backup-app.service führt das eigentliche Backup aus.
Den richtigen Timer-Ausdruck wählen
OnCalendar= ist der nächste Ersatz für die Cron-Syntax, liest sich aber anders. Sie können überprüfen, was systemd von einem Ausdruck hält, bevor Sie ihn ausliefern:
systemd-analyze calendar 'Mon..Fri 03:30'
systemd-analyze calendar '*-*-01 04:00:00'
systemd-analyze calendar 'Sun *-*-* 23:00:00'
Verwenden Sie Kalender-Timer für Wanduhr-Arbeiten: nächtliche Backups, wöchentliche Berichte, monatliche Bereinigungen, Zertifikatsprüfungen und andere Aufgaben, bei denen der menschliche Kalender wichtig ist. Verwenden Sie monotone Timer für das Verhalten "Ausführung nachdem etwas passiert ist":
[Timer]
OnBootSec=10min
OnUnitActiveSec=1h
Dieses Muster startet den Service zehn Minuten nach dem Booten, dann erneut eine Stunde nach der letzten Aktivierung. Es ist gut geeignet für Polling, lokale Bereinigung und leichte Wartungsschleifen. Es ist nicht dasselbe wie "um Minute null jeder vollen Stunde". Wenn der Job zwölf Minuten dauert, wird die nächste Ausführung ab dem Aktivierungszeitpunkt gezählt, nicht von Ihrer Wanduhr-Erwartung.
Denken Sie auch an Überlappungen. Bei einer normalen Service-Unit startet systemd keine zweite Kopie derselben aktiven Unit, nur weil das nächste Timer-Ereignis eingetroffen ist. Wenn Ihr Job länger als sein Intervall laufen kann, entscheiden Sie, ob das akzeptabel ist. Manchmal ist die richtige Antwort eine Sperre im Skript, wie flock, weil es eine klare Meldung "vorherige Ausführung noch aktiv" erzeugen kann. Manchmal ist die richtige Antwort, das Intervall zu verlängern.
Betriebliche Gewohnheiten, die Zeit sparen
Die Timer-Ansicht ist Ihr erstes Dashboard:
systemctl list-timers --all
Sie zeigt die letzte Ausführung, die nächste Ausführung und die Unit, die jeder Timer aktiviert. Wenn der Timer aufgeführt ist, der Service aber nie läuft, überprüfen Sie den Kalenderausdruck und ob der Timer aktiviert ist. Wenn der Service läuft und fehlschlägt, ignorieren Sie den Timer für einen Moment und überprüfen Sie den Service:
systemctl status backup-app.service
journalctl -u backup-app.service --since today
Wenn Sie eine der Unit-Dateien bearbeiten, führen Sie aus:
sudo systemctl daemon-reload
sudo systemctl restart backup-app.timer
Das Neustarten des Timers nach Zeitplanänderungen ist eine gute Gewohnheit, da es die nächste Aktivierungszeit sofort aktualisiert. Wenn Sie nur das Skript selbst geändert haben, benötigen Sie normalerweise kein daemon-reload.
Für Benutzer-Timer verwenden Sie systemctl --user und legen Sie Units unter ~/.config/systemd/user/ ab. Sie sind nützlich für Entwickler-Workstations und benutzerspezifische Automatisierung, haben aber einen wichtigen Haken: Standardmäßig sind Benutzer-Services an die Login-Sitzung des Benutzers gebunden. Wenn Sie benötigen, dass ein Benutzer-Timer nach dem Abmelden weiterläuft, aktivieren Sie Lingering mit loginctl enable-linger username. Das ist eine bewusste administrative Entscheidung, kein magischer Fix, der im Artikel versteckt werden sollte.
Wann Cron immer noch das bessere Werkzeug ist
Verschieben Sie nicht alles blind. Cron ist für winzige benutzerlokale Aufgaben leichter zu lesen, insbesondere auf älteren Servern oder minimalen Containern, in denen systemd nicht PID 1 ist. Wenn Ihre einzige Anforderung "diesen harmlosen Befehl alle fünf Minuten ausführen" ist, könnte Cron die klarste Antwort sein.
Systemd-Timer zahlen sich aus, wenn der Job serviceähnliche Anforderungen hat: kontrollierte Identität, Logs im Journal, Ressourcenlimits, Abhängigkeiten, Nachholverhalten oder standardmäßige Bereitstellung durch Unit-Dateien. In der Praxis greife ich zu Timern, wenn die geplante Aufgabe jemanden aufwecken würde, wenn sie fehlschlägt. Die zusätzliche Unit-Datei ist es wert, wenn sie dem nächsten Operator einen direkten Weg von "Was lief?" zu "Was ist fehlgeschlagen?" zu "Was hat sich geändert?" gibt.
Eine letzte Gewohnheit, die es wert ist, während Migrationen übernommen zu werden: Behalten Sie den alten Cron-Eintrag nur so lange kommentiert in der Nähe, bis der Timer einige Male erfolgreich gelaufen ist, und entfernen Sie ihn dann. Doppelte Zeitpläne sind eine leise Quelle von Schäden. Zwei Backup-Jobs können um dieselbe Sperre konkurrieren, zwei Bereinigungsjobs können Dateien früher als erwartet löschen, und zwei Berichts-Jobs können doppelte E-Mails senden. Nachdem Sie den Timer aktiviert haben, überprüfen Sie systemctl list-timers --all, bestätigen Sie das Service-Journal und stellen Sie sicher, dass der alte Cron-Pfad nicht mehr aktiv ist.