TYPO3 auf Kubernetes unter IT-Grundschutz, Teil 9: Autoscaling und Probes
Ist TYPO3 zustandslos, lässt es sich skalieren. Verfügbar macht es das allein noch nicht. Dazu gehören Regeln, wann skaliert wird, eine Untergrenze für Wartungsfenster und Prüfungen, die erkennen, ob ein Pod wirklich Seiten ausliefert. Wie diese Teile ineinandergreifen, zeigt dieser Beitrag.
Teil 9 von 14 der Serie „TYPO3 auf Kubernetes unter IT-Grundschutz“. Die Übersicht aller Teile steht in Teil 1.
01 — Horizontal Pod Autoscaler: worauf skaliert wird
Der Horizontal Pod Autoscaler (HPA) passt die Zahl der Pods an eine Metrik an. Für TYPO3 ist CPU meist das richtige Signal. Im Worker-Modus, etwa mit FrankenPHP, hält jeder Pod eine feste Zahl an PHP-Workern im Speicher. Der Speicherverbrauch steigt deshalb kaum mit der Last, die CPU schon. Speicher als Skalierungsmetrik führt bei Worker-Modellen eher zu Fehlentscheidungen.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: typo3
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: typo3
minReplicas: 2
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
Die Zielauslastung bezieht sich auf die CPU-Requests. Ohne sauber gesetzte Requests skaliert der HPA ins Leere. Requests und Limits sind aber auch aus Sicherheitsgründen Pflicht: Ein Pod ohne Grenzen kann einen ganzen Knoten auslasten. minReplicas: 2 ist die Untergrenze für einen Dienst, der eine Wartung ohne Ausfall überstehen soll. Das Stabilisierungsfenster verhindert, dass der HPA nach einer kurzen Spitze sofort wieder abbaut.
Ein Signal passt allerdings nicht für alle Hosts eines Mandanten. Das Redaktions-Backend hat ein ganz anderes Lastprofil als die Website: tagsüber wenige, schwere Anfragen, nachts gar keine. Teilen sich beide einen Pod, bremsen sie sich gegenseitig aus, und der HPA kann ihre Last nicht auseinanderhalten. Mit einem eigenen Deployment für das Backend skaliert jede Seite nach ihrer eigenen Last, das Backend nach oben wie nach unten bis auf null. Wie die Trennung abläuft und warum danach beide Seiten schneller antworteten, steht in der Feldnotiz zum Backend im eigenen Pod.
02 — Rolling Updates und PodDisruptionBudget
Ein Deployment ersetzt Pods beim Update schrittweise. Mit maxSurge: 1 und maxUnavailable: 0 startet zuerst ein neuer Pod. Erst wenn er bereit ist, geht ein alter. Die Kapazität sinkt so während des Updates nie.
Das stille Rollout
Diese Einstellung hat eine Kehrseite. Wird der neue Pod nie bereit, bleibt das Rollout einfach stehen. Die alten Pods laufen weiter, die Website ist erreichbar, und nichts meldet sich. Mit progressDeadlineSeconds markiert Kubernetes so ein Rollout nach der eingestellten Zeit als fehlgeschlagen. Ein Alarm auf genau diesen Zustand gehört zu jedem Deployment. Sonst läuft eine Plattform unbemerkt tagelang auf einem alten Stand, und das nächste Sicherheitsupdate kommt nie an.
Updates sind aber nicht die einzigen Unterbrechungen. Knoten werden gewartet, entleert und neu gestartet. Dafür gibt es das PodDisruptionBudget (PDB). Es legt fest, wie viele Pods bei einer geplanten Unterbrechung mindestens laufen müssen.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: typo3
spec:
minAvailable: 1
selector:
matchLabels:
app: typo3
Ein PDB regelt nur freiwillige Unterbrechungen. Fällt ein Knoten aus, auf dem alle Replicas laufen, sind trotzdem alle weg. topologySpreadConstraints oder eine Anti-Affinity verteilen die Pods deshalb bewusst über Knoten und, wo vorhanden, über Verfügbarkeitszonen.
03 — Probes, die echte Fehler finden
Kubernetes kennt drei Prüfungen. Die Startup-Probe gibt einem Pod Zeit zum Hochfahren. Die Readiness-Probe entscheidet, ob ein Pod Anfragen bekommt. Die Liveness-Probe startet einen Pod neu, der hängt.
Die häufigste Schwäche sind Probes, die nur prüfen, ob ein Port offen ist. Der Webserver antwortet, PHP liefert aber nur Fehler aus, und Kubernetes hält den Pod für gesund. In der Praxis bewährt sich eine Readiness-Probe, die den echten Anwendungspfad aufruft, direkt am Pod, mit dem Host-Header der Site und, wo der Pod selbst TLS spricht, mit HTTPS. Eine Antwort mit Status 5xx gilt als Fehler.
Das hat eine Kehrseite, die man kennen sollte. Rendert die Probe die Startseite, blockiert eine kaputte Startseite jedes Deployment. Neue Pods werden nie bereit, das Rollout bleibt stehen. Für Readiness ist das beim Rollout gewollt, denn ein Pod mit kaputter Startseite soll keinen Verkehr bekommen. Im laufenden Betrieb hat das eine zweite Kehrseite: Ein Inhaltsfehler trifft alle Pods gleichzeitig. Alle werden unready, und die ganze Website liefert 503. Die Readiness-Probe prüft deshalb besser einen schlanken Pfad, der PHP, Datenbank und Cache durchläuft, aber nicht vom Inhalt der Startseite abhängt. Für Liveness ist es gefährlich: Ein Fehler im Inhalt würde alle Pods in Neustart-Schleifen schicken. Liveness prüft deshalb nur, ob der Prozess lebt. Readiness prüft die Anwendung.
Dazu gehört ein sauberes Herunterfahren. Ein preStop-Hook mit einer kurzen Pause gibt dem Ingress Zeit, den Pod aus der Verteilung zu nehmen, bevor er aufhört, Anfragen anzunehmen. Laufende Anfragen werden noch zu Ende bearbeitet.
Im gehärteten Image aus Teil 2 gibt es weder Shell noch sleep. Ein exec-Hook mit sleep 5 scheitert dort still. Seit Kubernetes 1.30 kann der Hook selbst warten, ganz ohne Programm im Image:
lifecycle:
preStop:
sleep:
seconds: 5
Die Pause und das Beenden der laufenden Anfragen müssen zusammen in die terminationGracePeriodSeconds des Pods passen. Sonst beendet Kubernetes den Container mitten in einer Anfrage.
04 — Mehr Knoten: Autoscaler, Spot und was mehrere Knoten verhindert
Der HPA erzeugt Pods. Passen sie nicht mehr auf die vorhandenen Knoten, braucht es einen Cluster Autoscaler, der neue Knoten startet. Für Lastspitzen eignen sich günstige Spot-Knoten. Voraussetzung ist eine zustandslose Anwendung und eine Verteilung, bei der nicht alle Replicas auf Spot-Knoten liegen, die gemeinsam verschwinden können.
Was einen TYPO3-Cluster in der Praxis am häufigsten an einen Knoten bindet, ist nicht TYPO3 selbst, sondern der Speicher. Volumes mit ReadWriteOnce hängen an einem Knoten, lokaler Speicher sogar an einer Maschine. Liegt die Datenbank auf einem solchen Volume, kann ihr Pod nicht einfach umziehen. Die Wege heraus sind Netzwerkspeicher, der zwischen Knoten einer Zone wandert, eine Datenbank mit Replikation über Knoten hinweg oder ein verwalteter Datenbankdienst. Mehr dazu in Teil 10 und Teil 12.
Bezug zum IT-Grundschutz
- SYS.1.6.A15 Limitierung der Ressourcen pro Container: Requests und Limits, zugleich Grundlage für den HPA.
- SYS.1.6.A25 Hochverfügbarkeit von containerisierten Anwendungen: mehrere Replicas, PDB, Verteilung über Knoten.
- APP.4.4.A11 Überwachung der Container: Probes, die den echten Anwendungspfad prüfen.
- APP.4.4.A19 Hochverfügbarkeit von Kubernetes: Pods und Knoten so verteilt, dass eine Unterbrechung den Dienst nicht stoppt.
- APP.4.4.A21 Regelmäßiger Restart von Pods: Rolling Updates ohne Kapazitätsverlust machen Neustarts risikoarm.
Häufige Fragen
Warum skaliert man TYPO3 auf CPU und nicht auf Speicher?+
Im Worker-Modus hält jeder Pod eine feste Zahl an PHP-Workern im Speicher. Der Speicherverbrauch ändert sich mit der Last kaum, die CPU-Auslastung schon. CPU ist daher das ehrlichere Signal.
Wie viele Replicas braucht ein TYPO3 mindestens?+
Zwei, wenn Wartungen und Updates ohne Ausfall laufen sollen. Mit nur einem Pod reicht ein Knoten-Neustart oder ein hängender Pod für eine Unterbrechung.
Sollte die Liveness-Probe die Startseite rendern?+
Nein. Ein Fehler im Inhalt würde dann alle Pods in Neustart-Schleifen schicken. Die Startseite gehört höchstens in die Readiness-Probe. Besser ist ein schlanker Pfad, der PHP, Datenbank und Cache prüft. Liveness prüft nur, ob der Prozess lebt.
Was bringt ein PodDisruptionBudget, wenn alle Pods auf einem Knoten liegen?+
Wenig. Ein PDB regelt nur geplante Unterbrechungen wie das Entleeren eines Knotens. Fällt der Knoten aus, auf dem alle Replicas laufen, sind trotzdem alle weg. Erst die Verteilung über mehrere Knoten macht den PDB wirksam.
Fazit
Autoscaling ist schnell eingeschaltet. Verfügbar wird ein TYPO3 aber erst, wenn die Teile zusammenpassen. Der HPA braucht ehrliche Requests. Das PDB wirkt nur, wenn die Pods auf verschiedenen Knoten liegen. Und die Probes müssen den Pfad prüfen, den auch Besucher nehmen. Der Engpass liegt danach selten bei TYPO3. Meist ist es der Speicher.
Genau darum geht es im nächsten Teil: Datenbank, Backup und Restore. Zurück zu Teil 8: TYPO3 zustandslos machen.
Ich richte Autoscaling, PodDisruptionBudgets und Probes für dein TYPO3 so ein, dass Updates und Wartung ohne Ausfall laufen.
Requests und Limits nach Messung statt nach Gefühl, ein HPA passend zum Worker-Modell, Probes auf dem echten Pfad und ein Plan für den Speicher, der mehrere Knoten möglich macht.
Plattform-Betrieb statt Beratung auf Papier: Auf Wunsch betreibe ich Skalierung und Rollouts dauerhaft mit, inklusive der Alarme auf hängende Deployments.
Über den Autor

Kai Ole Hartwig
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.
