1.什么是资源限制?
1.1在kubernetes集群中,为了使得系统能够稳定的运行,通常会对Pod的资源使用量进行限制。在kubernetes集群中,如果有一个程序出现异常,并且占用大量的系统资源,如果没有对该Pod进行资源限制的话,可能会影响其他的Pod正常运行,从而造成业务的不稳定性。
1.2正常情况下我们在一个宿主机的内核之上,这个宿主机可能管理了一组硬件包括CPU、内存。而后在这个宿主机的内核之上,我们可以运行多个容器,这些容器将共享底层的同一个内核,很显然,硬件是属于内核的,因此任何容器中的每一个进程默认可以请求占有这个内核管理的所有的可用硬件资源。
1.3尤其是多租户的环境当中,有人恶意的运行了一个容器,这个容器可能会大量的占用CPU和内存,进而会导致其他容器无法运行,Docker容器启动的时候默认是在内核的名称空间级别进行了隔离,在进程所运行的资源范围上并没有做太多的隔离操作。我们应该为每个应用设定内部进程运行最为合理的资源的最小保证量和最大保证量。
1.4官方文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-cpu-resource/
2.如何实现资源限制?
- kubernetes通过Requests和Limits字段来实现对Pod的资源限制;
- Requests: 启动Pod时申请分配的资源大小,即容器运行可能用不到这些额度的资源,但用到时必须确保有这么多的资源使用;(Pod在调度的时候requests比较重要)
- Limits: 限制Pod运行最大的可用的资源大小,即硬限制;(Pod在运行时limits比较重要)
- 相比较来说,CPU属于可压缩(compressible)资源,即资源额度可按需收缩,而内存则是不可压缩型资源,对其执行收缩操作可能会导致某种程度上的问题,如果超载,所有Pod内存加起来超过节点就会触发OOM机制,杀死Pod,具体杀死哪个Pod,就要看QOS服务质量。优先把评分低的杀掉;
- 正常情况下,Resources可以定义在Pod上也可以定义在Containers上。定义在Pod上是对Pod上所有容器都生效。一般而言我们定义资源是明确定义在每个容器上的,因为不同容器之间可能需求是不一样的。
spec.containers[].resources.request.cpu # Pod申请时的CPU,如果节点没有足够大,则Pod调度失败
spec.containers[].resources.request.memory # Pod申请时的内存
spec.containers[].resource.limits.cpu # Pod最大可使用的CPU
spec.containers[].resource.limits.memory # Pod最大可使用的内存
3.资源限制的目的
3.1我们为什么要进行资源限制?
- CPU: 为集群中运行的容器配置CPU请求和限制,可以有效的利用集群上可用的CPU资源;
设置Pod CPU请求,设置在较低的数值,可以使得Pod更有机会被调度节点;
通过设置CPU限制大于CPU请求,可以完成两件事:
1.当Pod碰到一些突发负载时,它可以合理利用可用的CPU资源。
2.当Pod在突发流量期间,可使用的CPU被限制为合理的值,从而可用避免影响其他Pod的正常运行; - memory: 为集群中运行的容器配置内存请求和限制,可以有效利用集群节点上的可以的内存资源;
通过将Pod的内存请求设定在较低的数值,可以有效的利用节点上可用的内存资源。通过让内存限制大于内存请求,可以完成如下:
1.当Pod碰到突发请求,可以更好的利用其主机上的可用内存;
2.当Pod在突发负载期间可使用的内存被限制为合理的数值,从而可用避免影响其他Pod的运行;
4.资源限制单位
4.1CPU限制单位
1核CPU=1000毫核,当定义容器为0.5时,所需要的CPU资源是1核心CPU的一般,对于CPU单位,表达式0.1等价于表达式100m,可以看作是100millicpu;
1核=1000millicpu (1 Core=1000m)
0.5核=500millicpu (0.5 Core=500m)
4.2内存限制单位
内存的基本单位是字节数(Bytes),也可以使用E、P、T、G、M、K作为单位后缀,或Ei、Pi、Ti、Gi、Mi和Ki形式单位后缀;
1MB=1000KB=1000000Bytes
1Mi=1024KB=1048576Bytes
5.资源限制配置案例
5.1CPU配置;
5.1.1设置容器的CPU请求与限制;
需安装Metrics-Server才能用top命令否则无法使用; # 创建一个具有一个容器的Pod,容器将请求0.5个CPU,最多限制1个CPU; root@kubernetes-master01:~# cat cpu-requests-limits.yaml apiVersion: v1 kind: Pod metadata: name: cpu-test-cpu-limits spec: containers: - name: cpu-test-stress image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest args: - -cpus # 容器尝试使用2核CPU - "2" resources: requests: # 限制Pod最多可申请0.5核CPU cpu: "500m" limits: # 限制Pod最大可使用1核CPU cpu: "1000m" root@kubernetes-master01:~# kubectl apply -f cpu-requests-limits.yaml pod/cpu-test-cpu-limits created # 查看资源限制情况,容器配置去尝试使用2个CPU,但是容器只能被允许使用1个CPU; root@kubernetes-master01:~# kubectl top pods cpu-test-cpu-limits NAME CPU(cores) MEMORY(bytes) cpu-test-cpu-limits 999m 1Mi
折叠
5.1.2创建一个Pod,设置该Pod中容器请求为100核,这个值会大于集群中所有内存的总和;
root@kubernetes-master01:~# cat cpu-requests-limits.yaml apiVersion: v1 kind: Pod metadata: name: cpu-test-cpu-limits spec: containers: - name: cpu-demo-ctr image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest args: - -cpus - "2" resources: requests: cpu: "100" # 设置Pod申请100GB,我们集群上没有100GB的资源 limits: cpu: "100" # 最大使用100GB,集群资源不足,无法创建; root@kubernetes-master01:~# kubectl apply -f cpu-requests-limits.yaml pod/cpu-test-cpu-limits created # 查看Pod的状态,为Pending,Pod未被调度到任何节点上; root@kubernetes-master01:~# kubectl get pods cpu-test-cpu-limits 0/1 Pending 0 4s # 查看Events,输出显示由于3节点的CPU资源不足,无法进行调度; root@kubernetes-master01:~# kubectl describe pods cpu-test-cpu-limits Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 113s default-scheduler 0/3 nodes are available: 3 Insufficient cpu. Warning FailedScheduling 113s default-scheduler 0/3 nodes are available: 3 Insufficient cpu.
折叠
5.1.3如果不指定CPU的Limits?
如果没有为容器配置CPU限制,那么容器在可以使用的CPU资源是没有上限的,因而可以使用所在节点上的所有可用CPU资源,这样会造成某一个Pod占用了大量的CPU,可能会影响其他Pod的正常允许,从而造成业务的不稳定性。
5.2内存配置;
5.2.1设置容器的内存请求与限制;
创建一个1个容器Pod,容器会将请求100MB内存,并且最大可用内存为200MB以内;
root@kubernetes-master01:~# cat memory-requests-limits.yaml apiVersion: v1 kind: Pod metadata: name: pod-memory-resource spec: containers: - name: momory-demo-stress image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest command: ["stress"] args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1" ] resources: requests: memory: "100Mi" # 设置最小请求为100Mi limits: memory: "200Mi" # 最大可使用200Mi root@kubernetes-master01:~# kubectl apply -f memory-requests-limits.yaml pod/pod-memory-resource created # 获取Pod的内存使用信息,输出结果显示Pod正在使用的内存约为150Mi,大于Pod请求的100Mi,但是在Pod限制的200Mi之内。 root@kubernetes-master01:~# kubectl top po pod-memory-resource NAME CPU(cores) MEMORY(bytes) pod-memory-resource 43m 151Mi # 也可用通过Yaml文件的方式来看limits最大可使用为200Mi resources: limits: memory: 200Mi requests: memory: 100Mi
折叠
5.2.2运行超过容器内存限制的应用
当节点拥有足够的可用内存时,容器可用使用其请求的内存,但是,容器不允许使用超过其限制的内存。如果容器分配的内存超过其限制,该容器会成为被终止的候选容器。如果容器继续消耗超过其限制的内存,则终止容器。如果终止的容器可以被重启,则kubelet会重新启动它。
5.2.2.1创建一个Pod,拥有一个Containers,该容器的内存请求为100Mi,内存限制为200Mi,尝试分配超出其限制的内存;
root@kubernetes-master01:~# cat memory-requests-limits.yaml apiVersion: v1 kind: Pod metadata: name: pod-memory-resource spec: containers: - name: momory-demo-ctr image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest command: ["stress"] args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1" ] # 强行分配250M resources: requests: memory: "100Mi" limits: memory: "200Mi" # 我们上限不能超过200,超过200Mi会发生OOM Kill root@kubernetes-master01:~# kubectl apply -f memory-requests-limits.yaml # 查看Pod,此时容器被杀死; root@kubernetes-master01:~# kubectl get pods -w pod-memory-resource 0/1 OOMKilled 0 9s pod-memory-resource 0/1 OOMKilled 1 11s pod-memory-resource 0/1 CrashLoopBackOff 1 12s # 查看更详细的信息,显示为OOMKilled; root@kubernetes-master01:~#kubectl get pod pod-memory-resource -o yaml lastState: terminated: containerID: docker://3d6e53a12a101f474d24c00b8e9c5b1e6da2ef26735b6e2cb6790184c6d69cfa exitCode: 1 finishedAt: "2022-07-27T06:40:07Z" reason: OOMKilled
折叠
5.2.3超过节点的内存分配
Pod的调度基于请求,只有当前节点拥有足够满足Pod内存请求的内存时,才会将Pod调度至该节点运行;
5.2.3.1创建一个Pod,其拥有一个请求1000GB内存的容器,超出了集群任何一个节点所拥有的内存;
root@kubernetes-master01:~/cloud-Native/resource# cat memory-requests-limits.yaml apiVersion: v1 kind: Pod metadata: name: pod-memory-resource spec: containers: - name: momory-demo-ctr image: registry.cn-hangzhou.aliyuncs.com/lengyuye/stress:latest command: ["stress"] args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1" ] resources: requests: memory: "100Gi" limits: memory: "200Gi" root@kubernetes-master01:~# kubectl apply -f memory-requests-limits.yaml # 查看Pod状态,发现处于pending,这意味着该Pod未被调度至任何节点; root@kubernetes-master01:~# kubectl get pods -w pod-memory-resource 0/1 Pending 0 6s # 通过describe查看更详细信息;显示为由于节点内存不足,该容器无法被调度; root@kubernetes-master01:~# kubectl describe pods pod-memory-resource Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 2m21s default-scheduler 0/3 nodes are available: 3 Insufficient memory. Warning FailedScheduling 2m20s default-scheduler 0/3 nodes are available: 3 Insufficient memory.
折叠
5.2.4如果没有指定内存限制;
如果为容器指定内存限制,容器可以无限的使用其所在节点的所有可用内存,进而可能导致该节点调用OOM Killer。此外如果发生OOM Kill,没有配置资源限制的容器将被杀死的可能性会更大。
6.QOS服务质量
6.1什么是QOS?
kubernetes允许节点资源对limits的过载使用,这意味着节点无法同时满足其上的所有Pod对象以资源满载的方式运行,于是在内存资源紧缺时,应该以何种次序先后终止哪些Pod对象?
kubernetes无法自行对此做出决策,它需要借助于Pod对象的优先级判定,根据Pod对象的Requests和Limits属性,kubernetes将Pod对象归类到BestEffort、Burstable和Guaranteed三个服务质量(Quality of Service,QOS);
6.1.1Guaranteed: Pod对象为每个容器都设置了CPU资源需求和资源限制,且两者的值相同,还同时为每个容器设置了内存需求与内存限制,并且两者的值相同,这类Pod对象具有最高级别服务质量。
6.1.2Burstable: 至少有一个容器设置了CPU或内存资源Requests属性,但不满足Guaranteed,这类Pod具有中级服务质量;
6.1.3BestEffort: 没有为任何容器设置Requests和Limits属性,这类Pod对象服务质量是最低级别。
1.当kubernetes集群内存资源紧缺,优先杀死BestEffort类别的容器,因为系统不为该类资源提供任何服务保证,但是此类资源最大的好处就是能够尽可能的使用资源。
2.如果系统中没有BestEffort类别的容器,接下来就轮到Burstable类别的容器,如果有多个Burstable类别的容器,就看谁的资源占用多就优先杀死谁。对于Guaranteed类别的容器拥有最高优先级,他们不会被杀死,除非其内存需求超限,或OMM时没有其他更低优先级的Pod对象存在,才干掉Guaranteed类容器;