/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package deployment
import (
"context"
apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/deployment/util"
)
// rolloutRecreate implements the logic for recreating a replica set.
func (dc *DeploymentController) rolloutRecreate(ctx context.Context, d *apps.Deployment, rsList []*apps.ReplicaSet, podMap map[types.UID][]*v1.Pod) error {
// Don't create a new RS if not already existed, so that we avoid scaling up before scaling down.
newRS, oldRSs, err := dc.getAllReplicaSetsAndSyncRevision(ctx, d, rsList, false)
if err != nil {
return err
}
allRSs := append(oldRSs, newRS)
activeOldRSs := controller.FilterActiveReplicaSets(oldRSs)
// scale down old replica sets.
scaledDown, err := dc.scaleDownOldReplicaSetsForRecreate(ctx, activeOldRSs, d)
if err != nil {
return err
}
if scaledDown {
// Update DeploymentStatus.
return dc.syncRolloutStatus(ctx, allRSs, newRS, d)
}
// Do not process a deployment when it has old pods running.
if oldPodsRunning(newRS, oldRSs, podMap) {
return dc.syncRolloutStatus(ctx, allRSs, newRS, d)
}
// If we need to create a new RS, create it now.
if newRS == nil {
newRS, oldRSs, err = dc.getAllReplicaSetsAndSyncRevision(ctx, d, rsList, true)
if err != nil {
return err
}
allRSs = append(oldRSs, newRS)
}
// scale up new replica set.
if _, err := dc.scaleUpNewReplicaSetForRecreate(ctx, newRS, d); err != nil {
return err
}
if util.DeploymentComplete(d, &d.Status) {
if err := dc.cleanupDeployment(ctx, oldRSs, d); err != nil {
return err
}
}
// Sync deployment status.
return dc.syncRolloutStatus(ctx, allRSs, newRS, d)
}
//该函数实现了Deployment控制器中重新创建副本集的逻辑。具体步骤如下:
//1. 调用getAllReplicaSetsAndSyncRevision函数获取当前所有的副本集,并同步修订版本。如果已有新的副本集,则不再创建新的副本集,以避免先扩大规模再缩小规模。
//2. 过滤出活动的旧副本集。
//3. 调用scaleDownOldReplicaSetsForRecreate函数缩小旧副本集的规模。如果成功缩小规模,则更新Deployment状态。
//4. 如果存在正在运行的旧Pods,则只更新Deployment状态。
//5. 如果需要创建新的副本集,则调用getAllReplicaSetsAndSyncRevision函数创建新的副本集,并更新allRSs。
//6. 调用scaleUpNewReplicaSetForRecreate函数扩大新副本集的规模。
//7. 如果Deployment已完成,则调用cleanupDeployment函数清理旧的副本集。
//8. 最后,调用syncRolloutStatus函数更新Deployment状态。
// scaleDownOldReplicaSetsForRecreate scales down old replica sets when deployment strategy is "Recreate".
func (dc *DeploymentController) scaleDownOldReplicaSetsForRecreate(ctx context.Context, oldRSs []*apps.ReplicaSet, deployment *apps.Deployment) (bool, error) {
scaled := false
for i := range oldRSs {
rs := oldRSs[i]
// Scaling not required.
if *(rs.Spec.Replicas) == 0 {
continue
}
scaledRS, updatedRS, err := dc.scaleReplicaSetAndRecordEvent(ctx, rs, 0, deployment)
if err != nil {
return false, err
}
if scaledRS {
oldRSs[i] = updatedRS
scaled = true
}
}
return scaled, nil
}
//该函数用于在Recreate部署策略下,将旧的ReplicaSet缩容为0。
//它遍历旧的ReplicaSet列表,对每个非0副本数的ReplicaSet调用scaleReplicaSetAndRecordEvent函数进行缩容,并记录事件。
//如果有任何一个ReplicaSet成功缩容,则函数返回true,否则返回false。
// oldPodsRunning returns whether there are old pods running or any of the old ReplicaSets thinks that it runs pods.
func oldPodsRunning(newRS *apps.ReplicaSet, oldRSs []*apps.ReplicaSet, podMap map[types.UID][]*v1.Pod) bool {
if oldPods := util.GetActualReplicaCountForReplicaSets(oldRSs); oldPods > 0 {
return true
}
for rsUID, podList := range podMap {
// If the pods belong to the new ReplicaSet, ignore.
if newRS != nil && newRS.UID == rsUID {
continue
}
for _, pod := range podList {
switch pod.Status.Phase {
case v1.PodFailed, v1.PodSucceeded:
// Don't count pods in terminal state.
continue
case v1.PodUnknown:
// v1.PodUnknown is a deprecated status.
// This logic is kept for backward compatibility.
// This used to happen in situation like when the node is temporarily disconnected from the cluster.
// If we can't be sure that the pod is not running, we have to count it.
return true
default:
// Pod is not in terminal phase.
return true
}
}
}
return false
}
//该函数用于判断是否有旧的Pod正在运行或任何一个旧的ReplicaSet认为它正在运行Pod。
//函数首先通过util.GetActualReplicaCountForReplicaSets函数获取旧的ReplicaSet的实际副本数,如果大于0,则返回true。
//然后,函数遍历podMap,检查每个Pod的状态,如果Pod属于新的ReplicaSet,则忽略;
//如果Pod处于失败或成功状态,则继续;如果Pod状态未知,则为了向后兼容,返回true;
//否则,返回true。
//如果遍历结束后没有返回true,则返回false。
// scaleUpNewReplicaSetForRecreate scales up new replica set when deployment strategy is "Recreate".
func (dc *DeploymentController) scaleUpNewReplicaSetForRecreate(ctx context.Context, newRS *apps.ReplicaSet, deployment *apps.Deployment) (bool, error) {
scaled, _, err := dc.scaleReplicaSetAndRecordEvent(ctx, newRS, *(deployment.Spec.Replicas), deployment)
return scaled, err
}
//该函数用于在Recreate策略下,增加新副本集的副本数。
//函数内部调用了scaleReplicaSetAndRecordEvent函数,传入新副本集、部署的期望副本数和部署本身,该函数会尝试增加新副本集的副本数,并记录相应的事件。
//函数返回一个布尔值和一个错误,表示是否成功增加副本数以及是否发生了错误。