1. updater整体逻辑
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逻辑
2. 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, 就是前面满足了驱逐条件,到这里也不会驱逐。
2.1 总结
感觉驱逐逻辑迁移到cgroupManager可以如下:
(1)继承上诉的步骤1,步骤2,因为是热升级,所以不用关注步骤三(考虑min-replica )
(2)OOM事件会影响pod驱逐,但是是热升级的情况下感觉可以忽略
3.vpa的修改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