美文网首页
kubelet中的Cgroup维护笔记

kubelet中的Cgroup维护笔记

作者: Teddy_b | 来源:发表于2023-08-07 12:25 被阅读0次

总览

这篇笔记主要记录了kubelet中cgroup的管理原理,涉及几个方面

  • kubelet主要资源提供者CAdvisor获取资源的途径

  • kubelet怎么维护自己的CgroupRoot=kubepods.slice路径

  • 针对QoS的不同POD类型,kubelet怎么维护对应的CgroupRoot路径

CAdvisor获取资源

CAdvisor获取CPU

CAdvisor获取CPU的数量和NUMA的相关概念有关,可以先行了解NUMA的相关概念(https://blog.csdn.net/yk_wing4/article/details/87474172)

获取流程

# 1. 获取NODE的数量,NODE对应的就是一个NUMA NODE
root@ubuntu:~# ls -la /sys/devices/system/node/
total 0

drwxr-xr-x  4 root root    0 8月   2 14:34 node0
drwxr-xr-x  4 root root    0 8月   2 14:34 node1

# 2. 获取每个NODE下的CPU核心数量,对应的是逻辑CPU核心数量
root@ubuntu:~# ls -la /sys/devices/system/node/node0/
total 0

lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu0 -> ../../cpu/cpu0
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu1 -> ../../cpu/cpu1
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu12 -> ../../cpu/cpu12
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu13 -> ../../cpu/cpu13
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu14 -> ../../cpu/cpu14
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu15 -> ../../cpu/cpu15
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu16 -> ../../cpu/cpu16
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu17 -> ../../cpu/cpu17
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu2 -> ../../cpu/cpu2
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu3 -> ../../cpu/cpu3
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu4 -> ../../cpu/cpu4
lrwxrwxrwx 1 root root    0 8月   2 14:36 cpu5 -> ../../cpu/cpu5

上述所有NODE下的逻辑CPU核心数量对应的就是CAdvisor中的CPU核心数量了

小插曲:怎么知道每个逻辑CPU核心对应的物理CPU核心呢?

root@ubuntu:~# cat /sys/devices/system/node/node0/cpu0/topology/core_id 
0
root@ubuntu:~# cat /sys/devices/system/node/node0/cpu1/topology/core_id 
1

通过查看每个逻辑CPU所属的CPU核心,就能知道我们的主机是几核几线程的啦

CAdvisor获取内存
root@ubuntu:~# cat /proc/meminfo
MemTotal:       49422924 kB

直接读取文件 /proc/meminfo,关注它的 MemTotal字段即可,Cadvisor中通常会把它转成Byte单位

CAdvisor获取内存大页HugePages
root@ubuntu:~# ls -la /sys/kernel/mm/hugepages/
total 0

drwxr-xr-x 2 root root 0 8月   2 14:37 hugepages-1048576kB
drwxr-xr-x 2 root root 0 8月   2 14:37 hugepages-2048kB

root@ubuntu:~# cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
0

该目录下对应的就是2M和1G的内存大页,nr_hugepages文件中记录的是对应内存大页的数量

kubelet维护cgroup

kubelet中和cgroup打交道的主要是ContainerManager,我们具体看下kubelet是怎么维护cgroup信息的

func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.Interface, nodeConfig NodeConfig, failSwapOn bool, devicePluginEnabled bool, recorder record.EventRecorder) (ContainerManager, error) {

    // 读取 /proc/$pid/mountinfo 获取进程的所有挂载信息
    // 读取 /proc/$pid/cgroup 获取进程的cgroup信息
    // 再mountinfo中找出所有挂载了的 cgroup 路径
    subsystems, err := GetCgroupSubsystems()
    ...

    var internalCapacity = v1.ResourceList{}
    
    machineInfo, err := cadvisorInterface.MachineInfo()
    // 从CAdvisor中获取到CPU、内存、内存大页容量
    capacity := cadvisor.CapacityFromMachineInfo(machineInfo)
    for k, v := range capacity {
        internalCapacity[k] = v
    }

        // 额外从文件 /proc/sys/kernel/pid_max 获取PID最大值
    pidlimits, err := pidlimit.Stats()
    if err == nil && pidlimits != nil && pidlimits.MaxPID != nil {
        internalCapacity[pidlimit.PIDs] = *resource.NewQuantity(
            int64(*pidlimits.MaxPID),
            resource.DecimalSI)
    }

    // CgroupRoot 由于默认启用了CgroupsPerQOS 默认会设置为 "/"
    cgroupRoot := ParseCgroupfsToCgroupName(nodeConfig.CgroupRoot)
    cgroupManager := NewCgroupManager(subsystems, nodeConfig.CgroupDriver)
    // Check if Cgroup-root actually exists on the node
    if nodeConfig.CgroupsPerQOS {
        
        // 检查 /sys/fs/cgroup/memory/ 等各个子系统路径必须存在
        if !cgroupManager.Exists(cgroupRoot) {
            return nil, fmt.Errorf("invalid configuration: cgroup-root %q doesn't exist", cgroupRoot)
        }
        // cgroupRoot 默认设置为 kubepods
        cgroupRoot = NewCgroupName(cgroupRoot, "kubepods")
    }
    
    cm := &containerManagerImpl{
        cadvisorInterface:   cadvisorInterface,
        mountUtil:           mountUtil,
        NodeConfig:          nodeConfig,
        subsystems:          subsystems,
        cgroupManager:       cgroupManager,
        capacity:            capacity,
        internalCapacity:    internalCapacity,
        cgroupRoot:          cgroupRoot,
        recorder:            recorder,
        qosContainerManager: qosContainerManager,
    }
        ...
    return cm, nil
}

