资源配额
当多个用户或团队共享具有固定数量节点的集群时,存在一个问题,即一个团队可能会使用超出其公平份额的资源。
资源配额是管理员解决此问题的工具。
资源配额(由 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 发行版默认情况下启用了资源配额支持。当 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 | 命名空间中可以存在的 持久卷声明 的总数。 |
<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
如果您以这种方式定义配额,它将应用于 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 | 命名空间中可以存在的分配给类型为 NodePort 或 LoadBalancer 的 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
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
,并且只允许特定命名空间使用它们,则可以将 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 亲和性中使用 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