Kubernetes_Scheduler_资源调度

news2024/11/26 7:27:24

文章目录

  • 一、前言
  • 二、k8s 资源模型
    • 2.1 Node 资源抽象
      • 2.1.1 Capacity
      • 2.1.2 Allocatable
      • 2.1.3 Allocated
    • 2.2 Node 资源切分(预留)
      • 2.2.1 SystemReserved
      • 2.2.2 KubeReserved
      • 2.2.3 EvictionThreshold(驱逐门限)
      • 2.2.4 Allocatable
    • 2.3 kubelet 相关配置参数
  • 三、k8s cgroup 层次设计
    • 3.1 Linux cgroup 概要
    • 3.2 两种 cgroup runtime driver
    • 3.3 四种 cgroup 层级
      • 3.1.1 Container 级别 cgroup
      • 3.1.2 Pod 级别 cgroup
      • 3.1.3 QoS 级别 cgroup
      • 3.1.4 Node 级别 cgroup
    • 3.3 四种 cgroup(v1) 层级配置目录
      • 3.3.1 kubelet cgroup root
      • 3.3.2 /kubepods(node 级别配置)
      • 3.3.3 QoS 级别配置
      • 3.3.4 Pod 级别配置
      • 3.3.5 Container 级别配置
  • 四、问题讨论
    • 4.1 requests/limits 对应到具体 cgroup 配置文件
      • 4.1.1 CPU
      • 4.1.2 Memory
      • 4.1.3 其他
    • 4.2 requests/limits 与调度的关系
      • 4.2.1 根据 requests 调度
      • 4.2.2 根据 limits 限额(enforcement)
    • 4.3 kubelet 计算 pod requets/limits 的过程
    • 4.4 资源使用量超出 limits 的后果
    • 4.5 Node 资源紧张时,按 QoS 分配资源比例
  • 五、k8s cgroup 相关代码实现
    • 5.1 调用栈和重要结构体
      • 5.1.1 源码containerManagerImpl.Start()
      • 5.1.2 检查几种必须要支持的 cgroup 资源类型
    • 5.2 kubelet 启动配置
    • 5.3 通过 k8s metrics API 查看 requests/limits 信息
    • 5.4 定期获取 pod CpuLoad 信息
    • 5.5 通过 container pid 查看 cgroup 信息
  • 六、尾声

一、前言

对于一个编排系统来说,资源管理至少需要考虑以下几个方面:

  1. 资源模型的抽象;包括,
    (1) 有哪些种类的资源,例如,CPU、内存等;
    (2) 如何用数据结构表示这些资源;

  2. 资源的调度
    (1) 如何描述一个 workload 的资源申请(spec),例如,“该容器需要 4 核和 12GB~16GB 内存”;
    (2) 如何描述一台 node 当前的资源分配状态,例如已分配/未分配资源量,是否支持超分等;
    (3) 调度算法:如何根据 workload spec 为它挑选最合适的 node;

  3. 资源的限额(capacity enforcement)
    (1) 如何确保 workload 使用的资源量不超出预设范围(从而不会影响其他 workload);
    (2) 如何确保 workload 和系统/基础服务的限额,使二者互不影响。

k8s 是目前最流行的容器编排系统,那它是如何解决这些问题的呢?

二、k8s 资源模型

对照上面几个问题,我们来看下 k8s 是怎么设计的:

  1. 资源模型:

(1) 抽象了 cpu/memory/device/hugepage 等资源类型;
(2) 抽象了 node 概念;

  1. 资源调度:

(1) 抽象了 request 和 limit 两个概念,分别表示一个容器所需要的最小(request)和最大(limit)资源量;
(2) 调度算法根据各 node 当前可供分配的资源量(Allocatable),为容器选择合适的 node; 注意,k8s 的调度只看 requests,不看 limits。

  1. 资源 enforcement:
    (1) 使用 cgroup 在多个层面确保 workload 使用的最大资源量不超过指定的 limits。
    (2) --system-reserved-cgroup=“” 和 --kube-reserved-cgroup=“” 默认为空,表示不创建,也就是系统组件和 pod 之间并没有严格隔离

一个资源申请(容器)的例子:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: busybox
    image: busybox
    resources:
      limits:
        cpu: 500m
        memory: "400Mi"
      requests:
        cpu: 250m
        memory: "300Mi"
    command: ["md5sum"]
    args: ["/dev/urandom"]

这里面 requests 和 limits 分别表示所需资源的最小和最大值,
(1) CPU 资源的单位 m 是 millicores 的缩写,表示千分之一核, 因此 cpu: 500m 就表示需要 0.5 核;
(2) 内存的单位很好理解,就是 MB、GB 等常见单位。

2.1 Node 资源抽象

$ k describe node <node>
...
Capacity:
  cpu:                          48
  mem-hard-eviction-threshold:  500Mi
  mem-soft-eviction-threshold:  1536Mi
  memory:                       263192560Ki
  pods:                         256
Allocatable:
  cpu:                 46
  memory:              258486256Ki
  pods:                256
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource            Requests     Limits
  --------            --------     ------
  cpu                 800m (1%)    7200m (15%)
  memory              1000Mi (0%)  7324Mi (2%)
  hugepages-1Gi       0 (0%)       0 (0%)
...

分别来看下这几个部分。

2.1.1 Capacity

这台 node 的总资源量(可以简单理解为物理配置), 例如上面的输出显示,这台 node 有 48CPU、256GB 内存等等。

