1、Kubernetes对资源的限制
在Kubernetes中,对资源(CPU、内存等)的限制,需要定义在yaml中,以Deployment举例:
apiVersion: v1 kind: Pod metadata: name: cpu-overload namespace: test spec: containers: - name: cpu-overload image: stress/stress:latest resources: limits: cpu: "2" memory: 1000Mi requests: cpu: "1" memory: 500Mi command: ["stress"] args: ["-c", "2"]
其中,CPU 有2个限制:
- requests:相对限制,是容器的最低申请资源,这个限制是相对的,无法做到绝对严格。
- limits:绝对限制,这个是限制的绝对的,不可能超越。
本例中,对容器 cpu-overload 的 CPU 的限制,是,申请1个核的运算资源,最多可以使用2个核。
这里需要特别说明一点,所谓的最多2个核,其实是均摊的,如果这个容器真的触发了计算瓶颈,在docker中看,CPU使用率是200%,但在宿主机去看,其实并非是将2个核占满了,而是将压力分摊到了多个CPU的核上。
对Kubernetes来说,只能做到限制容器资源,无法对pod资源做限制,Kubernetes官方认为,要计算一个pod的资源限制,将pod中各个容器的资源做加和就行了。
2、资源限制的传递
Kubernetes其实可以认为是一系列组件包装起来的一个大型工具。关于资源限制,其实Kubernetes自己做不了这些,而是将对资源限制,通过yaml中的定义,传递到Docker容器中。比如,之前我们在Deployment中容器的CPU,限制为最多使用2个核,这个限制,Kubernetes会传递给Docker来做,所以本质上,Kubernetes资源的限制能力,来源于Docker,而Docker能做到什么程度的限制,又取决于Linux的cgroups,所以在很早之前的Docker是不支持在Windows平台运行的,归根结底,还是因为cgroups是Linux内核支持的产物。
说了这么多,我们可以通过一个实例来说明这个传递性。在开始前,简单说一下步骤:
- 1、在Kubernetes中启动一个单独的pod,资源限制为最多4个CPU核。
- 2、找到这个pod对应的容器,看一下容器的运行配置,是 不是限制了4个核。
- 3、找到这个容器对应的Cgroups配置,看是否对容器限制了4个核。
3、实验
3.1 创建一个限制了1个核的pod
apiVersion: v1 kind: Pod metadata: labels: system: centos7 name: centos7 namespace: test spec: containers: - image: centos:7 imagePullPolicy: IfNotPresent name: centos7 command: [ "/bin/bash", "-c", "--" ] args: [ "while true; do sleep 30; done;" ] ports: - containerPort: 30008 hostPort: 30008 name: http protocol: TCP resources: limits: cpu: "1" memory: 2000Mi requests: cpu: "0.1" memory: 100Mi hostNetwork: true restartPolicy: Always
在这个yaml中,我们对centos容器,限制为1核2G内存。我们通过
kubectl apply -f centos.yaml
将pod运行起来。
3.2 查看容器的运行时限制
运行为容器后,查看此pod所在节点,进入到节点,找到这个容器,通过下面指令查看此容器的运行时配置
[root@kubernetes-master ~]# kubectl get po -n test -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES centos7 1/1 Running 0 2m10s 172.20.5.11 kubernetes-node1 <none> <none>
docker inspect 7e5f03672fb6
然后,从一大堆输出中,找到下面的重点部分:
"Isolation": "", "CpuShares": 102, "Memory": 2097152000, "NanoCpus": 0, "CgroupParent": "kubepods-burstable-podb4283fa0_6576_4bd2_8c52_2183a5e2a566.slice", "BlkioWeight": 0, "BlkioWeightDevice": null, "BlkioDeviceReadBps": null, "BlkioDeviceWriteBps": null, "BlkioDeviceReadIOps": null, "BlkioDeviceWriteIOps": null, "CpuPeriod": 100000, "CpuQuota": 100000, "CpuRealtimePeriod": 0, "CpuRealtimeRuntime": 0, "CpusetCpus": "", "CpusetMems": "", "Devices": [], "DeviceCgroupRules": null, "DeviceRequests": null, "KernelMemory": 0, "KernelMemoryTCP": 0, "MemoryReservation": 0, "MemorySwap": 2097152000, "MemorySwappiness": null, "OomKillDisable": false, "PidsLimit": null, "Ulimits": null, "CpuCount": 0, "CpuPercent": 0,
其中:
- Memory:限制内存资源,单位为byte,2097152000 = 2G
- CpuShares:CPU使用的相对权重,一个核为1024,我们设置的request cpu为 0.1 ,所以就是 102
- CpuPeriod:一个CPU为100000,也就是100个milicpu,这个一般不需要改。
- CpuQuota:CPU资源的绝对限制,一般和CpuPeriod结合在一起,CpuQuota/CpuPeriod,就是能够使用的核数,100000/100000=1,表示我们能最多使用1个CPU核心。
- CpusetCpus:这个值表示当前容器运行时,绑定到哪几个CPU编号上,注意:这个不是CPU个数,而是绑定到哪几个CPU上,多个CPU编号用逗号分割。
从上面的docker运行时限制看,和Kubernetes的Pod的定义完全吻合。下面再看Cgroups的限制,这才是核心。
3.3 根据容器,查Cgroups的限制内容
首先,我们看一下pod的名称:
[root@kubernetes-master ~]# kubectl get po -n test -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES centos7 1/1 Running 0 2m10s 172.20.5.11 kubernetes-node1 <none> <none>
然后,在pod所在的宿主机,找到这个pod对应的容器Id
docker ps | grep centos7
[root@kubernetes-node1 ~]# docker ps | grep centos7 7e5f03672fb6 7e6257c9f8d8 "/bin/bash -c -- 'wh…" 6 minutes ago Up 6 minutes k8s_centos7_centos7_test_b4283fa0-6576-4bd2-8c52-2183a5e2a566_0 67d02f7cf04d k8s.gcr.io/pause:3.2 "/pause" 6 minutes ago Up 6 minutes k8s_POD_centos7_test_b4283fa0-6576-4bd2-8c52-2183a5e2a566_0
我们可以注意到,一个匹配出来2个容器,一个是 centos7 容器,一个是pause容器,pause容器,是 Kubernetes pod 的基础容器。我们只需要 centos7:7e5f03672fb6,我们要通过它拿到这个容器的Cgroup信息
[root@kubernetes-node1 ~]# docker inspect 7e5f03672fb6 | grep Cgroup "Cgroup": "", "CgroupParent": "kubepods-burstable-podb4283fa0_6576_4bd2_8c52_2183a5e2a566.slice", "DeviceCgroupRules": null,
好了,我们直接进入Cgroup配置目录:
cd /sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb4283fa0_6576_4bd2_8c52_2183a5e2a566.slice
在这个目录下,有很多文件:
[root@kubernetes-node1 kubepods-burstable-podb4283fa0_6576_4bd2_8c52_2183a5e2a566.slice]# ll total 0 -rw-r--r-- 1 root root 0 Nov 17 14:50 cgroup.clone_children --w--w--w- 1 root root 0 Nov 17 14:50 cgroup.event_control -rw-r--r-- 1 root root 0 Nov 17 14:50 cgroup.procs -r--r--r-- 1 root root 0 Nov 17 14:50 cpuacct.stat -rw-r--r-- 1 root root 0 Nov 17 14:50 cpuacct.usage -r--r--r-- 1 root root 0 Nov 17 14:50 cpuacct.usage_percpu -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.cfs_period_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.cfs_quota_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.rt_period_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.rt_runtime_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.shares -r--r--r-- 1 root root 0 Nov 17 14:50 cpu.stat drwxr-xr-x 2 root root 0 Nov 17 14:50 docker-67d02f7cf04d6eb6ed637e40e1ccebd18b09eb323ad262c1fe3e8aa75ea46edf.scope drwxr-xr-x 2 root root 0 Nov 17 14:50 docker-7e5f03672fb63500910b092ecfcc793a8f21450528bb32c9d078d31dc4846cb2.scope -rw-r--r-- 1 root root 0 Nov 17 14:50 notify_on_release -rw-r--r-- 1 root root 0 Nov 17 14:50 tasks
其中,当前目录下有很多Cgroup内容,而有2个子目录:
docker-67d02f7cf04d6eb6ed637e40e1ccebd18b09eb323ad262c1fe3e8aa75ea46edf.scope docker-7e5f03672fb63500910b092ecfcc793a8f21450528bb32c9d078d31dc4846cb2.scope
这2个目录,其实就是 pod 中的2个容器(pause容器,centos7容器),我们进入 centos7容器的目录下
cd docker-7e5f03672fb63500910b092ecfcc793a8f21450528bb32c9d078d31dc4846cb2.scope
查看下目录下的内容
-rw-r--r-- 1 root root 0 Nov 17 14:50 cgroup.clone_children --w--w--w- 1 root root 0 Nov 17 14:50 cgroup.event_control -rw-r--r-- 1 root root 0 Nov 17 14:50 cgroup.procs -r--r--r-- 1 root root 0 Nov 17 14:50 cpuacct.stat -rw-r--r-- 1 root root 0 Nov 17 14:50 cpuacct.usage -r--r--r-- 1 root root 0 Nov 17 14:50 cpuacct.usage_percpu -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.cfs_period_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.cfs_quota_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.rt_period_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.rt_runtime_us -rw-r--r-- 1 root root 0 Nov 17 14:50 cpu.shares -r--r--r-- 1 root root 0 Nov 17 14:50 cpu.stat -rw-r--r-- 1 root root 0 Nov 17 14:50 notify_on_release -rw-r--r-- 1 root root 0 Nov 17 14:50 tasks
其中,能够看到好几个熟悉的词,比如 cpu.cfs_quota_us,这个正是对CPU资源做限制的。我们查看一下其内容:
[root@kubernetes-node1 ...scope]# cat cpu.cfs_quota_us 100000
没错,这个值正是100000,也就是1个CPU。
3.4 Linux Cgroup 是如何与Dock而关联的?
上面的方式,已经层层找到了对CPU、内存等限制,是如何通过Kubernets的Deployment,一步步追查到Cgroup的。那么,Linux Cgroup,怎么与容器关联起来的呢?
我们看一个cgroup目录中的tasks
[root@kubernetes-node1 docker-7e5f03672fb63500910b092ecfcc793a8f21450528bb32c9d078d31dc4846cb2.scope]# cat tasks 22552
这个值,就是进程ID,所以,Cgroup对资源的限制,就是对进程ID来限制的。我们看一下这个进程ID
[root@kubernetes-node1 docker-7e5f03672fb63500910b092ecfcc793a8f21450528bb32c9d078d31dc4846cb2.scope]# ps -ef | grep 22552 root 22552 22534 0 14:50 ? 00:00:00 /bin/bash -c -- while true; do sleep 30; done; root 30414 22552 0 15:08 ? 00:00:00 sleep 30 root 30443 23532 0 15:08 pts/0 00:00:00 grep --color=auto 22552
此进程ID,正是cadvisor的进程ID,其实这个进程,是容器内的进程,换句话说,其父进程,肯定是一个容器进程:
[root@kubernetes-node1 docker-7e5f03672fb63500910b092ecfcc793a8f21450528bb32c9d078d31dc4846cb2.scope]# ps -ef | grep 22534 root 22534 906 0 14:50 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/7e5f03672fb63500910b092ecfcc793a8f21450528bb32c9d078d31dc4846cb2 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc -systemd-cgroup root 22552 22534 0 14:50 ? 00:00:00 /bin/bash -c -- while true; do sleep 30; done; root 30875 23532 0 15:09 pts/0 00:00:00 grep --color=auto 22534
4、总结
- Kubernete对资源的限制,靠的是Docker,Docker对资源的限制,靠的是Linux Cgroup 。
- Linux Cgroup 限制资源,是限制进程,只需要在Cgroup配置目录的tasks文件中,添加进程ID,限制立即生效。
- Linux Cgroup 不仅仅可以限制CPU,内存,还可以限制磁盘IO等。