浅析Resource Quota中limits计算机制

前言

        在生产环境中,通常需要通过配置资源配额(Resource Quota)来限制一个命名空间(namespace)能使用的资源量。在资源紧张的情况下,常常需要调整工作负载(workload)的请求值(requests)和限制值(limits),以确保工作负载能够顺利部署。本文将从Kubernetes源码的角度,简要分析Kubernetes如何计算Pod中的限制值(limits)对资源配额(Resource Quota)限制的影响。

源代码地址

k8s版本 V1.29

kubernetes/pkg/api/v1/resource/helpers.go at master · kubernetes/kubernetes · GitHub

源码解析

container分类

这里以Pod limits为例,计算给定Pod的资源限制(limits),包括普通容器和初始化容器的资源限制。

这里实际上把容器分成了3类

  • 普通容器
  • 初始化容器
    • 可重启初始化容器(RestartPolicy不为空,且RestartPolicy=Always)
    • 其他情况初始化容器

在1.29版本以前

initcontainer RestartPolicy只能为空默认为Always,且无法设置

在1.29版本以及之后

集群启用了 SidecarContainers 特性门控 (该特性自 Kubernetes v1.29 起默认启用),你可以为 Pod 的 initContainers 字段中列出的容器指定 restartPolicy

启动以上参数之后,RestartPolicy可以在container级别设置,此时initcontainer的RestartPolicy就可能为其他值,如果restartPolicy设置为always,则此初始化容器为特殊情况的边车容器,在执行完初始化之后不会退出,会随主容器一起运行

参考链接: 边车容器 | Kubernetes

源码

// PodLimits 计算根据提供的PodResourcesOptions选项计算Pod的资源限制(limits)。
// 如果PodResourcesOptions为nil,则返回的限制包含任何非零限制的Pod开销。
// 这个计算是API的一部分,必须作为API更改来审查。
func PodLimits(pod *v1.Pod, opts PodResourcesOptions) v1.ResourceList {
    // 尝试重用传递的maps,如果没有传递则分配新的
    limits := reuseOrClearResourceList(opts.Reuse)

    // 遍历Pod中的每个容器
    for _, container := range pod.Spec.Containers {
        // 如果提供了ContainerFn函数,则调用它处理容器资源限制
        if opts.ContainerFn != nil {
            opts.ContainerFn(container.Resources.Limits, podutil.Containers)
        }
        // 将容器的资源限制添加到总的limits中
        addResourceList(limits, container.Resources.Limits)
    }


    restartableInitContainerLimits := v1.ResourceList{}
    initContainerLimits := v1.ResourceList{}

    // 初始化容器定义了任何资源的最小值
    //
    // 假设 `InitContainerUse(i)` 是第i个初始化容器初始化时的资源需求,
    // 则 `InitContainerUse(i) = sum(重启初始化容器的资源,索引 < i) + 第i个初始化容器的资源`。
    //
    // 详细信息见 https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/753-sidecar-containers#exposing-pod-resource-requirements

    for _, container := range pod.Spec.InitContainers {
        containerLimits := container.Resources.Limits
        // 检查初始化容器是否标记为可重启的初始化容器,实际上目前initcontainer必须设置为可重启
        if container.RestartPolicy != nil && *container.RestartPolicy == v1.ContainerRestartPolicyAlways {
            // 将容器限制添加到总的limits中
            addResourceList(limits, containerLimits)

            // 跟踪累积的重启初始化容器资源
            addResourceList(restartableInitContainerLimits, containerLimits)
            containerLimits = restartableInitContainerLimits
        } else {
            // 创建一个临时的资源列表,包含当前初始化容器和累积的重启初始化容器资源
            tmp := v1.ResourceList{}
            addResourceList(tmp, containerLimits)
            addResourceList(tmp, restartableInitContainerLimits)
            containerLimits = tmp
        }

        // 如果提供了ContainerFn函数,则调用它处理初始化容器资源限制
        if opts.ContainerFn != nil {
            opts.ContainerFn(containerLimits, podutil.InitContainers)
        }
        // 取initContainerLimits和containerLimits的最大值
        maxResourceList(initContainerLimits, containerLimits)
    }

    // 取limits和initContainerLimits的最大值
    maxResourceList(limits, initContainerLimits)

    // 如果不排除开销,并且Pod的Spec中有开销,则添加开销到非零限制中
    if !opts.ExcludeOverhead && pod.Spec.Overhead != nil {
        for name, quantity := range pod.Spec.Overhead {
            if value, ok := limits[name]; ok && !value.IsZero() {
                value.Add(quantity)
                limits[name] = value
            }
        }
    }

    // 返回计算的资源限制
    return limits
}

重要概念

在上述代码中,有以下几点需要理解

  1. 需要取一个满足任何情况的最大limits计算
  2. 如果有普通容器和initcontainer,则先运行完initcontainer(其他情况初始化容器+可重启初始化容器(RestartPolicy不为空,RestartPolicy: Always)),再同时运行普通容器+可重启初始化容器(RestartPolicy不为空,RestartPolicy: Always)

Pod limits计算规则

理解了以上几点,那么Pod limits的计算规则就很容易得出了:

  1. 普通容器资源需求:计算Pod中所有普通容器的资源限制总和 + 可重启初始化容器(RestartPolicy不为空,RestartPolicy: Always)

  2. 初始化容器资源需求:其他情况启初始化容器的资源需求总和+可重启初始化容器(RestartPolicy不为空,RestartPolicy: Always)

  3. 计算公式:Pod资源总需求 = max(普通容器资源需求, 初始化容器资源需求)

典型情况

假设你的Pod包含以下容器:

  • 一个可重启的初始化容器,资源限制为:CPU 100m,内存 100Mi
  • 一个普通的初始化容器,资源限制为:CPU 200m,内存 200Mi
  • 一个主容器,资源限制为:CPU 300m,内存 300Mi

根据代码逻辑:

  1. 主容器:将CPU 300m和内存300Mi添加到limits中。
  2. 可重启初始化容器
    • 添加到limits中:limits现在为CPU 400m,内存400Mi。
    • 累加到restartableInitContainerLimits中:restartableInitContainerLimits为CPU 100m,内存100Mi。
  3. 普通初始化容器
    • 创建临时资源列表:临时资源为CPU 300m(200m + 100m),内存300Mi(200Mi + 100Mi)。
    • 取最大值:initContainerLimits现在为CPU 300m,内存300Mi。
  4. 比较limitsinitContainerLimits
    • limits取最大值后保持为CPU 400m,内存400Mi。

最终,Pod的资源限制(limits)为:

  • CPU:400m
  • 内存:400Mi

  • 20
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值