Das Staleness-Problem in Kubernetes-Controllern führt zu inkorrekten Entscheidungen. In v1.36 wurden Mechanismen hinzugefügt, die das Verhalten der Controller vorhersagbarer machen.
Wenn der Controller auf Grundlage eines veralteten Zustands handelt, führt das zu falschen Entscheidungen. In Kubernetes-Controllern ist dies mit dem lokalen Cache verbunden, der über die Watch-API des Servers gefüllt wird. Ein solcher Cache beschleunigt die Verarbeitung, bringt jedoch das Risiko der Desynchronisation mit sich. Infolgedessen kann der Controller eine falsche Entscheidung treffen, eine erforderliche Aktion überspringen oder mit Verzögerung reagieren. Dies ist insbesondere nach einem Neustart des Controllers oder bei Nichterreichbarkeit des API-Servers bemerkbar, wenn der Cache hinter dem tatsächlichen Zustand des Clusters zurückbleibt.
In Kubernetes v1.36 wurde eine pragmatische Lösung gewählt: den Cache nicht zu beseitigen, sondern einen Mechanismus zur Überprüfung seiner Aktualität hinzuzufügen. In client-go wurde AtomicFIFO eingeführt — eine Erweiterung der Warteschlange, die Ereignisse atomar verarbeitet, insbesondere bei Batch-Operationen (z. B. initial list). Dies beseitigt den Zustand, in dem Ereignisse in unterschiedlicher Reihenfolge eintreffen und die Konsistenz des Caches stören. Zusätzlich wurde die Funktion LastStoreSyncResourceVersion() eingeführt, die es ermöglicht, eindeutig zu verstehen, welche Version der Ressource der Cache gesehen hat. Der Kompromiss ist hier offensichtlich: Es wird eine zusätzliche Überprüfung vor der Aktion hinzugefügt, was die Reaktion verlangsamen kann, aber das Risiko inkorrekter Operationen verringert.
Auf der Implementierungsebene nutzt der kube-controller-manager diese Möglichkeit über Feature Gates wie StaleControllerConsistency. Bei Aktivierung vergleicht der Controller vor der Aktion die Resource-Version im Cache mit der, die er selbst im API-Server gespeichert hat. Wenn der Cache hinterherhinkt, wird die Aktion nicht ausgeführt. Dieses Verhalten implementiert das Prinzip „read-your-own-writes“, ohne dass synchrone Anfragen an die API erforderlich sind. In client-go wurde auch ConsistencyStore hinzugefügt — eine Struktur, die die Versionen der Ressourcen verfolgt und den Informern hilft zu bestimmen, ob der Cache den aktuellen Zustand eingeholt hat. Beispielsweise verfolgt der ReplicaSet-Controller die Versionen sowohl des ReplicaSets als auch der zugehörigen Pods, um Aktionen auf veralteten Daten zu vermeiden.
Eine separate Schicht ist die Beobachtbarkeit (observability). Im kube-controller-manager wurde die Metrik stale_sync_skips_total hinzugefügt, die zeigt, wie oft der Controller die Synchronisation aufgrund eines veralteten Caches übersprungen hat. Außerdem veröffentlicht client-go store_resource_version für jeden Informer. Dies ermöglicht den Vergleich des Cache-Zustands mit dem API-Server und die Diagnose von Lag. Die Metriken sind standardmäßig aktiviert, befinden sich jedoch im Alpha-Status. Dies ist ein Signal, dass sich die Schnittstelle und Semantik ändern können.
Das Ergebnis ist ein vorhersehbareres Verhalten der Controller unter Bedingungen von Wettlauf und hoher Konkurrenz, insbesondere für pod-orientierte Controller. Dabei werden keine genauen Metriken zur Verbesserung angegeben. Wichtig ist etwas anderes: Das System beginnt, klar zwischen „keine Aktion, weil nicht nötig“ und „keine Aktion, weil der Cache veraltet ist“ zu unterscheiden. Dies verringert die Klasse schwer fassbarer Fehler, die zuvor nur in der Produktion auftraten.
Die weitere Entwicklung wird voraussichtlich in Richtung controller-runtime verschoben, um diese Garantien zum Standard für alle benutzerdefinierten Controller zu machen. Dies ist ein logischer Schritt: Das Staleness-Problem ist nicht einzigartig für integrierte Komponenten, sondern ein systemisches Merkmal der Architektur mit Cache und eventual consistency.