在 Kubernetes 中,Node 提供资源,而 Pod 使用资源。其中资源分为计算(CPU、Memory、GPU)、存储(Disk、SSD)、网络(Network Bandwidth、IP、Ports)。这些资源提供了应用运行的基础,正确理解这些资源以及集群调度如何使用这些资源,对于大规模的 Kubernetes 集群来说至关重要,不仅能保证应用的稳定性,还可以提高资源的利用率。在日常工作中,我们一般比较关心其中的计算资源,包括CPU和内存。CPU 的单位是 core,memory 的单位是 byte。
目前kubernetes支持的可压缩资源是CPU。它的特点是,当可压缩资源不足时,Pod 只会饥饿少用,但不会退出。CPU 资源的限制和请求以cpu为单位。Kubernetes 中的一个 cpu 就是一个核,就是一个逻辑CPU。一个核相当于1000个微核,即1=1000m,0.5=500m。
目前kubernetes支持的不可压缩资源是内存。它的特点是,当不可压缩资源不足时,Pod 就会因为 OOM被内核杀掉。内存的限制和请求以字节为单位。可以使用以下后缀之一作为平均整数或定点整数表示内存:E,P,T,G,M,K。还可以使用两个字母的等效的幂数:Ei,Pi,Ti ,Gi,Mi,Ki。
requests 资源请求 pod最低需求(表示Pod对资源的最小需求,因此在调度的时候会如果Node剩余的资源不能满足Pod的需求,则不会调度到对应的Node上。Scheduler调度的时候并不关注在调度时具体的资源使用情况,而是根据现存Pod的资源请求情况来进行调度。调度器首先将不符合请求的Node排除在外,然后在执行优选策略最后在选定pod)
由于 Pod 可以由多个 Container 组成,所以 CPU 和内存资源的限额,是要配置在每个Container的定义上的。这样,Pod 整体的资源配置,就由这些 Container的配置值累加得到。
执行下面yaml的内容:
[root@k8s-m1 k8s-resource]# cat resource-limit-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: resource-limit-pod
namespace: default
labels:
name: myapp
spec:
containers:
- name: myapp
image: ikubernetes/stress-ng
command: ["/usr/bin/stress-ng","-c 1","--metrics-brief"]
ports:
- name: http
containerPort: 80
resources:
requests:
memory: "128Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
[root@k8s-m1 k8s-resource]# kubectl apply -f resource-limit-pod.yaml
pod/resource-limit-pod created
查看结果:
[root@k8s-m1 k8s-resource]# kubectl exec -it resource-limit-pod -- top
Mem: 7805096K used, 202092K free, 6444K shrd, 2104K buff, 4575292K cached
CPU: 3% usr 1% sys 0% nic 88% idle 6% io 0% irq 0% sirq
Load average: 0.14 0.55 0.71 3/1509 33
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
6 1 root R 6892 0% 15 2% {stress-ng-cpu} /usr/bin/stress-ng -c 1 --metrics-brief
1 0 root S 6244 0% 9 0% /usr/bin/stress-ng -c 1 --metrics-brief
29 0 root R 1500 0% 9 0% top
我们看到CPU占用是3%,为什么呢?因为我的集群node都是是16个core。我们最大限制是0.5核。所以应该是0.5/16≈3.1%。
kubelet在启动Pod的某个容器时,会将容器的Requests和Limits值转化为相应的容器启动参数传递给容器执行器。如果是docker 容器:
Kubernetes是根据Pod的Requests和Limits配置来实现针对Pod的不同级别的资源服务质量控制。当 Kubernetes 创建一个 Pod 时,它就会给这个 Pod 分配一个 QoS 等级。
应用场景:当宿主机资源紧张的时候,kubelet会根据服务质量等级对pod进行eviction,即驱逐pod进行资源回收。
完全可靠的资源,资源量的大小等于Requests值;
不可靠的资源,资源量最大等于Limits与 Requests的差额,这份不可靠的资源能够申请到多少,取决于当时主机上容器可用资源的余量;通过这种机制,Kubernetes可以实现节点资源的超售(Over Subscription),超售机制能有效提高资源的利用率,同时不会影响容器申请的完全可靠资源的可靠性。
https://www.cnblogs.com/wtzbk/p/15816291.html
[root@k8s-m1 k8s-resource]# cat guaranteed-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
labels:
name: nginx
spec:
containers:
- name: myapp
image: nginx
ports:
- name: http
containerPort: 80
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "500m"
[root@k8s-m1 k8s-resource]# kubectl apply -f guaranteed-pod.yaml
pod/guaranteed-pod created
结果:
[root@k8s-m1 k8s-resource]# kubectl describe po guaranteed-pod
......
Volumes:
default-token-glxls:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-glxls
Optional: false
QoS Class: Guaranteed
Node-Selectors: <none>
.....
更详细检查
1)查看该pod被分配到的节点
[root@k8s-m1 k8s-resource]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
guaranteed-pod 1/1 Running 0 8s 10.244.11.39 k8s-m3 <none> <none>
2)然后到k8s-m3上去查看这个pod里面启动的容器的资源限制是否生效
[root@k8s-m3 ~]# docker ps |grep guaranteed
434a95e67849 nginx "/docker-entrypoint.…" 6 minutes ago Up 6 minutes k8s_myapp_guaranteed-pod_default_7095c77a-7c31-4a0a-ba1c-c347c690cfe8_0
32a0053da67b registry.aliyuncs.com/google_containers/pause:3.2 "/pause" 6 minutes ago Up 6 minutes k8s_POD_guaranteed-pod_default_7095c77a-7c31-4a0a-ba1c-c347c690cfe8_0
#查看主容器的信息
[root@k8s-m3 ~]# docker inspect 434|egrep -i 'cpu|mem'
"CpuShares": 512,
"Memory": 536870912,
"NanoCpus": 0,
"CpuPeriod": 100000,
"CpuQuota": 50000,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 536870912,
"MemorySwappiness": null,
"CpuCount": 0,
"CpuPercent": 0,
关注Memory、CpuPeriod、CpuQuota这三个值
3)通过cgroup的相关信息查看
从上面的结果实际上我们就可以看到这个容器的一些资源情况,Pod 上的资源配置最终也还是通过底层的容器运行时去控制 CGroup 来实现的,我们可以进入如下目录查看 CGroup 的配置,该目录就是 CGroup 父级目录,而 CGroup 是通过文件系统来进行资源限制的,所以我们上面限制容器的资源就可以在该目录下面反映出来:
##查看CPU的限制##
[root@k8s-m3 ~]# docker exec -it 434 /bin/bash
root@guaranteed-pod:/# cd /sys/fs/cgroup/cpu/
root@guaranteed-pod:/sys/fs/cgroup/cpu# cat cpu.cfs_period_us
100000
root@guaranteed-pod:/sys/fs/cgroup/cpu# cat cpu.cfs_quota_us
50000
root@guaranteed-pod:/sys/fs/cgroup/cpu#
#反向计算出--cpus参数
#cpu.cfs_quota_us / cpu.cfs_period_us = cpu的限制
50000/100000=0.5核(也就是500m)
##查看内存的限制##
root@guaranteed-pod:/sys/fs/cgroup/cpu# cd /sys/fs/cgroup/memory/
root@guaranteed-pod:/sys/fs/cgroup/memory# cat memory.limit_in_bytes
536870912
root@guaranteed-pod:/sys/fs/cgroup/memory#
内存的计算方法为:536870912÷1024÷1024÷1024 = 0.5(G)
[root@k8s-m1 k8s-resource]# cat burstable-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
labels:
name: nginx
spec:
containers:
- name: myapp
image: nginx
ports:
- name: http
containerPort: 80
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
[root@k8s-m1 k8s-resource]# kubectl apply -f burstable-pod.yaml
pod/burstable-pod created
结果:
[root@k8s-m1 k8s-resource]# kubectl describe po burstable-pod
......
Volumes:
default-token-glxls:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-glxls
Optional: false
QoS Class: Burstable
Node-Selectors: <none>
......
上面Burstable的示例中,可以看到limit和Request都设置了的,且limit大于Request。大家可以自行测试只设置其中一个的Qos等级。当只设置Request时,系统是不会补全limit的相关配置,也是Burstable。而当只设置limit时,系统会自动补全Request的相关配置和limit一样,Qos是Guaranteed类型。
[root@k8s-m1 k8s-resource]# cat besteffort-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: besteffort-pod
labels:
name: myapp
tier: appfront
spec:
containers:
- name: http
image: nginx
ports:
- name: http
containerPort: 80
[root@k8s-m1 k8s-resource]# kubectl apply -f besteffort-pod.yaml
kupod/besteffort-pod created
结果:
[root@k8s-m1 k8s-resource]# kubectl describe po besteffort-pod
......
Volumes:
default-token-glxls:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-glxls
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
.....
总结:
CPU是可以压缩资源,所以在CPU不够的时候会压缩限流。而内存是不可压缩资源,所以QoS主要用于内存限制。
如果kubelet无法在系统OOM之前回收足够的内存,则oom_killer 会根据内存使用比率来计算oom._score, 将得出的结果和oom_score_adj相加,最后得分最高的Pod会被首先驱逐。这个策略的思路是,QoS最低且相对于调度的Request来说消耗最多内存的Pod会被首先清除,来保障内存的回收。这意味着与Burstable或Guaranteed QoS类别的容器相比,BestEffort这种类别的容器被杀死的可能性更高。
与Pod驱逐不同,如果一个Pod的容器被oom杀掉,是可能被 kubelet根据restartpolicy重启的。
在Kubernetes环境中部署Java程序不一会就重启了,这意味着你的pod是不健康的。然后我们可以通过describe去查看一下重启的原因。发现是因为Pod超出了资源限制被kill掉,在日志最后一行会出现一个kill的字段。为什么Kubernetes会kill掉,因为它超出了Kubernetes对Pod的资源限制。默认情况下Docker容器会使用宿主机所有的资源,但如果不做资源限制,会影响整个宿主机。然后整个宿主机资源不够会实现飘移,会转移到其他主机上,然后再异常,可能会起到一种雪崩的效应,所以一般我们都是要做Pod资源限制。如果Java容器中未设置JVM的-Xmx(最大的堆内存使用)参数,一旦这个Pod的使用内存超出Kubernetes的limits限制,Kubernetes就会把它杀掉并重启一个新的Pod。所以在实际使用中,JVM中的这个值建议要比limits要小一点,小个10%左右,因为超过这个limits限制就可能会被杀死掉再重新起一个Pod。
更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出