资源配额

当多个用户或团队共享一个具有固定数量节点的集群时,会担心一个团队可能会使用超过其公平份额的资源。

资源配额是管理员解决此问题的工具。

资源配额由 ResourceQuota 对象定义,它提供了限制每个命名空间聚合资源消耗的约束。它可以限制命名空间中可以创建的对象的类型数量,以及该命名空间中资源可能消耗的计算资源总量。

资源配额的工作方式如下

  • 不同的团队在不同的命名空间中工作。这可以通过 RBAC 来强制执行。

  • 管理员为每个命名空间创建一个 ResourceQuota。

  • 用户在命名空间中创建资源(Pod、服务等),配额系统会跟踪使用情况,以确保其不超过 ResourceQuota 中定义的硬资源限制。

  • 如果创建或更新资源违反了配额约束,则请求将失败,并显示 HTTP 状态代码 403 FORBIDDEN 以及解释将要违反的约束的消息。

  • 如果在命名空间中为 cpumemory 等计算资源启用了配额,则用户必须指定这些值的请求或限制;否则,配额系统可能会拒绝创建 Pod。提示:使用 LimitRanger 准入控制器来强制要求未做出计算资源要求的 Pod 的默认值。

    有关如何避免此问题的示例,请参见演练

ResourceQuota 对象的名称必须是有效的DNS 子域名

以下是可以使用命名空间和配额创建的策略示例

  • 在容量为 32 GiB RAM 和 16 个核心的集群中,允许 A 团队使用 20 GiB 和 10 个核心,允许 B 团队使用 10 GiB 和 4 个核心,并保留 2 GiB 和 2 个核心以供将来分配。
  • 限制 “testing” 命名空间使用 1 个核心和 1 GiB RAM。允许 “production” 命名空间使用任何数量。

如果集群的总容量小于命名空间的配额总和,则可能会发生资源争用。这是按先到先得的原则处理的。

争用和配额更改都不会影响已创建的资源。

启用资源配额

许多 Kubernetes 发行版默认启用 ResourceQuota 支持。当API 服务器 --enable-admission-plugins= 标志以 ResourceQuota 作为其参数之一时,便会启用此功能。

当特定命名空间中存在 ResourceQuota 时,在该命名空间中强制执行资源配额。

计算资源配额

你可以限制在给定命名空间中可以请求的计算资源总和。

支持以下资源类型

资源名称描述
limits.cpu在所有非终端状态的 Pod 中,CPU 限制的总和不能超过此值。
limits.memory在所有非终端状态的 Pod 中,内存限制的总和不能超过此值。
requests.cpu在所有非终端状态的 Pod 中,CPU 请求的总和不能超过此值。
requests.memory在所有非终端状态的 Pod 中,内存请求的总和不能超过此值。
hugepages-<size>在所有非终端状态的 Pod 中,指定大小的巨页请求数不能超过此值。
cpurequests.cpu 相同
memoryrequests.memory 相同

扩展资源的资源配额

除了上述资源外,在 1.10 版本中,添加了对扩展资源的配额支持。

由于不允许扩展资源超额使用,因此在配额中为同一扩展资源同时指定 requestslimits 没有意义。因此,对于扩展资源,仅允许使用带有前缀 requests. 的配额项。

以 GPU 资源为例,如果资源名称为 nvidia.com/gpu,并且你想将命名空间中请求的 GPU 总数限制为 4,则可以定义如下配额

  • requests.nvidia.com/gpu: 4

有关更多详细信息,请参见查看和设置配额

存储资源配额

你可以限制在给定命名空间中可以请求的存储资源总和。

此外,你可以根据关联的存储类限制存储资源的消耗。

资源名称描述
requests.storage在所有持久卷申领中,存储请求的总和不能超过此值。
persistentvolumeclaims命名空间中可以存在的 PersistentVolumeClaims 总数。
<storage-class-name>.storageclass.storage.k8s.io/requests.storage在与 <storage-class-name> 关联的所有持久卷申领中,存储请求的总和不能超过此值。
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims在与 <storage-class-name> 关联的所有持久卷申领中,命名空间中可以存在的持久卷申领总数。

