美文网首页kubelet源码解析
Kubernetes下pod控制组管理解析

Kubernetes下pod控制组管理解析

作者: 微凉哇 | 来源:发表于2021-11-17 15:53 被阅读0次

    开始之前我们先了解下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-score.png

    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/<subsystem>/kubepods.slice/kubepods-pod<pod uid>.slice
    Burstable 至少有一个容器设置了CPU或内存的requests属性 /sys/fs/cgroup/<subsystem>/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod<pod uid>.slice
    BestEffort 所有容器均未设置requests和limits /sys/fs/cgroup/<subsystem>/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod<pod uid>.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

    参考文章

    相关文章

      网友评论

        本文标题:Kubernetes下pod控制组管理解析

        本文链接:https://www.haomeiwen.com/subject/vmwltrtx.html