Server-seitige sharded Liste und Watch in Kubernetes ändert das Verhalten von Controllern. Dies ist ein Versuch, die systembedingte Obergrenze bei der Arbeit mit hochgradigen Ressourcen zu beseitigen.
Wenn Kubernetes-Cluster auf Zehntausende von Knoten anwachsen, stoßen Controller nicht dort auf Skalierbarkeit, wo man es normalerweise erwartet. Das Problem tritt auf der Ebene der list/watch Interaktion mit dem API-Server auf. Jede Instanz eines horizontal skalierbaren Controllers erhält einen vollständigen Strom von Ereignissen zu Ressourcen wie Pods. Dann verbraucht sie CPU, Speicher und Netzwerk für die Deserialisierung, um den Großteil der Objekte abzulehnen. Horizontales Skalieren reduziert nicht die Kosten der Verarbeitung pro Replikat. Es multipliziert einfach den Gesamtverbrauch. Dies ist ein klassischer Fall, in dem das System nach der Anzahl der Instanzen skaliert, aber nicht nach der Effizienz.
Die Lösung in Kubernetes v1.36 ist server-side sharded list and watch. Dies ist eine Alpha-Funktion, die die Filterung auf die Ebene des API-Servers verlagert. Anstatt dass jeder Controller den Strom lokal filtert, sendet der Server nur relevante Ereignisse. Jede Replik des Controllers erhält ihr eigenes Datensegment, das über shardSelector definiert ist. Dies reduziert den überflüssigen Datenverkehr und beseitigt doppelte Arbeit. Der Kompromiss ist hier offensichtlich: Die Logik der Interaktion mit dem API wird komplizierter, und es entsteht eine Abhängigkeit von einer neuen Funktion, die sich noch in der Alpha-Phase befindet.
Auf der Implementierungsebene wird ein Feld shardSelector in ListOptions hinzugefügt. Der Client gibt einen Hashbereich über shardRange() an. Der API-Server berechnet einen deterministischen 64-Bit FNV-1a Hash basierend auf dem ausgewählten Feld und gibt nur die Objekte zurück, die in den Bereich [start, end) fallen. Dies funktioniert sowohl für List-Antworten als auch für Watch-Streams. Wichtig ist, dass die Hash-Funktion zwischen allen Instanzen des API-Servers deterministisch ist, sodass das Verhalten in einer verteilten Konfiguration konsistent bleibt.
Es werden spezifische Felder unterstützt: object.metadata.uid und object.metadata.namespace. Diese Einschränkung beeinflusst die Shardierungsstrategie. Controller, die Informer verwenden, können shardSelector über WithTweakListOptions implementieren. Im einfachsten Fall, beispielsweise mit zwei Replikaten, wird der Hashraum halbiert. Bei Bedarf können mehrere Bereiche mit logischem OR angegeben werden, um nicht benachbarte Segmente abzudecken.
Es gibt ein wichtiges Detail: Der API-Server signalisiert ausdrücklich, ob der Shard angewendet wurde. Im Antwortfeld erscheint shardInfo. Wenn es nicht vorhanden ist, hat der Server shardSelector ignoriert, und der Client hat den vollständigen Datensatz erhalten. In einem solchen Szenario muss der Controller bereit sein, zur client-seitigen Filterung zurückzukehren. Dies ist entscheidend für die Abwärtskompatibilität und Stabilität.
Aus Ergebnisperspektive ist der Haupteffekt die Reduzierung der Belastung von Netzwerk und CPU durch die Verringerung des Datenvolumens, das durch jeden Controller fließt. Konkrete Metriken in den Ausgangsdaten sind nicht angegeben, aber der architektonische Gewinn ist offensichtlich: Die doppelte Arbeit und die Belastung des API-Servers werden verringert. Es bleibt jedoch die Frage der Reife der Lösung, da die Funktion sich in der Alpha-Phase befindet und die Aktivierung des Feature-Gates ShardedListAndWatch erfordert.
In der Branche wird ein solcher Ansatz seit langem als korrektes Modell zur Skalierung von Beobachtern (watchers) diskutiert. Kubernetes macht einen pragmatischen Schritt in diese Richtung, indem die Filterung näher an die Datenquelle verlagert wird. Dies beseitigt nicht alle Einschränkungen, entfernt jedoch einen der kostspieligsten Engpässe.