// cleanupUnhealthyReplicas will scale down old replica sets with unhealthy replicas, so that all unhealthy replicas will be deleted. func (dc *DeploymentController) cleanupUnhealthyReplicas(oldRSs []*extensions.ReplicaSet, deployment *extensions.Deployment, maxCleanupCount int32) ([]*extensions.ReplicaSet, int32, error) { sort.Sort(controller.ReplicaSetsByCreationTimestamp(oldRSs)) // Safely scale down all old replica sets with unhealthy replicas. Replica set will sort the pods in the order // such that not-ready < ready, unscheduled < scheduled, and pending < running. This ensures that unhealthy replicas will // been deleted first and won't increase unavailability. totalScaledDown := int32(0) for i, targetRS := range oldRSs { if totalScaledDown >= maxCleanupCount { break } if *(targetRS.Spec.Replicas) == 0 { // cannot scale down this replica set. continue } glog.V(4).Infof("Found %d available pods in old RS %s/%s", targetRS.Status.AvailableReplicas, targetRS.Namespace, targetRS.Name) if *(targetRS.Spec.Replicas) == targetRS.Status.AvailableReplicas { // no unhealthy replicas found, no scaling required. continue } scaledDownCount := int32(integer.IntMin(int(maxCleanupCount-totalScaledDown), int(*(targetRS.Spec.Replicas)-targetRS.Status.AvailableReplicas))) newReplicasCount := *(targetRS.Spec.Replicas) - scaledDownCount if newReplicasCount > *(targetRS.Spec.Replicas) { return nil, 0, fmt.Errorf("when cleaning up unhealthy replicas, got invalid request to scale down %s/%s %d -> %d", targetRS.Namespace, targetRS.Name, *(targetRS.Spec.Replicas), newReplicasCount) } _, updatedOldRS, err := dc.scaleReplicaSetAndRecordEvent(targetRS, newReplicasCount, deployment) if err != nil { return nil, totalScaledDown, err } totalScaledDown += scaledDownCount oldRSs[i] = updatedOldRS } return oldRSs, totalScaledDown, nil }
// NewRSNewReplicas calculates the number of replicas a deployment's new RS should have. // When one of the followings is true, we're rolling out the deployment; otherwise, we're scaling it. // 1) The new RS is saturated: newRS's replicas == deployment's replicas // 2) Max number of pods allowed is reached: deployment's replicas + maxSurge == all RSs' replicas func NewRSNewReplicas(deployment *extensions.Deployment, allRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet) (int32, error) { switch deployment.Spec.Strategy.Type { case extensions.RollingUpdateDeploymentStrategyType: // Check if we can scale up. maxSurge, err := intstrutil.GetValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxSurge, int(*(deployment.Spec.Replicas)), true) if err != nil { return 0, err } // Find the total number of pods currentPodCount := GetReplicaCountForReplicaSets(allRSs) maxTotalPods := *(deployment.Spec.Replicas) + int32(maxSurge) if currentPodCount >= maxTotalPods { // Cannot scale up. return *(newRS.Spec.Replicas), nil } // Scale up. scaleUpCount := maxTotalPods - currentPodCount // Do not exceed the number of desired replicas. scaleUpCount = int32(integer.IntMin(int(scaleUpCount), int(*(deployment.Spec.Replicas)-*(newRS.Spec.Replicas)))) return *(newRS.Spec.Replicas) + scaleUpCount, nil case extensions.RecreateDeploymentStrategyType: return *(deployment.Spec.Replicas), nil default: return 0, fmt.Errorf("deployment type %v isn't supported", deployment.Spec.Strategy.Type) } }
// scaleDownOldReplicaSetsForRollingUpdate scales down old replica sets when deployment strategy is "RollingUpdate". // Need check maxUnavailable to ensure availability func (dc *DeploymentController) scaleDownOldReplicaSetsForRollingUpdate(allRSs []*extensions.ReplicaSet, oldRSs []*extensions.ReplicaSet, deployment *extensions.Deployment) (int32, error) { maxUnavailable := deploymentutil.MaxUnavailable(*deployment) // Check if we can scale down. minAvailable := *(deployment.Spec.Replicas) - maxUnavailable // Find the number of available pods. availablePodCount := deploymentutil.GetAvailableReplicaCountForReplicaSets(allRSs) if availablePodCount <= minAvailable { // Cannot scale down. return 0, nil } glog.V(4).Infof("Found %d available pods in deployment %s, scaling down old RSes", availablePodCount, deployment.Name) sort.Sort(controller.ReplicaSetsByCreationTimestamp(oldRSs)) totalScaledDown := int32(0) totalScaleDownCount := availablePodCount - minAvailable for _, targetRS := range oldRSs { if totalScaledDown >= totalScaleDownCount { // No further scaling required. break } if *(targetRS.Spec.Replicas) == 0 { // cannot scale down this ReplicaSet. continue } // Scale down. scaleDownCount := int32(integer.IntMin(int(*(targetRS.Spec.Replicas)), int(totalScaleDownCount-totalScaledDown))) newReplicasCount := *(targetRS.Spec.Replicas) - scaleDownCount if newReplicasCount > *(targetRS.Spec.Replicas) { return 0, fmt.Errorf("when scaling down old RS, got invalid request to scale down %s/%s %d -> %d", targetRS.Namespace, targetRS.Name, *(targetRS.Spec.Replicas), newReplicasCount) } _, _, err := dc.scaleReplicaSetAndRecordEvent(targetRS, newReplicasCount, deployment) if err != nil { return totalScaledDown, err } totalScaledDown += scaleDownCount } return totalScaledDown, nil }
func isLess(i, j reflect.Value) (bool, error) { switch i.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return i.Int() < j.Int(), nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return i.Uint() < j.Uint(), nil case reflect.Float32, reflect.Float64: return i.Float() < j.Float(), nil case reflect.String: return i.String() < j.String(), nil case reflect.Ptr: return isLess(i.Elem(), j.Elem()) case reflect.Struct: // sort metav1.Time in := i.Interface() if t, ok := in.(metav1.Time); ok { return t.Before(j.Interface().(metav1.Time)), nil } // fallback to the fields comparison for idx := 0; idx < i.NumField(); idx++ { less, err := isLess(i.Field(idx), j.Field(idx)) if err != nil || !less { return less, err } } return true, nil case reflect.Array, reflect.Slice: // note: the length of i and j may be different for idx := 0; idx < integer.IntMin(i.Len(), j.Len()); idx++ { less, err := isLess(i.Index(idx), j.Index(idx)) if err != nil || !less { return less, err } } return true, nil case reflect.Interface: switch itype := i.Interface().(type) { case uint8: if jtype, ok := j.Interface().(uint8); ok { return itype < jtype, nil } case uint16: if jtype, ok := j.Interface().(uint16); ok { return itype < jtype, nil } case uint32: if jtype, ok := j.Interface().(uint32); ok { return itype < jtype, nil } case uint64: if jtype, ok := j.Interface().(uint64); ok { return itype < jtype, nil } case int8: if jtype, ok := j.Interface().(int8); ok { return itype < jtype, nil } case int16: if jtype, ok := j.Interface().(int16); ok { return itype < jtype, nil } case int32: if jtype, ok := j.Interface().(int32); ok { return itype < jtype, nil } case int64: if jtype, ok := j.Interface().(int64); ok { return itype < jtype, nil } case uint: if jtype, ok := j.Interface().(uint); ok { return itype < jtype, nil } case int: if jtype, ok := j.Interface().(int); ok { return itype < jtype, nil } case float32: if jtype, ok := j.Interface().(float32); ok { return itype < jtype, nil } case float64: if jtype, ok := j.Interface().(float64); ok { return itype < jtype, nil } case string: if jtype, ok := j.Interface().(string); ok { return itype < jtype, nil } default: return false, fmt.Errorf("unsortable type: %T", itype) } return false, fmt.Errorf("unsortable interface: %v", i.Kind()) default: return false, fmt.Errorf("unsortable type: %v", i.Kind()) } }