这里创建ContainerManager主要获取当前节点上挂了的所有cgroup子系统,并校验所有cgroup子系统的路径必须存在

我们可以具体看下kubelet对应的pid中的cgroup相关的挂载信息,可以看到常见的cpu, memory等均有挂载

root@ubuntu:~# cat /proc/22507/mountinfo |grep cgroup
28 18 0:24 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755
29 28 0:25 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
31 28 0:27 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,freezer
32 28 0:28 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,cpu,cpuacct
33 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,memory
34 28 0:30 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,pids
35 28 0:31 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,cpuset
36 28 0:32 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,hugetlb
37 28 0:33 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,net_cls,net_prio
38 28 0:34 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,perf_event
39 28 0:35 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,devices
40 28 0:36 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,blkio

然后再kubelet启动的时候,ContainerManager也会相应的启动,我们看下它启动会做哪些工作

func (cm *containerManagerImpl) Start(node *v1.Node,
    activePods ActivePodsFunc,
    sourcesReady config.SourcesReady,
    podStatusProvider status.PodStatusProvider,
    runtimeService internalapi.RuntimeService) error {

    // 验证 节点的资源 必须大于 系统保留、kube保留、hard驱逐资源的和
    if err := cm.validateNodeAllocatable(); err != nil {
        return err
    }

    // Setup the node
    if err := cm.setupNode(activePods); err != nil {
        return err
    }

      // 检查是否存在定时任务,默认会存在docker的一个定时任务,用于检测dockerd的运行cgroup信息
      // 还存在一个kubelet的定时任务,用于检测kubelet的运行cgroup信息
    if len(cm.periodicTasks) > 0 {
        go wait.Until(func() {
            for _, task := range cm.periodicTasks {
                if task != nil {
                    task()
                }
            }
        }, 5*time.Minute, wait.NeverStop)
    }
}

func (cm *containerManagerImpl) setupNode(activePods ActivePodsFunc) error {
    // 默认启用了CgroupsPerQOS ,这里首先确保CgroupRoot路径是存在的,不存在则创建
       // 再创建ContainerManager的时候CgroupRoot路径已经被设置为了kubepods
    if cm.NodeConfig.CgroupsPerQOS {
        if err := cm.createNodeAllocatableCgroups(); err != nil {
            return err
        }
        err = cm.qosContainerManager.Start(cm.getNodeAllocatableAbsolute, activePods)
        
    }

    systemContainers := []*systemContainer{}
         // 默认是docker
    if cm.ContainerRuntime == "docker" {
        // 添加一个定时任务,用于定时更新docker运行的cgroup信息
        cm.periodicTasks = append(cm.periodicTasks, func() {
            klog.V(4).Infof("[ContainerManager]: Adding periodic tasks for docker CRI integration")
            cont, err := getContainerNameForProcess(dockerProcessName, dockerPidFile)
            
            cm.RuntimeCgroupsName = cont
        })
    }

        // 默认是空
    if cm.KubeletCgroupsName != "" {
        ...
    } else {
                // 再添加一个定时任务,用于定时更新kubelet运行的cgroup信息
        cm.periodicTasks = append(cm.periodicTasks, func() {
            if err := ensureProcessInContainerWithOOMScore(os.Getpid(), qos.KubeletOOMScoreAdj, nil); err != nil {
                ...
            }
            cont, err := getContainer(os.Getpid())
            
            cm.KubeletCgroupsName = cont
        })
    }
        // 默认为空
    cm.systemContainers = systemContainers
    return nil
}
首先是创建CgroupRoot路径
func (cm *containerManagerImpl) createNodeAllocatableCgroups() error {
    // 包括 cpu,内存、HugePages、Pid
    nodeAllocatable := cm.internalCapacity
    nc := cm.NodeConfig.NodeAllocatableConfig

      // EnforceNodeAllocatable 默认包括pods
       // 排除掉 系统保留、kube保留
    if cm.CgroupsPerQOS && nc.EnforceNodeAllocatable.Has("pods") {
        nodeAllocatable = cm.getNodeAllocatableInternalAbsolute()
    }

         // cgroupRoot 上面创建ContainerManager的时候设置成了 kubepods
    cgroupConfig := &CgroupConfig{
        Name: cm.cgroupRoot,
        ResourceParameters: getCgroupConfig(nodeAllocatable),
    }

        // /sys/fs/cgroup/cpu/kubepods.slice 目录
       // /sys/fs/cgroup/memory/kubepods.slice 目录 等等这些cgroup子系统路径是否存在
    if cm.cgroupManager.Exists(cgroupConfig.Name) {
        return nil
    }

         // 不存在则需要创建
    if err := cm.cgroupManager.Create(cgroupConfig); err != nil {
    }
    return nil
}

这一步主要就是创建kubelet的CgroupRoot目录

因为kubelet的CgroupDriver在启动参数中会设置为 systemd,因此 kubepods 这个CgroupRoot 会被转换为 systemd 风格

func (cgroupName CgroupName) ToSystemd() string {
    if len(cgroupName) == 0 || (len(cgroupName) == 1 && cgroupName[0] == "") {
        return "/"
    }
    newparts := []string{}
    for _, part := range cgroupName {
        part = escapeSystemdCgroupName(part)
        newparts = append(newparts, part)
    }

    result, err := cgroupsystemd.ExpandSlice(strings.Join(newparts, "-") + ".slice")
    
    return result
}

转成 systemd风格会把 中划线- 转换为 下划线_,然后添加 .slice后缀

/sys/fs/cgroup/cpu/ 等Cgroup子系统下新建目录后,会自动创建这些文件,并且默认会自动继承上一层即 /sys/fs/cgroup/cpu/目录下这些文件中的值

