kubelet - container manager

containerManager 负责 node 节点上运行的容器的配置信息,如cgroupcpudevice

 

pod的创建流程参考:

http://www.tianfeiyu.com/?p=2825

 

一构成

1.QOSContainerManager维护pod对应的Qos,用于调度、驱逐、设置各资源的cgroup等模块

2.cgroupManager根据pod的Qos设置不同级别的cgroup参数,并不断根据pod的变化维护这些cgroup参数

3.devicemanager管理节点上的device,主要包含分配、回收、本地记录存储;

4.cpumanager管理节点上的cpu,主要包含分配、回收、本地记录存储

5.devicemanager通过topologymanagercontainer选择最优的device组合

6.cpumanager通过topologymanagercontainer选择最优cpuset

 

调用流程:

1.kubeRuntimeManager.SyncPod创建完container后,通过cpumanager设置container的启动参数;

2.kubeRuntimeManager.SyncPod.startContainer启动container时会调用containerManager获取container跑在device上的参数,最终回调用到devicemanager

3.Kubelet.SyncPod设置cgroup参数阶段-->podContainerManager-->cgroupManager(依赖QOSContainerManager设置QOS)-->runc.libcontainer.cgroupfs将设置的参数apply到container

 

 

二、Kubelet.syncPod vs kubeRuntimeManager.SyncPod:调用关系:

 

Kubelet.syncPod

1.如果pod需要被删除,调用containerRuntime.KillPod杀掉pod

2.为第一次创建的pod记录创建时间,当pod状态变化时也记录当前时间

3.通过一系列admit判断pod是否能在当前node上运行,如安全相关的appArmorValidatorcontainerRuntime是否允许root Privilege

4.设置podcgroup参数

5.static pod创建mirror pod

6.创建相应的工作目录,如pod的工作目录、存储目录、插件plugins目录

7,等待volume挂载到pod的存储目录下

8.拉取下载container imagesecret

9.调用kubeRuntimeManager.SyncPodpod创建container

10.statusManager更新pod的状态

 

kubeRuntimeManager.SyncPod

//  1. Compute sandbox and container changes.
//  2. Kill pod sandbox if necessary.
//  3. Kill any containers that should not be running.
//  4. Create sandbox if necessary.
//  5. Create ephemeral containers.
//  6. Create init containers.
//  7. Create normal containers.

这部分的修改不会导致pod重建

 

 

二cgroupmanager

cgroupmanager负责 node 节点上运行的容器的 cgroup 配置信息,kubelet 启动参数如果指定 --cgroups-per-qos 的时候,kubelet 会启动 goroutine 来周期性的更新 pod 的 cgroup 信息,维护其正确性,该参数默认为 true,实现了 pod 的Guaranteed/BestEffort/Burstable 三种级别的 Qos。

目前 kubernetes 仅支持 cpu、memory、pids 、hugetlb 四个 cgroup 子系统

runtime 有两种 cgroup 驱动:一种是 systemd,另外一种是 cgroupfs

  • cgroupfs 比较好理解,比如说要限制内存是多少、要用 CPU share 为多少,其实直接把 pid 写入到对应cgroup task 文件中,然后把对应需要限制的资源也写入相应的 memory cgroup 文件和 CPU 的 cgroup 文件就可以了;

  • 另外一个是 systemd 的 cgroup 驱动,这个驱动是因为 systemd 本身可以提供一个 cgroup 管理方式。所以如果用 systemd 做 cgroup 驱动的话,所有的写 cgroup 操作都必须通过 systemd 的接口来完成,不能手动更改 cgroup 的文件;


 

Kubelet cgroup level:

/sys/fs/cgroup/cpu/kubepods – kubepods cgroup
/sys/fs/cgroup/cpu/kubepods/burstable – Qos level cgroup
/sys/fs/cgroup/cpu/kubepods/burstable/pod – pod level cgroup
/sys/fs/cgroup/cpu/kubepods/burstable/pod/container – container level cgroup

1.cgroupmanager 会把本机的 allocatable 资源写入到 kubepods 下对应的 cgroup 文件中,比如 kubepods/cpu.share