例如,如果你想分别使用 gold StorageClass 和 bronze StorageClass 配额存储,则可以定义如下配额

  • gold.storageclass.storage.k8s.io/requests.storage: 500Gi
  • bronze.storageclass.storage.k8s.io/requests.storage: 100Gi

在 1.8 版本中,作为 alpha 功能添加了对本地临时存储的配额支持

资源名称描述
requests.ephemeral-storage在命名空间中的所有 Pod 中,本地临时存储请求的总和不能超过此值。
limits.ephemeral-storage在命名空间中的所有 Pod 中,本地临时存储限制的总和不能超过此值。
ephemeral-storagerequests.ephemeral-storage 相同。

对象计数配额

你可以使用以下语法为 Kubernetes API 中**一种特定资源类型**的总数设置配额

  • 对于来自非核心组的资源,使用 count/<resource>.<group>
  • 对于来自核心组的资源,使用 count/<resource>

以下是用户可能希望置于对象计数配额下的资源示例集

  • count/persistentvolumeclaims
  • count/services
  • count/secrets
  • count/configmaps
  • count/replicationcontrollers
  • count/deployments.apps
  • count/replicasets.apps
  • count/statefulsets.apps
  • count/jobs.batch
  • count/cronjobs.batch

如果以此方式定义配额,则它适用于作为 API 服务器一部分的 Kubernetes API,以及任何由 CustomResourceDefinition 支持的自定义资源。如果你使用 API 聚合 来添加未定义为 CustomResourceDefinition 的其他自定义 API,则核心 Kubernetes 控制平面不会强制执行聚合 API 的配额。如果自定义 API 适用,则扩展 API 服务器应提供配额强制执行。例如,要在 example.com API 组中的 widgets 自定义资源上创建配额,请使用 count/widgets.example.com

当使用这种资源配额(几乎适用于所有对象类型)时,如果对象类型在控制平面中存在(已定义),则该对象会根据配额收费。这些类型的配额可用于防止存储资源耗尽。例如,你可能想限制服务器中 Secret 的数量,因为它们的大小很大。集群中过多的 Secret 实际上会阻止服务器和控制器启动。你可以为 Job 设置配额,以防止配置不当的 CronJob。在命名空间中创建太多 Job 的 CronJob 可能会导致拒绝服务。

还有另一种语法,仅为某些资源设置相同类型的配额。支持以下类型

资源名称描述
configmaps命名空间中可以存在的 ConfigMap 总数。
persistentvolumeclaims命名空间中可以存在的 PersistentVolumeClaims 总数。
pods命名空间中可以存在的非终端状态的 Pod 总数。如果 .status.phase in (Failed, Succeeded) 为真,则 Pod 处于终端状态。
replicationcontrollers命名空间中可以存在的 ReplicationController 总数。
resourcequotas命名空间中可以存在的 ResourceQuota 总数。
服务(services)命名空间中可以存在的服务总数。
services.loadbalancers命名空间中可以存在的 LoadBalancer 类型的服务总数。
services.nodeports可以分配给命名空间中 NodePortLoadBalancer 类型的服务的 NodePorts 总数。
密钥(secrets)命名空间中可以存在的密钥总数。

例如,pods 配额计算并强制限制单个命名空间中创建的、非终止状态的 pods 的最大数量。您可能希望在命名空间上设置 pods 配额,以避免用户创建大量小型 Pod 并耗尽集群的 Pod IP 资源的情况。

您可以在查看和设置配额中找到更多示例。

配额范围

每个配额都可以有一组关联的 scopes。只有在资源与枚举范围的交集匹配时,配额才会度量资源的使用情况。

当向配额添加范围时,它会将所支持的资源数量限制为与该范围相关的资源。在允许的集合之外的配额上指定的资源会导致验证错误。

