Blog background

Das ultimative Tutorial über Systemd Timer Job Scheduling - Der Ersatz für Cron Jobs

Ein umfassendes Tutorial über alles, was es über Linux Systemd Timer zu wissen gibt, einschließlich verständlicher Beispiele und Erklärungen aller Konfigurationsoptionen

Dieser Blogpost wurde von einer Maschine aus dem Englischen übersetzt. Die Originalversion finden Sie hier: Ultimate Tutorial About Systemd Timers Job Scheduling - The Replacement for Cron Jobs

Bitte kontaktieren Sie uns , wenn etwas nicht klar beschrieben ist, nicht funktioniert, falsch erscheint oder wenn Sie Unterstützung benötigen.

Dieser Blogpost beschreibt, wie man systemd -Timer erstellt, verwaltet und vollständig versteht, die ein Ersatz für klassische Cron-Jobs sind. Systemd-Timer ermöglichen eine viel feinkörnigere Konfiguration davon, wann oder bei welchen Ereignissen Jobs, Skripte, Befehle oder systemd-Services in verschiedenen Linux-Distributionen ausgeführt werden sollen.

Dieser Artikel ist für Linux-Anfänger und fortgeschrittene Benutzer geschrieben. Er zielt darauf ab, leicht verständlich zu sein. Während die Manualpage man systemd.timer manchmal verwirrend sein kann, enthält dieses Tutorial umfangreiche praktische Beispiele und erklärt alle Aspekte und Konfigurationsoptionen von systemd-Timern sehr detailliert.

Inhaltsverzeichnis

Was sind Systemd-Timer?

Systemd-Timer sind ein Scheduling-Mechanismus in Linux-Systemen, die das systemd-Init-System verwenden, wie Debian, Ubuntu, Fedora, RedHat, CentOS, openSUSE, Arch Linux, Oracle Linux, Linux Mint und viele andere. Systemd-Timer bieten eine modernere und flexiblere Alternative zu traditionellen Cron-Jobs und bieten eine viel feinkörnigere Granularität in Bezug auf das Timing sowie Abhängigkeitsmanagement.

Vergleich von Systemd-Timern versus Cron-Jobs

Hier sind die Hauptunterschiede zwischen Systemd-Timern und Linux-Cron-Jobs.

Vorteile der Verwendung von Systemd-Timern gegenüber Cron

  • Cron ist auf Minutenpräzision beschränkt, während systemd-Timer es Ihnen ermöglichen, die Ausführung mit Sekunden anzugeben beschrieben in Starten eines Systemd-Timers alle paar Sekunden

  • Systemd-Timer sind in das systemd-Ökosystem integriert: Timer können Abhängigkeiten zu anderen systemd-Timern, Services oder anderen Units haben und können in einer bestimmten Reihenfolge ausgeführt werden

  • Systemd-Timer können gemäß der tatsächlichen (Wanduhr-)Zeit, der Zeit seit dem Systemstart oder sogar durch Ereignisse wie das Booten des Systems ausgelöst werden

  • Systemd-Timer können zu einer zufälligen Zeit innerhalb eines gegebenen Zeitintervalls gestartet werden (zum Beispiel zu einer zufälligen Minute zwischen 13 und 14 Uhr)

  • Wenn die Maschine ausgeschaltet ist (oder anderweitig nicht betriebsbereit), wird systemd automatisch verpasste Timer nachholen, sobald das System wieder online ist (konfigurierbar mit dem Persistent= Parameter )

  • Logs sind im systemd-Journal zentralisiert. Cron-Jobs pflegten standardmäßig E-Mails an den Linux-Benutzer zu generieren, der den Cronjob besitzt, wenn Ausgabe erzeugt wird

  • Mehrere Systemd-Timer können für mehrere spezifische Zeitzonen eingerichtet werden, die sich von der Standardzeitzone unterscheiden, die für das System konfiguriert ist

  • Systemd-Timer können bei sehr ungewöhnlichen Dingen ausgelöst werden, wie dem Administrator einer Maschine, der manuell die Zeit ändert

Nachteile der Verwendung von Systemd-Timern gegenüber Cron

  • Das Erstellen eines systemd-Timers erfordert WEITAUS mehr Konfigurationszeilen und Folgebefehle als das Konfigurieren eines Cronjobs. Wenn Sie regelmäßig einen einfachen BASH-Befehl ausführen möchten, reicht das Erstellen eines systemd-Timers nicht aus - systemd-Timer können nur systemd-Services starten, was bedeutet, dass Sie einen systemd-Service konfigurieren müssen, um diesen Befehl auszuführen, der dann vom systemd-Timer ausgelöst wird.

  • Einige der Konfigurationsoptionen für systemd-Timer sind etwas schwer zu verstehen, wenn man das Manual liest - sie sind viel einfacher aus diesem sehr detaillierten Blogpost zu verstehen ;)

  • Systemd-Timer werden standardmäßig nicht zur exakt konfigurierten Zeit ausgeführt, sondern innerhalb von +60 Sekunden davon - dies kann jedoch konfiguriert (oder man könnte sagen behoben) werden mit AccuracySec=

Erstellen eines Systemd-Timer-Beispiels

Der folgende Abschnitt beschreibt, wie man einen systemd-Timer unter Verwendung des häufigsten Anwendungsfalls für Jobs auf Linux erstellt: periodisches Ausführen eines einfachen Befehls oder BASH-Skripts.

Erstellen einer Systemd-Timer-Konfigurationsdatei

Dieses Beispiel zeigt, wie man einen sehr einfachen und simplen systemd-Timer erstellt. Erstellen Sie zunächst ein einfaches BASH-Skript, das eine Zeichenkette und das aktuelle Datum in /root/timer-test.log schreibt:

cat << 'EOF' > /root/test.sh
#!/bin/bash
echo "I am a test script executing at $(date)" | tee -a /root/test.log
EOF

Machen Sie das Skript ausführbar:

chmod 700 /root/test.sh

Erstellen Sie nun eine systemd-Timer-Unit für das Skript: Diese systemd-Konfigurationsdatei ist das Äquivalent zu dem, was das Ausführungsintervall eines Cronjobs früher war, zum Beispiel */10 * * * *. Der folgende systemd-Timer wird alle 10 Minuten ausgeführt.

cat << EOF > /etc/systemd/system/test.timer
[Unit]
Description=Start the Systemd service test.service every 10 minutes

[Timer]
OnCalendar=*:0/10:*
Persistent=true

[Install]
WantedBy=timers.target
EOF

Erstellen eines Systemd-Service, den der Timer ausführt

Der obige systemd-Timer wird den systemd-Service unter Verwendung desselben Namensschemas ausführen - ein Timer namens /etc/systemd/system/test.timer wird den systemd-Service /etc/systemd/system/test.service ausführen. Lassen Sie uns einen systemd-Service unter Verwendung desselben Dateinamens erstellen, aber mit einer .service-Erweiterung:

cat << EOF > /etc/systemd/system/test.service
[Unit]
Description=Execute the BASH script /root/test.sh

[Service]
Type=oneshot
ExecStart=/root/test.sh
EOF

Nach dem Bearbeiten der Datei müssen Sie den folgenden Befehl ausführen, um systemd über die Änderungen an seiner Konfiguration zu informieren. Jedes Mal, wenn Sie eine Datei unterhalb von /etc/systemd/system/ ändern, müssen Sie diesen Befehl ausführen.

systemctl daemon-reload

Aktivieren eines Systemd-Timers

Wie bei systemd üblich, müssen neu erstellte Units, wie Timer, explizit aktiviert werden. Beachten Sie, dass wenn Sie systemctl start test.timer ausführen, dies den Timer unabhängig davon ausführt, zu welcher Zeit Sie ihn konfiguriert haben!

systemctl enable test.timer
Created symlink /etc/systemd/system/timers.target.wants/test.timer → /etc/systemd/system/test.timer.

Anzeigen des Status eines Systemd-Timers

Um den Status des neuen Timers zu überprüfen:

systemctl status test.timer
● test.timer - Execute the BASH script /root/test.sh every 10 seconds
     Loaded: loaded (/etc/systemd/system/test.timer; enabled; preset: enabled)
     Active: active (waiting) since Tue 2024-04-16 16:19:51 CEST; 48s ago
    Trigger: Tue 2024-04-16 16:20:50 CEST; 9s left
   Triggers: ● test.service

