Cronjob und Python virtualenv

Cronjob-Syntax Erläuterung

Cron ist auf Unix-Betriebssystemen verfügbar und wird zur Automatisierung von Aufgaben verwendet. Anhand der Konfiguration startet cron Skripte oder Binaries in regelmäßigen Abständen bzw. zu fest geplanten Zeitpunkten.
Beliebte Aufgaben sind z.B. automatisierte Backups (von Dateien, aber auch Datenbanken), Wartungstasks, Aufräumen von Logfiles, etc.
Letztlich kann damit jede auf dem System verfügbare Anwendung/App oder Skript zu einem geplanten Zeitpunkt gestartet bzw. gestoppt werden.
Cron wird von den meisten Unix-Betriebssystemen unterstützt, darunter Linux und macOS.

In diesem Beitrag möchte ich kurz erläutern, wie Python-Skripte in einem eigenen Python-virtualenv (venv) mittels Cronjob automatisiert ausgeführt werden können.

Starten wir mit einer kurzen Einführung in die Syntax von cron, den sog. Cron Ausdrücken (Englisch: Cron Expressions)

Cron Ausdrücke

┌───────────── Minute (0....59)
│ ┌───────────── Stunde (0....23)
│ │ ┌───────────── Tag (1....31 im Monat)
│ │ │ ┌───────────── Monat (1....12)
│ │ │ │ ┌───────────── Wochentag (0....6; 0: Sonntag, 6: Samstag)
│ │ │ │ │
* * * * * /path/to/script.sh
* * * * * /path/to/binary

Wie wir im Beispiel sehen, können wir minütlich Skripte oder Anwendungen starten. Diese Cron-Konfiguration würde jede Minute das Skript /path/to/script.sh und das Binary /path/to/binary ausführen.

20 8 10 * * /path/to/script.sh

In dieser Beispielkonfiguration würde cron das Skript /path/to/script.sh jeden Monat am 10. Tag ausführen, um 8:20 Uhr.

Kurzer Überblick: Konfiguration von cron via Terminal

In Abhängigkeit des Betriebssystems ist (oft) vi der Standardeditor für das Editieren von Cronjobs. Wer zum Editieren lieber nano verwenden möchte, kann das ändern, indem die Variable EDITOR auf nano gesetzt wird. Die Änderung ist temporär gültig, für die aktuelle Terminal-Session:

# cronjobs mit nano editieren anstatt mit vi - optional
# zuerst die Variable EDITOR setzen (persistent für die aktuelle Session)
export EDITOR=nano

Um nun aufzulisten, welche aktuellen Cronjobs aktiv sind, öffnen wir das Terminal und starten mit crontab -l

Die Auflistung bezieht sich immer auf den aktuell angemeldeten Benutzer. Jeder Benutzer hat am System seinen eigenen Crontab und kann diesen unabhängig verwalten.

crontab -l zeigt den Inhalt des crontab für den aktuell angemeldeten Benutzer
crontab -l zeigt den Inhalt des crontab für den aktuell angemeldeten Benutzer

Mit crontab -e können wir die Cronjobs für den aktuell angemeldeten Benutzer bearbeiten.
In dem folgenden Beispiel führt cron jede Minute das Skript main_app.py (mit dem systemweiten Python3 Interpreter) aus, das im Ordner /opt/my_python_app liegt. Die Terminal-Ausgaben des Skripts auf stdout werden in die Datei /opt/my_python_app/log.txt umgeleitet.

Beispiel für ein Python Skript das minütlich über einen cronjob ausgeführt wird. crontab -e öffnet den Editor dafür.
Beispiel für ein Python Skript das minütlich über einen cronjob ausgeführt wird. crontab -e öffnet den Editor dafür.

Kommen wir aber nun zum eigentlichen Punkt, der Ausführung eines Python-Skriptes in einer Python virtuellen Umgebung – als cronjob.

Cron-Konfiguration für Python-Skript in virtualenv

Um das Szenario zu veranschaulichen nehmen wir folgende Ausgangssituation an:

  • unser Python-Skript befindet sich im Ordner /opt/my_python_app und hat den Dateinamen main_app.py
  • unser Python virtualenv befindet sich im Ordner /opt/venv_my_python_app und wurde mit dem Befehl python3 -m venv venv_my_python_app erstellt

Um die virtuelle Umgebung im Terminal zu aktivieren würden wir folgenden Befehl verwenden: source ./venv_my_python_app/bin/activate

Wenn wir jetzt aber diese virtuelle Umgebung in einem cronjob nutzen wollen, um /opt/my_python_app/main_app.py minütlich auszuführen, dann sieht der Befehl für Cron folgendermaßen aus:

* * * * * cd /opt/my_python_app && /opt/venv_my_python_app/bin/python /opt/my_python_app/main_app.py

Mit cd wechseln wir in unser Skriptverzeichnis, in dem sich main_app.py befindet. Als Python Interpreter nehmen wir jetzt nicht den systemweiten python3, sondern nehmen den Interpreter der sich in unserer virtuellen Python Umgebung befindet. Dieser ruft dann unser Skript minütlich auf.

Selbstverständlich ist es in diesem Fall auch möglich die Ausgaben des Skripts in eine Datei (z.B. Logdatei) umzuleiten:

* * * * * cd /opt/my_python_app && /opt/venv_my_python_app/bin/python /opt/my_python_app/main_app.py > my_app_log.txt

Der o.g. Wechsel mit cd in unser Skriptverzeichnis ist nicht immer obligatorisch. Es kommt z.B. darauf an, ob aus main_app.py mit relativen oder absoluten Pfaden auf Ressourcen zugegriffen wird.