Author:rab
目录
前言
LimitRange 是限制命名空间内可为每个适用的对象类别 (例如 Pod 或 PersistentVolumeClaim) 指定的资源分配量(限制和请求)的策略对象。默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用 Kubernetes 资源配额, 管理员可以在一个指定的命名空间内限制集群资源的使用与创建。 在命名空间中,一个 Pod 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。
一个 LimitRange(限制范围) 对象提供的限制能够做到:
- 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
- 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
- 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
- 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。
当某命名空间中有一个 LimitRange 对象时,将在该命名空间中实施 LimitRange 限制。
且 LimitRange 的名称必须是合法的 DNS 子域名,这一要求意味着名称必须满足如下规则:
- 不能超过 253 个字符
- 只能包含小写字母、数字,以及 ‘-’ 和 ‘.’
- 必须以字母数字开头
- 必须以字母数字结尾
官方参考文档:https://kubernetes.io/zh-cn/docs/concepts/policy/limit-range/
一、限制范围
-
管理员在一个命名空间内创建一个
LimitRange
对象。 -
用户在此命名空间内创建(或尝试创建) Pod 和 PersistentVolumeClaim 等对象。
-
首先,
LimitRanger
准入控制器对所有没有设置计算资源需求的所有 Pod(及其容器)设置默认请求值与限制值。 -
其次,
LimitRange
跟踪其使用量以保证没有超出命名空间中存在的任意LimitRange
所定义的最小、最大资源使用量以及使用量比值。 -
若尝试创建或更新的对象(Pod 和 PersistentVolumeClaim)违反了
LimitRange
的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码403 Forbidden
以及描述哪一项约束被违反的消息。 -
若你在命名空间中添加
LimitRange
启用了对cpu
和memory
等计算相关资源的限制, 你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod。 -
LimitRange
的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。 如果你添加或修改 LimitRange,命名空间中已存在的 Pod 将继续不变。 -
如果命名空间中存在两个或更多
LimitRange
对象,应用哪个默认值是不确定的。
二、配置案例
2.1 名称空间 CPU 与内存约束
我们可以通过 LimitRange 对象声明 CPU 的最小和最大值. 如果 Pod 不能满足 LimitRange 的限制,就无法在该命名空间中被创建。
2.1.1 CPU 约束
1、创建命名空间
创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。
kubectl create namespace constraints-cpu-example
2、创建 LimitRange
vim cpu-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-min-max-demo-lr
spec:
limits:
- max:
cpu: "800m"
min:
cpu: "200m"
type: Container
kubectl apply -f cpu-constraints.yaml -n constraints-cpu-example
kubectl get limitrange cpu-min-max-demo-lr --output=yaml -n constraints-cpu-example
输出结果显示 CPU 的最小和最大限制符合预期。但需要注意的是,尽管你在 LimitRange 的配置文件中你没有声明默认值,默认值也会被自动创建(且默认为你设置的 max 值)。
此时,每当你在 constraints-cpu-example
命名空间中创建 Pod 时,或者某些其他的 Kubernetes API 客户端创建了等价的 Pod 时,Kubernetes 就会执行下面的步骤:
- 如果 Pod 中的任何容器未声明自己的 CPU 请求和限制,控制面将为该容器设置默认的 CPU 请求和限制。
- 确保该 Pod 中的每个容器的 CPU 请求至少 200 millicpu(否则创建失败)。
- 确保该 Pod 中每个容器 CPU 请求不大于 800 millicpu(否则创建失败)。
接下来继续创建 Pod 进行测试。
3、创建 Pod
vim cpu-constraints-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo
spec:
containers:
- name: constraints-cpu-demo-ctr
image: nginx
resources:
limits:
cpu: "800m"
requests:
cpu: "500m"
kubectl apply -f cpu-constraints-pod.yaml -n constraints-cpu-example
4、验证 Pod 状态(确保健康)
kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example
5、查看 Pod 的详情
kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example
输出结果显示该 Pod 的容器的 CPU 请求为 500 millicpu,CPU 限制为 800 millicpu,这些参数满足 LimitRange 规定的限制范围。
Pod 异常情况分析:
Pod 的 limits 及 requests 的值只要在 LimitRange 的范围内时可正常运行,那如果超过这两个参数值超过 LimitRange 范围(即不再该范围)时又会怎么样呢?这里我们分三种情况来测试一下:超过最大 CPU 限制(limits)
、不满足最小 CPU 请求(requests)
和一个没有声明 CPU 请求和 CPU 限制的 Pod
。
1、超过最大 CPU 限制(limits)的 Pod
这里 Pod 的 limits 值超过 LimitRange 的 max 最大值。
vim cpu-constraints-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo-2
spec:
containers:
- name: constraints-cpu-demo-2-ctr
image: nginx
resources:
limits:
cpu: "1.5"
requests:
cpu: "500m"
kubectl apply -f cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example
输出结果:
Error from server (Forbidden): error when creating "cpu-constraints-pod-2.yaml": pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.
输出结果表明 Pod 没有创建成功,因为其中定义了一个无法被接受的容器,该容器之所以无法被接受是因为其中设定了过高的 CPU 限制值(即超过了 LimitRange 指定的 CPU 最大值)。
2、满足最小 CPU 请求(requests)的 Pod
这里 Pod 的 limits 值小于 LimitRange 的 min 最小值。
vim cpu-constraints-pod-3.yaml
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo-3
spec:
containers:
- name: constraints-cpu-demo-3-ctr
image: nginx
resources:
limits:
cpu: "800m"
requests:
cpu: "100m"
kubectl apply -f cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example
输出结果同样显示 Pod 没有创建成功,因为其中定义了一个无法被接受的容器。 该容器无法被接受的原因是其中所设置的 CPU 请求小于最小值的限制(即不满足 LimitRange 指定的 CPU 最小请求值)。
3、创建一个没有声明 CPU 请求和 CPU 限制的 Pod
这里的 Pod 没有设置 limits。
vim cpu-constraints-pod-4.yaml
apiVersion: v1
kind: Pod
metadata:
name: constraints-cpu-demo-4
spec:
containers:
- name: constraints-cpu-demo-4-ctr
image: vish/stress
kubectl apply -f cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example
查看 Pod 的详情:
kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml
输出结果显示 Pod 的唯一容器的 CPU 请求为 800 millicpu,CPU 限制为 800 millicpu。因此,如果容器没有声明自己的 CPU 请求和限制, 控制面会根据命名空间中配置 LimitRange 设置默认(default)的 CPU 请求和限制。
2.1.2 内存约束
1、创建命名空间
创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。
kubectl create namespace constraints-mem-example
2、创建 LimitRange
vim memory-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: mem-min-max-demo-lr
spec:
limits:
- max:
memory: 1Gi
min:
memory: 500Mi
type: Container
kubectl apply -f memory-constraints.yaml --namespace=constraints-mem-example
查看 LimitRange 的详情:
kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml
输出显示预期的最小和最大内存约束。 但请注意,即使你没有在 LimitRange 的配置文件中指定默认值,默认值也会自动生成(且默认值是你 LimitRange 中的 max 值)。
现在,每当在 constraints-mem-example 命名空间中创建 Pod 时,Kubernetes 就会执行下面的步骤:
- 如果 Pod 中的任何容器未声明自己的内存请求和限制,控制面将为该容器设置默认的内存请求和限制。
- 确保该 Pod 中的每个容器的内存请求至少 500 MiB。
- 确保该 Pod 中每个容器内存请求不大于 1 GiB。
以下为包含一个容器的 Pod 清单。该容器声明了 600 MiB 的内存请求和 800 MiB 的内存限制, 这些满足了 LimitRange 施加的最小和最大内存约束。
案例效果与 CPU 约束基本上是一致的,你就是说你的 Pod 也是不能小于或超过 LimitRange 范围值,否则 Pod 将创建失败,这里就不再演示了,大家可去看官方文档。
2.1.3 默认 CPU 申请约束
前面也提到了,当我们创建 LimitRange 资源并指定 CPU max/min 值时,会自动为我们添加上默认的 CPU 约束值(即 default 和 defaultRequest 值)且是将 max 值作为默认值,如下图:
那这两个值有什么用呢?
我们前面提到,如果创建的 Pod 没有指定约束,那 K8s 控制面板就会将 LimitRange 的 default 和 defaultRequest 值应用到 Pod 上。当然了,前提是你已经配置了 LimitRange,否则默认情况下 K8s 集群对 Pod 的资源(CPU/内存)是没有限制的。
案例也是一样,这里不再重复案例演示。
这里要注意的就是一下几种情况:
1、没有声明容器的限制(limits)和请求(requests)
此时 Pod 的 limits 和 requests 值就是 LimitRange 的 default 和 defaultRequest 值。
2、只声明容器的限制(limits)而不声明请求(requests)
此时 Pod 的 limits 和 requests 值就是你当前声明的值 limits。
3、只声明容器的请求(requests)而不声明限制(limits)
此时 Pod 的 limits 值为你 LimitRange 的 default 值,requests 值就是你当前声明的 requests 值。
因此不难总结出 LimitRange 的 limits 的作用:
-
default:在 Pod 没有指定 limits 约束时的 limits 值;
-
defaultRequest:在 Pod 没有指定 requests 约束时的 requests 值;
-
max:在 Pod 指定了 limits 约束时不能超过的最大值;
-
min:在 Pod 指定了 requests 约束时不能小于的最最值;
而且也要记住,你指定的 Pod 值也是不能超过 LimitRange 默认值范围。
2.1.4 默认内存申请约束
前面也提到了,当我们创建 LimitRange 资源并指定内存 max/min 值时,会自动为我们添加上默认的内存约束值(即 default 和 defaultRequest 值)且是将 max 值作为默认值,如下图:
那这两个值有什么用呢?
同理,当创建的 Pod 没有指定约束时,那 K8s 控制面板就会将 LimitRange 的 default 和 defaultRequest 值应用到 Pod 上。
案例也是一样,这里不再重复案例演示。
这里要注意的就是一下几种情况:
1、没有声明容器的限制(limits)和请求(requests)
此时 Pod 的 limits 和 requests 值就是 LimitRange 的 default 和 defaultRequest 值。
2、只声明容器的限制(limits)而不声明请求(requests)
此时 Pod 的 limits 和 requests 值就是你当前声明的值 limits。
3、只声明容器的请求(requests)而不声明限制(limits)
此时 Pod 的 limits 值为你 LimitRange 的 default 值,requests 值就是你当前声明的 requests 值。
因此不难总结出 LimitRange 的 limits 的作用:
-
default:在 Pod 没有指定 limits 约束时的 limits 值;
-
defaultRequest:在 Pod 没有指定 requests 约束时的 requests 值;
-
max:在 Pod 指定了 limits 约束时不能超过的最大值;
-
min:在 Pod 指定了 requests 约束时不能小于的最最值;
而且也要记住,你指定的 Pod 值也是不能超过 LimitRange 默认值范围。
2.2 名称空间总容量限额约束
以上的案例都是使用 LimitRange 对 namespace 中某个 Pod 的 CPU、内存资源约束,当我们希望控制单个名字空间总的可以消耗多少存储空间、CPU、内存以控制成本时,我们就可以使用名称空间存储容量约束
-StorageQuota。
而存储约束无非就这几个方面:
- 名字空间中持久卷申领(persistent volume claims)的数量
- 每个申领(claim)可以请求的存储量
- 名字空间可以具有的累计存储量
1、创建命名空间
创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。
kubectl create namespace quota-mem-cpu-example
2、创建 LimitRange
vim quota-mem-cpu.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-demo
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
kubectl apply -f quota-mem-cpu.yaml --namespace=quota-mem-cpu-example
查看 ResourceQuota 详情:
kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml
ResourceQuota 在 quota-mem-cpu-example 命名空间中设置了如下要求:
- 在该命名空间中的每个 Pod 的所有容器都必须要有内存请求和限制,以及 CPU 请求和限制。
- 在该命名空间中所有 Pod 的内存请求总和不能超过 1 GiB。
- 在该命名空间中所有 Pod 的内存限制总和不能超过 2 GiB。
- 在该命名空间中所有 Pod 的 CPU 请求总和不能超过 1 cpu。
- 在该命名空间中所有 Pod 的 CPU 限制总和不能超过 2 cpu。
3、创建 Pod
vim quota-mem-cpu-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: quota-mem-cpu-demo
spec:
containers:
- name: quota-mem-cpu-demo-ctr
image: nginx
resources:
limits:
memory: "800Mi"
cpu: "800m"
requests:
memory: "600Mi"
cpu: "400m"
kubectl apply -f quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example
查看 Pod 状态:
kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example
再次查看 ResourceQuota 的详情:
kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml
输出结果显示了配额以及有多少配额已经被使用,你可以看到 Pod 的内存和 CPU 请求值及限制值没有超过配额。
如果我们再继续创建一个 Pod,如下:
vim quota-mem-cpu-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: quota-mem-cpu-demo-2
spec:
containers:
- name: quota-mem-cpu-demo-2-ctr
image: redis
resources:
limits:
memory: "1Gi"
cpu: "800m"
requests:
memory: "700Mi"
cpu: "400m"
kubectl apply -f quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example
报错了,为什呢?因为在 quota-mem-cpu-example
名称空间中,内存请求与已经使用的内存请求之和超过了内存请求的配额:600 MiB + 700 MiB > 1 GiB
。
总结
1、LimitRange
- 用于对 namespace 中的 Pod 的 CPU、内存约束;
- 默认情况下 K8s 集群对 Pod 是没有约束的(即没有 LimitRange);
- 当对 namespace 进行准入控制时,在该 namespace 中的 Pod 需遵循 namespace 中的约束。
2、StorageQuota
- 用于限制某个 namespace 总的限额(即该 namespace 中的所有的 Pod 资源的 CPU、内存、存储不能超过指定的限额)。
—END