pod的requests、limits解读、LimitRange资源配额、Qos服务质量等级、资源配额管理 Resource Quotas

前言

环境:k8s-v1.22.17 docker-20.10.9 centos-7.9

什么是可计算资源

CPU、GPU、Memory等都是计算资源,所谓计算资源,就是可计量的、能被申请的、能被分配使用的资源。
CPU在容器技术中属于可压缩资源,因此,pod对CPU的使用超过其cpu.limit限制一般不会导致容器被系统"杀死",而Memory属于不可压缩资源,当容器使用的memory超过其memory.limit限制时,系统将可能会"杀掉"容器,这就是常见的OOM(out of memory)异常,如果该容器的重启策略是always,则kubelet将会重启该容器,因此评估好pod的memory.limit是一个重要的事情。

CPU、Memory计量单位

CPU:CPU的request和limits是通过cpu核数(cpus)来度量的,单位是m(milliunit),数量可以为整数或小数,如0.1m、50m,CPU的资源是绝对值而不是相对值,比如0.1CPU在单核或多核上是一样的,都严格等于0.1 CPU core。m,milliunit代表“千分之一核心”,譬如50m的含义是指50/1000核心,即5%

Memory:内存的计量单位是字节Byte,Byte是由8个位组成的一个单元,也就是1 Byte = 8 bit。pod的内存requests或limits都是使用整数加上国际单位制来表示内存值,国际单位制可以是
十进制的E、P、T、G、M、K、m或二进制的Ei、Pi、Gi、Mi、Ki。
常见的KiB和MiB是以二进制表示的字节单位,KB和MB是以十进制表示的字节单位。
十进制:1 KB = 1000 Byte = 8000 bit
二进制:1 KiB = 2的10次方 Byte = 1024 Byte = 8192 bit

Mi:1Mi = 1024乘1024,而平时使用的单为M是1M = 1000乘1000

memory:内存大小,可以使用Gi、Mi、G、M等单位
cpu的单位m:
注意:Gi和G,Mi和M优点区别,官网解释:Mi表示(1Mi=1024×1024),M表示(1M=1000×1000)(其它单位类推, 如Ki/K Gi/G)

pod资源请求、限额方式

在k8s中,全面限制一个应用及其中的pod所能占用的资源配额,具体可以使用下面三种方式:
1、定义每个pod的资源配额相关参数,如CPU/memory的request、limits;
2、自动为每个没有定义资源配额的pod添加资源配额模板(LimitRange);
3、从总量上限制一个租户(namespace)应用所能使用的资源配额(ResourceQuota)

pod的request、limits是指pod中所有容器的request、limits的总和,对于没有设置request、limits的容器,该值为0或者按照集群配置的默认值计算;
LimiteRang正是用于解决了没有设置配额参数的pod的默认资源配额问题;
REsourceQuota则约束租户的资源总量配额问题。

pod定义requests、limits

pod可以定义资源配额的相关参数:
spec.container[].resources.requests.cpu:容器初始要求的CPU数量
spec.container[].resources.limits.cpu:容器所能使用的最大CPU数量
spec.container[].resources.requests.memory:容器初始要求的内存数量
spec.container[].resources.limits.memory:容器所能使用的最大内存数量

默认情况下,pod中只写requests(cpu和memory写其中一个或两个都写)不写limits,则默认没有最大资源限制;
pod中只写limits.cpu、limits.memory,不写requests.cpu、requests.memory,默认的requests的cpu、memory其值等于对应的limits的cpu、memory值;
pod中只写limits的cpu或memory其中的一个,则requests对应的也等价于limits的对应的一个值。如只写limits.cpu,则requests.cpu值=limits.cpu值,limits.memory没写则requests.memory也没有值。

requests和limits背后的机制
如果容器运行时是docker,那么pod的requests和limits归根结底还是要转换为docker run启动容器的参数,对应如下:
spec.container[].resources.requests.cpu docker run --cpu-shares
spec.container[].resources.limits.cpu docker run --cpu-period
spec.container[].resources.requests.memory 无,请求内存只会作为调度器的参考,不会作为如何参数传递给docker run
spec.container[].resources.limits.memory docker run --memory

查看节点资源情况

kubectl describe node master01 :可以查看节点的计算资源总量和已分配量

pod使用request、limits示例

kubectl  create ns nginx	#创建命名空间
vim nginx-test.yaml 		#创建pod,pod包含2个容器
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
  namespace: nginx-test
