updater整体逻辑是比较简单的。
(1)定义了一个NewUpdater的对象
(2)然后每隔1分钟,执行NewUpdater.RunOnce
1.1 updater参数
updater轮转周期,默认是1分钟 updaterInterval = flag.Duration("updater-interval", 1*time.Minute, `How often updater should run`) rs最小可用的副本数。比如默认是2表示,如果一个rs当前只有2个副本可用,本轮是不会驱逐的,保障rs服务的可用性 minReplicas = flag.Int("min-replicas", 2, `Minimum number of replicas to perform update`) pod每轮的驱逐比例 evictionToleranceFraction = flag.Float64("eviction-tolerance", 0.5,`Fraction of replica count that can be evicted for update, if more than one pod can be evicted.`) 驱逐限速,这里用了令牌桶算法,就个就是令牌桶的QPS。如果是0或者-1表示不限速,默认是不限速 evictionRateLimit = flag.Float64("eviction-rate-limit", -1,`Number of pods that can be evicted per seconds. A rate limit set to 0 or -1 will disable the rate limiter.`) 令牌桶的burst大小 evictionRateBurst = flag.Int("eviction-rate-burst", 1, `Burst of pods that can be evicted.`) 暴露Prometheus metrics指标的地址 address = flag.String("address", ":8943", "The address to expose Prometheus metrics.") 是否检验AdmissionControllerStatus。如果开启的话,updater必须等 useAdmissionControllerStatus = flag.Bool("use-admission-controller-status", true, "If true, updater will only evict pods when admission controller status is valid.") vpa作用的命名空间,默认是所有命名空间 namespace = os.Getenv("NAMESPACE") vpaObjectNamespace = flag.String("vpa-object-namespace", apiv1.NamespaceAll, "Namespace to search for VPA objects. Empty means all namespaces will be used.") )
1.2 NewUpdater结构体对象
// NewUpdater creates Updater with given configuration func NewUpdater( cgClient dynamic.Interface, // 为了操作cgroups资源,增加的dynamic客户端 cgGvr schema.GroupVersionResource, // cgroups的 gvr kubeClient kube_client.Interface, vpaClient *vpa_clientset.Clientset, minReplicasForEvicition int, evictionRateLimit float64, evictionRateBurst int, evictionToleranceFraction float64, useAdmissionControllerStatus bool, statusNamespace string, recommendationProcessor vpa_api_util.RecommendationProcessor, // 处理推荐资源相关的process,在vertical-pod-autoscaler/pkg/utils/vpa/capping.go evictionAdmission priority.PodEvictionAdmission, // 默认为空 selectorFetcher target.VpaTargetSelectorFetcher, // selector相关 priorityProcessor priority.PriorityProcessor, // 驱逐优先级处理。会更加资源diff进行过滤 namespace string, ) (Updater, error) { evictionRateLimiter := getRateLimiter(evictionRateLimit, evictionRateBurst) factory, err := eviction.NewPodsEvictionRestrictionFactory(kubeClient, minReplicasForEvicition, evictionToleranceFraction) if err != nil { return nil, fmt.Errorf("Failed to create eviction restriction factory: %v", err) } return &updater{ cgClient: cgClient, cgGvr: cgGvr, vpaLister: vpa_api_util.NewVpasLister(vpaClient, make(chan struct{}), namespace), podLister: newPodLister(kubeClient, namespace), eventRecorder: newEventRecorder(kubeClient), evictionFactory: factory, recommendationProcessor: recommendationProcessor, evictionRateLimiter: evictionRateLimiter, evictionAdmission: evictionAdmission, priorityProcessor: priorityProcessor, selectorFetcher: selectorFetcher, useAdmissionControllerStatus: useAdmissionControllerStatus, statusValidator: status.NewValidator( kubeClient, status.AdmissionControllerStatusName, statusNamespace, ), }, nil }
1.3 updater流程
每隔1分钟进行以下的运行逻辑:
获取所有的vpa, 并进行遍历,对于每个vpa而言,执行以下步骤
1.1 如果vpa.model != auto 或者vpa.model != recreate, 直接跳过这个vpa
1.2 根据vpa对应的selector, 获取所有的pods,假设为 allPods
1.3 从allPods中过滤deletionTimestamp != nil 的pods, 假设为 activesPods
1.4 从activesPods中过滤可以进行驱逐的pod, 假设为canEvictedPods. 判断逻辑如下:
updater启动时有两个参数:defaultUpdateThreshold = 0.10。podLifetimeUpdateThreshold = time.Hour * 12
含义为:
如果一个pod从开始运行到当前时间,没有超过12小时(podLifetimeUpdateThreshold),不能进行驱逐
如果一个pod从资源改变在 0.1范围内,不应该驱逐。这个0.1是 cpu + memory的和。
结合OOM事件,判断pod是否应该Evict,这个目前待确定细节
经过第一步,得到一个map, key=vap, value= canEvictedPods (这是一个列表,表示需要驱逐的pod)
遍历上诉map, 针对某个vpa进行以下逻辑判断。
updater启动时还有两个参数,控制没轮驱逐时pod的数量
evictionTolerance 表示当前没轮驱逐的比例,默认0.5。 假设当前vpa对应的canEvictedPods有10个pod, 那么当前只会驱逐5个。
min-replica 表示rs最少可用的pod数量。例如一个rs 当前只有2个pod running, 就是前面满足了驱逐条件,到这里也不会驱逐。
热升级调整的逻辑:
(1)修改 podLifetimeUpdateThreshold = 30s
(2)跳过canEvictedPods逻辑
每隔1分钟进行以下的运行逻辑:
获取所有的vpa, 并进行遍历,对于每个vpa而言,执行以下步骤
1.1 如果vpa.model != auto 或者vpa.model != recreate, 直接跳过这个vpa
1.2 根据vpa对应的selector, 获取所有的pods,假设为 allPods
1.3 从allPods中过滤deletionTimestamp != nil 的pods, 假设为 activesPods
1.4 从activesPods中过滤可以进行驱逐的pod, 假设为canEvictedPods. 判断逻辑如下:
updater启动时有两个参数:defaultUpdateThreshold = 0.10。podLifetimeUpdateThreshold = time.Hour * 12
含义为:
如果一个pod从开始运行到当前时间,没有超过12小时(podLifetimeUpdateThreshold),不能进行驱逐
如果一个pod从资源改变在 0.1范围内,不应该驱逐。这个0.1是 cpu + memory的和。
结合OOM事件,判断pod是否应该Evict,这个目前待确定细节
经过第一步,得到一个map, key=vap, value= canEvictedPods (这是一个列表,表示需要驱逐的pod)
遍历上诉map, 针对某个vpa进行以下逻辑判断。
updater启动时还有两个参数,控制没轮驱逐时pod的数量
evictionTolerance 表示当前没轮驱逐的比例,默认0.5。 假设当前vpa对应的canEvictedPods有10个pod, 那么当前只会驱逐5个。
min-replica 表示rs最少可用的pod数量。例如一个rs 当前只有2个pod running, 就是前面满足了驱逐条件,到这里也不会驱逐。
2.1 总结
感觉驱逐逻辑迁移到cgroupManager可以如下:
(1)继承上诉的步骤1,步骤2,因为是热升级,所以不用关注步骤三(考虑min-replica )
(2)OOM事件会影响pod驱逐,但是是热升级的情况下感觉可以忽略
vap修改的都是pod的request值,但是limit值会按照比例进行扩缩
Deploy yaml中没有指定limit,vpa也不会修改limit
vpa不会修改pod的QOS类型
举例说明会更清楚一点:
场景1 deploy yaml中设置了requet, limit
假设deploy yaml中定义的资源为:(这里limit:request=2:1)
resources: limits: cpu: 2 memory: 2Gi requests: cpu: 1 memory: 1Gi
而vpa推荐值为:cpu=25m mem=262144k。那么这个pod实际运行时的request如下:
resources: limits: //limit会改变,会维持limit:request=2:1的关系 cpu: 50m memory: 500Mi requests: // request采用的是vpa的值 cpu: 25m memory: 262144k
场景2 pod yaml只中设置了requet没有设置limit
假设pod yaml中定义的资源为:(这里没有limit)
resources: requests: cpu: 1 memory: 1Gi
而vpa推荐值为:cpu=25m mem=262144k。那么这个pod实际运行时的request如下:
resources: requests: // request采用的是vpa的值。也不会有limit cpu: 25m memory: 262144k
场景3 pod yaml没有设置resources
假设pod yaml中定义没有定义资源:(besteffort)
而vpa推荐值为:cpu=25m mem=262144k。那么这个pod实际运行时的request如下:
resources: requests: // request采用的是vpa的值。也不会有limit cpu: 25m memory: 262144k