范围描述
正在终止(Terminating)匹配 .spec.activeDeadlineSeconds >= 0 的 Pod。
未终止(NotTerminating)匹配 .spec.activeDeadlineSecondsnil 的 Pod。
尽力而为(BestEffort)匹配具有尽力而为服务质量的 Pod。
非尽力而为(NotBestEffort)匹配不具有尽力而为服务质量的 Pod。
PriorityClass匹配引用指定的优先级类的 Pod。
跨命名空间 Pod 亲和性(CrossNamespacePodAffinity)匹配具有跨命名空间 Pod (反)亲和性术语的 Pod。

BestEffort 范围将配额限制为跟踪以下资源

  • pods

TerminatingNotTerminatingNotBestEffortPriorityClass 范围将配额限制为跟踪以下资源

  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

请注意,您不能在同一个配额中同时指定 TerminatingNotTerminating 范围,也不能在同一个配额中同时指定 BestEffortNotBestEffort 范围。

scopeSelectoroperator 字段中支持以下值

  • In(在其中)
  • NotIn(不在其中)
  • Exists(存在)
  • DoesNotExist(不存在)

当使用以下值之一作为定义 scopeSelector 时的 scopeName 时,operator 必须是 Exists

  • 正在终止(Terminating)
  • 未终止(NotTerminating)
  • 尽力而为(BestEffort)
  • 非尽力而为(NotBestEffort)

如果 operatorInNotIn,则 values 字段必须至少有一个值。例如

  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values:
          - middle

如果 operatorExistsDoesNotExist,则不能指定 values 字段。

每个优先级类的资源配额

功能状态: Kubernetes v1.17 [稳定]

可以以特定的优先级创建 Pod。您可以使用配额规范中的 scopeSelector 字段,根据 Pod 的优先级来控制 Pod 对系统资源的消耗。

只有当配额规范中的 scopeSelector 选择 Pod 时,配额才会被匹配并消耗。

当使用 scopeSelector 字段为优先级类设定配额范围时,配额对象被限制为仅跟踪以下资源

  • pods
  • cpu
  • memory
  • ephemeral-storage
  • limits.cpu
  • limits.memory
  • limits.ephemeral-storage
  • requests.cpu
  • requests.memory
  • requests.ephemeral-storage

此示例创建一个配额对象,并将其与特定优先级的 Pod 匹配。该示例的工作方式如下

  • 集群中的 Pod 具有三个优先级类之一:“低”、“中”、“高”。
  • 为每个优先级创建一个配额对象。