spec:
........
    spec:
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: nginx-test-1
        resources:                              #定义资源请求、资源限制
          requests:                             #资源请求
            memory: "20Mi"                      #内存请求
            cpu: "30m"                          #CPU请求
          limits:                               #资源限制
            memory: "50Mi"                      #内存限制
            cpu: "50m"                          #CPU限制
        ports:
        - containerPort: 80
          name: nginx
      - image: tomcat
        imagePullPolicy: IfNotPresent
        name: tomcat-test-2
        resources:                              #定义资源请求、资源限制
          requests:                             #资源请求
            memory: "10Mi"                      #内存请求
            cpu: "20m"                          #CPU请求
          limits:                               #资源限制
            memory: "40Mi"                      #内存限制
            cpu: "40m"                          #CPU限制
        ports:
        - containerPort: 8080
          name: tomcat

#查看pod占用的资源情况
[root@master ~]# kubectl describe node node2  | grep -C10  nginx-test-7d448999cb-mxq6s
  Namespace        Name                           CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------        ----                           ------------  ----------  ---------------  -------------  ----
  nginx-test       nginx-test-7d448999cb-mxq6s    50m (1%)      90m (2%)    30Mi (1%)        90Mi (4%)      6m31s
可以看到,我们创建的pod一共请求50m的CPU,30Mi的内存,最大CPU限制为90m,最大内存限制为90Mi

LimitRange限制命名空间下的pod的资源配额

前面我们讨论了pod中可以手动定义requests.cpu、requests.memory、limits.cpu、limits.memory参数实现pod的资源请求和资源限制,但是当集群很大,存在很多pod中,对每个pod定义资源请求和资源限制显得很繁琐,所以出现了LimitRange机制。

一个 LimitRange(限制范围) 对象提供的限制能够做到:
1、在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
2、在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
3、在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
4、设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。

limitrange属于命名空间,当在一个命名空间中创建了一个limitrange对象,将会在该命名空间中实施 LimitRange 限制。

LimitRange机制的原理:在某个命名空间下,创建一个limitrang资源对象,表示对该命名空间下创建的pod和容器的requests和limits配置进行限制,比如pod和容器的最大、最小、默认requests和limits值。

下面进行演示:

#创建一个命名空间
kubectl  create ns limitrang-nginx	#创建命名空间
#为命名空间创建一个limitrange
vim my-limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: my-limits
  namespace: limitrang-nginx
spec:
  limits:					#定义了容器的request和limit的最大最小值,以及默认request、limit值
  - type: Pod				#类型是pod,表示下面这段整对整个pod而言
    max:					#pod的最大值
      cpu: "4"				#pod的最大cpu
      memory: 2Gi			#pod的最大内存
    min:					#pod的最小值
      cpu: "200m"  			#pod的最小cpu
      memory: 6Mi			#pod的最小内存
    maxLimitRequestRatio:	#这个好像是pod的limits和requests的最大比例
      cpu: 3
      memory: 2
  - type: Container			#类型是容器,表示下面这段整对pod里面的每一个容器而言
    default:	#容器的默认限制值,注意这是默认limits值,当容器没有给定limits时将启动该值(下面说的default limit就是这个参数)
      cpu: 300m				#容器的默认limits.cpu
      memory: 200Mi			#容器的默认limits.memory
    defaultRequest:			#容器的默认请求值,当容器没有设置request时使用
      cpu: 100m				#容器的默认requests.cpu
      memory: 100Mi			#容器的默认requests.memory
    max:					#容器的最大值
      cpu: 2				#容器的最大cpu,即limits.cpu
      memory: 1Gi			#容器的最大memory,即limits.memory
    min:					#容器的最小值
      cpu: 100m				#容器的最小cpu,即requests.cpu
      memory: 3Mi			#容器的最小memory,即requests.memory
    maxLimitRequestRatio:	#这个好像是容器的limits和requests的最大比例
      cpu: 5
      memory: 4
    

参数说明:
在limitrange中,pod和container都可以设置CPU或内存的min、max、maxLimitRequestRatio参数,container还可以定义default request和
default limit参数,而pod不能设置default request和default limit参数。
对container参数解读如下:
container的min表示pod中所有容器的requests最小值;
container的max表示pod中所有容器的requests最大值;
container的defaultRequest是pod中所有未指定requests值的容器的的默认request值;
container的default是pod中所有未指定limits值的容器的默认limits值。(注意看了,default对应的是容器默认limits,不要被它的单词迷糊了)
maxLimitRequestRatio这个参数好像是容器的最大超卖比例。
四个参数关系:min<=defaultRequest<=default<=max

