kubernetes/kubernetes
Garbage collection
The Kubernetes garbage collector (pkg/controller/garbagecollector/) is what makes "delete the parent and the children disappear" work. It is a generic, dynamically-typed engine that walks the live cluster, builds a graph of OwnerReferences, and reacts to deletes by deleting dependents.
The GC verbs
Every Kubernetes Delete request carries an optional propagationPolicy:
Foreground— block the parent's actual removal until all dependents are gone. The parent is marked withmetadata.finalizers: [foregroundDeletion]; the GC removes dependents, then removes the finalizer.Background— return success immediately; remove dependents asynchronously. Default for most kinds.Orphan— remove thecontroller: trueOwnerReference from each dependent so they survive the parent's deletion. Default for ReplicaSets in legacy releases.
The choice is enforced by the apiserver (in staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go) and by the GC at runtime.
The graph
graph LR
Deploy[Deployment] --> RS1[ReplicaSet rev 1]
Deploy --> RS2[ReplicaSet rev 2]
RS1 --> P1[Pod a]
RS1 --> P2[Pod b]
RS2 --> P3[Pod c]
PVC[PVC] --> Pod[Pod with claim]The GC keeps an in-memory graph keyed by (ownerUID, dependentUID). Nodes are loaded from informers across every API group; the discovery loop refreshes the type list periodically (pkg/controller/garbagecollector/garbagecollector.go's runGCController).
The two queues
- Attempt-to-delete queue — fed by parent-deletes. Items here represent "this dependent's owner is gone; consider deleting the dependent."
- Attempt-to-orphan queue — fed by Orphan-mode deletes. Items here represent "remove me from this dependent's OwnerRef set."
A dedicated worker drains each queue. Both use the rate-limited workqueue pattern from controller-framework.
Rebuild on resync
Periodically the GC re-fetches the discovery document. New API groups bring new informers online; removed groups have their informers torn down. This is what lets the GC handle CRDs that come and go without restarting.
Failure modes
- Orphaned by accident. A controller forgets to set the OwnerRef. Children never get cleaned up. Workaround:
kubectl get pods -l app=foothenkubectl delete pod. - Stuck terminating. A foreground-deletion finalizer is hung because some dependents have their own finalizers that won't release. Inspect
metadata.finalizersof the dependents. - Discovery thrash. If a CRD is constantly added and removed, the discovery resync churns informers. Rare in practice; reflected in
garbagecollector_resync_*metrics.
Cluster-scope vs namespace-scope
Cluster-scoped objects can own cluster-scoped or namespace-scoped dependents. Namespace-scoped objects can only own dependents in the same namespace. The apiserver's owner-ref validation (in staging/src/k8s.io/apimachinery/pkg/api/validation) rejects cross-namespace owner references on namespace-scoped owners.
Key source files
| File | Purpose |
|---|---|
pkg/controller/garbagecollector/garbagecollector.go |
Top-level controller, runs the workers |
pkg/controller/garbagecollector/graph_builder.go |
Builds the in-memory ownership graph from informer events |
pkg/controller/garbagecollector/dump.go |
Debug dump of the graph |
pkg/controller/garbagecollector/operations.go |
DeletionPolicy resolution helpers |
pkg/controller/garbagecollector/uid_cache.go |
Per-UID cache to dedupe events |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.