2.1.2 Allocatable

可供 k8s 分配的总资源量, 显然,Allocatable 不会超过 Capacity,例如上面看到 CPU 就少了 2 个,只剩下 46 个。

2.1.3 Allocated

这台 node 目前已经分配出去的资源量,注意其中的 message 也说了,node 可能会超分,所以加起来可能会超过 Allocatable,但不会超过 Capacity。

Allocatable 不超过 Capacity,这个概念上也是很好理解的; 但具体是哪些资源被划出去,导致 Allocatable < Capacity 呢?

2.2 Node 资源切分(预留)

由于每台 node 上会运行 kubelet/docker/containerd 等 k8s 相关基础服务, 以及 systemd/journald 等操作系统本身的进程,因此并不是一台 node 的所有资源都能给 k8s 创建 pod 用。 所以,k8s 在资源管理和调度时,需要把这些基础服务的资源使用量和 enforcement 单独拎出来。

为此,k8s 提出了 Node Allocatable Resources 提案,上面的 Capacity、Allocatable 等术语正是从这里来的。几点说明:

(1) 如果 Allocatable 可用,调度器会用 Allocatable,否则会用 Capacity;
(2) 用 Allocatable 是不超分,用 Capacity 是超分(overcommit);

计算公式:[Allocatable] = [NodeCapacity] - [KubeReserved] - [SystemReserved] - [HardEvictionThreshold]

分别来看下这几种类型。

2.2.1 SystemReserved

操作系统的基础服务,例如 systemd、journald 等,在 k8s 管理之外。 k8s 不能管理这些资源的分配,但是能管理这些资源的限额(enforcement),后面会看到。

2.2.2 KubeReserved

k8s 基础设施服务,包括 kubelet/docker/containerd 等等。 跟上面系统服务类似,k8s 不能管理这些资源的分配,但是能管理这些资源的限额(enforcement),后面会看到。

2.2.3 EvictionThreshold(驱逐门限)

当 node memory/disk 等资源即将耗尽时,kubelet 就开始按照 QoS 优先级(besteffort/burstable/guaranteed)驱逐 pod, eviction 资源就是为这个目的预留的。 更多信息。

2.2.4 Allocatable

可供 k8s 创建 pod 使用的资源。

以上就是 k8s 的基本资源模型。下面再看几个相关的配置参数。

2.3 kubelet 相关配置参数

资源预留(切分)相关的 kubelet 命令参数:

--system-reserved=""
--kube-reserved=""
--qos-reserved=""
--reserved-cpus=""

也可以通过 kubelet 配置文件,例如,

$ cat /etc/kubernetes/kubelet/config
...
systemReserved:
  cpu: "2"      # 上面 describe node 输出中, Allocatable 比 Capacity 少 2 个 CPU 
  memory: "4Gi" # 以及少 4GB 内存

是否需要对这些 reserved 资源用专门的 cgroup 来做资源限额,以确保彼此互不影响:

--system-reserved-cgroup=""
--kube-reserved-cgroup=""

默认都是不启用。实际上也很难做到完全隔离。导致的后果就是系统进程和 pod 进程有可能相互影响, 例如,截至 v1.26,k8s 还不支持 IO 隔离,所以宿主机进程(例如 logrotate)IO 飙高, 或者某个 pod 进程执行 java dump 时,会影响这台 node 上所有 pod。

关于 k8s 资源模型就先介绍到这里,接下来进入本文重点,k8s 是如何用 cgroup 来限制 container、pod、基础服务等 workload 的资源使用量的(enforcement)。

三、k8s cgroup 层次设计

3.1 Linux cgroup 概要

cgroup 是 Linux 内核基础设施,可以限制、记录和隔离进程组(process groups) 使用的资源量(CPU、内存、IO 等)。

cgroup 有两个版本,v1 和 v2,二者的区别可参考 Control Group v2 (cgroupv2 权威指南)(KernelDoc, 2021)。 目前 k8s 默认使用的是 cgroup v1,因此本文以 v1 为主。

cgroup v1 能管理很多种类的资源,

$ mount | grep cgroup
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)

k8s/kubelet 中只用到了 cpu/memory/pid/hugetlb 等几种类型。

3.2 两种 cgroup runtime driver

k8s 通过配置 cgroup 来限制 container/pod 能使用的最大资源量。这个配置有两种实现方式, 在 k8s 中称为 cgroup runtime driver:

  1. cgroupfs这种比较简单直接,kubelet 往 cgroup 文件系统中写 limit 就行了。这也是目前 k8s 的默认方式。

  2. systemd所有 cgroup-writing 操作都必须通过 systemd 的接口,不能手动修改 cgroup 文件。 适用于 k8s cgroup v2 模式。

3.3 四种 cgroup 层级

如下图所示,cgroup in k8s

图片

kubelet 会在 node 上创建了 4 个 cgroup 层级,从 node 的 root cgroup (一般都是 /sys/fs/cgroup)往下:

(1) Node 级别:针对 SystemReserved、KubeReserved 和 k8s pods 分别创建的三个 cgroup;
(2) QoS 级别:在 kubepods cgroup 里面,又针对三种 pod QoS 分别创建一个 sub-cgroup:
(3) Pod 级别:每个 pod 创建一个 cgroup,用来限制这个 pod 使用的总资源量;
(4) Container 级别:在 pod cgroup 内部,限制单个 container 的资源使用量。