对pod的参数解读如下:
pod的min表示pod中全部容器的requests总和最小值;
pod的max表示pod中全部容器的limits总和最大值;
maxLimitRequestRatio这个参数好像是pod的最大超卖比例。

当一个pod没有定义requests或limits时,且该pod所属命名空间中存在limitrange,则系统将根据limitrange给pod默认设置对应的requests和
limits值;
当即没有limitrange时,参考上面小节《 pod定义requests、limits》讲的那样;
当有limitrange时,但是只给定了limits,没有给定requests值时,经验证,pod还是默认requests值与limits值相等,而不是设置为limitrange设定的defaultRequest值。

Qos服务质量等级

(暂时先忽略limitrange吧,因为Qos服务质量等级涉及pod中容器requests和limits)
在系统资源不足时,k8s会选择"杀掉"部分容器来释放资源,选择哪些pod进行杀掉呢,那么如何衡量一个pod的重要程度时,使用Qos服务质量等级衡量,k8s将容器划分为3个QoS等级,如下:

Guaranteed:完全可靠的,是指pod的所有容器都定义了requests和limits,并且每一个容器的requests和limits值都对应相等且不为0,我们指定,如果定义了limits没有定义requests,那么requests值就等于limits,这种pod的QoS等级就是Guaranteed。

Burstable:弹性波动,较可靠的,当pod的QoS等级既不是Guaranteed又不是BestEffect,那就是Burstable;
这分为2种情况:pod中一部分容器定义了requests和limits且requests值小于limits值;pod中一部分容器都未定义requests和limits。注意:这里说的是没有定义requests和limits,但是不代表这里limitrange不会默认设置,这里可以先暂时忽略limitrange吧。

BestEffect:尽力而为,不太可靠的,是指pod中所有容器都没有定义requests和limits,那么这种pod的QoS等级就是BestEffect。注意:这里说的是没有定义requests和limits,但是不代表这里limitrange不会默认设置,这里可以先暂时忽略limitrange吧。

Guaranteed > Burstable > BestEffect

资源配额管理 Resource Quotas

一个k8s集群可能被多个租户使用,如何分配不同租户可以使用的资源呢? Resource Quotas就是来解决这个问题的。
一般的,一个租户占用一个命名空间,则可以在该命名空间下创建 Resource Quotas来进行资源使用限制;
Resource Quotas可以限制命名空间中某种资源类型对象的总数上限,也可以限制命名空间中pod可使用的可计算资源(可计算资源:cpu、memory)的总上限;资源配额还支持作用域,对符合特定范围的对象加以限制。
官网:https://kubernetes.io/zh-cn/docs/concepts/policy/resource-quotas/

一个命名空间可以设定多个resourcequotas,resourcequotas在k8s中可以简写为quota。

#资源配额,对指定命名空间某种资源对象总数进行限制
[root@master ~]# vim resource-count.yaml 
apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-count
  namespace: limitrang-nginx				#quota在哪个命名空间就表示对该命名空间进行限制
spec:
  hard:										#目前支持对下面这些资源对象进行总数量限制
    configmaps: "50"                        #在该命名空间中允许存在的 ConfigMap 总数上限
    persistentvolumeclaims: "50"            #在该命名空间中允许存在的 PVC 的总数上限
    pods: "4"                               #在该命名空间中允许存在的非终止状态的 Pod 总数上限
    replicationcontrollers: "35"            #在该命名空间中允许存在的 ReplicationController 总数上限
    resourcequotas: "34"                    #在该命名空间中允许存在的 ResourceQuota 总数上限
    services: "68"                          #在该命名空间中允许存在的 Service 总数上限
    services.loadbalancers: "465"           #在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限
    services.nodeports: "65"                #在该命名空间中允许存在的 NodePort 类型的 Service 总数上限
    secrets: "6"                            #在该命名空间中允许存在的 Secret 总数上限

#测试,在limitrang-nginx命名空间中扩容副本数,单由于quota限制了pod副本数最大只能是4个,所以deployment扩容并不会成功
#扩容到10个副本
[root@master ~]# kubectl  scale  deployment -n limitrang-nginx  nginx  --replicas=10
[root@master ~]# kubectl  describe deployments.apps  nginx --namespace=limitrang-nginx  | grep Replicas
Replicas:               10 desired | 4 updated | 4 total | 4 available | 6 unavailable