2.Qos level cgroup有两个cgroupburstable besteffort,分别作为 burstable级别podbesteffort级别pod的父cgroup;而Guaranteed Qos 对应的 pod 会直接在 kubepods 同级的 cgroup 中创建 pod cgroup,原因:guaranteed 级别的 pod 有明确的资源申请量(request)和资源限制量(limit),不需要qos级别的cgroup来管理和限制资源

3.pod level cgroup包含pod对应的container level cgroup

4.创建流程kubepods>qos>pod>container,设置流程container->pod->qos->kubepods,即设置好container level cgroup中各文件的限制,再层层往外更新其它level的限制

5.从级别和目录可以看出:burstable 的 cgroup 需要为比他等级高的 guaranteed 级别的 pod 的内存资源做预留,而 besteffort 需要为 burstable 和 guaranteed 都要预留内存资源。


 

例:

/sys/fs/cgroup/cpu/kubepods/burstable/pod/container/cpu.shares=2

/sys/fs/cgroup/cpu/kubepods/burstable/pod/cpu.shares=204
/sys/fs/cgroup/cpu/kubepods/burstable/cpu.shares=1126
/sys/fs/cgroup/cpu/kubepods/cpu.shares=49512

 

对于每一个 pod 设定的 requests 和 limits,kubernetes 都会转换为 cgroup 中的计算方式,CPU 的转换方式如下所示:

  • cpu.shares = (cpu in millicores * 1024) / 1000

  • cpu.cfs_period_us = 100000 (i.e. 100ms)

  • cpu.cfs_quota_us = quota = (cpu in millicores * 100000) / 1000

  • memory.limit_in_bytes

CPU 最终都会转换为以微秒为单位,memory 会转换为以 bytes 为单位

如果 pod 指定了 requests 和 limits,kubelet 会按以上的计算方式为 pod 设置资源限制,如果没有指定 limit 的话,那么 cpu.cfs_quota_us 将会被设置为 -1,即没有限制。而如果 limit 和 request 都没有指定的话,cpu.shares 将会被指定为 2,这个是 cpu.shares 允许指定的最小数值了,可见针对这种 pod,kubernetes 只会给它分配最少的 cpu 资源。而对于内存来说,如果没有 limit 的指定的话,memory.limit_in_bytes 将会被指定为一个非常大的值,一般是 2^64 ,可见含义就是不对内存做出限制。

参考:https://www.jianshu.com/p/924e3c48cb9b

 

三QOSContainerManager

QoS(Quality of Service) 即服务质量,QoS 是一种控制机制,它提供了针对不同用户或者不同数据流采用相应不同的优先级,或者是根据应用程序的要求,保证数据流的性能达到一定的水准。kubernetes 中有三种 Qos,分别为:

  • 1、Guaranteed:pod 的 requests 与 limits 设定的值相等;

  • 2、Burstable:pod requests 小于 limits 的值且不为 0;

  • 3、BestEffort:pod 的 requests 与 limits 均为 0;

三者的优先级如下所示,依次递增,OOM_SCORE_ADJ递减

BestEffort -> Burstable -> Guaranteed

三种 Qos 在调度和底层表现上都不一样:

  • 1、在调度时调度器只会根据 request 值进行调度;

  • 2、二是当系统 OOM上时对于处理不同 OOMScore 的进程表现不同,OOMScore 是针对 memory 的,当宿主上 memory 不足时系统会优先 kill 掉 OOMScore 值低的进程,可以使用 $ cat /proc/$PID/oom_score 查看进程的 OOMScore。OOMScore 的取值范围为 [-1000, 1000],Guaranteed pod 的默认值为 -998,Burstable pod 的值为 2~999,BestEffort pod 的值为 1000,也就是说当系统 OOM 时,首先会 kill 掉 BestEffort pod 的进程,若系统依然处于 OOM 状态,然后才会 kill 掉 Burstable pod,最后是 Guaranteed pod;

  • 3、三是 cgroup 的配置不同,kubelet 为会三种 Qos 分别创建对应的 QoS level cgroups,Guaranteed Pod Qos 的 cgroup level 会直接创建在 RootCgroup/kubepods 下,Burstable Pod Qos 的创建在 RootCgroup/kubepods/burstable 下,BestEffort Pod Qos 的创建在 RootCgroup/kubepods/BestEffort 下,上文已经说了 root cgroup 可以通过 $ mount | grep cgroup看到,在 cgroup 的每个子系统下都会创建 Qos level cgroups, 此外在对应的 QoS level cgroups 还会为 pod 创建 Pod level cgroups;


 