为了使理解方便,接下来我们从最底层往最上层讲起。

3.1.1 Container 级别 cgroup

前面已经看到过,在创建 pod 使,可以直接在 container 级别设置 requests/limits:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: busybox
    image: busybox
    resources:
      limits:
        cpu: 500m
        memory: "400Mi"
      requests:
        cpu: 250m
        memory: "300Mi"
    command: ["md5sum"]
    args: ["/dev/urandom"]

kubelet 在这里做的事情很简单:创建 container 时,将 spec 中指定 requests/limits 传给 docker/containerd 等 container runtime。换句话说,底层能力都是 container runtime 提供的,k8s 只是通过接口把 requests/limits 传给了底层。

具体实现见 generateContainerConfig(): 生成一个 ContainerConfig,然后通过 CRI 传给 container runtime。

cgroup 控制资源的底层能力都是 container runtime 提供的,k8s 只是通过接口把 requests/limits 传给了底层。

3.1.2 Pod 级别 cgroup

顾名思义,这种级别的 cgroup 是针对单个 pod 设置资源限额的。 这里有一个很明显但又很容易被忽视的问题:k8s requets/limits 模型的 requests/limits 是声明在 container 上,而不是 pod 上。 一个 pod 经常有多个容器,那 pod 的 requests/limits 就是对它的 containers 简单累加得到吗?

并不是。这是因为,

(1) 某些资源是这个 pod 的所有 container 共享的;
(2) 每个 pod 也有自己的一些开销,例如 sandbox container;
(3) Pod 级别还有一些内存等额外开销;

因此,为了防止一个 pod 的多个容器使用资源超标,k8s 引入了 引入了 pod-level cgroup,每个 pod 都有自己的 cgroup。 后面会介绍如何根据 containers requests/limits 计算一个 pod 的 requests/limits。

3.1.3 QoS 级别 cgroup

实际的业务场景需要我们能根据优先级高低区分几种 pod。例如,

(1) 高优先级 pod:无论何时,都应该首先保证这种 pod 的资源使用量;
(2) 低优先级 pod:资源充足时允许运行,资源紧张时优先把这种 pod 赶走,释放出的资源分给中高优先级 pod;
(3) 中优先级 pod:介于高低优先级之间,看实际的业务场景和需求。

k8s 针对这种需求提供了 cgroups-per-qos 选项:

// pkg/kubelet/apis/config/types.go

// Enable QoS based Cgroup hierarchy: top level cgroups for QoS Classes
// And all Burstable and BestEffort pods are brought up under their specific top level QoS cgroup.
CgroupsPerQOS bool

如果设置了 kubelet --cgroups-per-qos=true 参数(默认为 true), 就会将所有 pod 分成三种 QoS,优先级从高到低:Guaranteed > Burstable > BestEffort。 三种 QoS 是根据 requests/limits 的大小关系来定义的:

(1) Guaranteed定义: requests == limits, requests != 0, 即 正常需求 == 最大需求,换言之 spec 要求的资源量必须得到保证,少一点都不行;
(2) Burstable定义: requests < limits, requests != 0, 即 正常需求 < 最大需求,资源使用量可以有一定弹性空间;
(3) BestEffort定义: request == limits == 0, 创建 pod 时不指定 requests/limits 就等同于设置为 0。

kubelet 对这种 BestEffort pod 将尽力而为;有好处也有坏处:

• 好处:node 的资源充足时,这种 pod 能使用的资源量没有限制;
• 坏处:这种 pod 的 QoS 优先级最低,当 node 资源不足时,最先被驱逐。

每个 QoS 对应一个子 cgroup,设置该 QoS 类型的所有 pods 的总资源限额, 三个 cgroup 共同构成了 kubepods cgroup。 每个 QoS cgroup 可以认为是一个资源池,每个池子内的 pod 共享资源。

3.1.4 Node 级别 cgroup

所有的 k8s pod 都会落入 kubepods cgroup,因此所有 k8s pods 占用的资源都已经能够通过 cgroup 来控制,剩下的就是那些 k8s 组件自身和操作系统基础服务所占用的资源了,即 KubeReserved 和 SystemReserved。 k8s 无法管理这两种服务的资源分配,但能管理它们的限额:有足够权限给它们创建并设置 cgroup 就行了。 但是否会这样做需要看 kubelet 配置,

--kube-reserved-cgroup=""
--system-reserved-cgroup=""

默认为空,表示不创建,也就是系统组件和 pod 之间并没有严格隔离。 但概念上二者始终是存在的,因此前面几节介绍的内容构成了 k8s cgroup 的四个层级(cgroup in k8s):

cgroup in k8s

3.3 四种 cgroup(v1) 层级配置目录

接下来看不同 cgroup 分别对应到 node 上哪些目录。

3.3.1 kubelet cgroup root

k8s 的 cgroup 路径都是相对于它的 cgroup root 而言的。 cgroup root 是个 kubelet 配置项,默认为空,表示使用底层 container runtime 的 cgroup root,一般是 /sys/fs/cgroup/。

3.3.2 /kubepods(node 级别配置)

cgroup v1 是按 resource controller 类型来组织目录的, 因此,/kubepods 会按 resource controller 对应到 /sys/fs/cgroup/{resource controller}/kubepods/,例如:

cpu资源: /sys/fs/cgroup/cpu/kubepods/ [此时 resource controller 是 cpu]
memory资源: /sys/fs/cgroup/memory/kubepods/ [此时 resource controller 是 memory]

前面已经介绍了每台 k8s node 的资源切分, 其中 Allocatable 资源量就是写到 kubepods 对应 cgroup 文件中, 例如 allocatable cpu 写到 /sys/fs/cgroup/kubepods/cpu.share。 这一工作是在 kubelet containerManager Start() 中完成的。

allocatable cpu 写到 /sys/fs/cgroup/kubepods/cpu.share
在这里插入图片描述
在这里插入图片描述

/sys/fs/cgroup/cpu 目录是cpu子系统的根目录,其中包含了多个控制组。每个控制组可以设置和调整其下属进程(或任务)的CPU资源使用限制和优先级。
/sys/fs/cgroup/cpu/kubepods.slice目录是Kubernetes中使用的控制组,用于将Pod进行分组管理。每个Pod对应一个控制组。
/sys/fs/cgroup/cpu/system.slice目录是系统服务的控制组,每个系统服务对应一个控制组。
/sys/fs/cgroup/cpu/user.slice目录是用户级别的控制组,每个用户级控制组对应一个控制组。

在这里插入图片描述
在这里插入图片描述

看内存

在这里插入图片描述

在这里插入图片描述

内存差 100Mi ,这个内存被 kubelet 服务使用了
在这里插入图片描述

3.3.3 QoS 级别配置

QoS cgroup 是 /kubepods 的 sub-cgroup,因此路径是 /kubepods/{qos}/,具体来说,

对于 Guaranteed:直接就是 /sys/fs/cgroup/{controller}/kubepods/;
对于 Burstable: 默认 /sys/fs/cgroup/{controller}/kubepods/burstable/;
对于 BestEffort: 默认 /sys/fs/cgroup/{controller}/kubepods/besteffort/;

eg1: Guaranteed 类型 Pod 没有单独的 cgroup 子目录。这是因为这种类型的 pod 都设置了 limits, 就无需再引入一层 wrapper 来防止这种类型的 pods 的资源使用总量超出限额。‘’

3.3.4 Pod 级别配置

Pod 配置在 QoS cgroup 配置的下一级,

对于 Guaranteed Pod:cgroup文件默认路径 /sys/fs/cgroup/{controller}/kubepods/{pod_id}/;
对于 Burstable Pod:cgroup文件默认路径 /sys/fs/cgroup/{controller}/kubepods/burstable/{pod_id}/;
对于 BestEffort Pod:cgroup文件默认路径/sys/fs/cgroup/{controller}/kubepods/besteffort/{pod_id}/。

3.3.5 Container 级别配置

Container 级别配置文件在 pod 的下一级:

对于 Guaranteed container:cgroup文件默认路径 /sys/fs/cgroup/{controller}/kubepods/{pod_id}/{container_id}/;

对于 Burstable container:cgroup文件默认路径 /sys/fs/cgroup/{controller}/kubepods/burstable/{pod_id}/{container_id}/;

对于 BestEffort container:cgroup文件默认路径 /sys/fs/cgroup/{controller}/kubepods/besteffort/{pod_id}/{container_id}/。

四、问题讨论

4.1 requests/limits 对应到具体 cgroup 配置文件

4.1.1 CPU

Spec 里的 CPU requests/limits 一般都是以 500m 这样的格式表示的,其中 m 是千分之一个 CPU, kubelet 会将它们转换成 cgroup 支持的单位,然后写入几个 cpu. 开头的配置文件。其中,

• requests 经过转换之后会写入 cpu.share, 表示这个 cgroup 最少可以使用的 CPU;
• limits 经过转换之后会写入 cpu.cfs_quota_us, 表示这个 cgroup 最多可以使用的 CPU;

在这里插入图片描述

4.1.2 Memory

内存的单位在 requests/limits 和在 cgroup 配置文件中都是一样的,所以直接写入 cgroup 内存配置文件。 limits 写入的是 memory.limit_in_bytes。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4.1.3 其他

略。

4.2 requests/limits 与调度的关系

requests 和 limits 分别和 k8s 里的一个重要概念相关,下面分别讨论一下。

4.2.1 根据 requests 调度

调度只看 requests: 如果一个 node 的 Allocatable 剩余资源大于 pod 的 requests ,就允许这个 pod 调度到这台 node 上。 limits 是限额(enforcement)用的,确保资源不会用超,在调度时用不到。

• requests/limits 都是可选字段,设置与否,会导致这个 pod 进入不同的 QoS 类别;
• 虽然资源是在 container 级别设置的,但 QoS 是 pod 级别的。

memory和cpu requests/limits 底层本质是 cgroup , cpu 500m 表示 500/1000 ,就是 0.5
requests/limits 都是可选字段,设置与否,会导致这个 pod 进入不同的 QoS 类别;
用于调度的只有 request , 没有 limit (request用于限额和调度,limit 仅用于限额)

4.2.2 根据 limits 限额(enforcement)

资源的隔离目前是用 cgroup 来实现的,它有两个版本,目前 k8s 默认用的 v1,本文内容也以 v1 为主。

4.3 kubelet 计算 pod requets/limits 的过程

前面已经介绍过,k8s spec 里的 requests/limits 是打在 container 上的,并没有打在 pod 上。 因此 pod 的 requests/limits 需要由 kubelet 综合统计 pod 的所有 container 的 request/limits 计算得到。 CPU 和内存的计算方式如下:

# 计算 pod 的 CPU request,通过 cpu.shares 能实现最小值控制
pod<pod_id>/cpu.shares            = sum(pod.spec.containers.resources.requests[cpu])
# 计算 pod 的 CPU limit,通过 cpu.cfs_quota_us 能实现最大值控制
pod<pod_id>/cpu.cfs_quota_us      = sum(pod.spec.containers.resources.limits[cpu])

# 计算 pod 的 Memory limit
pod<pod_id>/memory.limit_in_bytes = sum(pod.spec.containers.resources.limits[memory])

(1) 对于CPU:如果某个 container 的 cpu 字段,只设置了 request 没设置 limit, 则 pod 将只设置 cpu.shares,不设置 cpu.cfs_quota_us。

(2) :如果所有 container 都没有设置 cpu request/limit(等效于 requests==limits==0),这种Pod是BestEffort,则将 pod cpu.share 将设置为 k8s 定义的最小值 2 ( pod<UID>/cpu.shares = MinShares # const value 2)。 这种 pod 在 node 空闲时最多能使用整个 node 的资源;但 node 资源紧张时,也最先被驱逐。

具体的计算过程:ResourceConfigForPod()。

4.4 资源使用量超出 limits 的后果

对于CPU来说:
(1) Container CPU 使用量可能允许超过 limit,也可能不允许;
(2) Container CPU 使用量超过 limit 之后,并不会被干掉。

对于Memory来说:
(1) 如果 container 的内存使用量超过 request,那这个 node 内存不足时, 这个 Pod 可能会被驱逐;
(2) Container 的内存使用量超过 limit 时,这个Pod会被干掉(OOMKilled)。如果可重启,kubelet 会重启这个Pod。

4.5 Node 资源紧张时,按 QoS 分配资源比例

Kubelet 寻求最大资源效率,因此默认没有设置资源限制, Burstable and BestEffort pods 可以使用足够的的空闲资源。 但只要 Guaranteed pods 需要资源,这些低优先级的 pods 就必须及时释放资源。 如何释放呢?

对于 CPU 等 compressible resources,可以通过 CPU CFS shares,针对每个 QoS 分配一定比例的资源,确保在 CPU 资源受限时,每个 pod 能获得它所申请的 CPU 资源。

对于 burstable cgroup,

/burstable/cpu.shares            = max(sum(Burstable pods cpu requests), MinShares) # MinShares == 2

burstableLimit                  := allocatable — qosMemoryRequests[PodQOSGuaranteed]*percentReserve/100
/burstable/memory.limit_in_bytes = burstableLimit

这段源码表示:对于 burstable 类型的 Pod ,这里 cpu request 是2 核 (2000m, m表示千分之一的意思),这里 memory limit 存放在 memory.limit_in_bytes 里面

对于 bestEffort cgroup,

/besteffort/cpu.shares            = MinShares # MinShares == 2

bestEffortLimit                  := burstableLimit — qosMemoryRequests[PodQOSBurstable]*percentReserve/100
/besteffort/memory.limit_in_bytes = bestEffortLimit

这段源码表示:对于 bestEffort 类型的 Pod ,这里 cpu request 是2 核 (2000m, m表示千分之一的意思),这里 memory limit 存放在 memory.limit_in_bytes 里面

这几个 cgroup 初始化之后, kubelet 会调用 UpdateCgroups() 方法来定期更新这三个 cgroup 的 resource limit。

五、k8s cgroup 相关代码实现

5.1 调用栈和重要结构体

kubelet 中所有 cgroup 操作都由内部的 containerManager 模块完成。

实现主要在 pkg/kubelet/cm。

启动时的调用栈:cmd->kubelet->NewContainerManger

containerManagerImpl.Start
  |-cm.cpuManager.Start
  |-cm.memoryManager.Start
  |
  |-cmd.setupNode(activePods)
  |  |-validateSystemRequirements(cm.mountUtil)
  |  |  |-expectedCgroups := sets.NewString("cpu", "cpuacct", "cpuset", "memory")
  |  |-cm.createNodeAllocatableCgroups()
  |  |-cm.qosContainerManager.Start(cm.GetNodeAllocatableAbsolute, activePods)
  |  |-cm.enforceNodeAllocatableCgroups()
  |  |-cm.manage system containers (not managed by kubelet) if needed
  |  |-cm.periodicTasks = append(cm.periodicTasks, func() { // manage kubelet itself if it's deploed as a container
  |  |   ensureProcessInContainerWithOOMScore
  |  |   cont := getContainer(os.Getpid()) // return the cgroup id field in "cat /proc/<pid>/cgroup" output
  |  |   cm.KubeletCgroupsName = cont      // this container's cgroup id
  |  |})
  |
  |-cm.deviceManager.Start
  
重要内部结构体 struct containerManagerImpl,

// https://github.com/kubernetes/kubernetes/blob/v1.26.0/pkg/kubelet/cm/container_manager_linux.go#L100

type containerManagerImpl struct {
    cadvisorInterface cadvisor.Interface
    systemContainers []*systemContainer        // External containers being managed.
    periodicTasks    []func()                  // Tasks that are run periodically
    subsystems       *CgroupSubsystems         // Holds all the mounted cgroup subsystems
    cgroupManager     CgroupManager            // Interface for cgroup management

    capacity         v1.ResourceList           // Capacity of this node.
    internalCapacity v1.ResourceList           // Capacity of this node, including internal resources.
    cgroupRoot       CgroupName                // Absolute cgroupfs path, e.g. /sys/fs/cgroup

    qosContainerManager QOSContainerManager     // Interface for QoS cgroup management
    deviceManager       devicemanager.Manager   // Interface for exporting and allocating devices reported by device plugins.
    cpuManager          cpumanager.Manager      // Interface for CPU affinity management.
    memoryManager       memorymanager.Manager   // Interface for memory affinity management.
    topologyManager     topologymanager.Manager // Interface for Topology resource co-ordination
    draManager          dra.Manager             // Interface for Dynamic Resource Allocation management.
}

以上可以看出,k8s 1.26.0 实现了对下列资源的支持,

QoS
Device
CPU
Memory

但是还没有实现对 IO 的支持,所以我们还无法通过 k8s cgroup v1/v2 来做 IO 的隔离。

5.1.1 源码containerManagerImpl.Start()

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

    // Initialize CPU manager
    cm.cpuManager.Start(cpumanager.ActivePodsFunc(activePods), sourcesReady, podStatusProvider, runtimeService, containerMap)

    // Initialize memory manager
    containerMap := buildContainerMapFromRuntime(ctx, runtimeService)
    cm.memoryManager.Start(memorymanager.ActivePodsFunc(activePods), sourcesReady, podStatusProvider, runtimeService, containerMap)

    cm.nodeInfo = node

    if localStorageCapacityIsolation {
        rootfs := cm.cadvisorInterface.RootFsInfo()
        for rName, rCap := range cadvisor.EphemeralStorageCapacityFromFsInfo(rootfs) {
            cm.capacity[rName] = rCap
        }
    }

    cm.validateNodeAllocatable() // Ensure that node allocatable configuration (system/kube/eviction reserved) is valid.
    cm.setupNode(activePods) // Setup the node

    if hasEnsureStateFuncs { // Run ensure state functions every minute.
        go wait.Until(func() {
            for _, cont := range cm.systemContainers {
                cont.ensureStateFunc(cont.manager)
            }
        }, time.Minute, wait.NeverStop)
    }

    go wait.Until(func() {
        for _, task := range cm.periodicTasks { task() }
    }, 5*time.Minute, wait.NeverStop)

    cm.deviceManager.Start(devicemanager.ActivePodsFunc(activePods), sourcesReady)
}