root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice# ls -la 
total 0
-rw-r--r--  1 root root 0 8月   4 12:09 cgroup.clone_children
-rw-r--r--  1 root root 0 8月   4 12:09 cgroup.procs
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.stat
-rw-r--r--  1 root root 0 8月   4 12:09 cpuacct.usage
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_all
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_percpu
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_percpu_sys
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_percpu_user
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_sys
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_user
-rw-r--r--  1 root root 0 8月   4 12:09 cpu.cfs_period_us
-rw-r--r--  1 root root 0 8月   4 12:09 cpu.cfs_quota_us
-rw-r--r--  1 root root 0 8月   2 10:02 cpu.shares
-r--r--r--  1 root root 0 8月   4 12:09 cpu.stat
-rw-r--r--  1 root root 0 8月   4 12:09 notify_on_release
-rw-r--r--  1 root root 0 8月   4 12:09 tasks

这些文件自动生成出来后,就会对其中的资源进行设置,这些主要设置的是这些子系统

func getSupportedSubsystems() map[subsystem]bool {
    supportedSubsystems := map[subsystem]bool{
        &cgroupfs.MemoryGroup{}: true,
        &cgroupfs.CpuGroup{}:    true,
        &cgroupfs.PidsGroup{}:   true,
    }
    // not all hosts support hugetlb cgroup, and in the absent of hugetlb, we will fail silently by reporting no capacity.
    supportedSubsystems[&cgroupfs.HugetlbGroup{}] = false
    return supportedSubsystems
}

而这里设置的值就来自于CAdvisor中获取到的CPU容量、内存容量、PID容量、内存大页容量

func setSupportedSubsystemsV1(cgroupConfig *libcontainerconfigs.Cgroup) error {
    for sys, required := range getSupportedSubsystems() {
        if _, ok := cgroupConfig.Paths[sys.Name()]; !ok {
        }
        if err := sys.Set(cgroupConfig.Paths[sys.Name()], cgroupConfig); err != nil {
            return fmt.Errorf("failed to set config for supported subsystems : %v", err)
        }
    }
    return nil
}
CPU 子系统的设置
func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error {
       // CPU容量会转换为 CpuShares 
    if cgroup.Resources.CpuShares != 0 {
        shares := cgroup.Resources.CpuShares
        if err := fscommon.WriteFile(path, "cpu.shares", strconv.FormatUint(shares, 10)); err != nil {
            return err
        }
    }
        // kubelet不会设置这个文件,使用默认值 100ms
    if cgroup.Resources.CpuPeriod != 0 {
        if err := fscommon.WriteFile(path, "cpu.cfs_period_us", strconv.FormatUint(cgroup.Resources.CpuPeriod, 10)); err != nil {
            return err
        }
    }
        // kubelet不会设置这个文件,使用默认值  -1
    if cgroup.Resources.CpuQuota != 0 {
        if err := fscommon.WriteFile(path, "cpu.cfs_quota_us", strconv.FormatInt(cgroup.Resources.CpuQuota, 10)); err != nil {
            return err
        }
    }
    return s.SetRtSched(path, cgroup)
}

我们可以对比下 kubepods.slice/cpu.shares中的值和上一级的的值是不一样的,其中的值就来自于CAdvisor中获取到的CPU核数 减去kube保留的、减去系统保留的,(24000-500-500)*1024/1000=23552,我这里是24核CPU,1024/1000是CPU核数转换为CpuShares的一个比例参数

root@ubuntu:/sys/fs/cgroup/cpu# cat cpu.shares 
1024
root@ubuntu:/sys/fs/cgroup/cpu# cat kubepods.slice/cpu.shares 
23552
内存子系统的设置
func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error {
        // CAdvisor中获取的内存容量会转换为Memory,而swap我们一般都会关闭
    if cgroup.Resources.Memory == -1 && cgroup.Resources.MemorySwap == 0 {
        ...
    }

    // When memory and swap memory are both set, we need to handle the cases
    // for updating container.
    if cgroup.Resources.Memory != 0 && cgroup.Resources.MemorySwap != 0 {
        ...
    } else {
        if cgroup.Resources.Memory != 0 {
            if err := fscommon.WriteFile(path, "memory.limit_in_bytes", strconv.FormatInt(cgroup.Resources.Memory, 10)); err != nil {
                return err
            }
        }
        if cgroup.Resources.MemorySwap != 0 {
            ...
        }
    }

    if cgroup.Resources.KernelMemory != 0 {
        ...
    }

    if cgroup.Resources.MemoryReservation != 0 {
        ...
    }

    if cgroup.Resources.KernelMemoryTCP != 0 {
        ...
    }
    if cgroup.Resources.OomKillDisable {
        ...
    }
    if cgroup.Resources.MemorySwappiness == nil || int64(*cgroup.Resources.MemorySwappiness) == -1 {
        return nil
    } else if *cgroup.Resources.MemorySwappiness <= 100 {
        ...
    } else {
        ...
    }

    return nil
}

内存资源一般只会设置memory.limit_in_bytes,我们也可以对比下kubepods.slice/memory.limit_in_bytes和上一级的值是不一样的,其值就来自CAdvisor中拿到的节点的内存值,(49422924-512*1024-512*1024)*1024=49535332352

root@ubuntu:/sys/fs/cgroup/memory# cat memory.limit_in_bytes 
9223372036854771712
root@ubuntu:/sys/fs/cgroup/memory# cat kubepods.slice/memory.limit_in_bytes 
49535332352
PID 子系统设置
func (s *PidsGroup) Set(path string, cgroup *configs.Cgroup) error {
    if cgroup.Resources.PidsLimit != 0 {
        // "max" is the fallback value.
        limit := "max"

        if cgroup.Resources.PidsLimit > 0 {
            limit = strconv.FormatInt(cgroup.Resources.PidsLimit, 10)
        }

        if err := fscommon.WriteFile(path, "pids.max", limit); err != nil {
            return err
        }
    }

    return nil
}

