1 - 限制范围
默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。
使用 Kubernetes 资源配额,
管理员(也称为 集群操作者)可以在一个指定的命名空间内限制集群资源的使用与创建。
在命名空间中,一个 Pod 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。
作为集群操作者或命名空间级的管理员,你可能也会担心如何确保一个 Pod 不会垄断命名空间内所有可用的资源。
LimitRange 是限制命名空间内可为每个适用的对象类别
(例如 Pod 或 PersistentVolumeClaim)
指定的资源分配量(限制和请求)的策略对象。
一个 LimitRange(限制范围) 对象提供的限制能够做到:
- 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
- 在一个命名空间中实施对每个 PersistentVolumeClaim
能申请的最小和最大的存储空间大小的限制。
- 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
- 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。
当某命名空间中有一个 LimitRange 对象时,将在该命名空间中实施 LimitRange 限制。
LimitRange 的名称必须是合法的
DNS 子域名。
资源限制和请求的约束
- 管理员在一个命名空间内创建一个
LimitRange
对象。
- 用户在此命名空间内创建(或尝试创建) Pod 和 PersistentVolumeClaim 等对象。
- 首先,
LimitRange
准入控制器对所有没有设置计算资源需求的所有 Pod(及其容器)设置默认请求值与限制值。
- 其次,
LimitRange
跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange
所定义的最小、最大资源使用量以及使用量比值。
- 若尝试创建或更新的对象(Pod 和 PersistentVolumeClaim)违反了
LimitRange
的约束,
向 API 服务器的请求会失败,并返回 HTTP 状态码 403 Forbidden
以及描述哪一项约束被违反的消息。
- 若你在命名空间中添加
LimitRange
启用了对 cpu
和 memory
等计算相关资源的限制,
你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod。
LimitRange
的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。
如果你添加或修改 LimitRange,命名空间中已存在的 Pod 将继续不变。
- 如果命名空间中存在两个或更多
LimitRange
对象,应用哪个默认值是不确定的。
Pod 的 LimitRange 和准入检查
LimitRange
不 检查所应用的默认值的一致性。
这意味着 LimitRange
设置的 limit 的默认值可能小于客户端提交给 API 服务器的规约中为容器指定的 request 值。
如果发生这种情况,最终 Pod 将无法调度。
例如,你使用如下清单定义一个 LimitRange
:
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-resource-constraint
spec:
limits:
- default: # 此处定义默认限制值
cpu: 500m
defaultRequest: # 此处定义默认请求值
cpu: 500m
max: # max 和 min 定义限制范围
cpu: "1"
min:
cpu: 100m
type: Container
以及一个声明 CPU 资源请求为 700m
但未声明限制值的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: example-conflict-with-limitrange-cpu
spec:
containers:
- name: demo
image: registry.k8s.io/pause:2.0
resources:
requests:
cpu: 700m
那么该 Pod 将不会被调度,失败并出现类似以下的错误:
Pod "example-conflict-with-limitrange-cpu" is invalid: spec.containers[0].resources.requests: Invalid value: "700m": must be less than or equal to cpu limit
如果你同时设置了 request
和 limit
,那么即使使用相同的 LimitRange
,新 Pod 也会被成功调度:
apiVersion: v1
kind: Pod
metadata:
name: example-no-conflict-with-limitrange-cpu
spec:
containers:
- name: demo
image: registry.k8s.io/pause:2.0
resources:
requests:
cpu: 700m
limits:
cpu: 700m
资源约束示例
能够使用限制范围创建的策略示例有:
- 在一个有两个节点,8 GiB 内存与16个核的集群中,限制一个命名空间的 Pod 申请
100m 单位,最大 500m 单位的 CPU,以及申请 200Mi,最大 600Mi 的内存。
- 为 spec 中没有 cpu 和内存需求值的 Container 定义默认 CPU 限制值与需求值
150m,内存默认需求值 300Mi。
在命名空间的总限制值小于 Pod 或 Container 的限制值的总和的情况下,可能会产生资源竞争。
在这种情况下,将不会创建 Container 或 Pod。
竞争和对 LimitRange 的改变都不会影响任何已经创建了的资源。
接下来
关于使用限值的例子,可参阅:
有关上下文和历史信息,请参阅 LimitRanger 设计文档。
2 - 资源配额
当多个用户或团队共享具有固定节点数目的集群时,人们会担心有人使用超过其基于公平原则所分配到的资源量。
资源配额是帮助管理员解决这一问题的工具。
资源配额,通过 ResourceQuota
对象来定义,对每个命名空间的资源消耗总量提供限制。
它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命名空间中的 Pod 可以使用的计算资源的总上限。
资源配额的工作方式如下:
-
不同的团队可以在不同的命名空间下工作,这可以通过
RBAC 强制执行。
-
集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。
-
当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会跟踪集群的资源使用情况,
以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。
-
如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN),
并在消息中给出有可能违反的约束。
-
如果命名空间下的计算资源(如 cpu
和 memory
)的配额被启用,
则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。
提示: 可使用 LimitRanger
准入控制器来为没有设置计算资源需求的 Pod 设置默认值。
若想避免这类问题,请参考
演练示例。
说明:
- 对于
cpu
和 memory
资源:ResourceQuota 强制该命名空间中的每个(新)Pod 为该资源设置限制。
如果你在命名空间中为 cpu
和 memory
实施资源配额,
你或其他客户端必须为你提交的每个新 Pod 指定该资源的 requests
或 limits
。
否则,控制平面可能会拒绝接纳该 Pod。
- 对于其他资源:ResourceQuota 可以工作,并且会忽略命名空间中的 Pod,而无需为该资源设置限制或请求。
这意味着,如果资源配额限制了此命名空间的临时存储,则可以创建没有限制/请求临时存储的新 Pod。
你可以使用限制范围自动设置对这些资源的默认请求。
ResourceQuota 对象的名称必须是合法的
DNS 子域名。
下面是使用命名空间和配额构建策略的示例:
- 在具有 32 GiB 内存和 16 核 CPU 资源的集群中,允许 A 团队使用 20 GiB 内存 和 10 核的 CPU 资源,
允许 B 团队使用 10 GiB 内存和 4 核的 CPU 资源,并且预留 2 GiB 内存和 2 核的 CPU 资源供将来分配。
- 限制 "testing" 命名空间使用 1 核 CPU 资源和 1GiB 内存。允许 "production" 命名空间使用任意数量。
在集群容量小于各命名空间配额总和的情况下,可能存在资源竞争。资源竞争时,Kubernetes 系统会遵循先到先得的原则。
不管是资源竞争还是配额的修改,都不会影响已经创建的资源使用对象。
启用资源配额
资源配额的支持在很多 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 相同。 |
扩展资源的资源配额
除上述资源外,在 Kubernetes 1.10 版本中,
还添加了对扩展资源
的支持。
由于扩展资源不可超量分配,因此没有必要在配额中为同一扩展资源同时指定 requests
和 limits
。
对于扩展资源而言,目前仅允许使用前缀为 requests.
的配额项。
以 GPU 拓展资源为例,如果资源名称为 nvidia.com/gpu
,并且要将命名空间中请求的 GPU
资源总数限制为 4,则可以如下定义配额:
requests.nvidia.com/gpu: 4
有关更多详细信息,请参阅查看和设置配额。
存储资源配额
用户可以对给定命名空间下的存储资源
总量进行限制。
此外,还可以根据相关的存储类(Storage Class)来限制存储资源的消耗。
资源名称 |
描述 |
requests.storage |
所有 PVC,存储资源的需求总量不能超过该值。 |
persistentvolumeclaims |
在该命名空间中所允许的 PVC 总量。 |
<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
在 Kubernetes 1.8 版本中,本地临时存储的配额支持已经是 Alpha 功能:
资源名称 |
描述 |
requests.ephemeral-storage |
在命名空间的所有 Pod 中,本地临时存储请求的总和不能超过此值。 |
limits.ephemeral-storage |
在命名空间的所有 Pod 中,本地临时存储限制值的总和不能超过此值。 |
ephemeral-storage |
与 requests.ephemeral-storage 相同。 |
说明:
如果所使用的是 CRI 容器运行时,容器日志会被计入临时存储配额,
这可能会导致存储配额耗尽的 Pod 被意外地驱逐出节点。
参考日志架构了解详细信息。
对象数量配额
你可以使用以下语法为 Kubernetes API 中“一种特定资源类型的总数”设置配额:
count/<resource>.<group>
:用于非核心(core)组的资源
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 而导致集群拒绝服务。
还有另一种语法仅用于为某些资源设置相同类型的配额。
支持以下类型:
资源名称 |
描述 |
configmaps |
在该命名空间中允许存在的 ConfigMap 总数上限。 |
persistentvolumeclaims |
在该命名空间中允许存在的 PVC 的总数上限。 |
pods |
在该命名空间中允许存在的非终止状态的 Pod 总数上限。Pod 终止状态等价于 Pod 的 .status.phase in (Failed, Succeeded) 为真。 |
replicationcontrollers |
在该命名空间中允许存在的 ReplicationController 总数上限。 |
resourcequotas |
在该命名空间中允许存在的 ResourceQuota 总数上限。 |
services |
在该命名空间中允许存在的 Service 总数上限。 |
services.loadbalancers |
在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限。 |
services.nodeports |
在该命名空间中允许存在的 NodePort 或 LoadBalancer 类型的 Service 的 NodePort 总数上限。 |
secrets |
在该命名空间中允许存在的 Secret 总数上限。 |
例如,pods
配额统计某个命名空间中所创建的、非终止状态的 pods
个数并确保其不超过某上限值。
用户可能希望在某命名空间中设置 pods
配额,以避免有用户创建很多小的 Pod,
从而耗尽集群所能提供的 Pod IP 地址。
你可以在查看和设置配额节查看更多示例。
配额作用域
每个配额都有一组相关的 scope
(作用域),配额只会对作用域内的资源生效。
配额机制仅统计所列举的作用域的交集中的资源用量。
当一个作用域被添加到配额中后,它会对作用域相关的资源数量作限制。
如配额中指定了允许(作用域)集合之外的资源,会导致验证错误。
作用域 |
描述 |
Terminating |
匹配所有 spec.activeDeadlineSeconds 不小于 0 的 Pod。 |
NotTerminating |
匹配所有 spec.activeDeadlineSeconds 是 nil 的 Pod。 |
BestEffort |
匹配所有 Qos 是 BestEffort 的 Pod。 |
NotBestEffort |
匹配所有 Qos 不是 BestEffort 的 Pod。 |
PriorityClass |
匹配所有引用了所指定的优先级类的 Pod。 |
CrossNamespacePodAffinity |
匹配那些设置了跨名字空间(反)亲和性条件的 Pod。 |
BestEffort
作用域限制配额跟踪以下资源:
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
字段。
基于优先级类(PriorityClass)来设置资源配额
特性状态:
Kubernetes v1.17 [stable]
Pod 可以创建为特定的优先级。
通过使用配额规约中的 scopeSelector
字段,用户可以根据 Pod 的优先级控制其系统资源消耗。
仅当配额规范中的 scopeSelector
字段选择到某 Pod 时,配额机制才会匹配和计量 Pod 的资源消耗。
如果配额对象通过 scopeSelector
字段设置其作用域为优先级类,
则配额对象只能跟踪以下资源:
pods
cpu
memory
ephemeral-storage
limits.cpu
limits.memory
limits.ephemeral-storage
requests.cpu
requests.memory
requests.ephemeral-storage
本示例创建一个配额对象,并将其与具有特定优先级的 Pod 进行匹配,其工作方式如下:
- 集群中的 Pod 可取三个优先级类之一,即 "low"、"medium"、"high"。
- 为每个优先级创建一个配额对象。
将以下 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
命令运行以下操作。
kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created
使用 kubectl describe quota
操作验证配额的 Used
值为 0
。
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
创建优先级为 "high" 的 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
确认 "high" 优先级配额 pods-high
的 "Used" 统计信息已更改,并且其他两个配额未更改。
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 [stable]
集群运维人员可以使用 CrossNamespacePodAffinity
配额作用域来限制哪个名字空间中可以存在包含跨名字空间亲和性规则的 Pod。
更为具体一点,此作用域用来配置哪些 Pod 可以在其 Pod 亲和性规则中设置
namespaces
或 namespaceSelector
字段。
禁止用户使用跨名字空间的亲和性规则可能是一种被需要的能力,
因为带有反亲和性约束的 Pod 可能会阻止所有其他名字空间的 Pod 被调度到某失效域中。
使用此作用域操作符可以避免某些名字空间(例如下面例子中的 foo-ns
)运行特别的 Pod,
这类 Pod 使用跨名字空间的 Pod 亲和性约束,在该名字空间中创建了作用域为
CrossNamespacePodAffinity
的、硬性约束为 0 的资源配额对象。
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
且硬性约束大于或等于使用 namespaces
和 namespaceSelector
字段的 Pod
个数时,才可以在该名字空间中继续创建在其 Pod 亲和性规则中设置 namespaces
或 namespaceSelector
的新 Pod。
请求与限制的比较
分配计算资源时,每个容器可以为 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 与集群资源总量是完全独立的。它们通过绝对的单位来配置。
所以,为集群添加节点时,资源配额不会自动赋予每个命名空间消耗更多资源的能力。
有时可能需要资源配额支持更复杂的策略,比如:
- 在几个团队中按比例划分总的集群资源。
- 允许每个租户根据需要增加资源使用量,但要有足够的限制以防止资源意外耗尽。
- 探测某个命名空间的需求,添加物理节点并扩大资源配额值。
这些策略可以通过将资源配额作为一个组成模块、手动编写一个控制器来监控资源使用情况,
并结合其他信号调整命名空间上的硬性资源配额来实现。
注意:资源配额对集群资源总体进行划分,但它对节点没有限制:来自不同命名空间的 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 创建请求也被拒绝。
接下来
3 - 进程 ID 约束与预留
特性状态:
Kubernetes v1.20 [stable]
Kubernetes 允许你限制一个 Pod
中可以使用的进程 ID(PID)数目。
你也可以为每个节点预留一定数量的可分配的 PID,
供操作系统和守护进程(而非 Pod)使用。
进程 ID(PID)是节点上的一种基础资源。很容易就会在尚未超出其它资源约束的时候就已经触及任务个数上限,
进而导致宿主机器不稳定。
集群管理员需要一定的机制来确保集群中运行的 Pod 不会导致 PID 资源枯竭,
甚而造成宿主机上的守护进程(例如
kubelet 或者
kube-proxy
乃至包括容器运行时本身)无法正常运行。
此外,确保 Pod 中 PID 的个数受限对于保证其不会影响到同一节点上其它负载也很重要。
说明:
在某些 Linux 安装环境中,操作系统会将 PID 约束设置为一个较低的默认值,例如
32768
。这时可以考虑提升 /proc/sys/kernel/pid_max
的设置值。
你可以配置 kubelet 限制给定 Pod 能够使用的 PID 个数。
例如,如果你的节点上的宿主操作系统被设置为最多可使用 262144
个 PID,
同时预期节点上会运行的 Pod 个数不会超过 250
,那么你可以为每个 Pod 设置 1000
个 PID
的预算,避免耗尽该节点上可用 PID 的总量。
如果管理员系统像 CPU 或内存那样允许对 PID 进行过量分配(Overcommit),他们也可以这样做,
只是会有一些额外的风险。不管怎样,任何一个 Pod 都不可以将整个机器的运行状态破坏。
这类资源限制有助于避免简单的派生炸弹(Fork Bomb)影响到整个集群的运行。
在 Pod 级别设置 PID 限制使得管理员能够保护 Pod 之间不会互相伤害,
不过无法确保所有调度到该宿主机器上的所有 Pod 都不会影响到节点整体。
Pod 级别的限制也无法保护节点代理任务自身不会受到 PID 耗尽的影响。
你也可以预留一定量的 PID,作为节点的额外开销,与分配给 Pod 的 PID 集合独立。
这有点类似于在给操作系统和其它设施预留 CPU、内存或其它资源时所做的操作,
这些任务都在 Pod 及其所包含的容器之外运行。
PID 限制是与计算资源
请求和限制相辅相成的一种机制。不过,你需要用一种不同的方式来设置这一限制:
你需要将其设置到 kubelet 上而不是在 Pod 的 .spec
中为 Pod 设置资源限制。
目前还不支持在 Pod 级别设置 PID 限制。
注意:
这意味着,施加在 Pod 之上的限制值可能因为 Pod 运行所在的节点不同而有差别。
为了简化系统,最简单的方法是为所有节点设置相同的 PID 资源限制和预留值。
节点级别 PID 限制
Kubernetes 允许你为系统预留一定量的进程 ID。为了配置预留数量,你可以使用
kubelet 的 --system-reserved
和 --kube-reserved
命令行选项中的参数
pid=<number>
。你所设置的参数值分别用来声明为整个系统和 Kubernetes
系统守护进程所保留的进程 ID 数目。
Pod 级别 PID 限制
Kubernetes 允许你限制 Pod 中运行的进程个数。你可以在节点级别设置这一限制,
而不是为特定的 Pod 来将其设置为资源限制。每个节点都可以有不同的 PID 限制设置。
要设置限制值,你可以设置 kubelet 的命令行参数 --pod-max-pids
,或者在 kubelet
的配置文件中设置
PodPidsLimit
。
基于 PID 的驱逐
你可以配置 kubelet 使之在 Pod 行为不正常或者消耗不正常数量资源的时候将其终止。这一特性称作驱逐。
你可以针对不同的驱逐信号配置资源不足的处理。
使用 pid.available
驱逐信号来配置 Pod 使用的 PID 个数的阈值。
你可以设置硬性的和软性的驱逐策略。不过,即使使用硬性的驱逐策略,
如果 PID 个数增长过快,节点仍然可能因为触及节点 PID 限制而进入一种不稳定状态。
驱逐信号的取值是周期性计算的,而不是一直能够强制实施约束。
Pod 级别和节点级别的 PID 限制会设置硬性限制。
一旦触及限制值,工作负载会在尝试获得新的 PID 时开始遇到问题。
这可能会也可能不会导致 Pod 被重新调度,取决于工作负载如何应对这类失败以及
Pod 的存活性和就绪态探测是如何配置的。
可是,如果限制值被正确设置,你可以确保其它 Pod 负载和系统进程不会因为某个
Pod 行为不正常而没有 PID 可用。
接下来