qos的计算方式:

memoryRequest := container.Resources.Requests.Memory().Value()
        oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
        // A guaranteed pod using 100% of memory can have an OOM score of 10. Ensure
        // that burstable pods have a higher OOM score adjustment.
        if int(oomScoreAdjust) < (1000 + guaranteedOOMScoreAdj) {
                return (1000 + guaranteedOOMScoreAdj)
        }
        // Give burstable pods a higher chance of survival over besteffort pods.
        if int(oomScoreAdjust) == besteffortOOMScoreAdj {
                return int(oomScoreAdjust - 1)
        }
        return int(oomScoreAdjust)

可见:pod的container使用memory越多,它的 OOMScore越小,越不容易被驱逐。

 

 

 

三 device manager

 

四topology manager

白话:为container的resource选择最优组合,如同组的cpuset,container所需的device来自同一个NUMA Node.

topology manager不是单独起作用的,当前k8s 1.17.3中,它和CPU manager,device manager共同为pod分配资源,优化资源访问。

topology manager从Hint Providers中以bitmask的形式接受NUMA拓扑信息(Topology Hint),包括当前可用的NUMA节点和倾向的资源分配指示。Topology manager策略对提供的hint进行一系列操作以后将所有的hint决定汇聚起来,得出一个最佳的hint(bestHint)。通过配置的策略(policy),主机节点可以选择接受或者拒绝pod。

topology manager当前所支持的策略有四个:None,BestEffort,Restricted和SingleNumaNode。

 

None是默认的策略,这个策略不会做任何的NUMA感知的资源对齐。

 

BestEffort对于pod里面的每一个container都会调用它们的hint provider来发现它们的资源可用性。topology manager计算存储这些container的NUMA亲和度的信息,如果亲和度不能被满足的话,manager也会存储亲和度信息同时也会允许pod加入这个主机节点。

 

Restricted同样也会对pod里面的每一个container计算NUMA亲和度。不同的是,如果亲和度不能被满足的话,主机节点会拒绝这个pod的加入。这样会导致pod处于Terminated的状态。如果pod被接受了,那么亲和度信息将会被用于container的资源分配。

 

SingleNumaNode策略用来判断单个NUMA节点的亲和度是否满足,如果满足的话hint provider即可利用亲和度信息进行资源分配;如果不满足的话主机节点会拒绝pod的加入,这样也会导致pod处于Terminated的状态。

 

参考:https://blog.csdn.net/qjm1993/article/details/103237944

 

 

 

五cpu manager

白话:根据NUMA为container选择最优的cpuset,让container从cpu方向得到更高的性能

目前CPU Manager支持两种Policy,分别为none和static,通过kubelet --cpu-manager-policy设置,未来会增加dynamic policy做Container生命周期内的cpuset动态调整。

    • none: 为cpu manager的默认值,相当于没有启用cpuset的能力。cpu request对应到cpu share,cpu limit对应到cpu quota。

    • static: 目前,请设置--cpu-manager-policy=static来启用,kubelet将在Container启动前分配绑定的cpu set,分配时还会考虑cpu topology来提升cpu affinity,后面会提到。

  • 确保kubelet为--kube-reserved--system-reserved都配置了值,可以不是整数个cpu,最终会计算reserved cpus时会向上取整。这样做的目的是为了防止CPU Manager把Node上所有的cpu cores分配出去了,导致kubelet及系统进程都没有可用的cpu了。

注意CPU Manager还有一个配置项 --cpu-manager-reconcile-period,用来配置CPU Manager Reconcile Kubelet内存中CPU分配情况到cpuset cgroups的修复周期。如果没有配置该项,那么将使用 --node-status-update-frequency default 10s 配置的值。

使用CPU Manager的Pod、Container具备以下两个条件:

  • Pod QoS为Guaranteed;

  • Pod中该Container的Cpu request必须为整数CPUs;

