前情提要
- 在当前工作经验中,从未限制过
namespace
的资源,这次的实施工作中,使用的是第三方定制的 k8s集群,在namespace
被创建时,因为yaml
文件没有配置limits
和requests
两个参数,当yaml
文件被apply
后,自动对pod
配置了LimitRange
中定义的limits
和requests
,结果资源不够使用,导致容器启动过程中出现OOMKilled
报错- 后续在
yaml
中加上limits
和requests
依然有报错,导致控制器无法创建pod
,原因是LimitRange
中的limits.maxLimitRequestRatio
配置,对于limits
和requests
的比例有限制
开始复盘
下面两个是 k8s 官方文档
借用一下官方文档
什么是限制范围
LimitRange
是限制namespace(命名空间)
内可为每个适用的对象类别 (例如Pod
或PersistentVolumeClaim
)指定的资源分配量(limits
和requests
)的策略对象- 一个
LimitRange(限制范围)
对象提供的限制能够做到: - 在一个
namespace(命名空间)
中实施对每个Pod
或Container
最小和最大的资源使用量的限制。 - 在一个
namespace(命名空间)
中实施对每个PersistentVolumeClaim
能申请的最小和最大的存储空间大小的限制。 - 在一个
namespace(命名空间)
中实施对一种资源的requests(申请值)
和limits(限制值)
的比值的控制。 - 设置一个
namespace(命名空间)
中对计算资源的默认requests(申请值) / limits(限制值)
,并且自动的在运行时注入到多个 Container 中。 - 当某
namespace(命名空间)
中有一个LimitRange
对象时,将在该namespace(命名空间)
中实施LimitRange
限制 LimitRange
的名称必须是合法的DNS 子域名
资源限制和请求的约束
- 管理员在一个
namespace(命名空间)
内创建一个LimitRange
对象。 - 用户在此
namespace(命名空间)
内创建(或尝试创建)Pod
和PersistentVolumeClaim
等对象。 - 首先,
LimitRanger
准入控制器对所有没有设置
计算资源需求的所有 Pod(及其容器)
设置默认请求值与限制值。 - 其次,
LimitRange
跟踪其使用量以保证没有超出命名空间中存在的任意LimitRange
所定义的最小、最大资源使用量以及使用量比值。 - 若尝试创建或更新的对象(
Pod
和PersistentVolumeClaim
)违反了LimitRange
的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码403 Forbidden
以及描述哪一项约束被违反的消息。 - 若你在
namespace(命名空间)
中添加LimitRange
启用了对cpu
和memory
等计算相关资源的限制, 你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod
。 LimitRange
的验证仅在Pod 准入阶段
进行,不对正在运行的 Pod
进行验证。 如果你添加或修改LimitRange
,namespace(命名空间)
中已存在的Pod
将继续不变。
- 如果命名空间中存在两个或更多
LimitRange
对象,应用哪个默认值是不确定的
实践出真知
- 这里图省事,就直接拿 k8s 自带的
default
这个namespace
来做演示
这里图省事,就直接部署 pod
,没有使用任何控制器(deployment
,statefulset
,daemonset
这一类)
- 如果是控制器启动的,以下创建
pod
失败的场景,通过kubectl get pod
命令会查看不到pod
被创建的,使用kubectl describe <控制器名称>
命令能查看到同理的报错
- 以下的示例都是拿
cpu
做演示,内存是同理的
场景1
pod
配置的requests
超出了LimitRange
配置的limits
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: LimitRange metadata: name: cpu-resource-constraint spec: limits: - default: # 此处定义默认限制值(limits) cpu: 500m defaultRequest: # 此处定义默认请求值(requests) cpu: 500m type: Container EOF
可以通过 kubectl get limitrange
命令查看是否创建成功
创建一个 pod 来验证
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: busybox namespace: default spec: containers: - name: busybox image: busybox:1.28.3 command: - sleep - "36000" imagePullPolicy: IfNotPresent resources: requests: cpu: 700m restartPolicy: Always EOF
这个时候就会有报错出现 The Pod "busybox" is invalid: spec.containers[0].resources.requests: Invalid value: "700m": must be less than or equal to cpu limit
- 只需要把
cpu: 700m
调小一点,低于limitrange
里面配置的default
就可以重新运行 pod 了
场景2
继续使用场景1
的 limitrange
,但是创建的 pod
同时配置了 limits
和 requests
,并且均超过 limitrange
里面配置的资源限制
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: busybox-resource-limits-requests namespace: default spec: containers: - name: busybox image: busybox:1.28.3 command: - sleep - "36000" imagePullPolicy: IfNotPresent resources: limits: cpu: 700m requests: cpu: 700m restartPolicy: Always EOF
此时,pod 是可以被创建的
场景3
继续使用
场景1
的limitrange
,但是创建的pod
没有配置limits
和requests
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: busybox-no-resource-set namespace: default spec: containers: - name: busybox image: busybox:1.28.3 command: - sleep - "36000" imagePullPolicy: IfNotPresent restartPolicy: Always EOF
pod
是肯定可以创建的,并且会自动给 pod
赋值 limits
和 requests
kubectl get pod busybox-no-resource-set -o yaml | grep 'resources' -A 4
可以看出来,limits
和 requests
都是 limitrange
内配置的 500m
resources: limits: cpu: 500m requests: cpu: 500m
场景4
继续使用
场景1
的limitrange
,但是创建的pod
只配置了limits
,并且比limitrange
里面的值要高
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: busybox-just-limits namespace: default spec: containers: - name: busybox image: busybox:1.28.3 command: - sleep - "36000" imagePullPolicy: IfNotPresent resources: limits: cpu: 700m restartPolicy: Always EOF
pod
可以被成功创建,且 requests
被赋值和 limits
的值一致
kubectl get pod busybox-just-limits -o yaml | grep 'resources' -A 4
resources: limits: cpu: 700m requests: cpu: 700m
场景5
- 这里图省事,就拿内存来做示例了
limitrange
配置了maxLimitRequestRatio
,创建的pod
的limits/requests
的值不等于 1cat <<EOF | kubectl apply -f - apiVersion: v1 kind: LimitRange metadata: name: cpu-resource-constraint spec: limits: - default: # 此处定义默认限制值(limits) memory: 256Mi defaultRequest: # 此处定义默认请求值(requests) memory: 256Mi maxLimitRequestRatio: # 此处定义 limits/requests 的值必须等于 1 memory: 1 type: Container EOF
创建一个 pod
来验证
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: busybox-ratio-ne-one namespace: default spec: containers: - name: busybox image: busybox:1.28.3 command: - sleep - "36000" imagePullPolicy: IfNotPresent resources: limits: memory: 700Mi requests: memory: 70Mi restartPolicy: Always EOF
这个 pod 是无法被创建的,会返回 Error from server (Forbidden): error when creating "STDIN": pods "busybox-ratio-ne-one" is forbidden: memory max limit to request ratio per Container is 1, but provided ratio is 10.000000 这样的报错
这个 10.000000 就是 700/70=10 得来的
只需要把 limits 和 requests 的值改成一样的,就可以成功启动 pod 了
学习总结
- 当
namespace
配置了limitrange
,并且pod
创建时没有指明limits
或/和requests
时,pod
被创建后由limitrang
的配置来指明pod
的limits
或/和requests
- 当
namespace
配置了limitrange
,并且pod
创建时的requests
超出了limitrange
配置的limits
时,会有报错must be less than or equal to xxx limit
当 namespace
配置了 limitrange
以及 maxLimitRequestRatio
,并且 pod
创建时的 limits/requests
值大于 maxLimitRequestRatio
配置的值,会有报错 is forbidden: xxx max limit to request ratio per Container is xxx, but provided ratio is xxx
关于 limits
, requests
, maxLimitRequestRatio
的取值,主要是围绕 cpu
和 memroy
的单位来的
- 在 k8s 中,
cpu
的单位为m
,1000m
=1核
0.1m
将向上取整为1m
- 在 k8s 中,
memory
的单位为k | M | G | T | P | E
或者Ki | Mi | Gi | Ti | Pi | Ei
- 区别在于换算不同,一个是
1:1000
,另一个是1:1024
:
1m
表示1000k
1Mi
表示1024k
- 程序员眼中的整数,必须是
1024
最后,大家也可以去验证,当 memory 这里写 1.5 的时候,pod 创建完,再去 get -o yaml,就会发现 1.5 变成了 1500m