Die Empfehlung für den Einsatz von Docker-Containern ist klar: Jeder Container sollte eine einzige Aufgabe erfüllen. Dennoch kann es für die Bereitstellung von benutzerfreundlichen Docker-Images sinnvoll sein, Cronjobs vorkonfiguriert und innerhalb des Containers ausführen zu lassen.

Anfangs schien mir dies eine unkomplizierte Aufgabe, da Crontab unter Unix-basierten Images leicht einzurichten und zu konfigurieren ist.

Doch sobald ein Systemadministrator den Wunsch äußert, das Image im rootless-Modus zu betreiben, wird schnell klar, dass die klassische Crontab-Anwendung ohne aufwändige Konfiguration zwingend einen root-User erfordert.
Zudem erweist sich Crontab als wenig flexibel im Kontext moderner Container-Anforderungen.

Da ich zudem eine Lösung benötigte, die es ermöglicht, automatisierte Aufgaben zur Laufzeit dynamisch anzupassen, wollte ich mich nicht mit der Verwaltung von SIGUSR1-Signalen innerhalb von Containern beschäftigen. Dies wäre kaum mit dem Cloud-Native-Gedanken vereinbar gewesen.

Fündig wurde ich mit yacron, entwickelt von Gustavo J. A. M. Carneiro.
Dieses Tool ist schlicht, aber leistungsfähig und konnte meine wichtigsten Anforderungen abdecken:

  • Einfach ausführbare Datei, welche im Image hinterlegt werden kann
  • Cronjob-Zeitpläne werden bei Änderungen automatisch aktualisiert
  • Läuft unter jedem unix user, unabhängig von root
  • Nutzt YAML für die Konfiguration

Ein Beispiel für die Konfiguration von yacron in einem Docker-Image, das für das Hosting von WordPress verwendet wird, sieht wie folgt aus.
Hierbei wird der WordPress-Cron direkt über die CLI ausgeführt:

defaults:
    environment:
      - key: PATH
        value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    shell: /bin/sh
    utc: false

jobs:
  - name: WP-Cron
    command: cd /var/www/html/; wp cron event run --due-now
    schedule:
      minute: "*/10"
      dayOfMonth: "*"
      month: "*"
      year: "*"
      dayOfWeek: "*"
    captureStdout: trueCode-Sprache: YAML (yaml)

Als Prozessmanager im Image nutze ich Supervisor und folgende Konfiguration von yacron (und php-fpm, nginx):

[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/run/supervisord.pid

[program:php-fpm]
command=php-fpm -F
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0

[program:nginx]
command=nginx -g 'daemon off;'
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0

[program:yacron]
# We wait 30 seconds after check to allow 
command=/scripts/yacron -c /var/www/crons/cronjobs.yaml
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=true
startretries=30Code-Sprache: PHP (php)

Die binary von yacron lege ich einfach bereits beim Bauen in das Image:

https://github.com/swarnat/wordpress-docker-composer/tree/master/scripts

Die Dokumentation von yacron gibt noch viele weitere Beispiele, wie umfangreich dieses Tool genutzt werden kann.

Evtl. hilft das ganze auch anderen Architekten von Docker Images. Viel Spaß damit!