Wenn wir nun in die Datei /root/test.log schauen, zeigt sie, dass das Skript alle 10 Sekunden ausgeführt wird:

cat test.log
I am a test script executing at Tue Apr 16 02:23:50 PM UTC 2024
I am a test script executing at Tue Apr 16 02:24:02 PM UTC 2024
I am a test script executing at Tue Apr 16 02:24:13 PM UTC 2024

Wenn wir ins systemd-Journal schauen (dort sind die Logs), können wir Informationen über die Ausführung des Timers sehen:

journalctl _COMM=systemd
[...] 
Apr 16 14:23:50 p1 systemd[1]: Starting test.service - MyScript Service...
Apr 16 14:23:50 p1 systemd[1]: test.service: Deactivated successfully.
Apr 16 14:23:50 p1 systemd[1]: Finished test.service - MyScript Service.
Apr 16 14:24:02 p1 systemd[1]: Starting test.service - MyScript Service...
Apr 16 14:24:02 p1 systemd[1]: test.service: Deactivated successfully.
Apr 16 14:24:02 p1 systemd[1]: Finished test.service - MyScript Service.
Apr 16 14:24:13 p1 systemd[1]: Starting test.service - MyScript Service...
Apr 16 14:24:13 p1 systemd[1]: test.service: Deactivated successfully.
Apr 16 14:24:13 p1 systemd[1]: Finished test.service - MyScript Service.
[...]

Aufmerksame Leser haben möglicherweise bemerkt, dass wir tee im test.sh-Bash-Skript verwenden, was bedeutet, dass das Skript nicht nur die Ausgabe des echo-Befehls in /root/test.log schreibt, sondern auch Ausgabe erzeugt, wenn es ausgeführt wird. Um die Ausgabe anzuzeigen, die von diesem Skript erzeugt wurde, das vom systemd test.service ausgeführt wurde, verwenden Sie diesen Befehl:

journalctl _SYSTEMD_UNIT=test.service
Apr 16 14:46:02 p1 test.sh[749373]: I am a test script executing at Tue Apr 16 14:43:31 UTC 2024
Apr 16 14:46:11 p1 test.sh[749420]: I am a test script executing at Tue Apr 16 14:43:31 UTC 2024
Apr 16 14:46:23 p1 test.sh[749467]: I am a test script executing at Tue Apr 16 14:43:31 UTC 2024

Deaktivieren eines Systemd-Timers

Um einen Systemd-Timer wieder zu deaktivieren, verwenden Sie die folgenden Befehle. Das Deaktivieren eines Systemd-Timers bedeutet, zu verhindern, dass er in Zukunft automatisch startet. Wenn Sie einen Timer deaktivieren, entfernen Sie im Wesentlichen seine Verbindung zu allen Ziel-Units, wodurch er inaktiv wird. Wenn der Timer jedoch gerade läuft, stoppt das Deaktivieren seine aktuelle Ausführung nicht.

systemctl disable test.timer
Removed "/etc/systemd/system/timers.target.wants/test.timer".

Stoppen eines Systemd-Timers

Das Stoppen eines systemd-Timers bedeutet, seine aktuelle Ausführung anzuhalten, wenn er läuft. Diese Aktion beendet sofort jede laufende Ausführung des Timers. Das Stoppen eines Timers beeinflusst jedoch nicht seinen Status als aktiviert oder deaktiviert. Selbst wenn Sie einen Timer stoppen, kann er in Zukunft automatisch starten, wenn er aktiviert ist.

systemctl stop test.timer
# dieser Befehl gibt bei erfolgreicher Ausführung keine Ausgabe aus

Systemd-Timer OnCalendar= Syntax mit Beispielen

Schauen wir uns /etc/systemd/system/test.timer und /etc/systemd/system/test.service die wir oben erstellt haben im Detail an. Um alle verfügbaren Informationen zu systemd-Timern anzuzeigen, siehe man systemd.timer.

Bei systemd-Timern ist OnCalendar wohl die relevanteste Konfigurationsoption. Sie ermöglicht eine sehr feinkörnige Spezifikation, wann ein bestimmter Timer ausgeführt werden soll. Wie bei cron ist die korrekte Angabe zunächst etwas verwirrend. Die Syntax für OnCalendar ist:

OnCalendar=DayOfWeek Year-Month-Day Hour:Minute:Second

Um einen systemd-Timer einmalig zur Epoche (ein üblicher Referenzpunkt in der Zeit in der IT) auszuführen, wäre dies Folgendes. Der folgende Timer würde donnerstags (also jeden Donnerstag) am 1. Januar 1970 um 0 Uhr, 0 Minuten und 0 Sekunden ausgeführt. Beachten Sie, dass diese Aussage exemplarisch ist und natürlich in der realen Welt keinen Sinn macht.

OnCalendar=Thu 1970-1-1 0:0:0

Welche Werte können weggelassen werden

Wie Sie sehen können, ergibt donnerstags (jeden Donnerstag) in der obigen Konstellation keinen Sinn, da wir ein spezifisches Datum angeben. Daher können wir das Thu weglassen, um genau dasselbe Ergebnis zu erhalten. Wenn Sie den DayOfWeek weglassen, bedeutet das entweder jeden Tag der Woche, oder wie im folgenden Beispiel, es bedeutet den Wochentag, der der 1. Januar ist:

OnCalendar=1970-1-1 0:0:0

Jeden Donnerstag

Systemd-Timer sind normalerweise nicht dazu gedacht, einen systemd-Service einmalig zu starten, sondern periodisch (Sie können sie natürlich so konfigurieren, dass sie nur einmal starten). Um den zugewiesenen systemd-Service eines Timers jeden Donnerstag auszuführen, können wir angeben:

OnCalendar=Thu

Nur Thu anzugeben bedeutet, dass der Timer um 00:00:00 ausgeführt wird. Wenn Sie Werte weglassen, ist das Tool systemd-analyze calendar sehr nützlich, um Ihnen eine normalisierte Form der OnCalendar-Spezifikation zu zeigen, die tatsächlich verwendet wird:

systemd-analyze calendar "Thu"
  Original form: Thu
Normalized form: Thu *-*-* 00:00:00
    Next elapse: Thu 2024-04-18 00:00:00 UTC
       From now: 1 day 6h left

Angabe aller möglichen Werte mit einem Sternchen

Wie wir es von cron gewohnt sind, wird ein Sternchen (*) verwendet, um alle möglichen Werte anzugeben. Um einen systemd-Timer seinen systemd-Service zu jeder 10. Minute jeder Stunde starten zu lassen (um 0:00:00, 0:10:00, 1:10:00, …, 2:10:00, und so weiter), wäre dies:

systemd-analyze calendar "*:0/10:*"
  Original form: *:0/10:*
Normalized form: *-*-* *:00/10:*
    Next elapse: Tue 2024-04-16 17:40:00 UTC
       From now: 8min left

Bereiche zwischen Tagen

Um einen Bereich anzugeben, können Sie zwei Punkte verwenden, im folgenden Beispiel wäre das jeden Tag von Montag bis Freitag um 20 Uhr:

systemd-analyze calendar "Mon..Fri 20:0"
  Original form: Mon..Fri 20:0
Normalized form: Mon..Fri *-*-* 20:00:00
    Next elapse: Tue 2024-04-16 20:00:00 UTC
       From now: 2h 26min left

Listen von Tagen

Sie können ein Komma verwenden, um eine Liste anzugeben, in diesem Beispiel montags, mittwochs und freitags um 5 Uhr:

systemd-analyze calendar "Mon,Wed,Fri 5:0"
  Original form: Mon,Wed,Fri 5:0
Normalized form: Mon,Wed,Fri *-*-* 05:00:00
    Next elapse: Wed 2024-04-17 05:00:00 UTC
       From now: 11h left

Angabe einer anderen Zeitzone

Wenn Sie Server an mehreren geografischen Standorten betreiben, setzen Sie die Zeitzone Ihres Servers üblicherweise auf UTC. Wenn Sie möchten, dass ein systemd-Timer zu einer bestimmten Zeit in einer anderen Zeitzone läuft, können Sie das auch angeben - in diesem Beispiel samstags und sonntags um 13 Uhr.

systemd-analyze calendar "Sat,Sun 13:0 Europe/Berlin"
  Original form: Sat,Sun 13:0 Europe/Berlin
