资源配额
当多个用户或团队共享具有固定数量节点的集群时,需要关注的是一个团队可能会使用超过其公平份额的资源。
资源配额是管理员解决此问题的工具。
资源配额由 ResourceQuota
对象定义,它提供了限制每个命名空间聚合资源消耗的约束。它可以限制命名空间中可以创建的对象类型的数量,以及该命名空间中资源可以消耗的计算资源总量。
资源配额的工作原理如下
不同的团队在不同的命名空间中工作。这可以通过 RBAC 来强制执行。
管理员为每个命名空间创建一个 ResourceQuota。
用户在命名空间中创建资源(Pod、服务等),配额系统会跟踪使用情况,以确保不超过 ResourceQuota 中定义的硬资源限制。
如果创建或更新资源违反了配额约束,请求将失败,并显示 HTTP 状态代码
403 FORBIDDEN
,并显示一条消息,说明将违反的约束。如果在命名空间中为
cpu
和memory
等计算资源启用了配额,则用户必须为这些值指定请求或限制;否则,配额系统可能会拒绝创建 Pod。提示:使用LimitRanger
准入控制器为没有计算资源需求的 Pod 强制默认值。有关如何避免此问题的示例,请参阅演练。
注意
- 对于
cpu
和memory
资源,ResourceQuotas 强制该命名空间中的每个(新)Pod 都为该资源设置限制。如果您在命名空间中为cpu
或memory
强制执行资源配额,则您和其他客户端必须为提交的每个新 Pod 指定该资源的requests
或limits
。否则,控制平面可能会拒绝该 Pod 的准入。 - 对于其他资源:ResourceQuota 会起作用,并且会忽略命名空间中没有为该资源设置限制或请求的 Pod。这意味着,如果资源配额限制了该命名空间的临时存储,则可以创建一个没有限制/请求临时存储的新 Pod。您可以使用LimitRange 自动为这些资源设置默认请求。
ResourceQuota 对象的名称必须是有效的DNS 子域名。
可以使用命名空间和配额创建的策略示例如下
- 在一个容量为 32 GiB RAM 和 16 核的集群中,让 A 团队使用 20 GiB 和 10 核,让 B 团队使用 10 GiB 和 4 核,并预留 2 GiB 和 2 核供将来分配。
- 将“测试”命名空间限制为使用 1 核和 1 GiB RAM。让“生产”命名空间使用任意数量。
如果集群的总容量小于命名空间配额的总和,则可能会出现资源争用。这是按照先到先得的原则处理的。
争用和配额更改都不会影响已创建的资源。
启用资源配额
许多 Kubernetes 发行版默认启用资源配额支持。当API 服务器 --enable-admission-plugins=
标志的参数之一为 ResourceQuota
时,将启用该功能。
当特定命名空间中存在 ResourceQuota 时,将在该命名空间中强制执行资源配额。
计算资源配额
您可以限制在给定命名空间中可以请求的计算资源的总和。
支持以下资源类型
资源名称 | 描述 |
---|---|
limits.cpu | 在处于非终止状态的所有 Pod 中,CPU 限制的总和不能超过此值。 |
limits.memory | 在处于非终止状态的所有 Pod 中,内存限制的总和不能超过此值。 |
requests.cpu | 在处于非终止状态的所有 Pod 中,CPU 请求的总和不能超过此值。 |
requests.memory | 在处于非终止状态的所有 Pod 中,内存请求的总和不能超过此值。 |
hugepages-<size> | 在处于非终止状态的所有 Pod 中,指定大小的巨页请求数量不能超过此值。 |
cpu | 与 requests.cpu 相同 |
memory | 与 requests.memory 相同 |
扩展资源的资源配额
除了上述资源之外,在 1.10 版本中,还添加了对扩展资源的配额支持。
由于不允许对扩展资源进行超额分配,因此在配额中为同一扩展资源同时指定 requests
和 limits
是没有意义的。因此,对于扩展资源,目前只允许使用前缀为 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
存储类对存储进行配额,并与 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-storage | 与 requests.ephemeral-storage 相同。 |
注意
使用 CRI 容器运行时,容器日志将计入临时存储配额。这可能会导致意外驱逐已耗尽其存储配额的 Pod。有关详细信息,请参阅日志架构。对象计数配额
您可以使用以下语法为 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 聚合添加未定义为 CustomResourceDefinitions 的其他自定义 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) 为 true,则 Pod 处于终止状态。 |
replicationcontrollers | 命名空间中可以存在的 ReplicationController 总数。 |
resourcequotas | 命名空间中可以存在的 ResourceQuota 总数。 |
services | 命名空间中可以存在的 Service 总数。 |
services.loadbalancers | 命名空间中可以存在的 LoadBalancer 类型 Service 总数。 |
services.nodeports | 分配给命名空间中可以存在的 NodePort 或 LoadBalancer 类型 Service 的 NodePort 总数。 |
secrets | 命名空间中可以存在的 Secret 总数。 |
例如,pods
配额会统计并强制限制单个命名空间中创建的非终止状态 pods
的数量。您可能希望在命名空间上设置 pods
配额,以避免用户创建许多小型 Pod 并耗尽集群的 Pod IP 地址供应的情况。
您可以在查看和设置配额中找到更多示例。
配额范围
每个配额都可以有一组关联的 scopes
。只有当资源的使用量与枚举范围的交集匹配时,配额才会对其进行度量。
将范围添加到配额时,它会将其支持的资源数量限制为与该范围相关的资源。在允许集之外的配额上指定的资源会导致验证错误。
范围 | 描述 |
---|---|
Terminating(终止中) | 匹配 .spec.activeDeadlineSeconds >= 0 的 Pod |
NotTerminating(非终止中) | 匹配 .spec.activeDeadlineSeconds is nil 的 Pod |
BestEffort(尽力而为) | 匹配具有尽力而为服务质量的 Pod。 |
NotBestEffort(非尽力而为) | 匹配不具有尽力而为服务质量的 Pod。 |
PriorityClass | 匹配引用了指定优先级类的 Pod。 |
CrossNamespacePodAffinity(跨命名空间 Pod 亲和性) | 匹配具有跨命名空间 Pod (反)亲和性术语的 Pod。 |
BestEffort
范围将配额限制为仅跟踪以下资源:
pods
Terminating
、NotTerminating
、NotBestEffort
和 PriorityClass
范围将配额限制为仅跟踪以下资源:
pods
cpu
memory
requests.cpu
requests.memory
limits.cpu
limits.memory
请注意,您不能在同一个配额中同时指定 Terminating
和 NotTerminating
范围,也不能在同一个配额中同时指定 BestEffort
和 NotBestEffort
范围。
scopeSelector
在 operator
字段中支持以下值:
In(包含)
NotIn(不包含)
Exists(存在)
DoesNotExist(不存在)
在定义 scopeSelector
时,如果使用以下值之一作为 scopeName
,则 operator
必须为 Exists
。
Terminating(终止中)
NotTerminating(非终止中)
BestEffort(尽力而为)
NotBestEffort(非尽力而为)
如果 operator
为 In
或 NotIn
,则 values
字段必须至少有一个值。例如:
scopeSelector:
matchExpressions:
- scopeName: PriorityClass
operator: In
values:
- middle
如果 operator
为 Exists
或 DoesNotExist
,则 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 亲和性术语中设置 namespaces
或 namespaceSelector
字段。
可能需要阻止用户使用跨命名空间的亲和性术语,因为具有反亲和性约束的 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
如果操作员希望默认情况下禁止使用 namespaces
和 namespaceSelector
,并且只允许特定命名空间使用,则可以通过将 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
使用上述配置,只有当 Pod 创建所在的命名空间具有 CrossNamespacePodAffinity
范围和大于或等于使用这些字段的 Pod 数量的硬限制的资源配额对象时,Pod 才可以在 Pod 亲和性中使用 namespaces
和 namespaceSelector
。
请求与限制的比较
分配计算资源时,每个容器都可以为 CPU 或内存指定请求值和限制值。可以将配额配置为对任一值进行配额。
如果配额为 requests.cpu
或 requests.memory
指定了值,则要求每个传入容器都对这些资源进行显式请求。如果配额为 limits.cpu
或 limits.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
配额和集群容量
ResourceQuotas 独立于集群容量。它们以绝对单位表示。因此,如果您向集群添加节点,这并不会自动赋予每个命名空间消耗更多资源的能力。
有时可能需要更复杂的策略,例如:
- 在多个团队之间按比例分配集群总资源。
- 允许每个租户根据需要增加资源使用量,但要有一个宽松的限制,以防止意外耗尽资源。
- 检测来自一个命名空间的需求,添加节点并增加配额。
可以通过编写一个“控制器”来实现此类策略,该控制器将 ResourceQuotas
作为构建块,监视配额使用情况,并根据其他信号调整每个命名空间的配额硬限制。
请注意,资源配额会划分聚合集群资源,但不会围绕节点创建任何限制:来自多个命名空间的 Pod 可以在同一个节点上运行。
默认情况下限制优先级类的消耗
可能希望只有在存在匹配的配额对象的情况下,才允许在命名空间中使用特定优先级的 Pod,例如“cluster-services”。
通过这种机制,操作员能够将某些高优先级类的使用限制在有限数量的命名空间内,并且默认情况下并非每个命名空间都能够消耗这些优先级类。
要强制执行此操作,应使用 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:
- 未指定 Pod 的
priorityClassName
。 - 将 Pod 的
priorityClassName
指定为除cluster-services
以外的值。 - 将 Pod 的
priorityClassName
设置为cluster-services
,并且要在kube-system
命名空间中创建它,并且它已通过资源配额检查。
如果 Pod 创建请求的 priorityClassName
设置为 cluster-services
,并且要在 kube-system
以外的命名空间中创建它,则该请求将被拒绝。
下一步
- 有关更多信息,请参阅ResourceQuota 设计文档。
- 有关如何使用资源配额的详细示例,请参阅此处。
- 阅读优先级类配额支持设计文档。
- 请参阅LimitedResources