func (cm *containerManagerImpl) setupNode(activePods ActivePodsFunc) error {
    validateSystemRequirements(cm.mountUtil)

    // Setup top level qos containers only if CgroupsPerQOS flag is true
    if cm.NodeConfig.CgroupsPerQOS {
        cm.createNodeAllocatableCgroups()
        cm.qosContainerManager.Start(cm.GetNodeAllocatableAbsolute, activePods)
    }

    // Enforce Node Allocatable (if required)
    cm.enforceNodeAllocatableCgroups()

    systemContainers := []*systemContainer{} // containers not managed by kubelet

    if cm.SystemCgroupsName != "" {
        cont := newSystemCgroups(cm.SystemCgroupsName)
        cont.ensureStateFunc = func(manager cgroups.Manager) error {
            return ensureSystemCgroups("/", manager)
        }
        systemContainers = append(systemContainers, cont)
    }

    if cm.KubeletCgroupsName != "" {
        cont := newSystemCgroups(cm.KubeletCgroupsName)
        cont.ensureStateFunc = func(_ cgroups.Manager) error {
            return ensureProcessInContainerWithOOMScore()
        }
        systemContainers = append(systemContainers, cont)
    } else {
        cm.periodicTasks = append(cm.periodicTasks, func() {
            ensureProcessInContainerWithOOMScore(os.Getpid(), cm.KubeletOOMScoreAdj)
            cont := getContainer(os.Getpid()) // return the cgroup id field in "cat /proc/<pid>/cgroup" output
            cm.KubeletCgroupsName = cont      // this container's cgroup id
        })
    }

    cm.systemContainers = systemContainers
}

5.1.2 检查几种必须要支持的 cgroup 资源类型

启动时会检查几种必须要支持的 cgroup 类型:

// https://github.com/kubernetes/kubernetes/blob/v1.26.0/pkg/kubelet/cm/container_manager_linux.go#L161


// checks if the required cgroups subsystems are mounted.
// As of now, only 'cpu' and 'memory' are required.
// cpu quota is a soft requirement.
func validateSystemRequirements(mountUtil mount.Interface) (features, error) {
    ...
    expectedCgroups := sets.NewString("cpu", "cpuacct", "cpuset", "memory")
}

5.2 kubelet 启动配置

cgroup 相关的几个参数,可以通过命令行或者 kubelet config 文件配置:

查看配置文件:

root@node: $ grep -i cgroup /etc/kubernetes/config
kubeletCgroups: ""
systemCgroups: ""
cgroupRoot: "" # if empty, default to "/" in the code
cgroupsPerQOS: true
cgroupDriver: cgroupfs

5.3 通过 k8s metrics API 查看 requests/limits 信息

$ kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods/smoke-pod-01" | jq -C .
{
  "kind": "PodMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "smoke-pod-01",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods/smoke-pod-01",
  },
  "containers": [
    {
      "name": "netperf",
      "usage": {
        "cpu": "0",
        "memory": "1808Ki"
      }
    }
  ]
}