Normalized form: Sat,Sun *-*-* 13:00:00 Europe/Berlin
    Next elapse: Sat 2024-04-20 11:00:00 UTC         
       From now: 3 days left

Um alle verfügbaren Zeitzonen aufzulisten:

timedatectl list-timezones
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
[...]

Verschiedene Arten von Systemd-Timern

Systemd bietet verschiedene Methoden oder Kategorien zum Auslösen der Ausführung ihrer zugewiesenen Systemd-Services:

  • Echtzeit
    OnCalendar=, ausgelöst durch spezifische Zeitintervalle oder Zeitpläne
  • Monoton
    Ausgelöst durch Ablauf einer bestimmten Zeitspanne seit einem Ereignis
  • Transient
    Timer, die nur für die aktuelle Sitzung aktiv bleiben
  • Änderungen der Systemzeit
    Ausgelöst durch Änderungen der Systemzeit

Echtzeit-Timer

Echtzeit-Timer verwenden die tatsächliche echte (Wanduhr-)Zeit, um die Ausführung auszulösen.

OnCalendar: Angabe spezifischer Zeitintervalle

Echtzeit-Timer werden mit OnCalendar= konfiguriert, wie oben beschrieben.

Monotone Timer

Monotone Timer werden nach einer festgelegten Dauer ab einem bestimmten Ereignis aktiviert, wie dem Systemstart oder der Unit-Aktivierung. Die unterstützten Einheiten sind (N bedeutet eine beliebige Zahl)

  • 1microsecond oder Nmicroseconds

  • 1millisecond oder Nmilliseconds

  • 1second oder Nseconds

  • 1minute oder Nminutes

  • 1hour oder Nhours

  • 1day oder Ndays

  • 1week oder Nweeks

  • 1month oder Nmonths

  • 1year oder Nyears

Für singuläre Werte wie 1year können Sie das s in yearS weglassen. Die folgenden Optionen existieren zum Konfigurieren monotoner Timer:

OnActiveSec: Seit Aktivierung des Timers

Startet einen Timer, nachdem er aktiviert wurde, wie in systemctl activate test.timer:

OnActiveSec=3hours 10minutes 17seconds

OnBootSec: Seit dem Booten der Maschine

Startet einen Timer relativ zum Booten der Maschine:

OnBootSec=2days 50seconds

OnStartupSec: Seit Systemd erstmals gestartet wurde

Startet einen Timer, nachdem systemd selbst gestartet wurde - dies ist üblicherweise fast gleich OnBootSec=.

OnStartupSec=1minute 10seconds

OnUnitActiveSec: Seit Aktivierung des zugehörigen Systemd-Service

Startet einen Timer relativ dazu, wann der Timer zuletzt aktiviert wurde:

OnUnitActiveSec=10seconds

Dies bedeutet, dass nach Ausführung von:

systemctl enable test.timer

Der Timer 10 Sekunden später ausgeführt wird.

OnUnitInactiveSec: Seit dem letzten Stopp des zugehörigen Systemd-Service

Startet einen Timer relativ dazu, wann der systemd-Service, den er starten soll, zuletzt gestoppt wurde. Wenn wir ein Skript ausführen, das 5 Sekunden läuft, dann würde das folgende Beispiel den Timer 45 Sekunden später erneut ausführen:

OnUnitInactiveSec=45seconds

Transiente Timer - ein Ersatz für den “at”-Befehl

Transiente Timer funktionieren nur, während Ihre Sitzung geöffnet ist - also während Sie mit dem aktuellen Linux-Benutzer angemeldet sind. Wenn Sie sich per SSH mit einem Server verbinden und einen transienten Timer starten und sich dann wieder abmelden, wird er nicht ausgeführt. Sie können jedoch Folgendes ausführen:

loginctl enable-linger <username>

um die Sitzung des Benutzers im Hintergrund aktiv zu lassen, nachdem Sie sich abgemeldet haben.

Um transiente Timer zu erstellen, wird der Befehl systemd-run verwendet.

Transienter Systemd-Timer 10 Minuten ab jetzt

Führen Sie den systemd-Service test.service in 10 Minuten aus:

systemd-run --on-active="10minutes" --unit="test.service"

Transienter Systemd-Timer zum direkten Ausführen eines Befehls oder Skripts ohne Verwendung eines Systemd-Service

Sie können auch einen Befehl oder ein Skript direkt ausführen. Alles nach dem auszuführenden Befehl wird als Argument für diesen Befehl oder dieses Skript betrachtet:

systemd-run --on-active="55minutes" /root/test.sh
systemd-run --on-active="55minutes" /root/test.sh --arg1 --arg2

Das Tool systemd-run akzeptiert die Argumente, die den Argumenten ähneln, die eine Systemd-Timer-Konfigurationsdatei akzeptiert, wie --on-active=, --on-startup=, --on-calendar=, --on-clock-change und ähnliche. Weitere Informationen finden Sie unter man systemd-run.

Auslösen von Systemd-Timern bei Änderungen der Systemzeit

Die folgenden Konfigurationsoptionen können verwendet werden, um systemd-Timer bei Änderungen der Uhr auszulösen. Ich bin nicht sicher, wie nützlich das ist… Aber es ist Teil der Systemd-Timer.

OnClockChange: Wenn die Systemuhr geändert wird

boolean, Standard: false

Aus man systemd.timer: “Wenn true, wird die Service-Unit ausgelöst, wenn die Systemuhr (CLOCK_REALTIME) relativ zur monotonen Uhr (CLOCK_MONOTONIC) springt”.

  • CLOCK_REALTIME
    die reguläre Uhr, die Sie höchstwahrscheinlich unten / oben auf Ihrem Bildschirm in Ihrer Dock-Leiste sehen, oder was Sie sehen, wenn Sie den Befehl date verwenden. Sie können sie manuell oder mit NTP anpassen.
  • CLOCK_MONOTONIC
    wird beim Systemstart gestartet und zählt die Zeit seitdem. Sie stoppt, wenn sich das System im Suspend-Modus befindet (Suspend to RAM oder Suspend to Disk).

Dies bedeutet, dass dieser Timer ausgelöst wird, wenn eine manuelle Aktualisierung der Systemzeit vorgenommen wird. Aus der Manualpage könnte man annehmen, dass dies auch für die regulären Updates gelten würde, die über NTP durchgeführt werden, wie durch die Verwendung von ntpdate, ntpd oder systemd-timesyncd, jedoch konnte ich dies nicht reproduzieren. Die einzige Möglichkeit, wie ich dies auslösen konnte, war durch manuelles erzwungenes Setzen der Zeit, wie im folgenden Beispiel gezeigt:

Ein Systemd-Timer, der OnClockChange=true als Trigger verwendet:

[Unit]
Description=Execute the BASH script /root/test.sh

[Timer]
OnClockChange=true

[Install]
WantedBy=timers.target

Der systemd-Service, den dieser Timer auslöst:

[Unit]
Description=MyScript Service

[Service]
Type=oneshot
ExecStart=/root/test.sh

Das BASH-Skript, das der Service ausführt:

#!/bin/bash

echo "I am a test script executing at $(date)" | tee -a /root/test.log

NTP für das System deaktivieren:

timedatectl set-ntp false

Manuelles Setzen der Zeit:

date -s '2019-10-17 12:00:00'
Do 17. Okt 12:00:00 UTC 2019

Das folgende Ergebnis zeigt, dass der Timer ausgelöst wurde, der den Dienst startete, der das BASH-Skript ausführte:

cat /root/test.log
I am a test script executing at Do 17. Okt 12:00:00 UTC 2019

NTP wieder aktivieren:

timedatectl set-ntp true

Das Zurückschalten auf die von NTP verwaltete Zeit löste ebenfalls den Systemd-Timer aus:

cat /root/test.log
I am a test script executing at Do 17. Okt 12:00:00 UTC 2019
I am a test script executing at Mi 17. Apr 21:02:13 UTC 2024

OnTimezoneChange: Wenn die Systemzeitzone geändert wird

boolean, Standard: false

Führt den Timer aus, wenn Sie eine neue Zeitzone festlegen:

OnTimezoneChange=true

Ändern Sie die Zeitzone mit dem folgenden Befehl, um den Timer auszulösen:

timedatectl set-timezone America/New_York

Konfigurieren von Systemd-Timern mit randomisierten Ausführungsintervallen

Die folgenden Konfigurationsoptionen können verwendet werden, um systemd-Timer zu zufälligen Zeiten innerhalb eines definierbaren Zeitrahmens auszuführen.

AccuracySec: Wie genau der Systemd-Timer die angegebene Ausführungszeit einhalten soll

Standard: 1min

Diese Konfigurationsoption ist sehr wichtig zu verstehen, wenn Sie einen Timer genau zu einer bestimmten Zeit ausführen möchten, bis auf die Sekunde genau.

Standardmäßig werden systemd-Timer NICHT GENAU zur angegebenen Zeit ausgeführt, sondern innerhalb einer Abweichung von AccuracySec=, die standardmäßig eine Minute beträgt!

Das bedeutet, wenn Sie einen Timer für die Ausführung um OnCalendar=21:00:00 einstellen, wird er möglicherweise nicht genau um 21:00:00 ausgeführt, sondern irgendwo zwischen 21:00:00 und 21:01:00, es sei denn, Sie haben die Einstellung AccuracySec= angepasst, die standardmäßig eine Minute beträgt.

Hier ist ein Beispiel: ein systemd-Timer, der so eingestellt ist, dass er alle 15 Sekunden ausgeführt wird:

cat /etc/systemd/system/test.timer
[Unit]
Description=Execute the BASH script /root/test.sh every 15 seconds

[Timer]
OnCalendar=*:*:0/15

[Install]
WantedBy=timers.target

Wenn wir uns die Protokolle ansehen, können wir sehen, dass das Skript nicht genau alle 15 Sekunden ausgeführt wird, sondern bei jeder Ausführung eine Abweichung von einigen zusätzlichen Sekunden aufweist:

journalctl -n 0 -f _SYSTEMD_UNIT=test.service
Apr 17 22:02:16 host test.sh[3536562]: I am a test script executing at Mi 17. Apr 22:02:16 UTC 2024
Apr 17 22:02:31 host test.sh[3536572]: I am a test script executing at Mi 17. Apr 22:02:31 UTC 2024
Apr 17 22:02:48 host test.sh[3536576]: I am a test script executing at Mi 17. Apr 22:02:48 UTC 2024
Apr 17 22:03:05 host test.sh[3536657]: I am a test script executing at Mi 17. Apr 22:03:05 UTC 2024
Apr 17 22:03:18 host test.sh[3536695]: I am a test script executing at Mi 17. Apr 22:03:18 UTC 2024

Sie können dies mit AccuracySec=1s “beheben”:

cat /etc/systemd/system/test.timer
[Unit]
Description=Execute the BASH script /root/test.sh

[Timer]
OnCalendar=*:*:0/15
AccuracySec=1

[Install]
WantedBy=timers.target

Wie Sie in den folgenden Ergebnissen sehen können, ist die Ausführungszeit jetzt vollkommen genau:

journalctl -n 0 -f _SYSTEMD_UNIT=test.service
Apr 17 22:00:00 host test.sh[3536066]: I am a test script executing at Mi 17. Apr 22:00:00 UTC 2024
Apr 17 22:00:15 host test.sh[3536136]: I am a test script executing at Mi 17. Apr 22:00:15 UTC 2024
Apr 17 22:00:30 host test.sh[3536142]: I am a test script executing at Mi 17. Apr 22:00:30 UTC 2024
Apr 17 22:00:45 host test.sh[3536168]: I am a test script executing at Mi 17. Apr 22:00:45 UTC 2024
Apr 17 22:01:00 host test.sh[3536270]: I am a test script executing at Mi 17. Apr 22:01:00 UTC 2024

Die Manualpage man systemd.timer besagt: “Um die beste Genauigkeit zu erzielen, setzen Sie diese Option auf 1us” - ich konnte keine besseren Ergebnisse erzielen, indem ich dies auf einen niedrigeren Wert als 1 Sekunde setzte. Der Grund für dieses Verhalten oder für den Standardwert von einer Minute ist laut Manualpage: “um den Stromverbrauch zu optimieren, um unnötige CPU-Aufwachvorgänge zu unterdrücken”.

Wenn Sie von Cron migrieren, wird dieses Verhalten für die meisten Benutzer höchstwahrscheinlich sehr verwirrend (und unentdeckt) sein. Da Sie höchstwahrscheinlich wiederkehrende Jobs auf einem Server ausführen (wo Sie sich nicht um den marginal winzigen zusätzlichen Stromverbrauch von systemd-Timern neben Ihrer glühend heißen Datenbank oder PHP-Anwendung kümmern), würde ich empfehlen, diese Einstellung in allen von Ihnen konfigurierten systemd-Timern zu definieren und auf 1 Sekunde zu setzen.

RandomizedDelaySec: Randomisierte Ausführungszeit

Wenn Sie Programme auf mehreren Maschinen in Ihrem Cluster ausführen müssen, aber nicht alle zur gleichen Zeit, ist diese Option für Sie. Sie wird die Ausführung des Timers einfach innerhalb des angegebenen Zeitrahmens randomisieren. Das folgende Beispiel führt den Timer jeden Tag zu einer zufälligen Zeit zwischen 00:00:00 und 00:10:00 aus.

Beachten Sie, dass das Sec in RandomizedDelaySec tatsächlich für Sekunden steht - wenn Sie RandomizedDelaySec=30 angeben, bedeutet das 30 Sekunden. Sie können auch RandomizedDelaySec=30seconds angeben, was dem einfachen Angeben von 30 entspricht, oder Sie können RandomizedDelaySec=5minutes oder Ähnliches angeben.

OnCalendar=0:0:0
RandomizedDelaySec=10minutes

Zur Verdeutlichung des 0:0:0:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "0:0:0"
  Original form: 0:0:0
Normalized form: *-*-* 00:00:00
    Next elapse: Fri 1970-01-02 00:00:00 UTC
       From now: 23h left

Beachten Sie, dass in dieser Konstellation der Wert von AccuracySec=, wie oben beschrieben , ebenfalls wichtig sein könnte. AccuracySec= ist standardmäßig eine Minute. Wenn Sie RandomizedDelaySec=5minutes für einen systemd-Timer definieren, der täglich um 00:00:00 ausgeführt werden soll, wird er zwischen 00:00:00 und 00:06:00 ausgeführt. Um dies zu verhindern, setzen Sie AccuracySec=1 (eine Sekunde). Das Handbuch (oft) empfiehlt, AccuracySec=1us (eine Mikrosekunde) zu setzen, was meiner Meinung nach keinen Sinn macht und genau das Gegenteil von dem erreicht, wofür AccuracySec= entwickelt wurde - unnötige CPU-Aufwachvorgänge zu verhindern. Informationen zur besten Ausführung von Aufgaben häufiger als einmal pro Minute finden Sie unter Starten eines Systemd-Timers alle paar Sekunden - Die bessere Alternative .

FixedRandomDelay: Randomisierte Ausführungszeit über Neustarts hinweg speichern

boolean, Standard: false

Diese boolesche Konfigurationsoption gibt an, ob die Randomisierungszeit über Neustarts hinweg festgelegt ist. Wenn Sie dies auf true setzen, wird das zufällige Intervall immer identisch sein. Das Setzen von RandomizedDelaySec=0 deaktiviert dies. Der Standardwert ist false.

Die genaue zufällige Zeit, die systemd zur Ausführung Ihres Timers wählt, kann in diesem Blogbeitrag offensichtlich nicht vorhergesagt werden. Aus man systemd.timer: “Die Verschiebung hängt von der Maschinen-ID, der Benutzerkennung und dem Timernamen ab”. Daher ist das folgende Beispiel nur zum besseren Verständnis gedacht - wir können davon ausgehen, dass das folgende Beispiel den Timer IMMER um 00:07:31 ausführen wird, auch nach einem Neustart.

OnCalendar=daily
RandomizedDelaySec=10minutes
FixedRandomDelay=true

Zusätzliche Systemd-Timer-Konfigurationsoptionen

Unit: Name des zugehörigen Systemd-Service

Standardmäßig würde ein Timer /etc/systemd/systemd/test.timer einen systemd-Service /etc/systemd/system/test.service aktivieren. Um dieses Verhalten zu überschreiben, können Sie die Unit= wie folgt angeben:

Unit=my_test

Was /etc/systemd/system/my_test.service aktivieren würde. Ich empfehle nicht, von der Standardeinstellung abzuweichen, indem Sie diese Option verwenden, da dies Ihre Konfiguration unter /etc/systemd/system/ nur verwirrender macht.

Persistent: Erneut auslösen, wenn das System während der geplanten Ausführung heruntergefahren war

boolean, Standard: false

Diese boolesche Konfigurationsoption gilt nur für mit OnCalendar= konfigurierte Timer. Wenn ein Timer während eines Systemausfalls ausgelöst werden sollte, wird er ausgeführt, sobald das System wieder online ist. Wenn auf false gesetzt, wird der Timer nicht ausgeführt, sobald die Maschine startet. Wenn er mehrere Ausführungen verpasst hat, während das System ausgeschaltet war oder anderweitig nicht betriebsbereit war, wird er nur einmal ausgeführt.

Das Setzen auf true speichert die letzte Ausführungszeit auf der Festplatte. Diese Datei wird in /var/lib/systemd/timers/stamp-test.timer für eine systemd-Timer-Datei namens /etc/systemd/system/test.timer gespeichert. Der Zeitstempel wird nicht tatsächlich in der Datei gespeichert, sondern die Änderungszeit der Datei wird einfach aktualisiert. Hier sind die Zeitstempel für eine solche Datei für einen Systemd-Timer mit Persistent=true, der so konfiguriert ist, dass er einmal pro Sekunde ausgeführt wird:

while true; do ls -la --time-style=full-iso /var/lib/systemd/timers/stamp-test.timer; sleep 1; done
-rw-r--r-- 1 root root 0 2024-04-18 00:44:36.096114000 +0000 /var/lib/systemd/timers/stamp-test.timer
-rw-r--r-- 1 root root 0 2024-04-18 00:44:37.831349000 +0000 /var/lib/systemd/timers/stamp-test.timer
-rw-r--r-- 1 root root 0 2024-04-18 00:44:38.831427000 +0000 /var/lib/systemd/timers/stamp-test.timer
-rw-r--r-- 1 root root 0 2024-04-18 00:44:39.831595000 +0000 /var/lib/systemd/timers/stamp-test.timer

Sie können systemctl clean --what=state test.timer verwenden, um diese Zeitstempeldatei zu löschen. Das Handbuch man systemd.timer empfiehlt, dass Sie diesen Befehl ausführen, bevor Sie einen Timer deinstallieren, der mit Persistent=true konfiguriert wurde. Wenn Sie einen Timer nur stoppen und deaktivieren, wird diese Datei nicht entfernt.

WakeSystem: System aus dem Suspend-Modus aufwecken

boolean, Standard: false

Diese Konfigurationsoption ist weniger für Server als für Workstations gedacht: Wenn sich Ihre Workstation im Ruhezustand befindet (Suspend to Disk oder Suspend to RAM), bewirkt das Setzen dieser Option auf true, dass Ihre Maschine aufwacht und den Timer ausführt. (Hinweis: Ich habe dies nicht getestet und gehe davon aus, dass dies etwas Debugging erfordern wird). Beachten Sie auch, dass sie nach der Ausführung des Timers nicht wieder in den Ruhezustand versetzt wird. Beachten Sie außerdem, dass dies nur funktioniert, wenn der systemd-Timer als Root-Benutzer konfiguriert ist.

Wenn false, verwendet der Timer eine monotone Uhr namens “CLOCK_MONOTONIC”, die während eines suspendierten Systems angehalten wird. CLOCK_MONOTONIC wird beim Systemstart gestartet und zählt die Zeit seitdem. Weitere Informationen finden Sie unter man clock_gettime und suchen Sie nach “CLOCK_MONOTONIC”.

Wenn true, verwendet der Timer die monotone Uhr “CLOCK_BOOTTIME”, die ähnlich wie CLOCK_MONOTONIC ist, außer dass sie weiterläuft, wenn das System suspendiert ist. Aus diesem Grund können systemd-Timer auch dann ausgelöst werden, wenn das System suspendiert ist. Weitere Informationen finden Sie unter man clock_getres und suchen Sie nach “CLOCK_BOOTTIME”.

RemainAfterElapse: Timer nach finaler Ausführung deaktivieren

boolean, Standard: true

Systemd-Timer sprechen oft von “abgelaufenen Timern” - das bedeutet, dass die Zeit bis zum Auslösen des systemd-Timers “abgelaufen” ist - was bedeutet, dass es jetzt an der Zeit ist, den systemd-Service zu aktivieren, für den der systemd-Timer konfiguriert ist.

Wenn auf false gesetzt, wird diese Konfigurationsoption einen systemd-Timer nach seiner letzten konfigurierten Ausführung deaktivieren.

Der Unterschied zwischen einem aktiven und einem inaktiven Timer ist dieser:

systemctl status test.timer | grep Active
     Active: active (waiting) since Wed 2024-04-17 20:32:59 UTC; 8s ago

systemctl stop test.timer 

systemctl status test.timer | grep Active
     Active: inactive (dead)

Im folgenden Beispiel habe ich einen OnCalendar-Wert verwendet, der den Timer nur eine endliche Anzahl von Malen auslöst. Ich habe dies mit OnCalendar=*:0/1:0 (einmal pro Minute) versucht und es hat nicht funktioniert - der Timer blieb aktiv und wurde weiter ausgeführt. Daher ist diese Option nützlich, um einen Timer zu deaktivieren, der eine begrenzte Anzahl von Malen ausgelöst wird und dann nie wieder, und nachdem alle möglichen Auslösezeiten vorbei sind, wird er den systemd-Timer automatisch deaktivieren.

Hier ist das Beispiel-systemd-Timer - es führt einen systemd-Service aus, der das BASH-Skript /root/test.sh alle 10 Sekunden von 21:30:00 bis 21:30:50 ausführt.

cat /etc/systemd/system/test.timer
[Unit]
Description=Execute the BASH script /root/test.sh

[Timer]
OnCalendar=2024-04-17 21:30:0/10
RemainAfterElapse=false

[Install]
WantedBy=timers.target

Beim ersten Anzeigen des Timer-Status um 21:29:53 - es heißt, er wird in 7 Sekunden ausgeführt:

systemctl status test.timer 

● test.timer - Execute the BASH script /root/test.sh
     Loaded: loaded (/etc/systemd/system/test.timer; enabled; preset: enabled)
     Active: active (waiting) since Wed 2024-04-17 21:29:50 UTC; 1s ago
      Until: Wed 2024-04-17 21:29:50 UTC; 1s ago
    Trigger: Wed 2024-04-17 21:30:00 UTC; 7s left
   Triggers: ● test.service

Ich habe jetzt eine Minute gewartet und der Timer wurde alle 10 Sekunden von 21:30:00 bis 21:30:50 ausgeführt. Danach hörte er auf, wie in seiner OnCalendar-Einstellung definiert, und deaktivierte sich auch um 21:30:56 selbst:

systemctl status test.timer 
○ test.timer - Execute the BASH script /root/test.sh
     Loaded: loaded (/etc/systemd/system/test.timer; enabled; preset: enabled)
     Active: inactive (dead) since Wed 2024-04-17 21:30:56 UTC; 14s ago
   Duration: 1min 6.515s
    Trigger: n/a
   Triggers: ● test.service

Apr 17 21:29:50 host systemd[1]: Started test.timer - Execute the BASH script /root/test.sh.
Apr 17 21:30:56 host systemd[1]: test.timer: Deactivated successfully.

Praktische Systemd-Timer-Beispiele

Hier sind einige praktische Beispiele für die Planung von systemd-Timern. Bitte beziehen Sie sich auf die obige Beschreibung , wie Sie die systemd-Timer-Konfigurationsdateien dafür erstellen.

Der folgende Abschnitt enthält Beispiele für die Konfigurationsoption OnCalendar=. Bitte beziehen Sie sich auf die detaillierte Beschreibung zur Verwendung von OnCalendar= für alle Informationen zu seiner Syntax.

Um die Syntax Ihrer eigenen Konfigurationen von OnCalendar= zu überprüfen, können Sie das Tool systemd-analyze calendar verwenden. Alle folgenden Beispiele verwenden den Befehl faketime '1970-01-01 00:00:00' vor dem Befehl systemd-analyze calendar, um die Lesbarkeit bei der Identifizierung der verbleibenden Zeit für die Ausführung des systemd-Timers zu verbessern.