工作流程:

  • Kuberuntime调用容器运行时去create该Container。

  • 创建完后,调用PreStartContainer将该Container交给CPU Manager处理。

  • 创建业务container时,先把init container占用的cpu释放

  • CPU Manager为Container按照static policy逻辑进行处理。

  • CPU Manager从当前Shared Pool中挑选“最佳”Set拓扑结构的CPU,对于不满足Static Policy的Contianer,则返回Shared Pool中所有CPUS组成的Set。

  • 挑选方式:根据Node上的NUMA CPU Topology划分cpu组,当container需要多个cpu时,让这几个cpu尽量来自同一个组(简单理解为一个插槽),这样同组访问能加快速度提高性能。

  • CPU Manager将对该Container的CPUs分配情况记录到Checkpoint State中,并且从Shared Pool中删除刚分配的CPUs。

  • CPU Manager再从state中读取该Container的CPU分配信息,然后通过UpdateContainerResources cRI接口将其更新到Cpuset Cgroups中,包括对于非Static Policy Container。

  • Kuberuntime调用容器运行时Start该容器。

Checkpoint文件内容就是CPUManagerCheckpoint结构体的json格式,其中Entries的key是ContainerID,value为该Container对应的Assigned CPU Set信息。

当这些通过CPU Manager分配CPUs的Container要Delete时,CPU Manager工作流大致如下:

  1. Kuberuntime会调用CPU Manager去按照static policy中定义逻辑处理。

  2. CPU Manager将该Container分配的Cpu Set重新归还到Shared Pool中。

  3. Kuberuntime调用容器运行时Remove该容器。

  4. CPU Manager会异步地进行Reconcile Loop,为使用Shared Pool中的Cpus的Containers更新CPU集合。

 

CPU Manager Reconcile按照--cpu-manager-reconcile-period配置的周期进行Loop,Reconcile注意进行如下处理:

  • 遍历所有activePods中的所有Containers,注意包括InitContainers,对每个Container继续进行下面处理。

  • 检查该ContainerID是否在CPU Manager维护的Memory State assignments中,

    • 如果不在Memory State assignments中:

      • 再检查对应的Pod.Status.Phase是否为Running且DeletionTimestamp为nil,如果是,则调用CPU Manager的AddContainer对该Container/Pod进行QoS和cpu request检查,如果满足static policy的条件,则调用takeByTopology为该Container分配“最佳”CPU Set,并写入到Memory State和Checkpoint文件(cpu_manager_sate)中,并继续后面流程。

      • 如果对应的Pod.Status.Phase是否为Running且DeletionTimestamp为nil为false,则跳过该Container,该Container处理结束。不满足static policy的Containers因为不在Memory State assignments中,所以对它们的处理流程也到此结束。

    • 如果ContainerID在CPU Manager assignments维护的Memory State中,继续后面流程。

  • 然后从Memory State中获取该ContainerID对应的CPU Set。

  • 最后调用CRI UpdateContainerCPUSet更新到cpuset cgroups中。

 

cpumanager调用State进行validate处理:

  • 当Memory State中Shared(Default) CPU Set为空时,CPU Assginments也必须为空,然后对Memory State中的Shared Pool进行初始化,并写入到Checkpoint文件中(初始化Checkpoint)。

  • 只要我们没有手动去删Checkpoint文件,那么在前面提到的state.NewCheckpointState中会根据Checkpoint文件restore到Memory State中,因此之前Assgned CPU Set、Default CPU Set都还在。

  • 当检测到Memory State已经成功初始化(根据Checkpoint restore),则检查这次启动时reserved cpu set是否都在Default CPU Set中,如果不是(比如kube/system reserved cpus增加了),则报错返回,因为这意味着reserved cpu set中有些cpus被Assigned到了某些Container中了,这可能会导致这些容器启动失败,此时需要用户自己手动的去修正Checkpoint文件。

  • 检测reserved cpu set通过后,再检测Default CPU Set和Assigned CPU Set是否有交集,如果有交集,说明Checkpoint文件restore到Memory State的数据有错,报错返回。

  • 最后检查这次启动时从cAdvisor中获取到的CPU Topology中的所有CPUs是否与Memory State(从Checkpoint中restore)中记录的所有CPUs(Default CPU Set + Assigned CPU Set)相同,如果不同,则报错返回。可能因为上次CPU Manager停止到这次启动这个时间内,Node上的可用CPUs发生变化。

参考:https://blog.csdn.net/weixin_34050389/article/details/92574505

 

 

 

 

 

 

你可能感兴趣的:(kubelet - container manager)