#通过查看deployment对应的rs,我们看到rs的报错信息,正是由于quota限制命名空间pod副本数最大只能是4个才导致报错扩容不成功
[root@master ~]# kubectl -n limitrang-nginx  describe  rs nginx-6799fc88d8 | tail  -n 1
  Warning  FailedCreate      14m   replicaset-controller  Error creating: pods "nginx-6799fc88d8-tmjpt" 
  is forbidden: exceeded quota: resource-count, requested: pods=1, used: pods=4, limited: pods=4
[root@master ~]# 
#资源配额,对指定命名空间可计算资源资源总数进行限制
[root@master ~]# vim compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "100"				#所有非终止状态的 Pod,其 CPU 需求总量不能超过该值
    requests.memory: 100Gi			#所有非终止状态的 Pod,其内存需求总量不能超过该值
    limits.cpu: "200"				#所有非终止状态的 Pod,其 CPU 限额总量不能超过该值
    limits.memory: 200Gi			#所有非终止状态的 Pod,其内存限额总量不能超过该值
    requests.nvidia.com/gpu: 4		#所有非终止状态的 Pod,GPU需求总量不能超过该值
#   hugepages-<size>: xx			#对于所有非终止状态的 Pod,针对指定尺寸的巨页请求总数不能超过此值
EOF

#例子不举了

还可以对存储资源配额:

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 相关的所有持久卷申领中,命名空间中可以存在的持久卷申领总数

配额作用域
资源配额可以指定作用域,对符合特定范围的对象加以限制;
更详细可以看官网:https://kubernetes.io/zh-cn/docs/concepts/policy/resource-quotas/#quota-scopes

apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "5"
      memory: 10Gi
      pods: "10"
    scopeSelector:			#指定了作用域选择器
      matchExpressions:
      - operator: In
        scopeName: PriorityClass
        values: ["low"]

总结

1、可计算资源

cpu、memory是k8s中最常见的可计算资源,cpu是可压缩资源,cpu可被超量使用,memory是不可压缩资源,超出内存最大值将发生OOM异常;

2、cpu的单位

cpu的是通过cpu核数(cpus)来度量的,单位是m(milliunit),数量可以为整数或小数,如0.1m、50m;单位m,因为含义是milliunit,代表"千分
之一核心",譬如50m的含义是指50/1000核心,即5%。

3、memory的单位

Memory:内存的计量单位是字节Byte,Byte是由8个位组成的一个单元,也就是1 Byte = 8 bit。pod的内存requests或limits都是使用整数加上国际
单位制来表示内存值,国际单位制可以是
十进制的E、P、T、G、M、K、m或二进制的Ei、Pi、Gi、Mi、Ki。
常见的KiB和MiB是以二进制表示的字节单位,KB和MB是以十进制表示的字节单位。
十进制:1 KB = 1000 Byte = 8000 bit
二进制:1 KiB = 2的10次方 Byte = 1024 Byte = 8192 bit

Mi:1Mi = 1024乘1024,而平时使用的单为M是1M = 1000乘1000

memory:内存大小,可以使用Gi、Mi、G、M等单位
cpu的单位m:
注意:Gi和G,Mi和M优点区别,官网解释:Mi表示(1Mi=1024×1024),M表示(1M=1000×1000)(其它单位类推, 如Ki/K Gi/G)

4、pod定义requests、limits来进行容器的cpu、memory资源请求和限制

pod的request等于pod中所有容器的request相加之和。
pod的limit等于pod中所有容器的limits相加之和。

pod可以定义资源配额的相关参数:
spec.container[].resources.requests.cpu:容器初始要求的CPU数量
spec.container[].resources.limits.cpu:容器所能使用的最大CPU数量
spec.container[].resources.requests.memory:容器初始要求的内存数量
spec.container[].resources.limits.memory:容器所能使用的最大内存数量

默认情况下,pod中只写requests(cpu和memory写其中一个或两个都写)不写limits,则默认没有最大资源限制;
pod中只写limits.cpu、limits.memory,不写requests.cpu、requests.memory,默认的requests的cpu、memory其值等于对应的limits的cpu、
memory值;
pod中只写limits的cpu或memory其中的一个,则requests对应的也等价于limits的对应的一个值。如只写limits.cpu,则requests.cpu值
=limits.cpu值,limits.memory没写则requests.memory也没有值。

5、LimitRange限制命名空间下的pod的资源配额