Systemd-Timer jede Minute

Wenn Sie Systemd-Timer verwenden möchten, die präzise zu einer bestimmten Sekunde ausgeführt werden, wie z. B. um 05:45:00 Uhr, lesen Sie auch diesen Abschnitt , wie Sie die standardmäßig hinzugefügte zufällige Zeit von +60 Sekunden zu jedem Timer deaktivieren können!

Beispiel:

OnCalendar="*:0/1:0"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "*:0/1:0"
  Original form: *:0/1:0
Normalized form: *-*-* *:00/1:00
    Next elapse: Thu 1970-01-01 00:01:00 UTC
       From now: 59s left

Systemd-Timer alle 5 Minuten

Wenn Sie Systemd-Timer verwenden möchten, die präzise zu einer bestimmten Sekunde ausgeführt werden, wie z. B. um 05:45:00 Uhr, lesen Sie auch diesen Abschnitt , wie Sie die standardmäßig hinzugefügte zufällige Zeit von +60 Sekunden zu jedem Timer deaktivieren können!

Beispiel:

OnCalendar="*:0/5:0"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "*:0/5:0"
  Original form: *:0/5:0
Normalized form: *-*-* *:00/5:00
    Next elapse: Thu 1970-01-01 00:05:00 UTC
       From now: 4min 59s left

Systemd-Timer jede Stunde oder stündlich

Beispiel:

OnCalendar="hourly"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "hourly"
  Original form: hourly
Normalized form: *-*-* *:00:00
    Next elapse: Thu 1970-01-01 01:00:00 UTC
       From now: 59min left

Systemd-Timer alle 2 Stunden

Beispiel:

OnCalendar="0/2:0:0"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "0/2:0:0"
  Original form: 0/2:0:0
Normalized form: *-*-* 00/2:00:00
    Next elapse: Thu 1970-01-01 02:00:00 UTC
       From now: 1h 59min left

Systemd-Timer jeden Tag oder täglich

Beispiel:

OnCalendar="daily"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "daily"
  Original form: Mon..Sun
Normalized form: *-*-* 00:00:00
    Next elapse: Fri 1970-01-02 00:00:00 UTC
       From now: 23h left

Systemd-Timer jeden Tag zu einer bestimmten Zeit

Beispiel:

OnCalendar="Mon..Sun 14:30"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "Mon..Sun 14:30"
  Original form: Mon..Sun 14:30
Normalized form: *-*-* 14:30:00
    Next elapse: Thu 1970-01-01 14:30:00 UTC
       From now: 14h left

Systemd-Timer jede Woche oder wöchentlich

Die Angabe “weekly” führt den Timer jeden Montag um 00:00:00 aus. Beispiel:

OnCalendar="weekly"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "weekly"
  Original form: weekly
Normalized form: Mon *-*-* 00:00:00
    Next elapse: Mon 1970-01-05 00:00:00 UTC
       From now: 3 days left

Systemd-Timer jede Woche an einem bestimmten Tag und einer bestimmten Zeit

Das folgende Beispiel ist möglicherweise praktischer und führt den Timer jeden Dienstag um 8 Uhr morgens aus:

OnCalendar="Tue 8:00"

Überprüfung:

faketime '1970-01-01 00:00:00' systemd-analyze calendar "Tue 8:00"
  Original form: Tue 8:00
Normalized form: Tue *-*-* 08:00:00
    Next elapse: Tue 1970-01-06 08:00:00 UTC
       From now: 5 days left

Systemd-Timer beim Booten

Der beste Weg, einen systemd-Timer nach dem Booten der Maschine zu starten, ist die Verwendung der Konfigurationsoption OnStartupSec=, die den Timer innerhalb des angegebenen Zeitraums startet, nachdem systemd selbst zum ersten Mal gestartet wurde:

OnBootSec="10 seconds"

Systemd-Timer Service neu starten

Dieses Beispiel beschreibt, wie ein systemd-Service regelmäßig mit einem systemd-Timer neu gestartet wird.

Systemd-Timer selbst (die .timer-Dateien) sind nur für das Timing zum STARTEN von systemd-Services verantwortlich - wenn ein Timer seine konfigurierte Zeit zur Ausführung erreicht, führt er ein systemctl start some.service aus.

Sie können jedoch einfach einen OneShot-Service erstellen, der einen systemctl-Befehl ausführt, wie systemctl restart php8.4-fpm.service. Das Starten dieses speziellen Service würde dann einen anderen Service neu starten.

Um einen systemd-Service mit einem systemd-Timer neu zu starten, müssen wir sowohl eine .timer- als auch eine .service-Datei erstellen - wobei die .service-Datei einen einzigen systemctl-Befehl enthält, um den eigentlichen Service neu zu starten, den wir neu starten möchten.

Hier ist ein vollständiges Beispiel, das den systemd-Service php8.4-fpm alle zwei Stunden mit einem systemd-Timer neu startet.

Speichern Sie Folgendes in /etc/systemd/system/restart-php-fpm.timer:

[Unit]
Description=Trigger the systemd service restart-php-fpm.service every two hours

[Timer]
OnCalendar=*-*-* 0/2:00:00

[Install]
WantedBy=timers.target

Und erstellen Sie einen systemd-Service vom Typ OneShot, um tatsächlich den Befehl systemctl restart php8.4-fpm.service auszuführen. Speichern Sie den folgenden Inhalt in /etc/systemd/system/restart-php-fpm.service:

[Unit]
Description=Restart the systemd service php8.4-fpm.service

[Service]
Type=oneshot
ExecStart=/bin/systemctl restart php8.4-fpm.service

Um die Änderungen an den systemd-Konfigurationsdateien unter /etc/systemd/system/ anzuwenden:

systemctl daemon-reload

Um den Timer zu aktivieren:

systemctl enable restart-php-fpm.timer
Created symlink /etc/systemd/system/timers.target.wants/restart-php-fpm.timer → /etc/systemd/system/restart-php-fpm.timer.

Um den Status des Timers anzuzeigen:

systemctl status restart-php-fpm.timer
● restart-php-fpm.timer - Trigger the systemd service restart-php-fpm.service every two hours
     Loaded: loaded (/etc/systemd/system/restart-php-fpm.timer; enabled; preset: enabled)
     Active: active (waiting) since Tue 2024-04-16 22:00:01 UTC; 4ms ago
      Until: Tue 2024-04-16 22:00:01 UTC; 4ms ago
    Trigger: Wed 2024-04-17 00:00:00 UTC; 1h 59min left
   Triggers: ● restart-php-fpm.service

Wenn Sie den Timer testen möchten, können Sie ihn “starten” - beachten Sie, dass “enable” bedeutet, den Timer zu aktivieren, also darauf zu warten, dass seine Ausführungszeit erreicht wird, und “start” bedeutet, den Timer tatsächlich auszuführen, unabhängig davon, was Sie für seine Ausführungszeit mit OnCalendar= konfiguriert haben:

systemctl start restart-php-fpm.timer
# Dieser Befehl gibt bei erfolgreicher Ausführung keine Ausgabe aus

Weitere Informationen zum Überprüfen der journalctl-Protokolle, ob der systemd-Timer erfolgreich ausgeführt wurde, finden Sie im Debugging-Abschnitt dieses Blogposts .

Systemd-Timer alle 5 Sekunden, alle paar Sekunden oder jede Sekunde

Wie im Abschnitt unten beschrieben, wie man das besser macht , halte ich die Verwendung von systemd-Timern zur Ausführung eines systemd-Service häufiger als einmal pro Minute nicht für einen guten Ansatz. Es ist jedoch mit Systemd-Timern möglich, und hier ist eine vollständige Beschreibung, wie man dies korrekt und sekundengenau macht.

Bei der Ausführung von systemd-Timern in einem Sekundenintervall sind mehrere Dinge zu beachten. Erstens, wie im AccuracySec=-Abschnitt beschrieben, halten sich systemd-Timer nicht auf die Sekunde genau an die von Ihnen angegebene Zeit, z. B. mit OnCalendar=, es sei denn, Sie konfigurieren es explizit. Die Einstellung AccuracySec= hat einen Standardwert von einer Minute, was bedeutet, dass systemd den Timer mit einer Abweichung von plus einer Minute ausführt - wenn Sie einen Timer für 00:00:00 einstellen, wird er zwischen 00:00:00 und 00:01:00 ausgeführt.

