Skip to content

Commit 2364c10

Browse files
yujuhongsmarterclayton
authored andcommitted
kubelet: Don't delete pod until all container status is available
After a pod reaches a terminal state and all containers are complete we can delete the pod from the API server. The dispatchWork method needs to wait for all container status to be available before invoking delete. Even after the worker stops, status updates will continue to be delivered and the sync handler will continue to sync the pods, so dispatchWork gets multiple opportunities to see status. The previous code assumed that a pod in Failed or Succeeded had no running containers, but eviction or deletion of running pods could still have running containers whose status needed to be reported. This modifies earlier test to guarantee that the "fallback" exit code 137 is never reported to match the expectation that all pods exit with valid status for all containers (unless some exceptional failure like eviction were to occur while the test is running).
1 parent ad3d894 commit 2364c10

File tree

2 files changed

+36
-15
lines changed

2 files changed

+36
-15
lines changed

pkg/kubelet/kubelet.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,19 +2001,22 @@ func (kl *Kubelet) syncLoopIteration(configCh <-chan kubetypes.PodUpdate, handle
20012001
}
20022002

20032003
// dispatchWork starts the asynchronous sync of the pod in a pod worker.
2004-
// If the pod is terminated, dispatchWork will perform no action.
2004+
// If the pod has completed termination, dispatchWork will perform no action.
20052005
func (kl *Kubelet) dispatchWork(pod *v1.Pod, syncType kubetypes.SyncPodType, mirrorPod *v1.Pod, start time.Time) {
2006-
if kl.podIsTerminated(pod) {
2007-
klog.V(4).Infof("Pod %q is terminated, ignoring remaining sync work: %s", format.Pod(pod), syncType)
2008-
if pod.DeletionTimestamp != nil {
2009-
// If the pod is in a terminated state, there is no pod worker to
2010-
// handle the work item. Check if the DeletionTimestamp has been
2011-
// set, and force a status update to trigger a pod deletion request
2012-
// to the apiserver.
2013-
kl.statusManager.TerminatePod(pod)
2014-
}
2006+
// check whether we are ready to delete the pod from the API server (all status up to date)
2007+
containersTerminal, podWorkerTerminal := kl.podAndContainersAreTerminal(pod)
2008+
if pod.DeletionTimestamp != nil && containersTerminal {
2009+
klog.V(4).Infof("Pod %q has completed execution and should be deleted from the API server: %s", format.Pod(pod), syncType)
2010+
kl.statusManager.TerminatePod(pod)
20152011
return
20162012
}
2013+
2014+
// optimization: avoid invoking the pod worker if no further changes are possible to the pod definition
2015+
if podWorkerTerminal {
2016+
klog.V(4).Infof("Pod %q has completed, ignoring remaining sync work: %s", format.Pod(pod), syncType)
2017+
return
2018+
}
2019+
20172020
// Run the sync in an async worker.
20182021
kl.podWorkers.UpdatePod(&UpdatePodOptions{
20192022
Pod: pod,

pkg/kubelet/kubelet_pods.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,9 @@ func (kl *Kubelet) getPullSecretsForPod(pod *v1.Pod) []v1.Secret {
865865
return pullSecrets
866866
}
867867

868-
// podIsTerminated returns true if pod is in the terminated state ("Failed" or "Succeeded").
869-
func (kl *Kubelet) podIsTerminated(pod *v1.Pod) bool {
868+
// podStatusIsTerminal reports when the specified pod has no running containers or is no longer accepting
869+
// spec changes.
870+
func (kl *Kubelet) podAndContainersAreTerminal(pod *v1.Pod) (containersTerminal, podWorkerTerminal bool) {
870871
// Check the cached pod status which was set after the last sync.
871872
status, ok := kl.statusManager.GetPodStatus(pod.UID)
872873
if !ok {
@@ -875,11 +876,28 @@ func (kl *Kubelet) podIsTerminated(pod *v1.Pod) bool {
875876
// restarted.
876877
status = pod.Status
877878
}
878-
return status.Phase == v1.PodFailed || status.Phase == v1.PodSucceeded || (pod.DeletionTimestamp != nil && notRunning(status.ContainerStatuses))
879+
// A pod transitions into failed or succeeded from either container lifecycle (RestartNever container
880+
// fails) or due to external events like deletion or eviction. A terminal pod *should* have no running
881+
// containers, but to know that the pod has completed its lifecycle you must wait for containers to also
882+
// be terminal.
883+
containersTerminal = notRunning(status.ContainerStatuses)
884+
// The kubelet must accept config changes from the pod spec until it has reached a point where changes would
885+
// have no effect on any running container.
886+
podWorkerTerminal = status.Phase == v1.PodFailed || status.Phase == v1.PodSucceeded || (pod.DeletionTimestamp != nil && containersTerminal)
887+
return
888+
}
889+
890+
// podIsTerminated returns true if the provided pod is in a terminal phase ("Failed", "Succeeded") or
891+
// has been deleted and has no running containers. This corresponds to when a pod must accept changes to
892+
// its pod spec (e.g. terminating containers allow grace period to be shortened).
893+
func (kl *Kubelet) podIsTerminated(pod *v1.Pod) bool {
894+
_, podWorkerTerminal := kl.podAndContainersAreTerminal(pod)
895+
return podWorkerTerminal
879896
}
880897

881-
// IsPodTerminated returns true if the pod with the provided UID is in a terminated state ("Failed" or "Succeeded")
882-
// or if the pod has been deleted or removed
898+
// IsPodTerminated returns true if the pod with the provided UID is in a terminal phase ("Failed",
899+
// "Succeeded") or has been deleted and has no running containers. This corresponds to when a pod must
900+
// accept changes to its pod spec (e.g. terminating containers allow grace period to be shortened)
883901
func (kl *Kubelet) IsPodTerminated(uid types.UID) bool {
884902
pod, podFound := kl.podManager.GetPodByUID(uid)
885903
if !podFound {

0 commit comments

Comments
 (0)