change rolling update logic to exclude sunsetting nodes
This commit is contained in:
@@ -47,7 +47,7 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get node to daemon pod mapping for daemon set %q: %v", ds.Name, err)
|
||||
}
|
||||
maxSurge, maxUnavailable, err := dsc.updatedDesiredNodeCounts(ctx, ds, nodeList, nodeToDaemonPods)
|
||||
maxSurge, maxUnavailable, desiredNumberScheduled, err := dsc.updatedDesiredNodeCounts(ctx, ds, nodeList, nodeToDaemonPods)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get unavailable numbers: %v", err)
|
||||
}
|
||||
@@ -140,10 +140,12 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae
|
||||
// * An old available pod is deleted if a new pod is available
|
||||
// * No more than maxSurge new pods are created for old available pods at any one time
|
||||
//
|
||||
var oldPodsToDelete []string
|
||||
var oldPodsToDelete []string // these pods are already updated or unavailable on sunsetted node
|
||||
var shouldNotRunPodsToDelete []string // candidate pods to be deleted on sunsetted nodes
|
||||
var candidateNewNodes []string
|
||||
var allowedNewNodes []string
|
||||
var numSurge int
|
||||
var numAvailable int
|
||||
|
||||
for nodeName, pods := range nodeToDaemonPods {
|
||||
newPod, oldPod, ok := findUpdatedPodsOnNode(ds, pods, hash)
|
||||
@@ -153,6 +155,18 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae
|
||||
numSurge++
|
||||
continue
|
||||
}
|
||||
|
||||
// first count availability for all the nodes (even the ones that we are sunsetting due to scheduling constraints)
|
||||
if oldPod != nil {
|
||||
if podutil.IsPodAvailable(oldPod, ds.Spec.MinReadySeconds, metav1.Time{Time: now}) {
|
||||
numAvailable++
|
||||
}
|
||||
} else if newPod != nil {
|
||||
if podutil.IsPodAvailable(newPod, ds.Spec.MinReadySeconds, metav1.Time{Time: now}) {
|
||||
numAvailable++
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case oldPod == nil:
|
||||
// we don't need to do anything to this node, the manage loop will handle it
|
||||
@@ -160,6 +174,15 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae
|
||||
// this is a surge candidate
|
||||
switch {
|
||||
case !podutil.IsPodAvailable(oldPod, ds.Spec.MinReadySeconds, metav1.Time{Time: now}):
|
||||
node, err := dsc.nodeLister.Get(nodeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get node for nodeName %q: %v", nodeName, err)
|
||||
}
|
||||
if shouldRun, _ := NodeShouldRunDaemonPod(node, ds); !shouldRun {
|
||||
logger.V(5).Info("DaemonSet pod on node is not available and does not match scheduling constraints, remove old pod", "daemonset", klog.KObj(ds), "node", nodeName, "oldPod", klog.KObj(oldPod))
|
||||
oldPodsToDelete = append(oldPodsToDelete, oldPod.Name)
|
||||
continue
|
||||
}
|
||||
// the old pod isn't available, allow it to become a replacement
|
||||
logger.V(5).Info("Pod on node is out of date and not available, allowing replacement", "daemonset", klog.KObj(ds), "pod", klog.KObj(oldPod), "node", klog.KRef("", nodeName))
|
||||
// record the replacement
|
||||
@@ -167,10 +190,19 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae
|
||||
allowedNewNodes = make([]string, 0, len(nodeToDaemonPods))
|
||||
}
|
||||
allowedNewNodes = append(allowedNewNodes, nodeName)
|
||||
case numSurge >= maxSurge:
|
||||
// no point considering any other candidates
|
||||
continue
|
||||
default:
|
||||
node, err := dsc.nodeLister.Get(nodeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get node for nodeName %q: %v", nodeName, err)
|
||||
}
|
||||
if shouldRun, _ := NodeShouldRunDaemonPod(node, ds); !shouldRun {
|
||||
shouldNotRunPodsToDelete = append(shouldNotRunPodsToDelete, oldPod.Name)
|
||||
continue
|
||||
}
|
||||
if numSurge >= maxSurge {
|
||||
// no point considering any other candidates
|
||||
continue
|
||||
}
|
||||
logger.V(5).Info("DaemonSet pod on node is out of date, this is a surge candidate", "daemonset", klog.KObj(ds), "pod", klog.KObj(oldPod), "node", klog.KRef("", nodeName))
|
||||
// record the candidate
|
||||
if candidateNewNodes == nil {
|
||||
@@ -194,6 +226,27 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae
|
||||
// use any of the candidates we can, including the allowedNewNodes
|
||||
logger.V(5).Info("DaemonSet allowing replacements", "daemonset", klog.KObj(ds), "replacements", len(allowedNewNodes), "maxSurge", maxSurge, "numSurge", numSurge, "candidates", len(candidateNewNodes))
|
||||
remainingSurge := maxSurge - numSurge
|
||||
|
||||
// With maxSurge, the application owner expects 100% availability.
|
||||
// When the scheduling constraint change from node A to node B, we do not want the application to stay
|
||||
// without any available pods. Only delete a pod on node A when a pod on node B becomes available.
|
||||
if deletablePodsNumber := numAvailable - desiredNumberScheduled; deletablePodsNumber > 0 {
|
||||
if shouldNotRunPodsToDeleteNumber := len(shouldNotRunPodsToDelete); deletablePodsNumber > shouldNotRunPodsToDeleteNumber {
|
||||
deletablePodsNumber = shouldNotRunPodsToDeleteNumber
|
||||
}
|
||||
for _, podToDeleteName := range shouldNotRunPodsToDelete[:deletablePodsNumber] {
|
||||
podToDelete, err := dsc.podLister.Pods(ds.Namespace).Get(podToDeleteName)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("couldn't get pod which should be deleted due to scheduling constraints %q: %v", podToDeleteName, err)
|
||||
}
|
||||
logger.V(5).Info("DaemonSet pod on node should be deleted due to scheduling constraints", "daemonset", klog.KObj(ds), "pod", klog.KObj(podToDelete), "node", podToDelete.Spec.NodeName)
|
||||
oldPodsToDelete = append(oldPodsToDelete, podToDeleteName)
|
||||
}
|
||||
}
|
||||
|
||||
if remainingSurge < 0 {
|
||||
remainingSurge = 0
|
||||
}
|
||||
@@ -525,9 +578,9 @@ func (dsc *DaemonSetsController) snapshot(ctx context.Context, ds *apps.DaemonSe
|
||||
return history, err
|
||||
}
|
||||
|
||||
// updatedDesiredNodeCounts calculates the true number of allowed unavailable or surge pods and
|
||||
// updatedDesiredNodeCounts calculates the true number of allowed surge, unavailable or desired scheduled pods and
|
||||
// updates the nodeToDaemonPods array to include an empty array for every node that is not scheduled.
|
||||
func (dsc *DaemonSetsController) updatedDesiredNodeCounts(ctx context.Context, ds *apps.DaemonSet, nodeList []*v1.Node, nodeToDaemonPods map[string][]*v1.Pod) (int, int, error) {
|
||||
func (dsc *DaemonSetsController) updatedDesiredNodeCounts(ctx context.Context, ds *apps.DaemonSet, nodeList []*v1.Node, nodeToDaemonPods map[string][]*v1.Pod) (int, int, int, error) {
|
||||
var desiredNumberScheduled int
|
||||
logger := klog.FromContext(ctx)
|
||||
for i := range nodeList {
|
||||
@@ -545,12 +598,12 @@ func (dsc *DaemonSetsController) updatedDesiredNodeCounts(ctx context.Context, d
|
||||
|
||||
maxUnavailable, err := util.UnavailableCount(ds, desiredNumberScheduled)
|
||||
if err != nil {
|
||||
return -1, -1, fmt.Errorf("invalid value for MaxUnavailable: %v", err)
|
||||
return -1, -1, -1, fmt.Errorf("invalid value for MaxUnavailable: %v", err)
|
||||
}
|
||||
|
||||
maxSurge, err := util.SurgeCount(ds, desiredNumberScheduled)
|
||||
if err != nil {
|
||||
return -1, -1, fmt.Errorf("invalid value for MaxSurge: %v", err)
|
||||
return -1, -1, -1, fmt.Errorf("invalid value for MaxSurge: %v", err)
|
||||
}
|
||||
|
||||
// if the daemonset returned with an impossible configuration, obey the default of unavailable=1 (in the
|
||||
@@ -560,7 +613,7 @@ func (dsc *DaemonSetsController) updatedDesiredNodeCounts(ctx context.Context, d
|
||||
maxUnavailable = 1
|
||||
}
|
||||
logger.V(5).Info("DaemonSet with maxSurge and maxUnavailable", "daemonset", klog.KObj(ds), "maxSurge", maxSurge, "maxUnavailable", maxUnavailable)
|
||||
return maxSurge, maxUnavailable, nil
|
||||
return maxSurge, maxUnavailable, desiredNumberScheduled, nil
|
||||
}
|
||||
|
||||
type historiesByRevision []*apps.ControllerRevision
|
||||
|
Reference in New Issue
Block a user