Worker & Cron-Jobs
Die meisten Anwendungen brauchen mehr als den Webserver. Irgendetwas muss die Queue abarbeiten, nachts den Newsletter verschicken oder einen WebSocket offen halten. Dafür gibt es in Envoryx Worker und Cron-Jobs.


Ein Worker ist ein Prozess, der dauerhaft neben deiner Anwendung läuft - ein Queue-Worker, ein Scheduler, ein WebSocket-Server. Du legst ihn im Bereich Worker & Cron des Projekts an und wählst aus einer Liste von Vorlagen, was er ausführen soll:
| Runtime | Vorlagen |
|---|---|
| PHP | Laravel Scheduler (schedule:work), Queue-Worker (queue:work, Queue-Namen), Queue-Listener (queue:listen), Horizon, Reverb; Symfony Messenger-Consumer (Transports) und Symfony Scheduler; Shopware Message Queue und Geplante Aufgaben; Craft-Queue; PHP-Script; Composer-Script |
| Node.js | npm-Script (npm run <name>), Node.js-Script (node <datei>) |
| Python | Python-Script, Python-Modul (python -m), Django manage.py-Befehl, Celery-Worker, Celery Beat |
| Go | Go-Programm - baut ein Paket des Moduls und startet das Binary |
| Ruby | Solid Queue (bin/jobs start), GoodJob (optional Queues), Sidekiq (optional Queues; Redis dazunehmen), Rake-Task, Ruby-Skript |
| Java | Jar-Datei (java -jar <datei>), Build-Tool-Goal (mvn <goal> oder gradle <task>) |
| .NET | Projekt (veröffentlicht das Projekt und startet es), DLL (dotnet <datei>) |
Jeder Worker bekommt einen eigenen Container (envoryx-<projekt>-worker-<name>) aus dem Image
seiner Runtime. Er läuft als Projektbesitzer mit der Umgebung des Projekts, startet nach einem
Absturz von selbst neu und startet, stoppt und startet neu zusammen mit dem Projekt. Er läuft unter
Dockers Init und bekommt beim Stoppen ein SIGTERM, ein Stopp oder Neustart wirkt also sofort, statt
30 Sekunden auf das Kill zu warten. Ein Job, an dem er in dem Moment arbeitet, wird dabei allerdings
abgebrochen; für eine saubere Übergabe nimmst du den Befehl deines Frameworks (siehe unten). Du siehst
nur Vorlagen für Runtimes, die das Projekt auch hat. Entfernst du eine Runtime, pausieren ihre
Worker und kommen zurück, sobald die Runtime wieder da ist. Das Formular startet mit der Vorlage,
die zum Projekt passt: Shopwares Message Queue in einem Shopware-Shop, Crafts Queue in Craft,
Laravels Queue-Worker neben einer artisan, der Messenger-Consumer in einer Symfony-App mit
Messenger.
Ein paar Details ersparen dir Rätselraten. Ruby-Worker führen vorher bundle install aus, wenn
das Bundle unvollständig ist, und bekommen dieselbe Umgebung wie der Server (RAILS_ENV und so
weiter). Geprüft wird das allerdings beim Start: Ein laufender Worker behält die Gems, mit denen er
gestartet ist. Nach bundle add oder einer anderen Änderung am Gemfile startest du den Worker
mit Neustart neu, damit er das neue Bundle lädt. Ein Solid Queue-Worker, dem seine Tabellen fehlen, wartet mit einem Hinweis im Log,
statt in einer Schleife neu zu starten; führ rails db:prepare aus, dann legt er los (das
Rails-Template gibt Solid Queue eine eigene Datenbank <database>_queue, siehe die
Ruby-Seite). Die wiederkehrenden Aufgaben aus
config/recurring.yml laufen im selben Worker mit, dafür brauchst du also weder einen zweiten
Worker noch einen Cron-Job.
Der Symfony-Messenger-Consumer führt für seine Transports zuerst messenger:setup-transports
aus. Das legt die Tabelle messenger_messages an, die das doctrine://default?auto_setup=0 des
Recipes der Anwendung überlässt, und solange die Datenbank noch nicht erreichbar ist, wartet der
Worker mit einem Hinweis, statt in einer Schleife neu zu starten. Laravels queue:work beendet sich absichtlich nach einer Stunde, damit der automatische
Neustart deine Codeänderungen mitnimmt; sollen Änderungen sofort greifen, nimm queue:listen.
Auf die Stunde musst du aber auch nicht warten: php artisan queue:restart im PHP-Terminal sagt
dem Worker, er soll seinen aktuellen Job fertig machen und sich beenden, und Envoryx startet ihn mit
dem neuen Code wieder. Der Symfony-Messenger-Consumer hört ebenfalls nach einer Stunde auf, bei
ihm erledigt php bin/console messenger:stop-workers dasselbe.
Die Vorlage npm-Script ruft immer npm run auf, auch in einem pnpm- oder Yarn-Projekt. Das ist in
Ordnung, npm führt das Script aus der package.json genauso aus. Die Vorlage Composer-Script
startet composer run-script --timeout=0, damit die übliche Grenze von 300 Sekunden pro Script
deinen Worker nicht nach fünf Minuten beendet.
Node.js-Worker und die Cron-Jobs im Node.js-Container übernehmen ihr NODE_ENV vom Dev-Server:
production, solange er einen Produktions-Build ausliefert, sonst development, auch in einem
Projekt ohne Dev-Server. In der Entwicklung gewinnt ein NODE_ENV, das du im Bereich Umgebung
setzt, genau wie beim Dev-Server; im Produktionsmodus ist es in jedem Fall production. Ein Projekt, das seinen Produktions-Build laufen lässt, arbeitet seine Queue
also nicht im Entwicklungsmodus ab. Wie der Dev-Server wartet ein Node.js-Worker auf node_modules,
statt abzustürzen, überlässt die Installation aber dem Dev-Server oder dir. Python-Worker warten,
solange die .venv für eine andere Python-Version gebaut ist, siehe die
Python-Seite.
Die Ausgabe jedes Workers steht im Bereich Logs, und ein Projekt kann bis zu 10 davon haben.
Neustart neben einem Worker startet nur diesen einen Container neu, der Rest des Projekts läuft weiter. Das brauchst du immer dann, wenn ein Worker neuen Code laden soll (ein Queue-Worker lädt deine Anwendung nur einmal, beim Start), und sobald du behoben hast, woran er abgestürzt ist. Ein Worker, der kurz nach jedem Start wieder endet, steht auf restarting statt running, das Projekt auf teilweise laufend; warum, steht in seinem Log. Docker wartet vor jedem neuen Versuch etwas länger, der Neustart-Knopf überspringt diese Wartezeit. Neu starten dürfen auch Entwickler, Worker anlegen und ändern bleibt den Admins des Projekts vorbehalten.
Ein neues Laravel-Projekt aus der Vorlage führt artisan migrate aus, sobald es läuft. So sind die
Tabellen für Jobs, Cache und Sitzungen da, bevor der Queue-Worker startet. Bei einer geklonten oder
importierten App startest du die Migration selbst über Aktionen.
Cron-Jobs
Abschnitt betitelt „Cron-Jobs“Weiter unten im selben Bereich Worker & Cron stehen die Cron-Jobs. Sie führen beliebige
Shell-Befehle nach Zeitplan im PHP-, Python-, Go-, Ruby-, Java-, .NET- oder Node.js-Container des Projekts aus.
Das passiert als Projektbesitzer im Projektverzeichnis, über
sh -c - Pipes und && gehen also - und mit der Umgebung des Projekts.
Du wählst einen Zeitplan (alle paar Minuten, stündlich, täglich, wöchentlich, monatlich) oder
gibst einen Cron-Ausdruck ein; das Formular zeigt dir die nächsten Läufe, eine gute Kontrolle,
ob du dich nicht vertippt hast. Die Zeiten gelten in der Zeitzone von Envoryx - stimmen sie
nicht, setz TZ am Envoryx-Container. Ein neuer oder geänderter Job läuft zum ersten Mal zu der
nächsten Zeit, die das Formular beim Speichern gezeigt hat; speicherst du einen Job für jede Minute
um 10:15:40, läuft er um 10:16.
Über dem Formular stehen ein paar Ausgangspunkte für die Runtimes des Projekts, darunter Laravel-Scheduler, TYPO3-Scheduler und Drupal-Cron. Ein Klick trägt den Befehl und einen passenden Zeitplan ein.
Jobs laufen nur, solange das Projekt läuft, und ein Job, der noch läuft, wird nicht ein zweites Mal gestartet - langsame Läufe stapeln sich also nicht. Ein Timeout (standardmäßig 10 Minuten, bis zu 24 Stunden) beendet Befehle, die hängen. Jetzt ausführen startet einen Job sofort - praktisch, solange du ihn einrichtest. Die letzten 20 Läufe behalten ihre Ausgabe, und schlägt ein Lauf fehl, bekommst du eine Benachrichtigung.
Shops und CMS
Abschnitt betitelt „Shops und CMS“Shopware
Abschnitt betitelt „Shopware“Ein Shopware-Shop will zwei Worker, und für beide gibt es eine Vorlage. Message Queue startet
messenger:consume async low_priority und richtet vorher beide Transports ein. Geplante Aufgaben
startet scheduled-task:run, das Shopwares Aufgaben in die Queue stellt, sobald sie fällig sind;
ohne den ersten Worker bringt er also nichts. Beide starten jede Stunde neu und haben ein
Speicherlimit von 512 MB, damit ein langsames Leck nicht immer weiter wächst. Die Vorlage Symfony
Scheduler lässt du links liegen: Sie verarbeitet scheduler_default, und das benutzt Shopware
nicht. Wie du den Admin-Worker abschaltest, der sonst dieselbe Arbeit erledigt, solange der Admin
offen ist, steht auf der PHP-Seite.
workers: - {name: queue, preset: "shopware:queue"} - {name: scheduled-tasks, preset: "shopware:scheduled-tasks"}TYPO3, Drupal und Craft
Abschnitt betitelt „TYPO3, Drupal und Craft“TYPO3 und Drupal brauchen nur einen Cron-Job, und das Cron-Formular hat für beide einen
Ausgangspunkt. Der Scheduler von TYPO3 läuft mit vendor/bin/typo3 scheduler:run jede Minute. Er
ist eine eigene Extension, typo3/cms-scheduler: Das TYPO3-Template installiert sie, ein mitgebrachtes
TYPO3 hat sie aber vielleicht nicht, und dann scheitert der Job mit There are no commands defined in
the “scheduler” namespace. Führ einmal composer require typo3/cms-scheduler und vendor/bin/typo3 extension:setup im PHP-Terminal aus.
Drupal startet seinen Cron mit vendor/bin/drush cron alle 15 Minuten, das reicht völlig; der Lauf
ist stumm, eine leere Ausgabe ist also normal. Craft arbeitet seine Queue ab, solange jemand das
Control Panel offen hat. Die Vorlage Craft-Queue (php craft queue/listen) erledigt das auch
ohne, damit Bild-Transformationen nach einem Upload fertig werden, auch wenn gerade niemand
hinschaut.
In der envoryx.yml
Abschnitt betitelt „In der envoryx.yml“Worker und Cron-Jobs gehören zum Projekt-Manifest und wandern damit mit dem Repository:
workers: - {name: queue, preset: "laravel:queue", arg: default}cron: - {name: prune, schedule: "0 3 * * *", command: php artisan model:prune, timeout: 5m}Von der Kommandozeile
Abschnitt betitelt „Von der Kommandozeile“Die CLI macht dasselbe aus einer Shell oder einer Pipeline heraus:
envoryx worker add shop queue --preset laravel:queue --arg defaultenvoryx cron add shop prune --schedule "0 3 * * *" --max-time 5m -- php artisan model:pruneenvoryx cron run shop prune --wait # führt ihn sofort aus und endet mit seinem Exit-Code