ReplicaSet Reconcile and Ownership
How does Kubernetes keep exactly the right number of identical Pods alive? A ReplicaSet runs a control loop comparing how many Pods it should have against how many it can see, then creates or deletes until the two match. It owns them through ownerReferences, adopts any matching Pod that no other controller already owns, and releases one whose labels stop matching. Deployments never manage Pods directly, they manage ReplicaSets.
Play the animation 6 steps, animated, best on a wide screen
ReplicaSet Reconcile and Ownership, step by step
-
Every Pod the ReplicaSet manages carries a metadata.ownerReferences entry pointing back to it, with controller set to true. Those are the links drawn into the owned band, and they are what lets garbage collection remove the Pods when the ReplicaSet is deleted. This ReplicaSet is standalone: no Deployment owns it, so you scale it by editing its own spec.replicas, and it is what actually creates and deletes the Pods. One owned by a Deployment also selects on a pod-template-hash label.
Play from step 1 -
The controller runs a continuous reconcile loop. On every relevant change it compares the desired count against the Pods it owns and acts only on the difference. The mark on the band is spec.replicas, so a set that ends exactly on the mark needs nothing done. Because the loop is level-triggered it works off the current observed state rather than off one-time events, so a missed event or a controller restart still converges to the same result.
Play from step 2 -
One Pod is lost: it was deleted, or its Node failed and the Pod was evicted once its default 300 second toleration ran out. A Pod being deleted no longer counts, so the owned band falls to two and a gap opens before the mark, and the controller creates a replacement to close it. It learns of the loss through its Pod watch and acts on the difference, not on the event. This self-healing is the whole point of a controller. A bare Pod created on its own has no owner watching it, so once gone it stays gone.
Play from step 3 -
A standalone Pod is already running with the label app=web and no controller ownerReference, which is what the unowned band holds. The ReplicaSet matches Pods by selector and not by who created them, so it adopts this one: it PATCHes metadata.ownerReferences to point at itself. The Pod was already running, adoption only restamps its owner, and it crosses up into the owned band past the mark as a fourth replica.
Play from step 4 -
Adoption pushed the owned count to four, one past the mark. The same reconcile loop now deletes a Pod to return to exactly three. A ReplicaSet never settles above its desired count, no matter where the extra Pod came from. Picking the victim prefers pending and unschedulable Pods, then not ready ones, then the lowest controller.kubernetes.io/pod-deletion-cost, which is honored best effort, not guaranteed.
Play from step 5 -
The reverse of adoption. A Pod is relabelled so it no longer matches the selector, here app=web becomes app=debug. The ReplicaSet releases it by removing its ownerReference, and it drops into the unowned band still running. That leaves the owned band short of the mark, so the same loop creates a replacement to close the gap. Labels are the binding: change them and a Pod crosses between the two bands.
Play from step 6