Kai Ole Hartwig
7 Min. Lesezeit
Niedrig
Von

TYPO3 auf Kubernetes unter IT-Grundschutz, Teil 8: TYPO3 zustandslos machen

Kubernetes geht davon aus, dass jeder Pod jederzeit durch einen anderen ersetzt werden kann. TYPO3 ist so nicht gebaut. Es legt eine erstaunliche Menge an Zustand im eigenen Dateisystem ab, und das merkt man erst, wenn der zweite Pod dazukommt. Dieser Teil geht den Zustand Stück für Stück durch und zeigt, wohin er im Cluster gehört.

Teil 8 von 14 der Serie „TYPO3 auf Kubernetes unter IT-Grundschutz“. Die Übersicht aller Teile steht in Teil 1.

01 — Welchen Zustand TYPO3 mitbringt

Eine klassische TYPO3-Installation hält an mehreren Stellen Zustand, den ein zweiter Pod nicht sieht:

Solange nur ein Pod läuft, fällt das kaum auf. Mit dem zweiten Pod beginnen die seltsamen Fehler. Nutzer werden abgemeldet, Bilder fehlen, Caches widersprechen sich. Die Regel für den Cluster lautet deshalb: Der Pod enthält nur Code und Konfiguration. Alles andere lebt außerhalb.

02 — Sessions, Locks und Caches

Sessions und Locks gehören in einen gemeinsamen Key-Value-Store wie Valkey oder Redis. Für Sessions bringt der TYPO3-Core ein Redis-Backend mit. Für Locks braucht es eine Strategie, die über Pods hinweg wirkt. Die dateibasierten Strategien des Core tun das nur mit einem gemeinsamen Dateisystem. Eine Strategie für Valkey bringt der Core nicht mit, er lässt sich aber erweitern. Eine eigene Klasse implementiert das LockingStrategyInterface und wird in ext_localconf.php über LockFactory::addLockingStrategy() angemeldet. Liefert sie eine höhere Priorität als die Dateistrategie mit 75, nutzt der Core sie auch für das Seiten-Rendering. Technisch reicht ein SET … NX PX mit Ablaufzeit. So hinterlässt ein abgestürzter Pod keinen Lock, der alle anderen blockiert. Wichtig ist, dass der Store selbst abgesichert ist: TLS, ein eigener Nutzer pro Mandant, keine Verbindung von außen. Wie das mit mTLS auf jeder Strecke aussieht, beschreibt Teil 5.

Bei den Caches gibt es zwei Wege. Häufig genutzte Caches wie pages und hash können in Valkey liegen. Für Caches, die auf dem Dateisystem liegen müssen, braucht es ein Backend, das mit mehreren Pods umgehen kann. Wie das ohne ReadWriteMany-Volume geht, zeigt der Beitrag TYPO3 auf Kubernetes ohne RWX-Volume. Entscheidend ist in beiden Fällen: Ein Cache-Flush muss alle Pods erreichen, nicht nur den, auf dem er ausgelöst wurde.

03 — fileadmin und verarbeitete Bilder auf Objektspeicher

Uploads und _processed_-Bilder müssen alle Pods sehen. Ein einzelnes Volume mit ReadWriteOnce bindet die Anwendung an einen Knoten. Ein Netzwerk-Dateisystem mit ReadWriteMany ist oft langsam und schwer zu betreiben. Ein Objektspeicher wie S3 ist die robustere Basis.

Zwei Wege führen dorthin. TYPO3 kann über einen FAL-Treiber direkt mit S3 sprechen. Oder ein CSI-Treiber bindet einen Bucket als Dateisystem in den Pod ein, und TYPO3 merkt nichts davon. Der zweite Weg ist transparent, bringt aber Latenz: Jeder Dateizugriff wird zu einer Anfrage an den Speicher. Ein Cache davor ist deshalb Pflicht, für Metadaten wie für ausgelieferte Dateien. Wie das in der Praxis aussieht, beschreibt fileadmin auf S3 mit Caching.

Der Bucket gehört pro Mandant getrennt, mit Versionierung und eigener Zugriffsrolle. Uploads sollten zusätzlich auf Schadsoftware geprüft werden, bevor sie ausgeliefert werden.

04 — Nur-Lese-Code, Setup einmal je Release, ein Scheduler

Wenn aller Zustand draußen liegt, kann der Code schreibgeschützt sein. Das Root-Dateisystem des Containers ist read-only. Beschreibbar sind nur ausgewählte Verzeichnisse, etwa ein tmpfs für temporäre Dateien. Ein Angreifer, der Code ausführen kann, kann ihn so nicht dauerhaft verändern.

Setup und Migrationen laufen nicht im Hauptcontainer und auch nicht in jedem Pod, sondern einmal je Release in einem Hook vor dem Rollout. Eine Sperre in der Datenbank sorgt dafür, dass nie zwei Migrationen gegeneinander laufen, und scheitert der Hook, bleibt das Deployment, wie es ist. Der Pod selbst kopiert nur noch die im Build vorgewärmten Code-Caches und ist damit nach wenigen Sekunden bereit. Der Scheduler wird ein eigener CronJob mit concurrencyPolicy: Forbid, damit keine Aufgabe doppelt läuft. Und der Cache-Flush nach einem Deployment wird ein Hook, der nach dem Rollout genau einmal auslöst. Wie der Start eines Pods so von 54 auf zwölf Sekunden fiel, steht in der Feldnotiz zum Pod-Start.

 

