资源配额
当多个用户或团队共享一个具有固定数量节点的集群时,会担心一个团队可能会使用超过其公平份额的资源。
资源配额是管理员解决此问题的工具。
资源配额由 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 个核心以供将来分配。
- 限制 “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 中,指定大小的巨页请求数不能超过此值。 |
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
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-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 聚合 来添加未定义为 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 | 可以分配给命名空间中 NodePort 或 LoadBalancer 类型的服务的 NodePorts 总数。 |
密钥(secrets) | 命名空间中可以存在的密钥总数。 |
例如,pods
配额计算并强制限制单个命名空间中创建的、非终止状态的 pods
的最大数量。您可能希望在命名空间上设置 pods
配额,以避免用户创建大量小型 Pod 并耗尽集群的 Pod IP 资源的情况。
您可以在查看和设置配额中找到更多示例。
配额范围
每个配额都可以有一组关联的 scopes
。只有在资源与枚举范围的交集匹配时,配额才会度量资源的使用情况。
当向配额添加范围时,它会将所支持的资源数量限制为与该范围相关的资源。在允许的集合之外的配额上指定的资源会导致验证错误。
范围 | 描述 |
---|---|
正在终止(Terminating) | 匹配 .spec.activeDeadlineSeconds >= 0 的 Pod。 |
未终止(NotTerminating) | 匹配 .spec.activeDeadlineSeconds 为 nil 的 Pod。 |
尽力而为(BestEffort) | 匹配具有尽力而为服务质量的 Pod。 |
非尽力而为(NotBestEffort) | 匹配不具有尽力而为服务质量的 Pod。 |
PriorityClass | 匹配引用指定的优先级类的 Pod。 |
跨命名空间 Pod 亲和性(CrossNamespacePodAffinity) | 匹配具有跨命名空间 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
通过上述配置,只有在其创建的命名空间中存在具有 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
配额和集群容量
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
- 未指定 Pod 的
priorityClassName
。 - Pod 的
priorityClassName
指定为cluster-services
以外的值。 - Pod 的
priorityClassName
设置为cluster-services
,它将在kube-system
命名空间中创建,并且已通过资源配额检查。
如果 Pod 的 priorityClassName
设置为 cluster-services
,并且它将在 kube-system
以外的命名空间中创建,则 Pod 创建请求将被拒绝。
下一步
- 有关更多信息,请参阅ResourceQuota 设计文档。
- 请参阅如何使用资源配额的详细示例。
- 阅读优先级类的配额支持设计文档。
- 请参阅LimitedResources。