对于Kubernetes资源,有两个重要参数:CPU Request与Memory Request。
通常在定义Pod时并没有定义这两个参数,此时Kubernetes会认为该Pod所需的资源很少,并可以将其调度到任何可用的Node上。因此,当集群中的计算资源不很充足时,如果集群中的Pod负载突然增大,就会使某个Node的资源严重不足。为了避免Node系统挂掉,该Node会选择“清理”某些Pod来释放资源,此时每个Pod都可能被“清理”。若其中某些Pod非常重要,比如与数据存储相关的、与登录相关的、与查询余额相关的,需要在系统资源严重不足时,也得保障这些Pod的存活。
Kubernetes中该保障机制的核心如下:
Kubernetes集群里的节点提供的资源主要是计算资源,计算资源是可计量的能被申请、分配和使用的基础资源,这使之区别于API资源(API Resources,例如Pod和Services等)。
当前Kubernetes集群中的计算资源主要包括CPU、GPU及Memory,通常绝大多数常规应用不需要GPU资源。CPU与Memory是被Pod使用的,因此在配置Pod时可以通过参数CPU Request及Memory Request为其中的每个容器指定所需使用的CPU与Memory量,Kubernetes会根据Request的值去查找有足够资源的Node来调度此Pod,如果没有相应的Node能满足,则调度失败。
通常,一个程序所使用的CPU与Memory是一个动态的量,确切地说,是一个范围,跟它的负载密切相关:负载增加时,CPU和Memory的使用量也会增加。因此最准确的说法是,某个进程的CPU使用量为0.1个CPU~1个CPU,内存占用则为500MB~1GB。对应到Kubernetes的Pod容器上,就是下面这4个参数:
其中,limits对应资源量的上限,即最多允许使用这个上限的资源量。由于CPU资源是可压缩的,进程无论如何也不可能突破上限,因此设置起来比较容易。对于Memory这种不可压缩资源来说,需要进行一定的规划,若设置得小了,当进程在业务繁忙期试图请求超过Limit限制的Memory时,此进程就会被Kubernetes杀掉。因此,Memory的Request与Limit的值需要结合进程的实际需求谨慎设置。
若存在成百上千个不同的Pod,那么先手动设置每个Pod的这4个参数,再检查并确保这些参数的设置是否合理。比如不能出现内存超过2GB或者CPU占据2个核心的Pod。最后需要手工检查不同租户(Namespace)下的Pod的资源使用量是否超过限额。
若上设置相对繁琐复杂,为此,Kubernetes提供了另外两个相关对象:LimitRange及ResourceQuota,前者解决request与limit参数的默认值和合法取值范围等问题,后者则解决约束租户的资源配额问题。集群管理涉及计算资源管理(ComputeResources)、服务质量管理(QoS)、资源配额管理(LimitRange、ResourceQuota)等方面。
ResourceQoS解读:若不设置CPU或Memory的Limit值,该Pod的资源使用量有一个弹性范围,假设Pod A的Memory Request被设置为1GB,Node A当时空闲的Memory为1.2GB,符合Pod A的需求,因此Pod A被调度到Node A上。运行3天后,Pod A的访问请求大增,内存需要增加到1.5GB,此时Node A的剩余内存只有200MB,由于Pod A新增的内存已经超出系统资源,所以在这种情况下,Pod A就会被Kubernetes杀掉。没有设置Limit的Pod,或者只设置了CPULimit或者MemoryLimit两者之一的Pod,表面看都是很有弹性的,但实际上,相对于4个参数都被设置的Pod,是处于一种相对不稳定的状态的,它们与4个参数都没设置的Pod相比,只是稳定一点而已。
以CPU为例,下图显示了未设置Limits和设置了Requests、Limits的CPU使用率的区别。
尽管Requests和Limits只能被设置到容器上,但是设置Pod级别的Requests和Limits能大大提高管理Pod的便利性和灵活性,因此在Kubernetes中提供了对Pod级别的Requests和Limits的配置。对于CPU和内存而言,Pod的Requests或Limits是指该Pod中所有容器的Requests或Limits的总和(对于Pod中没有设置Requests或Limits的容器,该项的值被当作0或者按照集群配置的默认值来计算)。
CPU的Requests和Limits是通过CPU数(cpus)来度量的。CPU的资源值是绝对值,而不是相对值,比如0.1CPU在单核或多核机器上是一样的,都严格等于0.1CPUcore。
Memory内存的Requests和Limits计量单位是字节数。使用整数或者定点整数加上国际单位制来表示内存值。国际单位制包括十进制的E、P、T、G、M、K、m,或二进制的Ei、Pi、Ti、Gi、Mi、Ki。KiB与MiB是以二进制表示的字节单位,常见的KB与MB则是以十进制表示的字节单位,比如:
因此,128974848、129e6、129M、123Mi的内存配置是一样的。
Kubernetes的计算资源单位是大小写敏感的,因为m可以表示千分之一单位(milliunit),而M可以表示十进制的1000,二者的含义不同;同理,小写的k不是一个合法的资源单位。
示例1:
[root@k8smaster01 study]# vi requests01.yaml
1 apiVersion: v1
2 kind: Pod
3 metadata:
4 name: frontend
5 spec:
6 continers:
7 - name: db
8 image: mysql
9 resources:
10 requests:
11 memory: "64Mi"
12 cpu: "250m"
13 limits:
14 memory: "128Mi"
15 cpu: "500m"
16 - name: wp
17 image: wordpress
18 resources:
19 requests:
20 memory: "64Mi"
21 cpu: "250m"
22 limits:
23 memory: "128Mi"
24 cpu: "500m"
25
解读:如上所示,该Pod包含两个容器,每个容器配置的Requests都是0.25CPU和64MiB(226 Bytes)内存,而配置的Limits都是0.5CPU和
128MiB(227 Bytes)内存。
这个Pod的Requests和Limits等于Pod中所有容器对应配置的总和,所以Pod的Requests是0.5CPU和128MiB(227 Bytes)内存,Limits是1CPU和256MiB(228 Bytes)内存。
当一个Pod创建成功时,Kubernetes调度器(Scheduler)会为该Pod选择一个节点来执行。对于每种计算资源(CPU和Memory)而言,每个节点都有一个能用于运行Pod的最大容量值。调度器在调度时,首先要确保调度后该节点上所有Pod的CPU和内存的Requests总和,不超过该节点能提供给Pod使用的CPU和Memory的最大容量值。
例如,某个节点上的CPU资源充足,而内存为4GB,其中3GB可以运行Pod,而某Pod的Memory Requests为1GB、Limits为2GB,那么在这个节点上最多可以运行3个这样的Pod。假设该节点已经启动3个此Pod实例,而这3个Pod的实际内存使用都不足500MB,那么理论上该节点的可用内存应该大于1.5GB。但是由于该节点的Pod Requests总和已经达到节点的可用内存上限,因此Kubernetes不会再将任何Pod实例调度到该节点上。
注意:可能某节点上的实际资源使用量非常低,但是已运行Pod配置的Requests值的总和非常高,再加上需要调度的Pod的Requests值,会超过该节点提供给Pod的资源容量上限,这时Kubernetes仍然不会将Pod调度到该节点上。如果Kubernetes将Pod调度到该节点上,之后该节点上运行的Pod又面临服务峰值等情况,就可能导致Pod资源短缺。
kubelet在启动Pod的某个容器时,会将容器的Requests和Limits值转化为相应的容器启动参数传递给容器执行器(Docker或者rkt)。如果容器的执行环境是Docker,那么会传递如下4个参数给Docker容器:
这个参数会转化为core数(比如配置的100m会转化为0.1),然后乘以1024,再将这个结果作为--cpu-shares参数的值传递给docker run命令。在docker run命令中,--cpu-share参数是一个相对权重值(RelativeWeight),这个相对权重值会决定Docker在资源竞争时分配给容器的资源比例。
举例说明--cpu-shares参数在Docker中的含义:比如将两个容器的CPU Requests分别设置为1和2,那么容器在docker run启动时对应的--cpu-shares参数值分别为1024和2048,在主机CPU资源产生竞争时,Docker会尝试按照1∶2的配比将CPU资源分配给这两个容器使用。
注意这个参数对于Kubernetes而言是绝对值,主要用于Kubernetes调度和管理;同时Kubernetes会将这个参数的值传递给docker run的--cpu-shares参数。--cpu-shares参数对于Docker而言是相对值,主要用于资源分配比例。
这个参数会转化为millicore数(比如配置的1被转化为1000,而配置的100m被转化为100),将此值乘以100000,再除以1000,然后将结果值作为--cpu-quota参数的值传递给docker run命令。docker run命令中另外一个参数--cpu-period默认被设置为100000,表示Docker重新计量和分配CPU的使用时间间隔为100000μs(100ms)。
Docker的--cpu-quota参数和--cpu-period参数一起配合完成对容器CPU的使用限制:比如Kubernetes中配置容器的CPU Limits为0.1,那么计算后--cpu-quota为10000,而--cpu-period为100000,这意味着Docker在100ms内最多给该容器分配10ms×core的计算资源用量,10/100=0.1core的结果与Kubernetes配置的意义是一致的。
注意:如果kubelet的启动参数--cpu-cfs-quota被设置为true,那么kubelet会强制要求所有Pod都必须配置CPU Limits(如果Pod没有配置,则集群提供了默认配置也可以)。从Kubernetes1.2版本开始,这个--cpu-cfs-quota启动参数的默认值就是true。
这个参数值只提供给Kubernetes调度器作为调度和管理的依据,不会作为任何参数传递给Docker。
这个参数值会转化为单位为Bytes的整数,数值会作为--memory参数传递给docker run命令。如果一个容器在运行过程中使用了超出了其内存Limits配置的内存限制值,那么它可能会被杀掉,如果这个容器是一个可重启的容器,那么之后它会被kubelet重新启动。
因此对容器的Limits配置需要进行准确测试和评估。与内存Limits不同的是,CPU在容器技术中属于可压缩资源,因此对CPU的Limits配置一般不会因为偶然超标使用而导致容器被系统杀。
Pod的资源用量会作为Pod的状态信息一同上报给Master。如果在集群中配置了Heapster来监控集群的性能数据,那么还可以从Heapster中查看Pod的资源用量信息。
如果Kubernetes调度器在集群中找不到合适的节点来运行Pod,那么这个Pod会一直处于未调度状态,直到调度器找到合适的节点为止。每次调度器尝试调度失败时,Kubernetes都会产生一个事件,我们可以通过下面这种方式来查看事件的信息:
kubectl describe pod <podname>| grep -A 3 Events
如果一个或者多个Pod调度失败且有这类错误,那么可以尝试以下几种解决方法:
可以使用kubectl describe nodes命令来查看集群中节点的计算资源容量和已使用量:
[root@k8smaster01 ~]# kubectl describe nodes k8snode01
超过可用资源容量上限(Capacity)和已分配资源量(Allocatedresources)差额的Pod无法运行在该Node上。
如果容器使用的资源超过了它配置的Limits,那么该容器可能会被强制终止。可以通过kubectl describe pod命令来确认容器是否因为这个原因被终止:
kubectl describe pod
Restart Count:5说明这个名为simmemleak的容器被强制终止并重启了5次。可以在使用kubectl get pod命令时添加-o go-template=...格式参数来读取已终止容器之前的状态信息:
kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name:"}}{{.name}}{{"\r\nLastate:"}}{{.lastState}}{{end}}'
可以看到这个容器因为reason:OOM Killed而被强制终止,说明这个容器的内存超过了限制(OutofMemory)。
在默认情况下,Kubernetes不会对Pod加上CPU和内存限制,这意味着Kubernetes系统中任何Pod都可以使用其所在节点的所有可用的CPU和内存。通过配置Pod的计算资源Requests和Limits,可以限制Pod的资源使用,但对于Kubernetes集群管理员而言,配置每一个Pod的Requests和Limits是烦琐的,而且很受限制。更多时候,需要对集群内Requests和Limits的配置做一个全局限制。
常见的配置场景如下:
针对这些需求,Kubernetes提供了LimitRange机制对Pod和容器的Requests和Limits配置进一步做出限制。
示例1:
[root@k8smaster01 study]# kubectl create namespace limit-example #创建namespace
[root@k8smaster01 study]# vi limits.yaml #创建limitrange
1 apiVersion: v1
2 kind: LimitRange
3 metadata:
4 name: mylimits
5 spec:
6 limits:
7 - max:
8 cpu: "4"
9 memory: 2Gi
10 min:
11 cpu: 200m
12 memory: 6Mi
13 maxLimitRequestRatio:
14 cpu: 3
15 memory: 2
16 type: Pod
17 - default:
18 cpu: 300m
19 memory: 200Mi
20 defaultRequest:
21 cpu: 200m
22 memory: 100Mi
23 max:
24 cpu: "2"
25 memory: 1Gi
26 min:
27 cpu: 100m
28 memory: 3Mi
29 maxLimitRequestRatio:
30 cpu: 5
31 memory: 4
32 type: Container
33
[root@k8smaster01 study]# kubectl create -f limits.yaml --namespace=limit-example #为Namespace“limit-example”创建LimitRange
[root@k8smaster01 study]# kubectl get limitranges -n limit-example
[root@k8smaster01 study]# kubectl describe limitranges mylimits -n limit-example
解读:
[root@k8smaster01 study]# kubectl run nginx --image=nginx --replicas=1 --namespace=limit-example
[root@k8smaster01 study]# kubectl get pods --namespace=limit-example
NAME READY STATUS RESTARTS AGE
nginx-7bb7cd8db5-mzcvb 1/1 Running 0 54s
解读:命名空间中LimitRange只会在Pod创建或者更新时执行检查。如果手动修改LimitRange为一个新的值,那么这个新的值不会去检查或限制之前已经在该命名空间中创建好的Pod。如果在创建Pod时配置的资源值(CPU或者内存)超过了LimitRange的限制,那么该创建过程会报错,在错误信息中会说明详细的错误原因。
[root@k8smaster01 study]# kubectl get pods nginx-7bb7cd8db5-mzcvb --namespace=limit-example -o yaml | grep resources -C 6 #查看该Pod的resource
1 uid: 5fd37e03-ea08-44f3-a2c7-30ad31c7ab4a
2 spec:
3 containers:
4 - image: nginx
5 imagePullPolicy: Always
6 name: nginx
7 resources:
8 limits:
9 cpu: 300m
10 memory: 200Mi
11 requests:
12 cpu: 200m
13 memory: 100Mi
14
解读:由于该Pod未配置资源Requests和Limits,所以使用了namespace limit-example中的默认CPU和内存定义的Requests和Limits值。
[root@k8smaster01 study]# vi invalid-pod.yaml
1 apiVersion: v1
2 kind: Pod
3 metadata:
4 name: invalid-pod
5 spec:
6 containers:
7 - name: kubernetes-serve-hostname
8 image: gcr.azk8s.cn/google_containers/server_hostname
9 resources:
10 limits:
11 cpu: "3"
12 memory: 100Mi
13
[root@k8smaster01 study]# kubectl create -f invalid-pod.yaml --namespace=limit-example
Error from server (Forbidden): error when creating "invalid-pod.yaml": pods "invalid-pod" is forbidden: maximum cpu usage per Container is 2, but limit is 3
解读:创建该Pod,会出现系统报错了,并且提供的错误原因为超过资源限制。
[root@k8smaster01 study]# vi limit-test-nginx.yaml
1 apiVersion: v1
2 kind: Pod
3 metadata:
4 name: limit-test-nginx
5 labels:
6 name: limit-test-nginx
7 spec:
8 containers:
9 - name: limit-test-nginx
10 image: nginx
11 resources:
12 limits:
13 cpu: "1"
14 memory: 512Mi
15 requests:
16 cpu: "0.8"
17 memory: 250Mi
18
[root@k8smaster01 study]# kubectl create -f limit-test-nginx.yaml -n limit-example
Error from server (Forbidden): error when creating "limit-test-nginx.yaml": pods "limit-test-nginx" is forbidden: memory max limit to request ratio per Pod is 2, but provided ratio is 2.048000
解读:由于limit-test-nginx这个Pod的全部内存Limits总和与Requests总和的比例为512∶250,大于在LimitRange中定义的Pod的最大比率2(maxLimitRequestRatio.memory=2),因此创建失败。
[root@k8smaster01 study]# vi valid-pod.yaml
1 apiVersion: v1
2 kind: Pod
3 metadata:
4 name: valid-pod
5 labels:
6 name: valid-pod
7 spec:
8 containers:
9 - name: kubernetes-serve-hostname
10 image: gcr.io/google_containers/serve_hostname
11 resources:
12 limits:
13 cpu: "1"
14 memory: 512Mi
15
[root@k8smaster01 study]# kubectl create -f valid-pod.yaml -n limit-example
[root@k8smaster01 study]# kubectl get pods valid-pod -n limit-example -o yaml | grep resources -C 6 #查看该Pod的资源信息
1 uid: 59e3d05a-8c09-479e-a3ad-1a4dbfd8e946
2 spec:
3 containers:
4 - image: gcr.io/google_containers/serve_hostname
5 imagePullPolicy: Always
6 name: kubernetes-serve-hostname
7 resources:
8 limits:
9 cpu: "1"
10 memory: 512Mi
11 requests:
12 cpu: "1"
13 memory: 512Mi
14
解读:该Pod配置了明确的Limits和Requests,因此该Pod不会使用在namespace limit-example中定义的default和default Request。
注意:CPU Limits强制配置这个选项在Kubernetes集群中默认是开启的;除非集群管理员在部署kubelet时,通过设置参数--cpucfs-quota=false来关闭该限制:如果集群管理员希望对整个集群中容器或者Pod配置的Requests和Limits做限制,那么可以通过配置Kubernetes命名空间中的LimitRange来达到该目的。在Kubernetes集群中,如果Pod没有显式定义Limits和Requests,那么Kubernetes系统会将该Pod所在的命名空间中定义的LimitRange的default和default Requests配置到该Pod上。
Kubernetes会根据Pod的Requests和Limits配置来实现针对Pod的不同级别的资源服务质量控制(QoS)。在Kubernetes的资源QoS体系中,需要保证高可靠性的Pod可以申请可靠资源,而一些不需要高可靠性的Pod可以申请可靠性较低或者不可靠的资源。
容器的资源配置分为Requests和Limits,其中Requests是Kubernetes调度时能为容器提供的完全可保障的资源量(最低保障),而Limits是系统允许容器运行时可能使用的资源量的上限(最高上限)。Pod级别的资源配置是通过计算Pod内所有容器的资源配置的总和得出来的。
Kubernetes中Pod的Requests和Limits资源配置有如下特点:
通过这种机制,Kubernetes可以实现节点资源的超售(OverSubscription),比如在CPU完全充足的情况下,某机器共有32GiB内存可提供给容器使用,容器配置为Requests值1GiB,Limits值为2GiB,那么在该机器上最多可以同时运行32个容器,每个容器最多可以使用2GiB内存,如果这些容器的内存使用峰值能错开,那么所有容器都可以正常运行。超售机制能有效提高资源的利用率,同时不会影响容器申请的完全可靠资源的可靠性。
容器的资源配置满足以下两个条件:
Kubernetes根据Pod配置的Requests值来调度Pod,Pod在成功调度之后会得到Requests值定义的资源来运行;而如果Pod所在机器上的资源有空余,则Pod可以申请更多的资源,最多不能超过Limits的值。Requests和Limits针对不同计算资源类型的限制机制存在差异。这种差异主要取决于计算资源类型是可压缩资源还是不可压缩资源。
Kubernetes目前支持的可压缩资源是CPU。
Pod可以得到Pod的Requests配置的CPU使用量,而能否使用超过Requests值的部分取决于系统的负载和调度。不过由于目前Kubernetes和Docker的CPU隔离机制都是在容器级别隔离的,所以Pod级别的资源配置并不能完全得到保障;Pod级别的cgroups等待引入,以便于确保Pod级别的资源配置准确运行。
空闲CPU资源按照容器Requests值的比例分配。举例说明:容器A的CPU配置为Requests 1 Limits 10,容器B的CPU配置为Request 2 Limits8,A和B同时运行在一个节点上,初始状态下容器的可用CPU为3 cores,那么A和B恰好得到在它们的Requests中定义的CPU用量,即1CPU和2CPU。如果A和B都需要更多的CPU资源,而恰好此时系统的其他任务释放出1.5CPU,那么这1.5 CPU将按照A和B的Requests值的比例1∶2分配给A和B,即最终A可使用1.5CPU,B可使用3CPU。
如果Pod使用了超过在Limits 10中配置的CPU用量,那么cgroups会对Pod中的容器的CPU使用进行限流(Throttled);如果Pod没有配置Limits 10,那么Pod会尝试抢占所有空闲的CPU资源(Kubernetes从1.2版本开始默认开启--cpu-cfs-quota,因此在默认情况下必须配置Limits)
Kubernetes目前支持的不可压缩资源是内存。
Pod可以得到在Requests中配置的内存。如果Pod使用的内存量小于它的Requests的配置,那么这个Pod可以正常运行;如果Pod使用的内存量超过了它的Requests的配置,那么这个Pod有可能被Kubernetes杀掉:比如Pod A使用了超过Requests而不到Limits的内存量,此时同一机器上另外一个Pod B之前只使用了远少于自己的Requests值的内存,此时程序压力增大,Pod B向系统申请的总量不超过自己的Requests值的内存,那么Kubernetes可能会直接杀掉Pod A,而优先保障Pod B的Requests得到满足;另外一种情况是Pod A使用了超过Requests而不到Limits的内存量,此时Kubernetes将一个新的Pod调度到这台机器上,新的Pod需要使用内存,而只有Pod A使用了超过了自己的Requests值的内存,那么Kubernetes也可能会杀掉Pod A来释放内存资源。
如果Pod使用的内存量超过了它的Limits设置,那么操作系统内核会杀掉Pod所有容器的所有进程中使用内存最多的一个,直到内存不超过Limits为止。
Kubernetes的kubelet通过计算Pod中所有容器的Requests的总和来决定对Pod的调度。
不管是CPU还是内存,Kubernetes调度器和kubelet都会确保节点上所有Pod的Requests的总和不会超过在该节点上可分配给容器使用的资源容量上限。
在一个超用(Over Committed,容器Limits总和大于系统容量上限)系统中,由于容器负载的波动可能导致操作系统的资源不足,最终可能导致部分容器被杀掉。在这种情况下,理想是优先杀掉那些不太重要的容器。Kubernetes将容器划分成3个QoS等级来衡量重要程度:
Guaranteed(完全可靠的)、Burstable(弹性波动、较可靠的)和BestEffort(尽力而为、不太可靠的),这三种优先级依次递减。
QoS等级和优先级的关系从理论上来说,QoS级别应该作为一个单独的参数来提供API,并由用户对Pod进行配置,这种配置应该与Requests和Limits无关。但在当前版本的Kubernetes的设计中,为了简化模式及避免引入太多的复杂性,QoS级别直接由Requests和Limits来定义。在Kubernetes中容器的QoS级别等于容器所在Pod的QoS级别,而Kubernetes的资源配置定义了Pod的如上三种QoS级别。
如果Pod中的所有容器对所有资源类型都定义了Limits和Requests,并且所有容器的Limits值都和Requests值全部相等(且都不为0),那么该Pod的QoS级别就是Guaranteed。
注意:在这种情况下,容器可以不定义Requests,因为Requests值在未定义时默认等于Limits。
示例1:如下定义的PodQoS级别就是Guaranteed。
1 containers:
2 name: foo
3 resources:
4 limits:
5 cpu: 10m
6 memory: 1Gi
7 name: bar
8 resources:
9 limits:
10 cpu: 100m
11 memory: 100Mi
12
解读:如上未定义Requests值,所以其默认等于Limits值。
示例2:
1 containers:
2 name: foo
3 resources:
4 requests:
5 cpu: 10m
6 memory: 1Gi
7 limits:
8 cpu: 10m
9 memory: 1Gi
10 name: bar
11 resources:
12 requests:
13 cpu: 10m
14 memory: 1Gi
15 limits:
16 cpu: 100m
17 memory: 100Mi
18
解读:该定义的Requests和Limits的值完全相同。
如果Pod中所有容器都未定义资源配置(Requests和Limits都未定义),那么该Pod的QoS级别就是BestEffort。
示例3:
1 containers:
2 name: foo
3 resources:
4 name: bar
5 resources:
6
解读:该容器都未定义资源配置。
当一个Pod既不为Guaranteed级别,也不为BestEffort级别时,该Pod的QoS级别就是Burstable。Burstable级别的Pod包括两种情况。
注意:在容器未定义Limits时,Limits值默认等于节点资源容量的上限。
示例4:容器foo的CPURequests不等于Limits。
1 containers:
2 name: foo
3 resources:
4 requests:
5 cpu: 5m
6 memory: 1Gi
7 limits:
8 cpu: 10m
9 memory: 1Gi
10 name: bar
11 resources:
12 requests:
13 cpu: 5m
14 memory: 1Gi
15 limits:
16 cpu: 100m
17 memory: 100Mi
18
示例5:容器bar未定义资源配置而容器foo定义了资源配置。
1 containers:
2 name: foo
3 resources:
4 requests:
5 cpu: 10m
6 memory: 1Gi
7 limits:
8 cpu: 10m
9 memory: 1Gi
10 name: bar
11
示例6:容器foo未定义CPU,而容器bar未定义内存。
1 containers:
2 name: foo
3 resources:
4 limits:
5 memory: 1Gi
6 name: bar
7 resources:
8 limits:
9 cpu: 100m
10
示例7:容器bar未定义资源配置,而容器foo未定义Limits值。
containers: name: foo resources: requests: cpu: 5m memory: 1Gi name: bar
Pod的CPU Requests无法得到满足(比如节点的系统级任务占用过多的CPU导致无法分配足够的CPU给容器使用)时,容器得到的CPU会被压缩限流。由于内存是不可压缩的资源,所以针对内存资源紧缺的情况,会按照以下逻辑进行处理。
OOM(Out Of Memory)计分规则包括如下内容:
OOM计分的计算方法为:计算进程使用内存在系统中占的百分比,取其中不含百分号的数值,再乘以10的结果,这个结果是进程OOM的基础分;将进程OOM基础分的分值再加上这个进程的OOM分数调整值OOM_SCORE_ADJ的值,作为进程OOM的最终分值(除root启动的进程外)。在系统发生OOM时,OOM Killer会优先杀掉OOM计分更高的进程。
进程的OOM计分的基本分数值范围是0~1000,如果A进程的调整值OOM_SCORE_ADJ减去B进程的调整值的结果大于1000,那么A进程的OOM计分最终值必然大于B进程,会优先杀掉A进程。
不论调整OOM_SCORE_ADJ值为多少,任何进程的最终分值范围也是0~1000。在Kubernetes,不同QoS的OOM计分调整值规则如下所示。
QoS等级 | oom_score_adj |
---|---|
Guaranteed | -998 |
BestEffort | 1000 |
Burstable | min(max(2,1000-(1000*memoryRequestBytes)/machineMemoryCapacityBytes),999) |
其中:
OOM还有一些特殊的计分规则,如下所述。
ResourceQuotas通常用于在共享集群资源场景中平衡资源,通过ResourceQuota对象,可以定义资源配额,这个资源配额可以为每个命名空间都提供一个总体的资源使用的限制:它可以限制命名空间中某种类型的对象的总数目上限,也可以设置命名空间中Pod可以使用的计算资源的总上限。
ResourceQuotas典型的场景如下:
场景示例:
在使用资源配额时,需要注意以下两点。
资源配额可以通过在kube-apiserver的--admission-control参数值中添加ResourceQuota参数进行开启。如果在某个命名空间的定义中存在ResourceQuota,那么对于该命名空间而言,资源配额就是开启的。一个命名空间可以有多个ResourceQuota配置项。
提示:在v 1.10后续的版本中,--admission-control 已经废弃,建议使用 --enable-admission-plugins。
[root@k8smaster01 study]# vi /etc/kubernetes/manifests/kube-apiserver.yaml …… - --enable-admission-plugins=NodeRestriction,ResourceQuota,LimitRanger …… [root@k8smaster01 study]# systemctl restart kubelet.service
资源配额可以限制一个命名空间中所有Pod的计算资源的总和。目前支持的计算资源类型如下表:
资源名称 | 说明 |
---|---|
cpu | 所有非终止状态的Pod,CPU Requests的总和不能超过该值 |
limits.cpu | 所有非终止状态的Pod,CPU Limits的总和不能超过该值 |
limits.memory | 所有非终止状态的Pod,内存Limits的总和不能超过该值 |
Memory | 所有非终止状态的Pod,内存Requests的总和不能超过该值 |
requests.cpu | 所有非终止状态的Pod,CPU Requests的总和不能超过该值 |
requests.memory | 所有非终止状态的Pod,内存Requests的总和不能超过该值 |
可以在给定的命名空间中限制所使用的存储资源(StorageResources)的总量,目前支持的存储资源名称如下表:
资源名称 | 说明 |
---|---|
requests.storage | 所有PVC,存储请求总量不能超过此值 |
PersistentVolumeclaims | 在该命名空间中能存在的持久卷的总数上限 |
.storageclass.storage.k8s.io/requests.storage | 和该存储类关联的所有PVC,存储请求总和不能超过此值 |
.storageclass.storage.k8s.io/persistentvolumeclaims | 和该存储类关联的所有PVC的总和 |
指定类型的对象数量可以被限制。 如下表列出了ResourceQuota支持限制的对象类型:
资源名称 | 说明 |
---|---|
Configmaps | 在该命名空间中能存在的ConfigMap的总数上限 |
Pods | 在该命名空间中能存在的非终止状态Pod的总数上限,Pod终止状态等价于Pod的status.phase in(Failed, Succeeded) = true |
Replicationcontrollers | 在该命名空间中能存在的RC的总数上限 |
Resourcequtas | 在该命名空间中能存在的资源配额项的总数上限 |
Services | 在该命名空间中能存在的Service的总数上限 |
service.loadbalancers | 在该命名空间中能存在的负载均衡的总数上限 |
services.nodeports | 在该命名空间中能存在的NodePort的总数上限 |
Secrets | 在该命名空间中能存在的Secret的总数上限 |
通常,可以通过资源配额来限制在命名空间中能创建的Pod的最大数量,这种设置可以防止某些用户大量创建Pod而迅速耗尽整个集群的Pod IP和计算资源。
每项资源配额都可以单独配置一组作用域,配置了作用域的资源配额只会对符合其作用域的资源使用情况进行计量和限制,作用域范围内超过了资源配额的请求都会报验证错误。ResourceQuota的4种作用域如下所示:
作用域 | 说明 |
---|---|
Termination | 匹配所有spec.activeDeadlineSeconds 不小于0的Pod |
NotTermination | 匹配所有spec.activeDeadlineSeconds是nil的Pod |
BestEffort | 匹配所有QoS是 BestEffort的Pod |
NotBestEffort | 匹配所有QoS不是BestEffort的Pod |
其中,BestEffort作用域可以限定资源配额来追踪pods资源的使用,Terminating、NotTerminating和NotBestEffort这三种作用域可以限定资源配额来追踪以下资源的使用:
资源配额也可以设置Requests和Limits。如果在资源配额中指定了requests.cpu或requests.memory,那么它会强制要求每个容器都配置自己的CPU Requests或CPU Limits(可使用Limit Range提供的默认值)。
同理,如果在资源配额中指定了limits.cpu或limits.memory,那么它也会强制要求每个容器都配置自己的内存Requests或内存Limits(可使用LimitRange提供的默认值)。
与LimitRange相似,ResourceQuota也被设置在Namespace中。
示例1:
[root@k8smaster01 study]# kubectl create namespace myspace #创建名为myspace的Namespace
[root@k8smaster01 study]# vi compute-resources.yaml #创建ResourceQuota配置文件
1 apiVersion: v1
2 kind: ResourceQuota
3 metadata:
4 name: compute-resources
5 spec:
6 hard:
7 pods: "4"
8 requests.cpu: "1"
9 requests.memory: 1Gi
10 limits.cpu: "2"
11 limits.memory: 2Gi
12
[root@k8smaster01 study]# kubectl create -f compute-resources.yaml --namespace=myspace #创建该ResourceQuota
创建另一个名为object-counts.yaml的文件,用于设置对象数量的配额:
[root@k8smaster01 study]# vi object-counts.yaml
1 apiVersion: v1
2 kind: ResourceQuota
3 metadata:
4 name: object-counts
5 spec:
6 hard:
7 configmaps: "10"
8 persistentvolumeclaims: "4"
9 replicationcontrollers: "20"
10 secrets: "10"
11 services: "10"
12 services.loadbalancers: "2"
13
[root@k8smaster01 study]# kubectl create -f object-counts.yaml --namespace=myspace
[root@k8smaster01 study]# kubectl describe quota compute-resources --namespace=myspace
[root@k8smaster01 study]# kubectl describe quota object-counts --namespace=myspace
资源配额与集群资源总量是完全独立的。资源配额是通过绝对的单位来配置的,这也就意味着如果在集群中新添加了节点,那么资源配额不会自动更新,而该资源配额所对应的命名空间中的对象也不能自动增加资源上限。在某些情况下,可能希望资源配额支持更复杂的策略,如下所述。
这些策略可以通过将资源配额作为一个控制模块、手动编写一个控制器来监控资源使用情况,并调整命名空间上的资源配额来实现。资源配额将整个集群中的资源总量做了一个静态划分,但它并没有对集群中的节点做任何限制:不同命名空间中的Pod仍然可以运行在同一个节点上。
参考链接:https://blog.csdn.net/dkfajsldfsdfsd/article/details/81004172