Der Daemon cron dient bereits seit den späten 70er-Jahren zur zeitbasierten Ausführung von Prozessen auf lokalen Rechnern oder entfernten Servern. Der Name cron steht angeblich für command run on notice. Man kann aber auch eine Verwandtschaft zum griechischen Gott Chronos oder dem Titanen Kronos als der Personifizierung der Zeit vermuten. Ken Thompson als ursprünglicher Autor sagt, cron sei ein Schreibfehler seinerseits, es sollte eigentlich chron heißen.
CronJobs
CronJobs werden in einer systemweiten oder User-spezifischen Tabelle gespeichert. Beim Plasma-Desktop lassen sich CronJobs in den Systemeinstellungen unter Starten und Beenden > Aufgabenplaner verwalten. Unter GNOME erledigt das die GUI Geplante Aufgaben, die als Paket gnome-schedule heißt.
systemd.timer
Wie nicht anders zu erwarten beherrscht auch systemd die Fähigkeit, über Timer zu vorgegebenen Zeiten automatisch Aufgaben auszuführen. Das dahinterstehende Modul heißt systemd.timer und wird über Timer-Units gesteuert. Da stellt sich mancher jetzt sicher die Frage, warum es neben dem bewährten cron einen systemd-Dienst gibt, der die gleiche Aufgabe übernimmt.
Dem wollen wir in diesem Artikel auf den Grund gehen. Dazu schauen wir zunächst, wie die beiden Varianten aufgesetzt werden und verwenden hierzu die Aufgabe, Flatpaks automatisch vom System aktualisieren zu lassen. Bei cron benötigen wir nur eine Zeile, aber die ist ein wenig fehleranfällig, wenn man das nicht täglich macht.
cron
0 6 * * * /usr/bin/flatpak update -y
Diese Zeile führt jeden Morgen um 06:00 ein Update der installierten Flatpaks aus. Im Fall von Flatpak benötigt man unter Umständen zwei CronJobs, da Flatpaks im User- und im System-Kontext installiert werden können. Die Definition des Ausführungszeitpunkts wird über die fünf Sterne vorgenommen und richtet sich nach folgendem Schema:
Crontab
Zum Erstellen und Editieren dient der Befehl crontab -e
, der eine Sitzung im bevorzugten Editor öffnet. Nicht funktionierende Einträge werden beim Speichern angemahnt. Ich finde das Schema zur Festlegung der Ausführungszeit wenig intuitiv und fehleranfällig.
systemd.timer
Unter systemd werden für die Aufgabe zwei Dateien erstellt, die mit .service und .timer enden. Erstere definiert, was zu tun ist, die Zweite legt fest, wann dies passieren soll. Die beiden Dateien sehen für unser Beispiel folgendermaßen aus:
sudo nano /etc/systemd/user/flatpak-updates.service
[Unit]
Description=Update user Flatpaks
[Service]
Type=oneshot
ExecStart=/usr/bin/flatpak --user update -y
Der System-Typ oneshot ist ähnlich wie beim Typ simple, allerdings betrachtet systemd die Einheit als beendet, nachdem der Hauptprozess beendet wurde. Weitere Informationen über Service-Typen kann man bei Red Hat nachlesen.
Timer:
[Unit]
Description=Update user Flatpaks daily at 06:00
[Timer]
OnCalendar=*-*-* 06:00:00
Persistent=true
[Install]
WantedBy=timers.target
WantedBy
Die Timer-Unit stellt sicher, dass die Service-Unit zum gewünschten Zeitpunkt ausgeführt wird. Der Parameter WantedBy=timers.target
sorgt in diesem Fall dafür, dass systemd den Timer beim Systemstart zusammen mit allen anderen Timern aktiviert.
Generell wird die Eigenschaft WantedBy verwendet, um die Abhängigkeiten zwischen verschiedenen Units zu kontrollieren. Durch die Angabe der Zieleinheiten, mit denen ein bestimmter Dienst gestartet werden soll, kann systemd sicherstellen, dass die erforderlichen Dienste in der richtigen Reihenfolge gestartet werden, um die Abhängigkeiten zwischen ihnen zu erfüllen.
Im User-Kontext speichern
Die beiden erstellten Dateien werden, mit beliebigen Namen, aber den korrekten Endungen versehen, in diesem Fall unter /etc/systemd/user
abgelegt. Neben der Direktive OnCalendar gibt es noch viele weitere Direktiven zur Definition des Ausführungszeitpunkts. Der Parameter Persistent=true sorgt dafür, dass Timer nachgeholt werden, falls der Rechner zum Ausführungszeitpunkt ausgeschaltet war.
Eine weitere absolute Zeitangabe bewirkt etwa, dass ein Timer an jedem Montag des Jahres 2023 um sechs Uhr morgens ausgeführt wird:
OnCalendar=Mon 2023-*-* 06:00
In den Beispielen haben wir absolute Zeitangaben vorgegeben. aber es können auch verschiedene Aufgaben kombiniert werden: Soll etwa ein Job 10 Minuten nach einem Neustart des Rechners und dann einmal täglich zur gleichen Zeit ausgeführt werden, nimmt man statt OnCalendar= die Direktiven:
OnBootSec=10m
OnUnitActiveSec=1d
Was in dem Zusammenhang möglich ist, vermittelt die Manpage von systemd.time. Eine gute Übersicht, wie systemd Kalenderzeit, Zeitstempel und Zeitspannen verwendet, vermittelt ein Artikel auf opensource.com. Auch das Arch-Wiki vermittelt, wie gewohnt, einen guten Überblick.
Sind die beiden Dateien erstellt und gespeichert, muss der Timer noch aktiviert werden:
sudo systemctl --user enable --now flatpak-update.timer
sudo systemctl daemon-reload
Um abschließend zu sehen, wann der Timer das nächste Mal startet, dient in unserem Beispiel der Befehl
systemd-analyze calendar 06:00
Alternativ zeigt der Befehl systemctl list-timers
alle aktiven Timer und deren nächste Ausführung.
Bewertung
Ein Vergleich systemdtimer vs. cron ist ein wenig wie systemd vs. SysVinit. Auf den ersten Blick scheint das Aufsetzen eines systemd.timers allerdings aufwändiger als das von cron zu sein. Was spricht also für den Einsatz von systemd anstelle von CronJobs?
Für mich spricht einiges für systemd:
- Die Definition von Zeitpunkten und Zeiträumen ist verständlicher und wesentlich flexibler
- Es fasst cron und anacron zusammen
- Es weckt per weckt
WakeSystem=true
Geräte aus dem Suspend-Modus - Das Journal gibt mit einem Befehl Auskunft über die Ausführung von Timer-Units
- Testing und Debugging ist wesentlich einfacher
- Mit der Option dependencies können wie bei anderen systemd-Diensten Abhängigkeiten zur Aktivierungszeit definiert werden.
- Einfach über systemctl enable/disable zu (de)aktivieren
- systemd.timer können mit verschiedenen Zeitzonen umgehen
- systemd verschickt auf Wunsch per OnFailure-Direktive eine E-Mail beim Scheitern eines Timers
Gegen systemd.timer sprechen für mich zwei Dinge:
- MAILTO bei cron ist einfacher als OnFailure bei systemd
- systemd.timer funktionieren nur unter Linux und nur bei Distributionen, die systemd verwenden.
Was verwendet ihr?
Letztlich sind systemd.timer für mich aber wesentlich flexibler und besser ins Gesamtsystem integriert. Wie haltet ihr es mit der zeitgesteuerten Ausführung von Befehlen, Scripten und anderen Aufgaben?
Bildquelle: Photo by Alex Guillaume on Unsplash