Kubernetes下pod控制组管理解析

开始之前我们先了解下Kubernetes QoS概念

Kubernetes里面,将资源分成不同的QoS类别,并且通过pod里面的资源定义来区分pod对于平台提供的资源保障的SLA等级:

  • Guaranteed: pod中每个容器都为CPU和内存设置了相同的requestslimits,此类pod具有最高优先级
  • Burstable: 至少有一个容器设置了CPU或内存的requests属性,但不满足Guaranteed类别要求的pod,它们具有中等优先级
  • BestEffort: 未为任何一个容器设置requestslimits属性的pod,它们的优先级为最低级别

针对不同的优先级的业务,在资源紧张或者超出的时候会有不同的处理策略。 同时,针对CPU和内存两类不同类型的资源,一种是可压缩的,一种是不可压缩的,所以资源的分配和调控策略也会有很大区别。 CPU资源紧缺时,如果节点处于超卖状态,则会根据各自的requests配置,按比例分配CPU时间片,而内存资源紧缺时需要内核的oom killer进行管控, Kubernetes负责为OOM killer提供管控依据:

  1. BestEfford类容器由于没有要求系统供任何级别的资源保证,将最先被终止;但是在资源不紧张时,它们能尽可能多地占用资源,实现资源的复用和部署密度的提高
  2. 如果BestEfford类容器都已经终止,Burstable中等优先级的的pod将被终止
  3. Guaranteed类容器拥有最高优先级,只有在内存资源使用超出limits的时候或者节点OOM时得分最高,才会被终止;

OOM得分主要根据QoS类和容器的requests内存占机器总内存比来计算:

OOM得分越高,该进程的优先级越低,越容易被终止;

根据公式,Burstable优先级的pod中,requests内存申请越多,越容易在OOM的时候被终止。

pod的cpu控制组解析

首先我们先看下pod的控制组层级

$ ls -l /sys/fs/cgroup/cpu/kubepods.slice
total 0
-rw-r--r--  1 root root 0 Aug 23 16:32 cgroup.clone_children
-rw-r--r--  1 root root 0 Aug 23 16:32 cgroup.procs
-r--r--r--  1 root root 0 Aug 23 16:32 cpuacct.stat
-rw-r--r--  1 root root 0 Aug 23 16:32 cpuacct.usage
-r--r--r--  1 root root 0 Aug 23 16:32 cpuacct.usage_all
-r--r--r--  1 root root 0 Aug 23 16:32 cpuacct.usage_percpu
-r--r--r--  1 root root 0 Aug 23 16:32 cpuacct.usage_percpu_sys
-r--r--r--  1 root root 0 Aug 23 16:32 cpuacct.usage_percpu_user
-r--r--r--  1 root root 0 Aug 23 16:32 cpuacct.usage_sys
-r--r--r--  1 root root 0 Aug 23 16:32 cpuacct.usage_user
-rw-r--r--  1 root root 0 Aug 23 16:32 cpu.cfs_period_us
-rw-r--r--  1 root root 0 Aug 23 16:32 cpu.cfs_quota_us
-rw-r--r--  1 root root 0 Aug 23 16:32 cpu.rt_period_us
-rw-r--r--  1 root root 0 Aug 23 16:32 cpu.rt_runtime_us
-rw-r--r--  1 root root 0 Sep  1 17:38 cpu.shares
-r--r--r--  1 root root 0 Aug 23 16:32 cpu.stat
drwxr-xr-x 55 root root 0 Aug 23 16:32 kubepods-besteffort.slice
drwxr-xr-x 51 root root 0 Aug 23 16:32 kubepods-burstable.slice
drwxr-xr-x  4 root root 0 Aug 23 16:54 kubepods-pod934b0aa2_1d1b_4a81_bfcf_89c4beef899e.slice
drwxr-xr-x  4 root root 0 Aug 23 16:39 kubepods-podca849e84_aa86_4402_bf31_e7e73faa77fe.slice
-rw-r--r--  1 root root 0 Aug 23 16:32 notify_on_release
-rw-r--r--  1 root root 0 Aug 23 16:32 tasks

其中kubepods-besteffort.slice存放besteffort类型pod配置,kubepods-burstable.slice存放burstable类型pod配置。

kubepods-pod934b0aa2_1d1b_4a81_bfcf_89c4beef899e.slicekubepods-podca849e84_aa86_4402_bf31_e7e73faa77fe.slice则为Guaranteed类型pod

为了更好的解释说明,我们创建一个新的Guaranteed类型的pod用于测试:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        cpu: "1"
        memory: 1Gi
      requests:
        cpu: 1
        memory: 1Gi
EOF
  1. 再次查看/sys/fs/cgroup/cpu/kubepods.slice下,发现新增了一个kubepods-podf56bf66f_3efb_4c80_8818_37de69ee5b72.slice目录

  2. 名称解析

kubepods-podf56bf66f_3efb_4c80_8818_37de69ee5b72.slice这个名称是怎么命名的呢?

命名格式为:kubepods-pod<pod uid>.slice,并且会将uid-转换为_