将以下 YAML 保存到文件 quota.yml

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-high
  spec:
    hard:
      cpu: "1000"
      memory: 200Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["high"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-medium
  spec:
    hard:
      cpu: "10"
      memory: 20Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["medium"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "5"
      memory: 10Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["low"]

使用 kubectl create 应用 YAML。

kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

使用 kubectl describe quota 验证 Used 配额是否为 0

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     1k
memory      0     200Gi
pods        0     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

创建一个优先级为“高”的 Pod。将以下 YAML 保存到文件 high-priority-pod.yml

apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high

使用 kubectl create 应用它。

kubectl create -f ./high-priority-pod.yml

验证“高”优先级配额 pods-high 的“Used”统计信息已更改,而其他两个配额未更改。

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  1k
memory      10Gi  200Gi
pods        1     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

跨命名空间 Pod 亲和性配额

功能状态: Kubernetes v1.24 [稳定]

操作员可以使用 CrossNamespacePodAffinity 配额范围来限制哪些命名空间允许拥有跨命名空间的亲和性术语的 Pod。具体来说,它控制允许哪些 Pod 在 Pod 亲和性术语中设置 namespacesnamespaceSelector 字段。

阻止用户使用跨命名空间亲和性术语可能是期望的,因为具有反亲和性约束的 Pod 可能会阻止来自所有其他命名空间的 Pod 在故障域中进行调度。

通过使用此范围,操作员可以通过在该命名空间中创建一个具有 CrossNamespacePodAffinity 范围且硬限制为 0 的资源配额对象来阻止某些命名空间(在下面的示例中为 foo-ns)拥有使用跨命名空间 Pod 亲和性的 Pod。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: disable-cross-namespace-affinity
  namespace: foo-ns
spec:
  hard:
    pods: "0"
  scopeSelector:
    matchExpressions:
    - scopeName: CrossNamespacePodAffinity
      operator: Exists

如果操作员希望默认禁止使用 namespacesnamespaceSelector,而仅允许特定命名空间使用,则可以通过将 kube-apiserver 标志 --admission-control-config-file 设置为以下配置文件的路径,将 CrossNamespacePodAffinity 配置为有限资源

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: CrossNamespacePodAffinity
        operator: Exists

通过上述配置,只有在其创建的命名空间中存在具有 CrossNamespacePodAffinity 范围且硬限制大于或等于使用这些字段的 Pod 数量的资源配额对象时,Pod 才能在 Pod 亲和性中使用 namespacesnamespaceSelector

请求与限制的比较

在分配计算资源时,每个容器可以为 CPU 或内存指定请求和限制值。可以将配额配置为配额其中任一值。

如果配额为 requests.cpurequests.memory 指定了一个值,则它要求每个传入容器对这些资源进行显式请求。如果配额为 limits.cpulimits.memory 指定了一个值,则它要求每个传入容器为这些资源指定显式限制。

查看和设置配额

kubectl 支持创建、更新和查看配额

kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s
object-counts           32s
kubectl describe quota compute-resources --namespace=myspace
Name:                    compute-resources
Namespace:               myspace
Resource                 Used  Hard
--------                 ----  ----
limits.cpu               0     2
limits.memory            0     2Gi
requests.cpu             0     1
requests.memory          0     1Gi
requests.nvidia.com/gpu  0     4
kubectl describe quota object-counts --namespace=myspace
Name:                   object-counts
Namespace:              myspace
Resource                Used    Hard
--------                ----    ----
configmaps              0       10
persistentvolumeclaims  0       4
pods                    0       4
replicationcontrollers  0       20
secrets                 1       10
services                0       10
services.loadbalancers  0       2

kubectl 还支持使用语法 count/<resource>.<group> 为所有标准命名空间资源设置对象计数配额

kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name:                         test
Namespace:                    myspace
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        1     2
count/pods                    2     3
count/replicasets.apps        1     4
count/secrets                 1     4

配额和集群容量

ResourceQuota 与集群容量无关。它们以绝对单位表示。因此,如果您向集群添加节点,这并 *不* 会自动使每个命名空间都能够消耗更多资源。

有时可能需要更复杂的策略,例如

  • 在多个团队之间按比例分配集群总资源。
  • 允许每个租户根据需要增长资源使用量,但设置一个宽松的限制以防止意外的资源耗尽。
  • 检测来自一个命名空间的需求、添加节点并增加配额。

可以通过编写一个“控制器”,监视配额使用情况并根据其他信号调整每个命名空间的配额硬限制,使用 ResourceQuotas 作为构建块来实现此类策略。

请注意,资源配额会划分集群的聚合资源,但它不会对节点施加任何限制:来自多个命名空间的 Pod 可以在同一节点上运行。

默认限制优先级类消耗

可能需要只在存在匹配的配额对象的情况下,才允许在命名空间中使用特定优先级(例如“cluster-services”)的 Pod。

通过这种机制,操作员可以将某些高优先级类的使用限制为有限数量的命名空间,并且并非每个命名空间都能够在默认情况下使用这些优先级类。

为了强制执行此操作,应使用 kube-apiserver 标志 --admission-control-config-file 将路径传递给以下配置文件

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

然后,在 kube-system 命名空间中创建一个资源配额对象

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-cluster-services
spec:
  scopeSelector:
    matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created

在这种情况下,如果满足以下条件,则将允许创建 Pod

  1. 未指定 Pod 的 priorityClassName
  2. Pod 的 priorityClassName 指定为 cluster-services 以外的值。
  3. Pod 的 priorityClassName 设置为 cluster-services,它将在 kube-system 命名空间中创建,并且已通过资源配额检查。

如果 Pod 的 priorityClassName 设置为 cluster-services,并且它将在 kube-system 以外的命名空间中创建,则 Pod 创建请求将被拒绝。

下一步

上次修改时间:2024 年 12 月 04 日下午 3:45 PST:清理策略概念页面:资源配额 (40e5543854)