PID 子系统中会设置kubepods.slice/pids.max的值,其值来自/proc/sys/kernel/pid_max文件,而上一级目录是没有这个文件的

root@ubuntu:/sys/fs/cgroup/pids# ls -la 
total 0
-rw-r--r--   1 root root   0 8月   2 14:19 cgroup.clone_children
-rw-r--r--   1 root root   0 8月   2 14:19 cgroup.procs
-r--r--r--   1 root root   0 8月   2 14:19 cgroup.sane_behavior
drwxr-xr-x   2 root root   0 8月   2 14:19 init.scope
-rw-r--r--   1 root root   0 8月   2 14:19 notify_on_release
-rw-r--r--   1 root root   0 8月   2 14:19 release_agent
drwxr-xr-x 215 root root   0 8月   7 17:05 system.slice
-rw-r--r--   1 root root   0 8月   2 14:19 tasks
drwxr-xr-x   8 root root   0 8月   7 16:32 user.slice
root@ubuntu:/sys/fs/cgroup/pids# cat kubepods.slice/pids.max 
4194304
root@ubuntu:/sys/fs/cgroup/pids# cat /proc/sys/kernel/pid_max
4194304
内存大页子系统设置
func (s *HugetlbGroup) Set(path string, cgroup *configs.Cgroup) error {
    for _, hugetlb := range cgroup.Resources.HugetlbLimit {
        if err := fscommon.WriteFile(path, strings.Join([]string{"hugetlb", hugetlb.Pagesize, "limit_in_bytes"}, "."), strconv.FormatUint(hugetlb.Limit, 10)); err != nil {
            return err
        }
    }

    return nil
}

内存大页主要包括2MB和1GB的内存大页,这里会分别设置对应的limit值,由于我这里没有,所以对应的值都是0,和上一级目录中的值是不一样的

root@ubuntu:/sys/fs/cgroup/hugetlb# cat hugetlb.2MB.limit_in_bytes
9223372036854771712
root@ubuntu:/sys/fs/cgroup/hugetlb# cat kubepods.slice/hugetlb.2MB.limit_in_bytes
0
root@ubuntu:/sys/fs/cgroup/hugetlb# cat hugetlb.1GB.limit_in_bytes
9223372036854771712
root@ubuntu:/sys/fs/cgroup/hugetlb# cat kubepods.slice/hugetlb.1GB.limit_in_bytes
0

上述子系统中现在就都存在kubepods.slice这个目录了,并且这个目录下的limit值也都根据CAdvisor获取到的容量进行了设置

再后续就是添加了两个定时任务,每五分钟执行一次

其中一个是获取docker运行的cgroup信息,优先读取/var/run/docker.pid文件获取dockerd的PID,然后获取这个PID的cgroup信息即可/proc/$PID/cgroup

如果这个文件没有,则会读取/proc下的所有文件,然后读取每一个PID下的/proc/#PID/cmdline文件,获取该PID的运行命令,然后匹配名称dockerd,从而确定dockerd的运行PID,然后再获取这个PID的cgroup信息即可/proc/$PID/cgroup

通常对应的cgroup信息都是/system.slice/docker.service

func getContainerNameForProcess(name, pidFile string) (string, error) {
    pids, err := getPidsForProcess(name, pidFile)
    ...
    cont, err := getContainer(pids[0])
    
    return cont, nil
}

另一个定时任务是获取kubelet的运行cgroup信息,首先会对oomscore值进行调整,通过对比kubelet进程和机器1号进程的namespace链接/proc/1/ns/pid是否一致,来判断kubelet是否运行再host namespace中

如果运行再hostnamespace中,那么需要确保kubelet进程的/proc/$PID/oom_score_adj的值固定为-999

最后就是读取kubelet进程的/proc/$PID/cgroup即可获取kubelet运行的cgroup信息,一般为/system.slice/kubelet.service

func ensureProcessInContainerWithOOMScore(pid int, oomScoreAdj int, manager cgroups.Manager) error {
    if runningInHost, err := isProcessRunningInHost(pid); err != nil {
        ...
    } else if !runningInHost {
        ...
        return nil
    }
        ...
    oomAdjuster := oom.NewOOMAdjuster()
    klog.V(5).Infof("attempting to apply oom_score_adj of %d to pid %d", oomScoreAdj, pid)
    if err := oomAdjuster.ApplyOOMScoreAdj(pid, oomScoreAdj); err != nil {
    }
    return utilerrors.NewAggregate(errs)
}

总结一下ContainerManager的工作流程

  • 初始化创建的时候会设置CgroupRootkubepods, 并且检测挂载了的cgroup子系统目录必须存在,对应的就是/sys/fs/cgroup/cpu等子系统的目录

  • 启动后会检测CgroupRoot目录是否存在,如果不存在则会为每个挂载了的cgroup子系统都创建kubepods.slice目录,会携带.slice后缀是因为会转换为systemd风格

  • kubepods.slice目录创建后,内核会自动再改目录下生成对应的默认文件,其中的值完全继承自上一级目录

  • 然后kubelet会再kubepods.slice目录,设置CPU、内存、内存大页、PID这些子系统的limit上限,上限值就来自CAdvisor中获取到的主机的容量值

QoS情况下的kubelet的Cgroup管理

在上面创建ContainerManager的时候,会同步创建QoSContainerManager