$ kubectl get pod nginx-demo -o yaml|grep uid
  uid: f56bf66f-3efb-4c80-8818-37de69ee5b72
  1. 目录解析
$ ls -l /sys/fs/cgroup/cpu/kubepods.slice/kubepods-podf56bf66f_3efb_4c80_8818_37de69ee5b72.slice
-rw-r--r-- 1 root root 0 Nov 17 11:23 cgroup.clone_children
-rw-r--r-- 1 root root 0 Nov 17 11:23 cgroup.procs
-r--r--r-- 1 root root 0 Nov 17 11:23 cpuacct.stat
-rw-r--r-- 1 root root 0 Nov 17 11:23 cpuacct.usage
-r--r--r-- 1 root root 0 Nov 17 11:23 cpuacct.usage_all
-r--r--r-- 1 root root 0 Nov 17 11:23 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Nov 17 11:23 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Nov 17 11:23 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Nov 17 11:23 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Nov 17 11:23 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Nov 17 11:23 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Nov 17 11:23 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Nov 17 11:23 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Nov 17 11:23 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Nov 17 11:23 cpu.shares
-r--r--r-- 1 root root 0 Nov 17 11:23 cpu.stat
drwxr-xr-x 2 root root 0 Nov 17 11:23 docker-08974ffd61043b34e4cd5710d5446eb423c6371afb4c9d106e608f08cc1182a3.scope
drwxr-xr-x 2 root root 0 Nov 17 11:24 docker-d33dc12340fd32b35148293c21f84dab14f2274046056bbeef9e9666d1d0dc2a.scope
-rw-r--r-- 1 root root 0 Nov 17 11:23 notify_on_release
-rw-r--r-- 1 root root 0 Nov 17 11:23 tasks

