Scale-to-Zero
Ein HTTP-Service kann auf 0 Replicas heruntergehen, wenn ihn niemand aufruft, und bei der nächsten Anfrage wieder aufwachen. Solange er schläft, zahlst du nichts für CPU und Arbeitsspeicher.
Diese Seite setzt voraus, dass du bereits eine funktionierende HTTPRoute hast, siehe Gateway API.
Einschalten
Du meldest dich pro Route an. Häng die Annotationen an die HTTPRoute, die du schon hast, und zeig mit backendRefs auf den Interceptor statt auf deinen eigenen Service:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp
annotations:
itsh.dev/scale-to-zero: "myapp"
itsh.dev/scale-to-zero-max-replicas: "5"
itsh.dev/scale-to-zero-target-concurrency: "10"
itsh.dev/scale-to-zero-idle: "300s"
spec:
parentRefs:
- name: default
namespace: nginx-gateway
hostnames:
- "myapp.<dein-namespace>.itsh.dev"
rules:
- backendRefs:
- name: keda-add-ons-http-interceptor-proxy
namespace: keda
port: 8080Dieser backendRefs-Block ist die einzige Änderung an deinen eigenen Manifesten. Die Scaler-Verdrahtung, die Rate-Limits und die Network Policy werden für dich angelegt und wieder entfernt, wenn du die Route löschst. Zum Abmelden nimmst du die Annotationen weg und zeigst mit backendRefs wieder auf deinen Service.
Der Wert der Annotation ist ein Name, der zweimal verwendet wird, für den Workload und für den Service davor. Die beiden müssen also gleich heißen.
Annotationen
| Annotation | Standard | Zulässig | Wirkung |
|---|---|---|---|
itsh.dev/scale-to-zero | keiner | ein Workload-Name | Schaltet es ein. Benennt sowohl den schlafenden Workload als auch den Service davor |
itsh.dev/scale-to-zero-max-replicas | 3 | positive ganze Zahl | Obergrenze beim Hochskalieren |
itsh.dev/scale-to-zero-target-concurrency | 100 | 1 bis 1000 | Gleichzeitige Anfragen pro Replica, bevor eine weitere startet |
itsh.dev/scale-to-zero-idle | 300s | 300s oder mehr | Wie lange ohne Traffic, bis er schlafen geht |
itsh.dev/scale-to-zero-cold-start | halten | placeholder | Sofort mit 503 antworten, statt die Verbindung zu halten |
Der Standardwert von 100 ist für die meisten Anwendungen zu hoch. Bei dieser Einstellung schluckt eine einzelne Replica hundert gleichzeitige Anfragen, bevor überhaupt etwas anderes startet. Die Anwendung wird unter Last also langsamer, statt zu skalieren. Setz den Wert auf ungefähr das, was eine Replica bequem bedient.
Was du tatsächlich zahlst
Abgerechnet werden die Resource Requests von Pods im Zustand Running. Ein schlafender Workload kostet also nichts an CPU und Arbeitsspeicher.
Deine Rechnung wird dadurch nicht null. Volumes bleiben belegt, während die Pods schlafen, PVC-Kapazität wird also normal weiter abgerechnet, und die Grundgebühr läuft weiter. Ein Namespace mit einem 20-GiB-Volume, der den ganzen Monat schläft, zahlt weiterhin Grundgebühr plus dieses Volume. Das Portal zeigt das als Untergrenze an: was der Namespace kostet, wenn ab jetzt alles schläft.
Kaltstarts
Die erste Anfrage nach dem Schlafen wird offen gehalten, während der Pod startet, und dann normal beantwortet. Der Aufrufer sieht also eine langsame Anfrage statt eines Fehlers. Ein kleiner Service mit gecachtem Image wacht in etwa 2 bis 3 Sekunden auf. Eine schwerere Runtime braucht so lange, wie sie eben zum Starten braucht.
Deine Readiness-Probe ist dabei der größte Hebel. Es wird kein Traffic geschickt, bevor die Probe durchläuft. initialDelaySeconds: 5, periodSeconds: 10 heißt also: frühestens nach 5 Sekunden, nächste Chance nach 15. Wenn dir die Aufwachzeit wichtig ist, nimm initialDelaySeconds: 0 und periodSeconds: 1.
Muss der Cluster erst einen Node dazunehmen, dauert das Aufwachen über zwei Minuten. Das ist länger, als wir die Verbindung halten, der Aufrufer bekommt also eine Fehlerseite und muss es erneut versuchen. Das passiert selten, ist dann aber eine fehlgeschlagene und keine langsame Anfrage.
Mit itsh.dev/scale-to-zero-cold-start: "placeholder" wird stattdessen sofort mit einem 503 und Retry-After geantwortet. Für einen Browser auf einer Web-UI ist das freundlicher, für alles andere falsch: Es bricht POSTs und jeden nicht interaktiven Aufrufer.
Wie es über null hinaus skaliert
Scale-to-Zero ist kein An-Aus-Schalter. Der Workload skaliert zwischen 0 und itsh.dev/scale-to-zero-max-replicas, und zwar nach gleichzeitig laufenden Anfragen: Sobald eine Replica mehr Anfragen hält als der Zielwert, startet eine weitere.
Die Anzahl gleichzeitiger Anfragen ist das einzige Signal. CPU, Speicher oder Queue-Länge gibt es hier nicht. Wenn deine Last nicht proportional zu gleichzeitigen HTTP-Anfragen ist, ist Scale-to-Zero das falsche Werkzeug und ein normaler HPA oder ein KEDA-ScaledObject das richtige.
Nicht zusammen mit einem anderen Autoscaler
Scale-to-Zero setzt bereits einen Autoscaler auf den Workload und kann sich diesen nicht mit einem HPA oder einem eigenen ScaledObject teilen. Pro Workload also eins von beidem.
Der Fall, auf den du achten musst, ist ein nachträglich angelegter HPA, denn den lehnt nichts ab. Dann schreiben zwei Autoscaler die Replica-Zahl desselben Deployments, und ein HPA mit minReplicas: 1 oder mehr hält den Workload dauerhaft wach. Er schläft also nie, und du zahlst CPU und Speicher weiter. Beide Fälle werden an der Route gemeldet, siehe Skalierung tut nicht, was du erwartest.
Wofür es sich nicht eignet
- Hintergrund-Worker und Queue-Consumer. Niemand ruft sie über HTTP auf, also weckt sie auch nichts. Nimm KEDA mit einem Queue-Trigger.
- WebSocket und SSE. Langlebige Verbindungen überleben das Herunterskalieren nicht.
- Alles, was einen warmen Cache braucht. In-Process-Caches, Opcache und JIT-Zustand sterben mit dem Pod, Anfragen bleiben nach dem Aufwachen also eine Weile langsam.
- Cron-getriebene Arbeit. Ein CronJob kostet ohnehin nichts, solange er nicht läuft.
- gRPC. Auf Scale-to-Zero-Routen nicht unterstützt, lass die durchlaufen.
- Alles, wo eine mehrere Sekunden langsame erste Anfrage nicht akzeptabel ist, etwa Zahlungs-Callbacks oder Webhook-Empfänger mit kurzem Timeout auf der Senderseite.
Im Free Tier
Schlafen ist keine Inaktivität. Für die 30-Tage-Sperre zählt Traffic, nicht laufende Pods. Ein Namespace, der den ganzen Monat schläft und gelegentlich eine Anfrage beantwortet, bleibt also aktiv. Kaltstarts sind hier langsamer, denn mit 200m CPU startet eine Anwendung nicht besonders flott.
Wie es weitergeht
- Autoscaling für die anderen Scaler
- Abrechnung und Kontingente, was der Zähler zählt