5.4 定期获取 pod CpuLoad 信息

kubelet INFO 日志中会定期打印类似下面的信息:

Task stats for "/sys/fs/cgroup/cpu,cpuacct/kubepods/pod<pod_id>/<container_id>": 
{NrSleeping:1 NrRunning:0 NrStopped:0 NrUninterruptible:0 NrIoWait:0}

代码:

// vendor/github.com/google/cadvisor/utils/cpuload/netlink/reader.go

// Returns instantaneous number of running tasks in a group.
// * Caller can use historical data to calculate cpu load.
// * path is an absolute filesystem path for a container under the CPU cgroup hierarchy.
// NOTE: non-hierarchical load is returned. It does not include load for subcontainers.
func (r *NetlinkReader) GetCpuLoad(name string, path string) (info.LoadStats, error) {
    cfd := os.Open(path)
    defer cfd.Close()

    stats := getLoadStats(r.familyID, cfd, r.conn)
    klog.V(4).Infof("Task stats for %q: %+v", path, stats)
    return stats, nil
}

5.5 通过 container pid 查看 cgroup 信息

# 查看指定进程pid的cgroup列表
$ cat /proc/1606/cgroup
11:net_cls:    /kubepods/besteffort/pod<pod_id>/<ctn_id>
10:cpu,cpuacct:/kubepods/besteffort/pod<pod_id>/<ctn_id>
9:memory:      /kubepods/besteffort/pod<pod_id>/<ctn_id>
8:blkio:       /kubepods/besteffort/pod<pod_id>/<ctn_id>
7:devices:     /kubepods/besteffort/pod<pod_id>/<ctn_id>
6:hugetlb:     /kubepods/besteffort/pod<pod_id>/<ctn_id>
5:pids:        /kubepods/besteffort/pod<pod_id>/<ctn_id>
4:freezer:     /kubepods/besteffort/pod<pod_id>/<ctn_id>
3:cpuset:      /kubepods/besteffort/pod<pod_id>/<ctn_id>
2:perf_event:  /kubepods/besteffort/pod<pod_id>/<ctn_id>
1:name=systemd:/kubepods/besteffort/pod<pod_id>/<ctn_id>
0::/

# 查看指定进程pid的指定cgroup
$ cat /sys/fs/cgroup/memory/kubepods/burstable/pod<pod_id>/memory.limit_in_bytes
1073741824

六、尾声

memory和cpu requests/limits 底层本质是 cgroup , cpu 500m 表示 500/1000 ,就是 0.5
requests/limits 都是可选字段,设置与否,会导致这个 pod 进入不同的 QoS 类别;
用于调度的只有 request , 没有 limit (request用于限额和调度,limit 仅用于限额)

参考资料 解析Kubernetes中的资源管理:抽象、调度与限额

failed to find database snapshot file (snap: snapshot file doesn’t exist)
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/903729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树搜索

✅<1>主页&#xff1a;我的代码爱吃辣&#x1f4c3;<2>知识讲解&#xff1a;数据结构——二叉搜索树☂️<3>开发环境 &#xff1a;Visual Studio 2022&#x1f4ac;<4>前言&#xff1a;在之前的我们已经学过了普通二叉树&#xff0c;了解了基本的二叉树…

Spring(四):Spring Boot 的创建和使用

关于Spring之前说到&#xff0c;Spring只是思想&#xff08;核心是IOC、DI和AOP&#xff09;&#xff0c;而具体的如何实现呢&#xff1f;那就是由Spring Boot 来实现&#xff0c;Spring Boot究竟是个啥呢&#xff1f; 什么是Spring Boot&#xff0c;为什么要学Spring Boot Sp…

Multi-UAV Disaster Environment Coverage Planning with Limited-Endurance

Multi-UAV Disaster Environment Coverage Planning with Limited-Endurance 有限续航时间下的多无人机灾害环境覆盖规划 定义问题将初始地图转换为热图产生优化路径 基于 已知的灾区热图&#xff0c;设计一个多无人机全覆盖搜索的路径规划方法。可以在无人机有限能量约束下探索…

dirsearch目录扫描工具的使用

文章目录 工具下载及环境准备查看帮助信息进行目录扫描 官方介绍 &#xff1a;An advanced command-line tool designed to brute force directories and files in webservers, AKA web path scanner 一个高级命令行工具&#xff0c;用于暴力破解网络服务器中的目录和文件&…

韦东山-电子量产工具项目:业务系统

代码结构 所有代码都已通过测试跑通&#xff0c;其中代码结构如下&#xff1a; 一、include文件夹 1.1 common.h #ifndef _COMMON_H #define _COMMON_Htypedef struct Region {int iLeftUpX; //区域左上方的坐标int iLeftUpY; //区域左下方的坐标int iWidth; //区域宽…

Apache DolphinScheduler 支持使用 OceanBase 作为元数据库啦!

DolphinScheduler是一个开源的分布式任务调度系统&#xff0c;拥有分布式架构、多任务类型、可视化操作、分布式调度和高可用等特性&#xff0c;适用于大规模分布式任务调度的场景。目前DolphinScheduler支持的元数据库有Mysql、PostgreSQL、H2&#xff0c;如果在业务中需要更好…

【视觉SLAM入门】5.2. 2D-3D PNP 3D-3D ICP BA非线性优化方法 数学方法SVD DLT