apiVersion: batch/v1
kind: CronJob
metadata:
  name: typo3-scheduler
spec:
  schedule: "*/5 * * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          automountServiceAccountToken: false
          initContainers:
            # same verification chain as the web pod (part 3), shortened, key volume omitted
            - name: verify-signature
              image: registry.example.org/tools/cosign@sha256:...
              args: ["verify", "--key", "/keys/release.pub",
                     "registry.example.org/typo3/site@sha256:..."]
          containers:
            - name: scheduler
              image: registry.example.org/typo3-runtime@sha256:...
              workingDir: /app
              args: ["php", "vendor/bin/typo3", "scheduler:run"]
              volumeMounts:
                - {name: app, mountPath: /app, readOnly: true}
          volumes:
            - name: app
              image:
                reference: registry.example.org/typo3/site@sha256:...
                pullPolicy: IfNotPresent

 

Der CronJob braucht dieselbe Kette wie der Web-Pod aus Teil 3. Das Laufzeit-Image enthält keinen TYPO3-Code. Also prüft auch hier ein initContainer das signierte Artefakt, bevor der kubelet es als schreibgeschütztes Image-Volume einhängt. Fehlt dieser Schritt, startet der Scheduler entweder gar nicht oder mit Code, den niemand geprüft hat.

Logs gehen auf stdout und stderr, nicht in Dateien im Container. So sammelt sie die Plattform ein, und nichts geht verloren, wenn ein Pod verschwindet. Das Install-Tool bleibt im Cluster abgeschaltet.

Bezug zum IT-Grundschutz

Häufige Fragen

Reicht nicht ein ReadWriteMany-Volume für das ganze TYPO3?+

Technisch ja, im Betrieb selten gut. Netzwerk-Dateisysteme sind bei vielen kleinen Dateien langsam. Caches, Sessions und Locks auf einem gemeinsamen Dateisystem erzeugen außerdem schwer zu findende Konflikte. Besser ist, jede Zustandsart dorthin zu legen, wo sie hingehört.

Wo sollten TYPO3-Sessions im Cluster liegen?+

In einem gemeinsamen Key-Value-Store wie Valkey oder Redis, abgesichert mit TLS und eigenem Nutzer. Der Core bringt ein Redis-Backend für Sessions mit. Die Datenbank funktioniert auch, belastet sie bei vielen Frontend-Sessions aber unnötig.

Wie verhindere ich doppelte Scheduler-Läufe?+

Den Scheduler aus dem Web-Pod herausnehmen und als eigenen CronJob mit concurrencyPolicy: Forbid betreiben. Dann läuft er genau einmal, egal wie viele Web-Pods gerade aktiv sind.

Kann das Root-Dateisystem wirklich schreibgeschützt sein?+

Ja, wenn alle beschreibbaren Pfade bewusst ausgelagert sind: temporäre Dateien in ein tmpfs, Caches in Valkey oder ein clusterfähiges Backend, Uploads in den Objektspeicher. Was dann noch schreiben will, zeigt der erste Start mit read-only sehr schnell.

Fazit

Zustandslosigkeit ist die Voraussetzung für alles Weitere: Rolling Updates, Autoscaling, Pods auf mehreren Knoten. Sessions und Locks in Valkey, Dateien im Objektspeicher, Caches mit einem Backend, das mehrere Pods kennt, Setup im initContainer. Danach ist jeder TYPO3-Pod austauschbar, vorausgesetzt, Valkey, Objektspeicher und Datenbank sind selbst ausfallsicher. Ein einzelner Valkey ohne Persistenz meldet bei einem Neustart alle Redakteure ab. Welche Irrtümer auf dem Weg dahin typisch sind, fasst der Beitrag Fünf Irrtümer: TYPO3 auf Kubernetes zusammen.

Wie sich diese Austauschbarkeit nutzen lässt, zeigt der nächste Teil: Autoscaling und Probes. Zurück zu Teil 7: Secrets ohne Git.

Ich mache dein TYPO3 fit für mehrere Pods. Sessions, Dateien, Caches und Scheduler dorthin, wo sie im Cluster hingehören.

Bestandsaufnahme des Zustands, Umstellung von Sessions und Locks, fileadmin auf Objektspeicher mit Cache, Setup im initContainer und ein Scheduler, der genau einmal läuft.

Plattform-Betrieb statt Beratung auf Papier: Auf Wunsch betreibe ich den zustandslosen Betrieb dauerhaft mit, inklusive Speicher und Cache.

Termin buchen →

Über den Autor

Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Freiberuflicher DevSecOps-Berater · OnlyOle Consulting

Programmiert seit 2002 – autodidaktisch gelernt, 2012 mit KO-Web selbständig gemacht. Über 100 Projekte, Fokus auf Security, Performance, Automatisierung und Qualität. Heute freiberuflich: DevSecOps-Beratung, Schulungen und Softwareentwicklung.