我们发现怎么有两个容器呢?(docker-08974ffd61043b34e4cd5710d5446eb423c6371afb4c9d106e608f08cc1182a3.scopedocker-d33dc12340fd32b35148293c21f84dab14f2274046056bbeef9e9666d1d0dc2a.scope

其实是业务容器 + infra沙箱容器,并且命名格式遵循:docker-<container id>.scope

$ docker ps|grep nginx
d33dc12340fd   nginx                                                                         "/docker-entrypoint.…"    7 minutes ago   Up 7 minutes             k8s_nginx_nginx-demo_default_f56bf66f-3efb-4c80-8818-37de69ee5b72_0
08974ffd6104   harbor.chs.neusoft.com/kubesphere/pause:3.2                                   "/pause"                  8 minutes ago   Up 8 minutes             k8s_POD_nginx-demo_default_f56bf66f-3efb-4c80-8818-37de69ee5b72_0

我们可根据以下命令获取业务容器id

$ kubectl get pod nginx-demo -o yaml|grep containerID
  - containerID: docker://d33dc12340fd32b35148293c21f84dab14f2274046056bbeef9e9666d1d0dc2a
  1. 业务容器cgroup解析
$ cd /sys/fs/cgroup/cpu/kubepods.slice/kubepods-podf56bf66f_3efb_4c80_8818_37de69ee5b72.slice/docker-d33dc12340fd32b35148293c21f84dab14f2274046056bbeef9e9666d1d0dc2a.scope
$ ls -l 
total 0
-rw-r--r-- 1 root root 0 Nov 17 11:24 cgroup.clone_children
-rw-r--r-- 1 root root 0 Nov 17 11:24 cgroup.procs
-r--r--r-- 1 root root 0 Nov 17 11:24 cpuacct.stat
-rw-r--r-- 1 root root 0 Nov 17 11:24 cpuacct.usage
-r--r--r-- 1 root root 0 Nov 17 11:24 cpuacct.usage_all
-r--r--r-- 1 root root 0 Nov 17 11:24 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Nov 17 11:24 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Nov 17 11:24 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Nov 17 11:24 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Nov 17 11:24 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Nov 17 11:24 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Nov 17 11:24 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Nov 17 11:24 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Nov 17 11:24 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Nov 17 11:24 cpu.shares
-r--r--r-- 1 root root 0 Nov 17 11:24 cpu.stat
-rw-r--r-- 1 root root 0 Nov 17 11:24 notify_on_release
-rw-r--r-- 1 root root 0 Nov 17 11:24 tasks

我们上述对pod配额的定义为:

resources:
  limits:
    cpu: "1"
    memory: 1Gi
  requests:
    cpu: 1
    memory: 1Gi

其实等同于以以下方式启动docker容器:

$ docker run --rm -dt --cpu-shares=1024 --cpu-quota=1024 --memory=1g nginx

我们可以看下docker容器的配额:

$ docker inspect d33dc12340fd32b35148293c21f84dab14f2274046056bbeef9e9666d1d0dc2a -f {{.HostConfig.CpuShares}}
1024
$ docker inspect d33dc12340fd32b35148293c21f84dab14f2274046056bbeef9e9666d1d0dc2a -f {{.HostConfig.CpuQuota}}
100000
$ docker inspect d33dc12340fd32b35148293c21f84dab14f2274046056bbeef9e9666d1d0dc2a -f {{.HostConfig.CpuPeriod}}
100000

.HostConfig.CpuShares对应控制内的cpu.shares文件内容 .HostConfig.CpuPeriod对应控制内的cpu.cpu.cfs_period_us文件内容 .HostConfig.CpuQuota对应控制内的cpu.cfs_quota_us文件内容

并且我们发现k8s基于pod管理控制组(同一pod内的容器所属同一控制组)

"CgroupParent": "kubepods-podf56bf66f_3efb_4c80_8818_37de69ee5b72.slice",

我们可以得出记录:k8s通过控制组的cpu.sharescpu.cpu.cfs_period_uscpu.cfs_quota_us配置,达到限制CPU的目的。

那么这三个文件是用来干嘛的?

cpu.shares解析

  1. cpu.shares用来设置CPU的相对值,并且是针对所有的CPU(内核),默认值是1024等同于一个cpu核心。 CPU Shares将每个核心划分为1024个片,并保证每个进程将按比例获得这些片的份额。如果有1024个片(即1核),并且两个进程设置cpu.shares均为1024,那么这两个进程中每个进程将获得大约一半的cpu可用时间。

当系统中有两个cgroup,分别是ABAshares值是1024,B 的shares值是512, 那么A将获得1024/(1024+512)=66%CPU资源,而B将获得33%CPU资源。shares有两个特点:

  • 如果A不忙,没有使用到66%CPU时间,那么剩余的CPU时间将会被系统分配给B,即BCPU使用率可以超过33%
  • 如果添加了一个新的cgroup C,且它的shares值是1024,那么A的限额变成了1024/(1024+512+1024)=40%B的变成了20%

从上面两个特点可以看出:

在闲的时候,shares基本上不起作用,只有在CPU忙的时候起作用,这是一个优点。

由于shares是一个绝对值,需要和其它cgroup的值进行比较才能得到自己的相对限额,而在一个部署很多容器的机器上,cgroup的数量是变化的,所以这个限额也是变化的,自己设置了一个高的值,但别人可能设置了一个更高的值,所以这个功能没法精确的控制CPU使用率。

  1. cpu.shares对应k8s内的resources.requests.cpu字段:

值对应关系为:resources.requests.cpu * 1024 = cpu.shares

如:resources.requests.cpu为3的时候,cpu.shares值为3072resources.requests.cpu100m的时候,cpu.shares值为102

cpu.cpu.cfs_period_uscpu.cfs_quota_us解析

  1. cpu.cfs_period_us用来配置时间周期长度,cpu.cfs_quota_us用来配置当前cgroup在设置的周期长度内所能使用的CPU时间数。 两个文件配合起来设置CPU的使用上限。两个文件的单位都是微秒(us),cfs_period_us的取值范围为1毫秒(ms)到1秒(s),cfs_quota_us的取值大于1ms即可,如果cfs_quota_us的值为-1(默认值),表示不受cpu时间的限制。

  2. cpu.cpu.cfs_period_uscpu.cfs_quota_us对应k8s中的resources.limits.cpu字段:

resources.limits.cpu = cpu.cfs_quota_us/cpu.cfs_period_us

并且k8s下容器控制组的cpu.cpu.cfs_period_us值固定为100000,实际只设置cpu.cfs_quota_us

例如:

cpu.cpu.cfs_period_us100000(单位微妙,即0.1秒),cpu.cfs_quota_us500000(单位微妙,即0.5秒)时,resources.limits.cpu为5,即5个cpu核心。 cpu.cpu.cfs_period_us100000(单位微妙,即0.1秒),cpu.cfs_quota_us10000(单位微妙,即0.01秒)时,resources.limits.cpu为0.1(或100m),即0.1个cpu核心。

pod的内存控制组解析

cpu不同,k8spod容器的requests.memory在控制组内没有对应的属性,未起到限制作用,只是协助k8s调度计算。 而pod容器的limits.memory对应控制组里的memory.limit_in_bytes值。

总结

  1. k8s基于pod管理控制组,同一pod内的容器所属同一控制组,并且每个控制组内包含一个infra沙箱容器

  2. k8s基于.spec.containers[x].resourcespod划分了三种类型,对应控制组路径如下:

Pod类型 描述 控制组
Guaranteed 内存与CPU设置了相同的requests和limits /sys/fs/cgroup//kubepods.slice/kubepods-pod.slice
Burstable 至少有一个容器设置了CPU或内存的requests属性 /sys/fs/cgroup//kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod.slice
BestEffort 所有容器均未设置requests和limits /sys/fs/cgroup//kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod.slice
  1. 控制组中cpu.shares对应k8s内的resources.requests.cpu字段,值对应关系为:
resources.requests.cpu * 1024 = cpu.shares
  1. 控制组中cpu.cpu.cfs_period_uscpu.cfs_quota_us对应k8s中的resources.limits.cpu字段,值对应关系为:
resources.limits.cpu = cpu.cfs_quota_us/cpu.cfs_period_us
  1. 控制组里的memory.limit_in_bytes对应k8s中的resources.limits.memory

参考文章

Copyright © weiliang 2021 all right reserved,powered by Gitbook本书发布时间: 2024-04-22 16:03:41

results matching ""

    No results matching ""