"养气之学&#xff0c;戒之躁急" 1. 3D-2D PNP1.1 代数法1.1.1 DLT(直接线性变换法)1.1.2. P3P 1.2 优化法BA (Bundle Adjustment)法 2. 3D-3D ICP2.1 代数法2.1.1 SVD方法 2.2 优化(BA)法2.2.2 非线性优化方法 前置事项&#xff1a; 1. 3D-2D PNP 该问题描述为&am…

使用swoole实现实时消息推送给客户端

一. 测试服务端 //测试服务端public function testServer(){$server new Server(192.168.0.144, 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP);$server->on(request, function ($request, $response) {$response->header(Content-Type, text/plain);$response->end("He…

【操作系统】虚拟内存相关分段分页页面置换算法

虚拟内存是什么&#xff1f; 【进程地址空间虚拟地址空间C/C程序地址空间就是那个4G的空间】 虚拟内存是操作系统内核为了对进程地址空间进行管理&#xff0c;而设计的一个逻辑意义上的内存空间概念。在程序运行过程中&#xff0c;虚拟内存中需要被访问的部分会被映射到物理内…

Mock平台-08开发:项目管理(四)编辑功能和Component抽离

【Mock平台】为系列测试开发教程&#xff0c;从0到1编码带你一步步使用Spring Boot 和 Antd React框架完成搭建一个测试工具平台&#xff0c;希望作为一个实战项目对各位的测试开发学习之路有帮助&#xff0c;大奇一个专注测试技术干货原创与分享的家伙。 本篇重点&#xff1a;…

HCIP生成树STP总结

STP生成树 网桥的4个选举 根网桥&#xff1a; 有且仅有一台&#xff0c;且由BPDU中的桥ID来决定 桥ID 网桥优先级&#xff08;0-65535公有&#xff09; 默认32768 MAC地址&#xff08;只有…

湘潭大学 湘大 XTU OJ 1055 整数分类 题解(非常详细)

链接 整数分类 题目 Description 按照下面方法对整数x进行分类&#xff1a;如果x是一个个位数&#xff0c;则x属于x类&#xff1b;否则将x的各位上的数码累加&#xff0c;得到一个新的x&#xff0c;依次迭代&#xff0c;可以得到x的所属类。比如说24&#xff0c;246&#…

漏洞指北-VulFocus靶场专栏-高级01

漏洞指北-VulFocus靶场专栏-高级01 高级001 &#x1f338;骑士cms任意代码执行&#xff08;CVE-2020-35339&#xff09;&#x1f338;step1&#xff1a;进入页面&#xff0c;登入后台step2 系统——网站配置——网站域名step3 中国蚁剑连接 高级002 &#x1f338;Django SQL注入…

安装ps找不到vcruntime140_1.dll怎么回事?有哪些解决方法

安装ps找不到vcruntime140_1.dll怎么回事&#xff1f;这可能是因为您的计算机Visual C Redistributable包损坏&#xff0c;其中包含vcruntime140_1.dll文件。VCRuntime140_1.dll是一个重要的动态链接库文件&#xff0c;它包含了Visual C运行时所需的一些函数和资源。当这个文件…

银行客户关系管理系统springboot财务金融进销存java jsp源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 银行客户关系管理系统springboot 系统有1权限&#x…

周易卦爻解读笔记——未济

第六十四卦未济 火水未济 离上坎下 未济卦由否卦所变&#xff0c;否卦六二与九五换位&#xff0c;象征尚未完成。 天地否 未济卦和既济卦既是错卦又是覆卦&#xff0c;这也是最后一卦&#xff0c;序卦传【物不可穷也&#xff0c;故受之以未济终焉】 未济卦象征尚未完成&…

NSSCTF之Misc篇刷题记录(14)

[SWPUCTF] 2021新生赛之Crypto篇刷题记录① [UUCTF 2022 新生赛]王八快跑[安洵杯 2020]BeCare4[HDCTF 2023]ExtremeMisc NSSCTF平台&#xff1a;https://www.nssctf.cn/ PS&#xff1a;记得所有的flag都改为NSSCTF [UUCTF 2022 新生赛]王八快跑 小游戏 小乌龟跑过线就有 fla…

BDA初级分析——SQL语句应用基础练习题

1、请检查OLYMPIC表中是否存在重复国家? SELECT Team,COUNT(*) AS 重复次数 FROM Olympic GROUP BY Team HAVING COUNT(*) > 1;SELECT COUNT(Team),COUNT(DISTINCT(Team)) FROM Olympic; 2、将OLYMPIC表中Armenia(ARM)的奖牌总数更新为14 UPDATE Olympic SET total_medals …

【C语言】使用C语言,实现九九乘法表(另附Python、Java、JavaScript实现方式)

文章目录 1. C语言实现1.1 思路1.2 代码实现 3.其他语言实现3.1 Python实现3.2 Java实现3.3 JavaScript实现 1. C语言实现 1.1 思路 九九乘法表图示&#xff1a; 思路如下&#xff1a;定义两层for循环即可实现九九乘法表 一共有9层&#xff0c;所以要定义一个变量i&#xff…

记录首次面试2023-08-18

人生第一次面试&#xff0c;大概一个小时左右。没有问我C的&#xff0c;上来一个数据库事务&#xff0c;虽然没有复习&#xff0c;但是还是能够记住一些&#xff0c;主要问的一些事务的隔离级别&#xff0c;以及都有什么作用&#xff0c;我是举例回答的&#xff0c;客户端A和客…