func NewQOSContainerManager(subsystems *CgroupSubsystems, cgroupRoot CgroupName, nodeConfig NodeConfig, cgroupManager CgroupManager) (QOSContainerManager, error) {
       // 默认情况下是启用了 CgroupsPerQOS 
    if !nodeConfig.CgroupsPerQOS {
        return &qosContainerManagerNoop{
            cgroupRoot: cgroupRoot,
        }, nil
    }

    return &qosContainerManagerImpl{
                // 这里是挂载了的cgroup子系统
        subsystems:    subsystems,
        cgroupManager: cgroupManager,
                 // 这里再ContainerManager中已经设置为了`kubepods`
        cgroupRoot:    cgroupRoot,
        qosReserved:   nodeConfig.QOSReserved,
    }, nil
}

ContainerManager创建了kubepods目录后,并且设置了对应的limit,然后就会来启动QoSContainerManager,启动这个会干啥呢

func (m *qosContainerManagerImpl) Start(getNodeAllocatable func() v1.ResourceList, activePods ActivePodsFunc) error {
    cm := m.cgroupManager
        // 这里再ContainerManager中已经初始化为了kubepods,并且完成了创建,所以这里检测存在的时候是已经存在了的
    rootContainer := m.cgroupRoot
    if !cm.Exists(rootContainer) {
        return fmt.Errorf("root container %v doesn't exist", rootContainer)
    }

    // 为Burstable和BestEffort类型的QoS分别创建新的CgroupRoot
    qosClasses := map[v1.PodQOSClass]CgroupName{
        "Burstable":  NewCgroupName(rootContainer, strings.ToLower(string("Burstable"))),
        "BestEffort": NewCgroupName(rootContainer, strings.ToLower(string("BestEffort"))),
    }

    // Create containers for both qos classes
    for qosClass, containerName := range qosClasses {
        resourceParameters := &ResourceConfig{}
        // BestEffort 类型的 CpuShares  设置为 最小值 2
        if qosClass == v1.PodQOSBestEffort {
            minShares := uint64(2)
            resourceParameters.CpuShares = &minShares
        }

        // containerConfig object stores the cgroup specifications
        containerConfig := &CgroupConfig{
            Name:               containerName,
            ResourceParameters: resourceParameters,
        }

        // 同样的设置内存大页,这里这是的是无无限,int64的最大值
        m.setHugePagesUnbounded(containerConfig)

        // 检查kubepods.slice/kubepods-besteffort.slice 路径是否存在
                // 检查kubepods.slice/kubepods-burstable.slice 路径是否存在
        if !cm.Exists(containerName) {
                       // 不存在的需要创建对应的Cgroup路径
            if err := cm.Create(containerConfig); err != nil {
            }
        } else {
            ...
        }
    }
    // 分别设置三种QoS类型的CgroupRoot路径
    m.qosContainersInfo = QOSContainersInfo{
        Guaranteed: rootContainer,
        Burstable:  qosClasses[v1.PodQOSBurstable],
        BestEffort: qosClasses[v1.PodQOSBestEffort],
    }
    m.getNodeAllocatable = getNodeAllocatable
    m.activePods = activePods

    // update qos cgroup tiers on startup and in periodic intervals
    // to ensure desired state is in sync with actual state.
    go wait.Until(func() {
        err := m.UpdateCgroups()
    }, 1min, wait.NeverStop)

    return nil
}

可以看到QoSContainerManager的工作原理和ContainerManager基本一致,只是它会维护不同QoS对应不同的CgroupRoot路径

先简单回顾下kubelet是怎么定义QoS的呢?

  • 如果每个容器都没设置limit和request,那么对应的QoS为BestEffort

  • 如果所有容器的CPU limit之和 与所有容器的CPU request之和相等、内存也相等,那么对应的QoS为Guaranteed

  • 其它情况为Burstable

QoSContainerManager会为besteffortburstable这两种类型的QoS创建额外的CgroupRoot路径,Guaranteed类型的QoS则不会创建,以CPU子系统为例,我们通常可以看到如下的目录划分

besteffortburstable这两种类型的QoS对应的POD会分别创建到对应的目录下,而Guaranteed类型的QoS的POD会直接创建在kubepods.slice目录下

root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice# ls -la 
total 0
-rw-r--r--  1 root root 0 8月   4 12:09 cgroup.clone_children
-rw-r--r--  1 root root 0 8月   4 12:09 cgroup.procs
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.stat
-rw-r--r--  1 root root 0 8月   4 12:09 cpuacct.usage
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_all
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_percpu
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_percpu_sys
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_percpu_user
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_sys
-r--r--r--  1 root root 0 8月   4 12:09 cpuacct.usage_user
-rw-r--r--  1 root root 0 8月   4 12:09 cpu.cfs_period_us
-rw-r--r--  1 root root 0 8月   4 12:09 cpu.cfs_quota_us
-rw-r--r--  1 root root 0 8月   2 10:02 cpu.shares
-r--r--r--  1 root root 0 8月   4 12:09 cpu.stat
drwxr-xr-x  6 root root 0 8月   4 12:09 kubepods-besteffort.slice
drwxr-xr-x 19 root root 0 8月   4 12:09 kubepods-burstable.slice
drwxr-xr-x  4 root root 0 8月   4 12:09 kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice
drwxr-xr-x  5 root root 0 8月   4 12:09 kubepods-pod36e2ba78_cd77_4527_89fd_f50e294e12db.slice
drwxr-xr-x  4 root root 0 8月   4 12:09 kubepods-pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa.slice
-rw-r--r--  1 root root 0 8月   4 12:09 notify_on_release
-rw-r--r--  1 root root 0 8月   4 12:09 tasks