limitrange属于命名空间,当在一个命名空间中创建了一个limitrange对象,将会在该命名空间中实施 LimitRange 限制。
LimitRange机制的原理:在某个命名空间下,创建一个limitrang资源对象,表示对该命名空间下创建的pod和容器的requests和limits配置进行限
制,比如pod和容器的最大、最小、默认requests和limits值。
spec:
  limits:					#定义了容器的request和limit的最大最小值,以及默认request、limit值
  - type: Pod				#类型是pod,表示下面这段整对整个pod而言
    max:					#pod的最大值
      cpu: "4"				#pod的最大cpu
      memory: 2Gi			#pod的最大内存
    min:					#pod的最小值
      cpu: "200m"  			#pod的最小cpu
      memory: 6Mi			#pod的最小内存
    maxLimitRequestRatio:	#这个好像是pod的limits和requests的最大比例
      cpu: 3
      memory: 2
  - type: Container			#类型是容器,表示下面这段整对pod里面的每一个容器而言
    default:	#容器的默认限制值,注意这是默认limits值,当容器没有给定limits时将启动该值(下面说的default limit就是这个参数)
      cpu: 300m				#容器的默认limits.cpu
      memory: 200Mi			#容器的默认limits.memory
    defaultRequest:			#容器的默认请求值,当容器没有设置request时使用
      cpu: 100m				#容器的默认requests.cpu
      memory: 100Mi			#容器的默认requests.memory
    max:					#容器的最大值
      cpu: 2				#容器的最大cpu,即limits.cpu
      memory: 1Gi			#容器的最大memory,即limits.memory
    min:					#容器的最小值
      cpu: 100m				#容器的最小cpu,即requests.cpu
      memory: 3Mi			#容器的最小memory,即requests.memory
    maxLimitRequestRatio:	#这个好像是容器的limits和requests的最大比例
      cpu: 5
      memory: 4

6、pod的Qos服务质量等级

在k8s系统中,使用Qos服务质量等级来衡量一个pod的重要程度,当k8s集群资源不足时,如内存不足,那么Qos服务质量等级低的pod将会被优先"杀
掉"以是否系统资源压力, k8s将pod划分为3个QoS等级,如下:

Guaranteed:完全可靠的,是指pod的所有容器都定义了requests和limits,并且每一个容器的requests和limits值都对应相等且不为0,我们指定,
如果定义了limits没有定义requests,那么requests值就等于limits,这种pod的QoS等级就是Guaranteed。

Burstable:弹性波动,较可靠的,当pod的QoS等级既不是Guaranteed又不是BestEffect,那就是Burstable;
这分为2种情况:pod中一部分容器定义了requests和limits且requests值小于limits值;pod中一部分容器都未定义requests和limits。注意:这里
说的是没有定义requests和limits,但是不代表这里limitrange不会默认设置,这里可以先暂时忽略limitrange吧。

BestEffect:尽力而为,不太可靠的,是指pod中所有容器都没有定义requests和limits,那么这种pod的QoS等级就是BestEffect。注意:这里说的
是没有定义requests和limits,但是不代表这里limitrange不会默认设置,这里可以先暂时忽略limitrange吧。

Guaranteed > Burstable > BestEffect

7、资源配额管理 Resource Quotas

k8s集群是一个多租户的系统,可能有多个命名空间,一般的每个租户占用一个命名空间进行自己的应用部署,那么如何分配不同租户可以使用的资源呢? Resource Quotas就是来解决这个问题的。
一般的,一个租户占用一个命名空间,则可以在该命名空间下创建 Resource Quotas来进行资源使用限制;
Resource Quotas可以限制命名空间中某种资源类型对象的总数上限,也可以限制命名空间中pod可使用的可计算资源(可计算资源:cpu、memory)的总上限;资源配额还支持作用域,对符合特定范围的对象加以限制。

8、limitrange和resourcequotas的区别

limitrange主要是对一个命名空间下创建的pod和容器的requests和limits配置进行限制,比如pod和容器的最大、最小、默认requests和limits值等,换句话说,limitrange针对的是单个pod的可计算资源;
resourcequotas是对命名空间中某种资源对象总数进行限制,如A命名空间能创建多少个pod,能创建多少个configmap等;
resourcequotas还可以对指定命名空间的可计算资源总数进行限制,如限制A命名空间中全部非终止状态的pod的CPU和memory不能超过多少;
resourcequotas还可以对指定命名空间的存储资源总数进行限制,如限制A命名空间中的pvc数量不能超过100个等。

猜你喜欢

转载自blog.csdn.net/MssGuo/article/details/129408723