k8s资源管理QoS、LimitRange和ResourceQuota
Kubernetes资源管理
资源简介
Kubernetes中有Node和Pod两种基础概念。一个Node就是Kubernetes集群中的一个节点,这个节点为Kubernetes提供计算资源(CPU、Memory、GPU等),并且由很多 Pod组成,一个Pod是Kubernetes中部署的最小的单位,由多个容器组成,这些容器会占用Node提供的资源来保持运行。当Kubernetes集群中计算资源不足时(如 Pod 占用资源过多),为了避免Kubernetes某个Node瘫痪,Kubernetes会清理已经存在的资源,比如杀死Pod来完成资源释放。为了避免或降低由于资源不足时导致Pod被杀死这种情况发生导致的风险,Kubernetes提供了资源限制的机制,其核心如下:
- 限制Pod资源占用率
- 为Pod划分等级(QoS),当资源不足时先杀死等级低的Pod来释放资源
在Kubernetes中资源又可分为API资源与计算资源,API资源主要包括Pod、Service、Deployment 等,计算资源主要包括CPU、GPU及Memory等,在计算资源中资源可分成 可压缩资源与不可压缩资源,因为在资源不足的时候,它们的表现不一样,对于不可压缩资源,资源不足也就无法继续申请资源(例如内存不足是无法再分配的),并且会导致Pod的运行产生无法预测的错误。对于可压缩资源,如CPU时间片,即使Pod使用的 CPU资源很多,CPU使用也可以按照权重分配给所有Pod使用,虽然当某个Pod分配 CPU过多时使其它Pod能够使用CPU的时间片减少,但不会导致程序直接不可用
Pod资源配置参数
Pod中可以配置CPU与Memory参数,如下:
- spec.container[].resources.requests.cpu
- spec.container[].resources.requests.memory
- spec.container[].resources.limits.cpu
- spec.container[].resources.limits.memory
其中requests是设置容器使用CPU与Memory资源占用预估值,它将作为容器调度分资源分配时的判断依据,只有当前节点上可分配的资源量大于requests中设置的值时才允许容器调度到该节点。limits是对容器使用CPU与Memory资源的上限,由于CPU是属于可压缩资源,Pod是无法突破其设置值的上限的,而Memory是不可压缩资源,当Pod突破内存上限时Kubernetes会杀死该Pod,所以设置内存限制时得慎重考虑
CPU与Memory
CPU
CPU的Requests和Limites能设置的值跟CPU的核心数息息相关,CPU的资源值是个绝对值,0.1CPU(100m)不论在单核心CPU的服务器上还是多核心CPU的服务器上,都是等于0.1CPU
Memory
Memory可以设置的Requests和Limits单位是字节数,需要整数加国际单位来表示内存值
国际单位制:
- 十进制:E、P、T、G、M、K、m
- 二进制:Ei、Pi、Ti、Gi、Mi、Ki、MiB
比如:
- 1 KB = 1000 Bytes = 8000 Bits;
- 1 KiB = 1024 Bytes = 8192 Bits;
一般推荐CPU是用 m,例如0.1个CPU使用100m表示,1个CPU用1000m表示;对于内存推荐使用Mi,例如256Mi、512Mi、1Gi
限额参数原理简介
requests.cpu
上面参数 spec.container[].resources.requests.cpu 设置的值会转换为CPU核心数,这个值乘以1024得到的结果,会作为docker run命令执行时–cpu-shares的参数。–cpu-shares参数是一个相对权重值,这个相对权重值会决定Docker在资源竞争时调整分配给容器可用资源的比例,默认对所有的容器的CPU资源可利用的占比都一致,即1024。这个值可以设置为2个CPU(值为 2048)或者 3个CPU(值为 3072)依次类推。启动两个容器且设置 --cpu-shares 参数分别为1024和2048,在主机CPU资源产生竞争时Docker 会按照1:2 的比例将CPU资源分配刚给这两个容器使用
由上可知requests.cpu参数对kubernetes来说这个值是一个绝对值,用于Kubernetes调度与管理,对于Docker来说是相对值,用于CPU资源比例分配
limits.cpu
Docker中有两个参数 --cpu-quota和–cpu-period,这两个参数一般配合使用,其中 --cpu-period是指使用CPU调度程序的周期,单位 μs (微秒),而 --cpu-quota 为在周期内使用CPU的配额,单位也是μs(微秒)。如何运行一个Docker容器并且设置它可用CPU为 1.5 个,那么久可用设置参数 --cpu-period=100000(100ms) 和 --cpu-quota=150000(150ms),这说明在100ms配置150ms的CPU量来完成任务,150/100 = 1.5,所以即完成1.5 个CPU的分配,关于Docker如何实现的这个限制功能,具体可用看内核的CPU分配策略
Kubernetes中的参数spec.container[].resources.limits.cpu如何限制CPU的,其实也是利用上面两个参数进行限制CPU使用的,利用公式 (limits.cpu * 100000)/1000得出结果的值作为–cpu-quota参数值,且将–cpu-period参数值设置为100000,然后这两个参数进行计算得出可用的CPU时间分片。Kubernetes中想限制只使用 0.1 个 CPU,就可以设置 limits.cpu=100m,然后可以按照上面公式计算一下,(100 * 100000)/1000 = 10000,将这个值赋给–cpu-quota参数,然后Kubernetes设置–cpu-period参数默认为100000,计算Docker分配容器最终CPU使用量为–cpu-quota/–cpu-period即10000/100000 = 0.1,符合Kubernetes中对CPU的限制
requests.memory
内存占用值,标识一般正常情况下镜像使用的内存量,这个参数值不会传递给Docker,它一般供Kubernetes调度器调度和作为资源使用指标的依据
limits.memory
容器内存限制,一般这个设置的值会传递给Docker启动时的–memory参数,用于限制启动的容器的内存使用大小,如果容器在运行过程中内存使用飙升超过这个限制的值,那么该容器可能会被杀掉,所以一般设置此值需要提前测试一下程序占用内存可能的值,进而考虑设置它的值的大小
limits与requests示例
Deployment对象来加上resources配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Requests和Limits对调度器调度的影响
Kubernetes中每次创建Pod都是将其启动到Kubernetes Node节点上面,每个节点的计算资源(例如 CPU、Memory)是有限制的,该节点上所以的Pod占用资源大小综合不能超过节点所拥有的真实的资源大小。例如一个节点上拥有10GB内存,在这个节点上启动 limits.memory=2GB,requests.memory=1GB 的 Pod,考虑到系统占用资源情况,所以该节点最多启 9 个一样的 Pod。一般 Pod中可设置的requests参数值会影响到调度器对 Pod的调度,而limits参数主要是对容器可用资源的限制,对资源的调度影响比较小
QoS资源服务质量控制
QoS 等级分类
Kubernetes中如果一个Node节点上的Pod 占用资源过多并且不断飙升导致Node节点资源不足,可能会导致为了保证节点可用,将容器被杀掉。在遇见这种情况时候希望先杀掉那些不太重要的容器,确保核心容器不会首先被杀掉。为了衡量先杀掉哪个程序,所以推出了优先级机制 QoS (Quality of Service)来做判断,Kubernetes将容器划分为三种QoS等级:
- Guaranteed: 完全可靠的
- Burstable: 较可靠的。
- BestEffort: 不太可靠的
Pod QoS特点
Kubernetes中资源不足时,根据QoS等级杀死Pod会有以下特点:
- BestEffort Pod: 优先级最低,在Kubernetes资源不足时候会将此Pod先杀掉
- Burstable Pod: 优先级居中,如果整个系统内存资源不足,且已经杀完全部 BestEffort 等级的Pod时可能被杀掉
- Guaranteed Pod: 优先级最高,一般情况下不会被杀掉,除非资源不足且系统 BestEffort和Burstable的Pod都不存在的情况下,才可能被杀掉
Pod QoS等级
Kubernetes中Qos等级是根据Limits和Requests这两个参数设置的值息息相关,Kubernetes会根据这两个值的不同而给Pod设置不同的QoS等级
Guaranteed (等级-最高)
如果Pod中所有容器都定义了 Limits和Requests,并且全部容器的Limits值 = Requests值(值不能为0),那么该Pod的QoS级别就是 Guaranteed
注意:这种情况下容器可以只设置Limits值即可,引入在只设置Limits不设置Requests 情况下,Requests值默认等于Limits的值
BestEffort(等级-最低)
如果Pod中所有容器都未定义Requests和Limits值,该Pod的Qos即为BestEffort
Burstable(等级-中等)
当一个Pod既不是Guaranteed 级别,也不是BestEffort级别时候,该Pod的QoS级别就是Burstable。例如Pod中全部或者部分容器Requests和Limits都定义,且Requests小于 Limits值,或者Pod中一部分容器未定义Requests和Limits资源时
三种Qos示例
Guaranteed
每个容器都设置Limits而不设置Requests
containers:
- name: example-container1
resources:
limits:
cpu: 10m
memory: 512Mi
- name: example-container2
resources:
limits:
cpu: 100m
memory: 100Mi
每个容器都设置Limits值和Requests值都相等
containers:
- name: example-container1
resources:
limits:
cpu: 100m
memory: 512Mi
requests:
cpu: 100
memory: 512Mi
- name: example-container2
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 200
memory: 256Mi
BestEffort
Pod中的所有容器都未定义Requests和Limits
containers:
- name: example-container1
resources:
- name: example-container2
resources:
Burstable
Pod中只要有一个容器的Requests和Limits的设置的值不相同
containers:
- name: example-container1
resources:
limits:
cpu: 100m
memory: 512Mi
requests:
cpu: 100
memory: 512Mi
- name: example-container2
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100
memory: 128Mi
Pod都存在Limits,但是Limits中限制的类型不同
containers:
- name: example-container1
resources:
limits:
memory: 512Mi
- name: example-container2
resources:
limits:
cpu: 200m
Pod中两个容器只有一个Limits ,另一个都没有设置
containers:
- name: example-container1
resources:
limits:
cpu: 100m
memory: 512Mi
requests:
cpu: 100
memory: 512Mi
- name: example-container2
resources:
资源范围管理对象LimitRange
默认情况下如果创建一个Pod没有设置Limits和Requests对其加以限制,那么这个Pod可能能够使用Kubernetes集群中全部资源, 但是每创建Pod资源时都加上这个动作是繁琐的,考虑到这点Kubernetes提供了LimitRange对象,它能够对一个Namespace下的全部 Pod使用资源设置默认值、并且设置上限大小和下限大小等操作
创建测试Namespace
[root@k8s01 ~]# kubectl create namespace limit-namespace
创建LimitRange对Namespace资源限制
创建一个LimitRange对象限制Namespace下的资源使用,其中limit的类型有两种:
- 对 Container 使用资源进行限制,在 Pod 中的每一个容器都受此限制
- 对 Pod 进行限制,即 Pod 中全部 Container 使用资源总和来进行限制
资源对象limit-range.yaml内容如下
[root@k8s01 ~]# vim limit-range.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: limit-test
spec:
limits:
- type: Pod #对Pod中所有容器资源总和进行限制
max:
cpu: 4000m
memory: 2048Mi
min:
cpu: 10m
memory: 128Mi
maxLimitRequestRatio:
cpu: 5
memory: 5
- type: Container #对Pod中所有容器资源进行限制
max:
cpu: 2000m
memory: 1024Mi
min:
cpu: 10m
memory: 128Mi
maxLimitRequestRatio:
cpu: 5
memory: 5
default:
cpu: 1000m
memory: 512Mi
defaultRequest:
cpu: 500m
memory: 256Mi
注意:LimitRange 类型为 Pod 中,不能设置 Default
[root@k8s01 ~]# kubectl apply -f limit-range.yaml -n limit-namespace
查看LimitRange
[root@k8s01 ~]# kubectl describe limitrange limit-test -n limit-namespace
Name: limit-test
Namespace: limit-namespace
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Pod cpu 10m 4 - - 5
Pod memory 128Mi 2Gi - - 5
Container cpu 10m 2 500m 1 5
Container memory 128Mi 1Gi 256Mi 512Mi 5
可以看到上面LimitRange对象中的配置可以了解到,如果容器使用默认值,则容器的 Request和Limits 一致
LimitRange 对象参数介绍
Container 参数:
- max: Pod中所有容器的Limits值上限
- min: Pod中所有容器的Requests值下限
- default: Pod中容器未指定Limits时,将此值设置为默认值
- defaultRequest: Pod中容器未指定Request时,将此值设置为默认值
- maxLimitRequestRatio: Pod中的容器设置Limits与Requests的比例的值不能超过maxLimitRequestRatio参数设置的值,即Limits/Requests ≤ maxLimitRequestRatio
Pod 参数:
- max: Pod中所有容器资源总和值上限
- min: Pod中所有容器资源总和值下限
- maxLimitRequestRatio: Pod中全部容器设置Limits总和与Requests总和的比例的值不能超过
- maxLimitRequestRatio参数设置的值,即(All Container Limits)/(All Container Requests) ≤ maxLimitRequestRatio
创建Pod测试
Container默认值测试
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: nginx1
image: nginx:latest
查看Pod的描述信息,可以看到Limits和Requests的值和上面LimitRange中配置的默认值一致
Containers:
nginx1:
Limits:
cpu: 1000m
memory: 512Mi
Requests:
cpu: 500m
memory: 256Mi
Container Max限制测试
上面设置Max中CPU和Memory的值分别为2000m与1024Mi,下面创建一个Pod并设置其中某一个容器limits值超过LimitRange中设置的值,查看是否能起到限制作用
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: nginx1
image: nginx:latest
- name: nginx2
image: nginx:latest
resources:
limits:
cpu: "3000m"
memory: "512Mi"
执行Kubectl命令创建Pod时并没有通过验证,并且已经提示CPU不能超过 2 个
[root@k8s01 ~]# kubectl apply -f test.yaml -n limit-namespace
Error from server (Forbidden): error when creating "test.yaml":
pods "test" is forbidden: maximum cpu usage per Container is 2, but limit is 3.
Container Min限制测试
上面设置Min中CPU和Memory的值分别为10m与128Mi,下面创建一个Pod并设置其中某一个容器Requests值小于LimitRange中设置的值,是否能起到限制作用
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: nginx1
image: nginx:latest
- name: nginx2
image: nginx:latest
resources:
requests:
cpu: "100m"
memory: "64Mi"
执行Kubectl命令创建Pod时并没有通过验证,并且已经提示Memory不能低于128Mi大小
[root@k8s01 ~]# kubectl apply -f test.yaml -n limit-namespace
Error from server (Forbidden): error when creating "test.yaml": pods "test" is forbidden:
[minimum memory usage per Container is 128Mi, but request is 64Mi.
, cpu max limit to request ratio per Container is 5, but provided ratio is 10.000000.
, memory max limit to request ratio per Container is 5, but provided ratio is 8.000000.]
Container MaxLimitRequestRatio限制测试
上面LimitRange中设置maxLimitRequestRatio值为5,就是限制Pod中容器CPU和 Memory的limit/request的值小于5,测试一下设置内存limit/request中值超过5创建Pod是否会报错
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: nginx1
image: nginx:latest
- name: nginx2
image: nginx:latest
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "1024Mi"
Kubectl命令创建Pod时并没有通过验证,并且已经提示limit/request ratio为8,超过了限制的值5
[root@k8s01 ~]# kubectl apply -f test.yaml -n limit-namespace
Error from server (Forbidden): error when creating "test.yaml":
pods "test" is forbidden: memory max limit to request ratio per Container is 5, but provided ratio is 8.000000.
资源配额管理对象ResourcesQuota
Kubernetes是一个多租户平台,更是一个镜像集群管理工具。一个Kubernetes集群中的资源一般是由多个团队共享的,经常要考虑的是如何对这个整体资源进行分配。在 kubernetes中提供了Namespace来讲应用隔离,那么是不是也能将资源的大小跟 Namespace挂钩进行一起隔离,Kubernetes提供了Resources Quotas工具,让集群管理员可以创建ResourcesQuota对象管理这个集群整体资源的配额,它可以限制某个Namespace下计算资源的使用量,也可以设置某个Namespace下某种类型对象的上限等
通过设置不同的Namespace与对应的 RBAC权限将各个团队隔离,然后通过 ResourcesQuota对象来现在该Namespace能够拥有的资源的多少
开启资源配额ResourceQuota
ResourceQuota对象一般在Kubernetes中是默认开启的,如果未开启且不能创建该对象,那么可以进入Master的Kubernetes配置目录修改Apiserver配置文件kube-apiserver.yaml,在添加参数 --admission-control=ResourceQuota 来开启
...
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --admission-control=ResourceQuota #开启ResourceQuota
...
注意:一个Namespace中可以拥有多个ResourceQuota对象
配额资源类型
- 计算资源配额: 限制一个 Namespace 中所有 Pod 的计算资源(CPU、Memory)的总和
- 存储资源配额: 限制一个 Namespace 中所有存储资源的总量
- 对象数量配额: 限制一个 Namespace 中指定类型对象的数量
ResourcesQuota支持的计算资源
- cpu: 所有非终止状态的Pod中,其CPU需求总量不能超过该值
- limits.cpu: 所有非终止状态的Pod中,其CPU限额总量不能超过该值
- limits.memory: 所有非终止状态的Pod中,其内存限额总量不能超过该值
- memory: 所有非终止状态的Pod中,其内存需求总量不能超过该值
- requests.cpu: 所有非终止状态的Pod中,其CPU需求总量不能超过该值
- requests.memory: 所有非终止状态的Pod中,其内存需求总量不能超过该值
ResourcesQuota支持限制的存储资源
- requests.storage:所有 PVC 请求的存储大小总量不能超过此值
- Persistentvolumeclaims: PVC 对象存在的最大数目
- .storageclass.storage.k8s.io/requests.storage: 和 StorageClass 关联的 PVC 的请求存储的量大小不能超过此设置的值
- .storageclass.storage.k8s.io/persistentvolumeclaims: 和 StorageClass 关联的 PVC 的总量
ResourcesQuota支持限制的对象资源
- Configmaps: 允许存在的 ConfigMap 的数量
- Pods: 允许存在的非终止状态的 Pod 数量,如果 Pod 的 status.phase 为 Failed 或 Succeeded , 那么其处于终止状态
- Replicationcontrollers: 允许存在的 Replication Controllers 的数量
- Resourcequotas: 允许存在的 Resource Quotas 的数量
- Services: 允许存在的 Service 的数量
- services.loadbalancers: 允许存在的 LoadBalancer 类型的 Service 的数量
- services.nodeports: 允许存在的 NodePort 类型的 Service 的数量
- Secrets: 允许存在的 Secret 的数量
配额作用域
每个配额都有一组相关的作用域(scope),配额只会对作用域内的资源生效。当一个作用域被添加到配额中后,它会对作用域相关的资源数量作限制,如配额中指定了允许(作用域)集合之外的资源,会导致验证错误
- Terminating: 匹配 spec.activeDeadlineSeconds ≥ 0 的 Pod
- NotTerminating: 匹配 spec.activeDeadlineSeconds 是 nil(空) 的 Pod
- BestEffort: 匹配所有 QoS 等级是 BestEffort 级别的 Pod
- NotBestEffort: 匹配所有 QoS 等级不是 BestEffort 级别的 Pod
BestEffort作用域限制配额跟踪以下资源
- pods
Terminating、 NotTerminating和NotBestEffort限制配额跟踪以下资源:
- cpu
- limits.cpu
- limits.memory
- memory
- pods
- requests.cpu
- requests.memory
ResourceQuota使用示例
设置Namespace计算资源的配额
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
spec:
hard:
pods: "4"
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
[root@k8s01 ~]# kubectl apply -f resources-test1.yaml -n limit-namespace
查看创建后的ResourcesQuota配额的详细信息
[root@k8s01 ~]# kubectl describe quota compute-resources -n limit-namespace
Name: compute-resources
Namespace: limit-namespace
Resource Used Hard
-------- ---- ----
limits.cpu 2 2
limits.memory 1Gi 2Gi
pods 2 4
requests.cpu 2 1
requests.memory 512Mi 1Gi
设置Namespace对象数量的配额
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
configmaps: "10"
persistentvolumeclaims: "4"
replicationcontrollers: "20"
secrets: "10"
services: "10"
services.loadbalancers: "2"
[root@k8s01 ~]# kubectl apply -f resources-test2.yaml -n limit-namespace
查看创建后的ResourcesQuota配额的详细信息
[root@k8s01 ~]# kubectl describe quota object-counts -n limit-namespace
Name: object-counts
Namespace: limit-namespace
Resource Used Hard
-------- ---- ----
configmaps 0 10
persistentvolumeclaims 0 4
replicationcontrollers 0 20
secrets 1 10
services 0 10
services.loadbalancers 0 2
限制Namespace下Pod数量并只作用域BestEffort
apiVersion: v1
kind: ResourceQuota
metadata:
name: besteffort
spec:
hard:
pods: "5"
scopes:
- BestEffort
[root@k8s01 ~]# kubectl apply -f resources-test3.yaml -n limit-namespace
查看创建后的ResourcesQuota配额的详细信息
[root@k8s01 ~]# kubectl describe quota besteffort -n limit-namespace
Name: besteffort
Namespace: limit-namespace
Scopes: BestEffort
* Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
Resource Used Hard
-------- ---- ----
pods 0 5
配额对象创建完成后,可以创建几个Pod对其配额规则进行验证