对应的CgroupRoot路径创建完成后,会进行资源的初始化工作,besteffort类型的CpuShares 会设置为2,besteffortburstable类型的内存大页都会设置为无上限,即int64的最大值

root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice# cat kubepods-besteffort.slice/cpu.shares 
2

root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice# cat ../../hugetlb/kubepods.slice/kubepods-besteffort.slice/hugetlb.2MB.limit_in_bytes 
4611686018427387904

root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice# cat ../../hugetlb/kubepods.slice/kubepods-besteffort.slice/hugetlb.1GB.limit_in_bytes 
4611686018427387904

root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice# cat ../../hugetlb/kubepods.slice/kubepods-burstable.slice/hugetlb.1GB.limit_in_bytes 
4611686018427387904

root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice# cat ../../hugetlb/kubepods.slice/kubepods-burstable.slice/hugetlb.2MB.limit_in_bytes 
4611686018427387904

最后会启动一个定时任务对Cgroup路径下的资源进行动态更新,更新周期为1min

func (m *qosContainerManagerImpl) UpdateCgroups() error {
    m.Lock()
    defer m.Unlock()

    qosConfigs := map[v1.PodQOSClass]*CgroupConfig{
        v1.PodQOSBurstable: {
            Name:               m.qosContainersInfo.Burstable,
            ResourceParameters: &ResourceConfig{},
        },
        v1.PodQOSBestEffort: {
            Name:               m.qosContainersInfo.BestEffort,
            ResourceParameters: &ResourceConfig{},
        },
    }

    // 获取节点上所有Burstable 类型POD的CPU request资源总和 作为 `kubepods-burstable.slice`中的CpuShares
        // `kubepods-besteffort.slice` 中的CpuShares一直维持在 2
    if err := m.setCPUCgroupConfig(qosConfigs); err != nil {
    }

    // 维持内存大页为int64最大值
    if err := m.setHugePagesConfig(qosConfigs); err != nil {
    }

        // 执行更新
    for _, config := range qosConfigs {
        err := m.cgroupManager.Update(config)
    }

    return nil
}

总结一下QosContainerManager的工作流程

  • ContainerManager创建完成kubepods.slice目录并且设置了limit后,会启动QosContainerManager

  • QosContainerManager会为besteffortburstable这两种类型的QoS创建额外的CgroupRoot路径,分别为kubepods-besteffort.slicekubepods-burstable.slice

  • Guaranteed这种QoS类型的POD会直接创建在kubepods.slice目录下

  • besteffort这种QoS类型的CpuShares会维持在最小值2,内存大页会维持在int64的最大值,依赖定时任务每分钟进行更新

  • burstable这种QoS类型的CpuShares会维持在所有burstable类型POD的request值总和,内存大页会维持在int64的最大值,依赖定时任务每分钟进行更新

POD创建时的Cgroup维护

