Summary
This RFC proposes a clear separation of responsibilities for handling Failed/inactive pods in the RBG controller system. With the removal of RecreateRBGOnPodRestart policy (#340), the remaining restart policies need well-defined handling paths for pod failures.
Background
Pod failures in Kubernetes (Evicted, UnexpectedAdmissionError, OOM-killed, etc.) result in pods entering the Failed phase. Unlike container restarts (where the pod remains Running/Pending), Failed pods are terminal and require explicit cleanup before replacements can be created.
Two-Level Handling Design
Level 1: Delete Failed Pods (applies to ALL RestartPolicies)
Failed pods must be explicitly deleted because they block replacement creation — hasOrphanPod detects the same-name pod still exists and prevents new pod creation.
Handler: RoleInstance Controller (instance_scale.go → calculateDiffsWithExpectation)
// Delete inactive (Failed) pods so that replacements can be created on next reconcile.
for _, p := range inactivePods {
if p.Status.Phase == v1.PodFailed && p.DeletionTimestamp == nil {
toDeletePods = append(toDeletePods, p)
}
}
Flow: Pod Failed → RoleInstance reconcile → delete Failed pod → next reconcile creates replacement
Level 2: Recreate Entire Instance (only for RecreateRoleInstanceOnPodRestart)
When restartPolicy=RecreateRoleInstanceOnPodRestart, a Pod failure should trigger recreation of ALL pods in the Instance (not just a replacement for the failed one). This ensures instance-level consistency.
Handler: RoleInstance Controller (instance_scale.go → shouldRecreateInstance)
func shouldRecreateInstance(instance, pods) bool {
// Only for RecreateRoleInstanceOnPodRestart policy
// Only when Instance was previously Ready (stable state)
// Only when spec is not being changed (Generation == ObservedGeneration)
// Trigger: any Pod in Failed phase
}
Flow: Pod Failed → RoleInstance reconcile → delete ALL pods → recreate entire Instance
Key Design Decisions
| Aspect |
Decision |
Rationale |
| Failed pod detection |
Check pod.Status.Phase == Failed |
Direct, unambiguous signal |
| Only trigger on stable state |
wasInstanceReady && Generation == ObservedGeneration |
Avoid recreation storms during scaling/rollout |
| Container restart handling |
Delegated to underlying workload controller (e.g. LWS) |
RoleInstance Controller only handles terminal pod states |
| Succeeded pods |
NOT handled (per Non-Goals) |
Represents normal completion, not failure |
Comparison: Container Restart vs Pod Failed
| Scenario |
Handler |
Action |
Container restart + None |
Underlying workload controller |
Let container restart |
Container restart + RecreateRoleInstanceOnPodRestart |
LWS Controller |
Recreate RoleInstance |
Pod Failed + None |
RoleInstance Controller |
Delete Failed pod → create replacement |
Pod Failed + RecreateRoleInstanceOnPodRestart |
RoleInstance Controller |
Recreate entire Instance |
Implementation
The implementation passes inactivePods (pods filtered out by IsPodActive) through the RoleInstance reconciler chain into calculateDiffsWithExpectation, making Failed pods visible to both Level 1 (deletion) and Level 2 (recreation) logic.
Related
Summary
This RFC proposes a clear separation of responsibilities for handling Failed/inactive pods in the RBG controller system. With the removal of
RecreateRBGOnPodRestartpolicy (#340), the remaining restart policies need well-defined handling paths for pod failures.Background
Pod failures in Kubernetes (Evicted, UnexpectedAdmissionError, OOM-killed, etc.) result in pods entering the
Failedphase. Unlike container restarts (where the pod remains Running/Pending), Failed pods are terminal and require explicit cleanup before replacements can be created.Two-Level Handling Design
Level 1: Delete Failed Pods (applies to ALL RestartPolicies)
Failed pods must be explicitly deleted because they block replacement creation —
hasOrphanPoddetects the same-name pod still exists and prevents new pod creation.Handler: RoleInstance Controller (
instance_scale.go→calculateDiffsWithExpectation)Flow: Pod Failed → RoleInstance reconcile → delete Failed pod → next reconcile creates replacement
Level 2: Recreate Entire Instance (only for
RecreateRoleInstanceOnPodRestart)When
restartPolicy=RecreateRoleInstanceOnPodRestart, a Pod failure should trigger recreation of ALL pods in the Instance (not just a replacement for the failed one). This ensures instance-level consistency.Handler: RoleInstance Controller (
instance_scale.go→shouldRecreateInstance)Flow: Pod Failed → RoleInstance reconcile → delete ALL pods → recreate entire Instance
Key Design Decisions
pod.Status.Phase == FailedwasInstanceReady && Generation == ObservedGenerationComparison: Container Restart vs Pod Failed
NoneRecreateRoleInstanceOnPodRestartNoneRecreateRoleInstanceOnPodRestartImplementation
The implementation passes
inactivePods(pods filtered out byIsPodActive) through the RoleInstance reconciler chain intocalculateDiffsWithExpectation, making Failed pods visible to both Level 1 (deletion) and Level 2 (recreation) logic.Related
keps/inactive-pod-handling/README.md