List-Watch and Informers
How does a controller keep an up-to-date picture of the cluster without hammering the API? It lists the objects it cares about once, then opens a watch that streams every later change to them. An informer turns that stream into a local cache, so the controller reconciles against memory and re-lists when its watch falls too far behind, which the API signals with a 410 Gone. Since 1.35 client-go folds that first list into the watch itself by default.
Play the animation 6 steps, animated, best on a wide screen
List-Watch and Informers, step by step
-
The controller first asks the API what it can talk to. GET /api and GET /apis return the discovery document, the catalogue of every group, version and resource the informer can list and watch.
Play from step 1 -
The informer fires the initial LIST at resourceVersion 0. The API keeps its watch cache filled from ETCD and answers the list from there, with no quorum read, so the full set lands in the Indexer at rv=842 and the controller reconciles from local memory.
Play from step 2 -
The informer opens GET /api/v1/pods?watch=true&resourceVersion=842. The API streams every change since that RV as a chunked HTTP response. The connection stays open for as long as the controller wants.
Play from step 3 -
A new Pod lands in ETCD. The API pushes an ADDED event over the open watch (rv=843). The informer queues the event in order and writes it to the Indexer cache.
Play from step 4 -
If the API has compacted history past the resourceVersion the informer holds, the next watch chunk returns HTTP 410 Gone. The informer drops its watch, re-LISTs to a fresh resourceVersion, and resumes the watch.
Play from step 5 -
CRDs add their own group and version (example.com/v1). The API serves them under /apis just like built-ins. Same list-then-watch contract, same informer story. Since 1.35 client-go opens that watch with the initial list on it, falling back to the LIST drawn here.
Play from step 6