在上一篇kubelet创建POD(https://www.jianshu.com/p/16f7dbccfadd)文中,在最后的syncPod()处理中,对POD的status完成生成和PATCH之后,就会为POD创建Cgroup路径了

我们具体看下是怎么创建的

// 先创建`PodContainerManager`
pcm := kl.containerManager.NewPodContainerManager()
    
      // 非终止状态的POD
    if !kl.podIsTerminated(pod) {
        // 如果是第一次出现的POD,第一次出现的POD不会有Running状态
        firstSync := true
        for _, containerStatus := range apiPodStatus.ContainerStatuses {
            if containerStatus.State.Running != nil {
                firstSync = false
                break
            }
        }
        // 针对kubelet重启的情况,已经存在Cgroup路径并且Running状态的容器需要立即重启
        podKilled := false
        if !pcm.Exists(pod) && !firstSync {
            if err := kl.killPod(pod, nil, podStatus, nil); err == nil {
                podKilled = true
            } 
        }
        // 没被重启的容器并且不是运行一次的容器,需要维持其cgroup路径
        if !(podKilled && pod.Spec.RestartPolicy == v1.RestartPolicyNever) {
                       // 检查POD的cgroup路径是否存在
            if !pcm.Exists(pod) { 
                                // 不存在的需要先更新一次`besteffort`和`burstable`这两种QoS类型的资源统计
                if err := kl.containerManager.UpdateQOSCgroups(); err != nil {
                }
                                // 再创建这个POD的cgroup路径,并进行资源限制
                if err := pcm.EnsureExists(pod); err != nil {
                }
            }
        }
    }

我们先看下创建PodContainerManager

func (cm *containerManagerImpl) NewPodContainerManager() PodContainerManager {
          // CgroupsPerQOS 默认就是true
    if cm.NodeConfig.CgroupsPerQOS {
        return &podContainerManagerImpl{
            qosContainersInfo: cm.GetQOSContainersInfo(),
            subsystems:        cm.subsystems,
            cgroupManager:     cm.cgroupManager,
            // 默认值 -1
            podPidsLimit: cm.ExperimentalPodPidsLimit,
            // 默认值 true
            enforceCPULimits: cm.EnforceCPULimits,
            // 默认值 100ms
            cpuCFSQuotaPeriod: uint64(cm.CPUCFSQuotaPeriod / time.Microsecond),
        }
    }
    return &podContainerManagerNoop{
        cgroupRoot: cm.cgroupRoot,
    }
}

可以看到本质和前面的ContainerManager或者QosContainerManager是一样的

再看下检测POD的cgroup路径

func (m *podContainerManagerImpl) Exists(pod *v1.Pod) bool {
        // 获取POD的cgroup名称
    podContainerName, _ := m.GetPodContainerName(pod)

        // 和前面一样的检测方式,检测这个cgroup名称对应的路径是否存在
    return m.cgroupManager.Exists(podContainerName)
}

func (m *podContainerManagerImpl) GetPodContainerName(pod *v1.Pod) (CgroupName, string) {
        // 获取POD的QoS
    podQOS := v1qos.GetPodQOS(pod)
    // 不同QoS对应不同的CgroupRoot
    var parentContainer CgroupName
    switch podQOS {
    case v1.PodQOSGuaranteed:
        parentContainer = m.qosContainersInfo.Guaranteed
    case v1.PodQOSBurstable:
        parentContainer = m.qosContainersInfo.Burstable
    case v1.PodQOSBestEffort:
        parentContainer = m.qosContainersInfo.BestEffort
    }

        // 获取POD的UID,然后拼接前缀pod
    podContainer := GetPodCgroupNameSuffix(pod.UID)

    // 将名称 `pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa`也拼接到CgroupRoot上去
    cgroupName := NewCgroupName(parentContainer, podContainer)
    // Get the literal cgroupfs name
    cgroupfsName := m.cgroupManager.Name(cgroupName)

    return cgroupName, cgroupfsName
}

可以看到就是再QoS的CgroupRoot的基础上扩展了pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa这一串和POD的UID相关的路径

这样处理后,不同QoS的POD对应的路径就不同了

  • Guaranteed这种QoS类型的POD,对应的CgroupRoot是kubepods,在这里处理后CgroupRoot变成了[kubepods, pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa],然后转换为systemd风格后,路径变为kubepods.slice/kubepods-pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa.slice

  • Burstable这种QoS类型的POD,对应的CgroupRoot是[kubepods, kubepods-burstable],在这里处理后CgroupRoot变成了[kubepods, ,kubepods-burstable, pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa],然后转换为systemd风格后,路径变为kubepods.slice/kubepods-burstable.slice/kubepods-pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa.slice

  • BestEffort这种QoS类型的POD,对应的CgroupRoot是[kubepods, kubepods-besteffort],在这里处理后CgroupRoot变成了[kubepods, kubepods-besteffort, pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa],然后转换为systemd风格后,路径变为kubepods.slice/kubepods-besteffort.slice/kubepods-pod9fc575d3_a7d2_4054_bf55_02b58ac99bfa.slice

然后就是创建POD的CgroupRoot路径了

func (m *podContainerManagerImpl) EnsureExists(pod *v1.Pod) error {
        // 获取POD对应的CgroupRoot
    podContainerName, _ := m.GetPodContainerName(pod)
    // 检查路径是否存在
    alreadyExists := m.Exists(pod)
    if !alreadyExists {
        // Create the pod container
        containerConfig := &CgroupConfig{
            Name:               podContainerName,
            ResourceParameters: ResourceConfigForPod(pod, m.enforceCPULimits, m.cpuCFSQuotaPeriod),
        }
                 // 默认值是-1
        if m.podPidsLimit > 0 {
            containerConfig.ResourceParameters.PidsLimit = &m.podPidsLimit
        }
                // 创建这个CgroupRoot路径,并设置资源限额
        if err := m.cgroupManager.Create(containerConfig); err != nil {
            return fmt.Errorf("failed to create container for %v : %v", podContainerName, err)
        }
    }
    return nil
}

其中路径的创建和设置资源限额和前面QoSContainerManager的处理是完全一样的

我们重点看下POD资源是怎么转换为cgroup中的资源的

func ResourceConfigForPod(pod *v1.Pod, enforceCPULimits bool, cpuPeriod uint64) *ResourceConfig {
    // POD中所有容器的request和limit资源求和
    reqs, limits := resource.PodRequestsAndLimits(pod)

    cpuRequests := int64(0)
    cpuLimits := int64(0)
    memoryLimits := int64(0)
    if request, found := reqs[v1.ResourceCPU]; found {
        cpuRequests = request.MilliValue()
    }
    if limit, found := limits[v1.ResourceCPU]; found {
        cpuLimits = limit.MilliValue()
    }
    if limit, found := limits[v1.ResourceMemory]; found {
        memoryLimits = limit.Value()
    }

    // CPU request转换为 CpuShares
    cpuShares := MilliCPUToShares(cpuRequests)
 
        // CPU limit转换为 CpuQuota
    cpuQuota := MilliCPUToQuota(cpuLimits, int64(cpuPeriod))

          // 是否存在容器没有设置limit,获取内存大页的request资源
    memoryLimitsDeclared := true
    cpuLimitsDeclared := true
    hugePageLimits := map[int64]int64{}
    for _, container := range pod.Spec.Containers {
        if container.Resources.Limits.Cpu().IsZero() {
            cpuLimitsDeclared = false
        }
        if container.Resources.Limits.Memory().IsZero() {
            memoryLimitsDeclared = false
        }
        containerHugePageLimits := HugePageLimits(container.Resources.Requests)
        for k, v := range containerHugePageLimits {
            if value, exists := hugePageLimits[k]; exists {
                hugePageLimits[k] = value + v
            } else {
                hugePageLimits[k] = v
            }
        }
    }

    // determine the qos class
    qosClass := v1qos.GetPodQOS(pod)

    // 根据QoS返回资源限额
    result := &ResourceConfig{}
    if qosClass == v1.PodQOSGuaranteed {
        result.CpuShares = &cpuShares
        result.CpuQuota = &cpuQuota
        result.CpuPeriod = &cpuPeriod
        result.Memory = &memoryLimits
    } else if qosClass == v1.PodQOSBurstable {
        result.CpuShares = &cpuShares
        if cpuLimitsDeclared {
            result.CpuQuota = &cpuQuota
            result.CpuPeriod = &cpuPeriod
        }
        if memoryLimitsDeclared {
            result.Memory = &memoryLimits
        }
    } else {
        shares := uint64(MinShares)
        result.CpuShares = &shares
    }
    result.HugePageLimit = hugePageLimits
    return result
}

POD的资源限额和QoS类型有关

  • Guaranteed类型的POD,cpu request转换为CpuShares、cpu limit 转换为 CpuQuota、cpu时钟周期固定为100ms、内存limit转换为 Memory

  • Burstable类型的POD,cpu request转换为CpuShares、如果所有容器都设置了cpu和内存 limit,则也会设置相应的值

  • BestEffort类型的POD,固定CpuShares为最小值2

我们以一个例子总结说明下

假设我这有个如下的POD资源限额

 resources:
          limits:
            cpu: 100m
            memory: 50Mi
          requests:
            cpu: 100m
            memory: 50Mi

可以看到这是个Guaranteed类型的POD,那么会有下面的资源设置

#1. cgroup路径应该为这个
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice#

#2. CpuShares应该为102,100*1024/1000=102    1024/1000前面已经提过了是转换比例
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice# cat cpu.shares 
102

#3. CpuQuota应该为10000, 100*100000/1000=10000    100000/1000 是转换比例,对应的是CPU的100ms周期内容器只能使用10ms的CPU时间
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice# cat cpu.cfs_quota_us 
10000
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice# cat cpu.cfs_period_us 
100000

#4. 内存limit应该为52428800byte,50*1024*1024=52428800 byte
root@ubuntu:/sys/fs/cgroup/memory/kubepods.slice/kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice# cat memory.limit_in_bytes 
52428800

#5. 没有内存大页限制
root@ubuntu:/sys/fs/cgroup/hugetlb/kubepods.slice/kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice# cat hugetlb.2MB.limit_in_bytes
0
root@ubuntu:/sys/fs/cgroup/hugetlb/kubepods.slice/kubepods-pod3001e7c4_5ad8_4bc5_831f_12ecdc006a09.slice# cat hugetlb.1GB.limit_in_bytes
0

POD的资源限额和QoS类型有关

  • Guaranteed类型的POD,cpu request转换为CpuShares、cpu limit 转换为 CpuQuota、cpu时钟周期固定为100ms、内存limit转换为 Memory

  • Burstable类型的POD,cpu request转换为CpuShares、如果所有容器都设置了cpu和内存 limit,则也会设置相应的值

  • BestEffort类型的POD,固定CpuShares为最小值2

我们以一个例子总结说明下

假设我这有个如下的POD资源限额

resources:
          limits:
            cpu: 200m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 100Mi

可以看到这是个Burstable类型的POD,那么会有下面的资源设置

#1. cgroup路径应该为这个
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1c6e95d0_1af7_4665_80f6_59e8fe5d04f6.slice#

#2. CpuShares应该为102,100*1024/1000=102    1024/1000前面已经提过了是转换比例
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1c6e95d0_1af7_4665_80f6_59e8fe5d04f6.slice# cat cpu.shares
102

#3. CpuQuota应该为20000, 200*100000/1000=10000    100000/1000 是转换比例,对应的是CPU的100ms周期内容器只能使用20ms的CPU时间
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1c6e95d0_1af7_4665_80f6_59e8fe5d04f6.slice# cat cpu.cfs_quota_us 
20000
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1c6e95d0_1af7_4665_80f6_59e8fe5d04f6.slice# cat cpu.cfs_period_us 
100000

#4. 内存limit应该为209715200byte,200*1024*1024=52428800 byte
root@ubuntu:/sys/fs/cgroup/memory/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1c6e95d0_1af7_4665_80f6_59e8fe5d04f6.slice# cat memory.limit_in_bytes 
209715200

假设我这有个如下的POD资源限额,即request和limit都没设置

resources:

可以看到这是个BestEffort类型的POD,那么会有下面的资源设置

#1. cgroup路径应该为这个
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod26fdf191_388d_45dc_a948_4d6aae3fe21c.slice#

#2. CpuShares应该为102,100*1024/1000=102    1024/1000前面已经提过了是转换比例
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod26fdf191_388d_45dc_a948_4d6aae3fe21c.slice# cat cpu.shares
2

#3. CpuQuota不限制
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod26fdf191_388d_45dc_a948_4d6aae3fe21c.slice# cat cpu.cfs_quota_us 
-1
root@ubuntu:/sys/fs/cgroup/cpu/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod26fdf191_388d_45dc_a948_4d6aae3fe21c.slice# cat cpu.cfs_period_us 
100000

#4. 内存limit不限制
root@ubuntu:/sys/fs/cgroup/memory/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod26fdf191_388d_45dc_a948_4d6aae3fe21c.slice# cat memory.limit_in_bytes 
9223372036854771712

附录

上面设置的CPU资源可能会有点难理解,我们这里加多下解释:

  • cpu.shares 对应的是CPU软限制,它的值相当于是权重,在CPU较忙时会按照这个权重分配CPU时间片,CPU不忙时它也可以使用超过该权重的CPU时间

  • BestEffort类型的POD设置的权重都是2,权重很低,调度优先级就低

  • cpu.cfs_quota_us 对应的是CPU硬限制,它的值是相对于cpu.cfs_period_us的,即在CPU的调度周期内能够执行多长时间,在进程使用较多CPU时,会被强行抑制,到下个周期才能再分配时间片

上面有个逻辑去维护/proc/$PID/oom_score_adj的值一直为-999;

这个作用是啥呢?其实就是防止kubelet进程被OOM干掉,设置的值越小越不会被OOM kill;参考(https://learning-kernel.readthedocs.io/en/latest/mem-management.html

相关文章

网友评论

      本文标题:kubelet中的Cgroup维护笔记

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