资源配额

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

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

资源配额(由 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 发行版默认情况下启用了资源配额支持。当 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命名空间中可以存在的 持久卷声明 的总数。
<storage-class-name>.storageclass.storage.k8s.io/requests.storage在与 <storage-class-name> 关联的所有持久卷声明中,存储请求总和不能超过此值。
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims在与 <storage-class-name> 关联的所有持久卷声明中,命名空间中可以存在的 持久卷声明 的总数。

例如,如果操作员想要将 gold 存储类与 bronze 存储类分开进行配额,则操作员可以定义以下配额

  • 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

如果您以这种方式定义配额,它将应用于 Kubernetes API 服务器中包含的 Kubernetes API,以及由 CustomResourceDefinition 支持的任何自定义资源。如果您使用 API 聚合 添加未定义为 CustomResourceDefinition 的其他自定义 API,则核心 Kubernetes 控制平面不会对聚合 API 强制执行配额。扩展 API 服务器应提供配额强制执行(如果对自定义 API 适用)。例如,要对 example.com API 组中的 widgets 自定义资源创建配额,请使用 count/widgets.example.com

使用这种资源配额(几乎针对所有对象类型)时,如果控制平面中存在对象类型(已定义),则该对象将计入配额。这些类型的配额对于防止存储资源耗尽很有用。例如,您可能希望限制服务器中的 Secrets 数量,因为它们的大小很大。集群中的 Secrets 太多实际上会阻止服务器和控制器启动。您可以为 Jobs 设置配额以防止配置错误的 CronJob。创建太多 Jobs 的 CronJob 可能会导致拒绝服务。

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

资源名称描述
configmaps命名空间中可以存在的 ConfigMap 的总数。
persistentvolumeclaims命名空间中可以存在的 持久卷声明 的总数。
pods命名空间中可以存在的不处于终止状态的 Pod 的总数。如果 .status.phase in (Failed, Succeeded) 为真,则 Pod 处于终止状态。
replicationcontrollers命名空间中可以存在的 ReplicationController 的总数。
resourcequotas命名空间中可以存在的 ResourceQuota 的总数。
services命名空间中可以存在的 Service 的总数。
services.loadbalancers命名空间中可以存在的类型为 LoadBalancer 的 Service 的总数。
services.nodeports命名空间中可以存在的分配给类型为 NodePortLoadBalancer 的 Service 的 NodePorts 的总数。
secrets命名空间中可以存在的 Secret 的总数。

例如,pods 配额计算并强制执行单个命名空间中创建的不处于终止状态的 pods 的数量上限。您可能希望在命名空间上设置 pods 配额,以避免用户创建许多小型 Pod 并耗尽集群的 Pod IP 供应的情况。

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

配额范围

每个配额都可以有一组关联的 scopes。配额仅在与枚举的范围的交集匹配时才测量资源的用法。

当将范围添加到配额时,它会将它支持的资源数量限制为与范围相关的资源。在配额中指定超出允许集的资源会导致验证错误。

范围描述
Terminating匹配 .spec.activeDeadlineSeconds >= 0 的 Pod。
NotTerminating匹配 .spec.activeDeadlineSeconds 为空 的 Pod。
BestEffort匹配具有最佳努力服务质量的 Pod。
NotBestEffort匹配没有最佳努力服务质量的 Pod。
PriorityClass匹配引用指定 优先级类 的 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,并且只允许特定命名空间使用它们,则可以将 CrossNamespacePodAffinity 配置为有限资源,方法是将 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: 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 年 5 月 30 日下午 2:06 PST:更新了 NodePort 和 LoadBalancer 的 ResourceQuota 文档 (c87b19193d)