Bis zur Ausführung eines Timers alle 15 Sekunden kann dieses Verhalten durch Setzen von AccuracySec=1second “behoben” werden. Ein systemd-Timer wie OnCalendar=*:*:0/15 wird dann zuverlässig alle 15 Sekunden auf die Sekunde genau ausgeführt. Bei der Angabe eines Ausführungsintervalls von weniger als 15 Sekunden habe ich beim Schreiben dieses Blogposts weitere Schwierigkeiten festgestellt (lesen Sie weiter für die Lösung).

Das erste Problem, auf das ich stieß, ist, dass systemd anfängt, sich zu beschweren, wenn Sie einen systemd-Service zu oft neu starten - da systemd-Timer einfach systemd-Services auslösen, gilt dies und löst die folgende Fehlermeldung aus. Konfigurieren wir OnCalendar so, dass es alle 5 Sekunden ausgeführt wird:

OnCalendar="*:*:0/5"

Resultierende Fehlermeldung:

root@host:~# journalctl -f --identifier systemd
Apr 18 00:09:46 host systemd[1]: test.service: Start request repeated too quickly.
Apr 18 00:09:46 host systemd[1]: test.service: Failed with result 'start-limit-hit'.
Apr 18 00:09:46 host systemd[1]: Failed to start test.service - MyScript Service.

Laut diesem Stackoverflow-Post ist das “Standardlimit, 5 Neustarts in einem Zeitraum von 10 Sekunden zu erlauben. Wenn ein Dienst diese Schwelle aufgrund der Restart=-Konfigurationsoption in der Dienstdefinition überschreitet, wird er keine weiteren Neustarts versuchen.” Mit dem Starten eines Dienstes alle 5 Sekunden sollten wir jedoch darunter liegen. Unabhängig davon hier ist die Lösung, wie man einen systemd-Timer einmal pro Sekunde verwendet. Aus man systemd.unit:

StartLimitIntervalSec=interval, StartLimitBurst=burst

Configure unit start rate limiting. Units which are started more than burst times within an interval time span are not permitted to start any more.
Use StartLimitIntervalSec= to configure the checking interval and StartLimitBurst= to configure how many starts per interval are allowed.

Wenn der systemd-Service, den wir einmal pro Sekunde mit dem Timer starten möchten, so konfiguriert wird:

[Unit]
Description=MyScript Service

[Service]
Type=oneshot
ExecStart=/root/test.sh
StartLimitIntervalSec=1
StartLimitBurst=10

konnte ich den systemd-Timer zuverlässig einmal pro Sekunde auslösen. Hier ist die Timer-Datei, die ich verwendet habe:

[Unit]
Description=Execute the BASH script /root/test.sh

[Timer]
OnCalendar=*:*:0/1
AccuracySec=1

[Install]
WantedBy=timers.target

Hier ist das Ergebnis:

root@host:~# journalctl -f --identifier test.sh
Apr 18 00:32:55 host test.sh[3570468]: I am a test script executing at Do 18. Apr 00:32:55 UTC 2024
Apr 18 00:32:56 host test.sh[3570472]: I am a test script executing at Do 18. Apr 00:32:56 UTC 2024
Apr 18 00:32:57 host test.sh[3570476]: I am a test script executing at Do 18. Apr 00:32:57 UTC 2024
Apr 18 00:32:58 host test.sh[3570480]: I am a test script executing at Do 18. Apr 00:32:58 UTC 2024
Apr 18 00:32:59 host test.sh[3570485]: I am a test script executing at Do 18. Apr 00:32:59 UTC 2024

Wie Sie sehen können, führt der Systemd-Timer seinen zugewiesenen Dienst nun zuverlässig einmal pro Sekunde ohne Fehler aus.

Starten eines Systemd-Timers alle paar Sekunden - Die bessere Alternative

Die Verwendung von systemd-Timern zur Ausführung eines systemd-Service alle paar Sekunden ist meiner Meinung nach kein guter Ansatz - was Sie höchstwahrscheinlich tun möchten, ist, ein Programm oder einen Daemon zu schreiben, der Ihre Aufgabe im gewünschten Sekundenintervall ausführt. Das einfachste Beispiel in einem BASH-Skript wäre:

cat << EOF > /usr/local/bin/my-daemon.sh
#!/bin/bash

while true; do
    echo "Do things"
    sleep 5
done
EOF

Machen Sie das Skript ausführbar:

chmod 700 /usr/local/bin/my-daemon.sh

Sie können dafür einen systemd-Service wie folgt einrichten:

cat << EOF > /etc/systemd/system/my-daemon.service
[Unit]
Description=My Daemon Service
After=syslog.target

[Service]
Type=simple
ExecStart=/usr/local/bin/my-daemon.sh
Restart=always
SyslogIdentifier=my-daemon

[Install]
WantedBy=multi-user.target
EOF

Laden Sie den systemd-Daemon neu, um die Änderungen zu übernehmen:

systemctl daemon-reload

Und starten Sie den Dienst:

systemctl start my-daemon.service

Dies verwendet SyslogIdentifier=my-daemon im systemd-Service, um einen eindeutigen Syslog-Identifier festzulegen, den wir nun verwenden können, um die Protokolle nur für diesen Dienst mit journalctl zu filtern:

journalctl -f --identifier my-daemon
Apr 18 00:00:00 host my-daemon[3564012]: Do things
Apr 18 00:00:05 host my-daemon[3564012]: Do things
Apr 18 00:00:10 host my-daemon[3564012]: Do things
Apr 18 00:00:15 host my-daemon[3564012]: Do things
Apr 18 00:00:20 host my-daemon[3564012]: Do things

Abhängigkeitsmanagement mit Systemd-Timern

Systemd ermöglicht es Ihnen, Abhängigkeiten zu anderen Systemd-Timern oder Systemd-Services mit den Konfigurationsoptionen Requires=, After= und Wants= zu definieren:

Requires=

  • Die Requires=-Direktive gibt andere Units an, die von der aktuellen Unit benötigt werden

  • Wenn die in Requires= angegebenen Units nicht aktiv sind, wird systemd versuchen, sie zu aktivieren, wenn die aktuelle Unit gestartet wird

  • Wenn eine der erforderlichen Units nicht aktiviert werden kann oder nicht verfügbar ist, schlägt auch der Start der aktuellen Unit fehl

  • Die Requires=-Direktive erzeugt eine starke Abhängigkeit zwischen Units und stellt sicher, dass sie zusammen gestartet und gestoppt werden

After=

  • Die After=-Direktive gibt andere Units an, die vor der aktuellen Unit gestartet werden sollen

  • Sie impliziert keine Abhängigkeitsbeziehung wie Requires=. Stattdessen gibt sie nur die Reihenfolge an, in der Units gestartet werden sollen

  • In After= aufgeführte Units können gleichzeitig mit der aktuellen Unit aktiviert werden oder auch nicht.

  • Verwenden Sie After=, um die Reihenfolge der Unit-Aktivierung zu steuern, um sicherzustellen, dass bestimmte Units vor anderen gestartet werden, ohne eine strikte Abhängigkeit zu schaffen

Wants=

  • Die Wants=-Direktive gibt andere Units an, mit denen die aktuelle Unit gestartet werden möchte, die aber nicht zwingend erforderlich sind

  • Im Gegensatz zu Requires= verhindert das Nichtstarten von in Wants= aufgeführten Units nicht den Start der aktuellen Unit

  • In Wants= aufgeführte Units werden nach Möglichkeit gestartet, aber ihr Nichtstarten beeinflusst die aktuelle Unit nicht

  • Wants= erzeugt eine schwächere Abhängigkeit im Vergleich zu Requires=, die es Units ermöglicht, unabhängig, aber wenn möglich gemeinsam zu starten

Abhängigkeiten zwischen mehreren Systemd-Timern

Hier ist ein Beispiel mit zwei systemd-Timer-Units: test1.timer und test2.timer. Wir möchten, dass test2.timer erst startet, nachdem test1.timer seinen zugewiesenen systemd-Service erfolgreich beendet hat.

Der erste Timer /etc/systemd/system/timer1.timer ist nur ein einfacherer Timer, der so konfiguriert ist, dass er täglich um 00:00:00 startet:

# timer1.timer
[Unit]
Description=Timer 1

[Timer]
OnCalendar=0:0:0
Persistent=true

[Install]
WantedBy=timers.target

Der zweite Timer ist so eingestellt, dass er um 01:00:00 startet. Er verwendet jedoch auch Requires=timer1.timer und After=timer1.timer.

# timer2.timer
[Unit]
Description=Timer 2
Requires=timer1.timer
After=timer1.timer

[Timer]
OnCalendar=1:0:0
Persistent=true

[Install]
WantedBy=timers.target

Das bedeutet, dass:

  • Wenn timer1 die Ausführung seines zugewiesenen systemd-Service um 01:15:00 erfolgreich beendet, startet timer2 um 01:15:00 und nicht um 01:00:00

  • Wenn der zugewiesene systemd-Service von timer1 während seiner Ausführung fehlschlägt, wird timer2 nicht ausgeführt

Debuggen von Systemd-Timern

Hier sind einige Informationen, die Ihnen beim Debuggen von Systemd-Timern hilfreich sein könnten.

Konfigurieren und Verwenden von Systemd-Timern mit Nicht-Root-Benutzern

Systemd-Timer-Units können nicht nur als Linux-Root-Benutzer konfiguriert werden - auch unprivilegierte Linux-Benutzer können sie verwenden. Hier ist, wie man einen beispielhaften Systemd-Timer als regulärer Linux-Benutzer erstellt.

Erstellen Sie zuerst ein Konfigurationsverzeichnis:

mkdir -p ~/.config/systemd/user/

Erstellen Sie nun eine Timer-Unit-Konfigurationsdatei:

cat << EOF > ~/.config/systemd/user/test.timer
[Unit]
Description=Start the non-root user Systemd service test.service every 10 minutes

[Timer]
OnCalendar=*:0/10:*
Persistent=true

[Install]
WantedBy=timers.target
EOF

Erstellen Sie nun eine Systemd-Service-Datei:

cat << EOF > ~/.config/systemd/user/test.service
[Unit]
Description=Execute the BASH script /root/test.sh

[Service]
Type=oneshot
ExecStart=/bin/echo teststring
EOF

Um den Timer zu aktivieren, laden Sie zuerst systemd neu, um die neue Konfiguration zu akzeptieren:

systemctl --user daemon-reload

Und aktivieren Sie dann den Timer:

systemctl --user enable test.timer

Wichtig: Benutzer-Timer laufen nur während einer aktiven Sitzung! Um Systemd-Timer nach dem Ausloggen weiterlaufen zu lassen, verwenden Sie dies:

sudo loginctl enable-linger <user>

Ausführen von Systemd-Timern als Nicht-Root-Benutzer

Da Systemd-Timer nur Systemd-Services starten, können Sie einfach den systemd-Service, den der Timer starten soll, so konfigurieren, dass er als bestimmter Benutzer und Gruppe ausgeführt wird:

[Unit]
Description=Your Service

[Service]
Type=simple
ExecStart=/path/to/your/command
User=your_username
Group=your_groupname

[Install]
WantedBy=multi-user.target

Mehr Iterationen anzeigen mit systemd-analyze calendar

Um mehr Iterationen in systemd-analyze calendar anzuzeigen, verwenden Sie --iterations=N:

systemd-analyze calendar "Sat,Sun 13:0 Europe/Berlin" --iterations=4
  Original form: Sat,Sun 13:0 Europe/Berlin
Normalized form: Sat,Sun *-*-* 13:00:00 Europe/Berlin
    Next elapse: Sat 2024-04-20 11:00:00 UTC
       From now: 3 days left
       Iter. #2: Sun 2024-04-21 11:00:00 UTC
       From now: 4 days left
       Iter. #3: Sat 2024-04-27 11:00:00 UTC
       From now: 1 week 3 days left
       Iter. #4: Sun 2024-04-28 11:00:00 UTC
       From now: 1 week 4 days left

Faking the Time for Testing Systemd Timers

Um die Lesbarkeit mit systemd-analyze calendar zu verbessern, können Sie faketime verwenden, um eine bestimmte Zeit für alle folgenden Befehle zu simulieren:

faketime 'last Friday 5 pm' systemd-analyze calendar "Sat,Sun 13:0 Europe/Berlin"
  Original form: Sat,Sun 13:0 Europe/Berlin
Normalized form: Sat,Sun *-*-* 13:00:00 Europe/Berlin
    Next elapse: Sat 2024-04-13 11:00:00 UTC
       From now: 17h left

Siehe man faketime für weitere Informationen.

Debuggen von Systemd-Timern

Hier sind einige Informationen, die Ihnen beim Debuggen Ihrer Systemd-Timer helfen könnten.

Systemd-Timer ignorieren Syntaxfehler in Konfigurationsdateien!

Beim Aktivieren eines Timers, der nicht-kritische Fehler enthält, ignoriert systemd diese stillschweigend! Zum Beispiel:

[Unit]
Description=Start the Systemd service test.service every 10 minutes

[Timer]
OnClendar=*:0/10:*
    &lt;== BEACHTEN SIE DEN FEHLENDEN BUCHSTABEN a IN Clendar
Persistent=true

[Install]
WantedBy=timers.target

Stellen Sie sicher, dass Sie Ihre Systemd-Unit-Dateien mit diesem Befehl überprüfen:

systemd-analyze verify  /etc/systemd/system/test.timer 
/etc/systemd/system/test.timer:5: Unknown key name 'OnClendar' in section 'Timer', ignoring.
test.timer: Timer unit lacks value setting. Refusing.
Unit test.timer has a bad unit file setting.

Beispiel journalctl-Logs:

journalctl -f
Apr 20 00:54:42 host systemd[1]: /etc/systemd/system/test.timer:5: Unknown key name 'OnClendar' in section 'Timer', ignoring.
Apr 20 00:54:42 host systemd[1]: test.timer: Timer unit lacks value setting. Refusing.

Alle Systemd-Timer auflisten

Um alle konfigurierten und aktiven Systemd-Timer aufzulisten:

systemctl list-timers
NEXT                        LEFT           LAST                        PASSED        UNIT                           ACTIVATES                       
Sat 2024-04-20 02:51:31 UTC 2h 2min left   Fri 2024-04-19 11:48:43 UTC 13h ago       plocate-updatedb.timer         plocate-updatedb.service
Sat 2024-04-20 06:59:18 UTC 6h left        Fri 2024-04-19 11:48:43 UTC 13h ago       apt-daily-upgrade.timer        apt-daily-upgrade.service
Sat 2024-04-20 07:33:29 UTC 6h left        Sat 2024-04-20 00:24:10 UTC 25min ago       anacron.timer                  anacron.service
[...]

Alle Systemd-Timer einschließlich inaktiver auflisten

Um auch inaktive Timer aufzulisten:

systemctl list-timers --all
NEXT        LEFT     LAST     PASSED    UNIT                           ACTIVATES                       
[...] 
-           -        -        -         apport-autoreport.timer        apport-autoreport.service
-           -        -        -         restart-php-fpm.timer          
-           -        -        -         snapd.snap-repair.timer        snapd.snap-repair.service

Bestimmte Systemd-Timer mit Wildcards auflisten

Um alle Systemd-Timer aufzulisten, die einem bestimmten Wildcard entsprechen:

systemctl list-timers apt*
NEXT                        LEFT    LAST                        PASSED       UNIT                    ACTIVATES
Sat 2024-04-20 06:59:18 UTC 6h left Fri 2024-04-19 11:48:43 UTC 13h ago      apt-daily-upgrade.timer apt-daily-upgrade.service
Sat 2024-04-20 10:20:51 UTC 9h left Fri 2024-04-19 20:32:56 UTC 4h 17min ago apt-daily.timer         apt-daily.service

2 timers listed.

Anzeigen von Systemd-Timer-Logs mit Journalctl

Im Gegensatz zu Systemd-Services, die eine SyslogIdentifier=-Einstellung bieten, haben Timer selbst keine solche Einstellung und protokollieren immer mit dem “syslog”-Identifier. Um alle Protokolle aus dem Journal anzuzeigen, die dem Syslog-Feld entsprechen:

journalctl -f _COMM=systemd