CGroups 以及在 K8s 中的应用 - 内存
c
group
s(
control groups
,
控制组群
) 是 Linux 内核的一个功能,用来限制、控制与分离一个进程组的资源(如CPU、内存、磁盘输入输出等)。它是由 Google 的两位工程师进行开发的,自 2008 年 1 月正式发布的 Linux 内核 v2.6.24 开始提供此能力。
cgroup
s到目前为止,有两个大版本, 即 v1 和 v2 。
cgroups 可以限制、记录、隔离进程组所使用的物理资源(包括: CPU 、 memory 、 IO 等),为容器实现虚拟化提供了基本保证,是构建 docker 、 containerd 、 ku bernetes 等一系列容器服务的基石。
上一节中,我们对一些基本概念进行了介绍,本节我们针对 cgroups 对内存资源的限制进行简单的测试。
作者:董卫国,中国移动云能力中心软件研发工程师,专注于云原生领域。
cgroups v1 版本测试我们当前的测试环境为 centos 7.9 ,内核为 5.4 版本,如下所示:
cgoups资源限制测试
创建一个名为 test 01 的 cgroups 组,并对内存进行资源限制,如下所示
现在我们对当前会话限制了 1 00 M 的内存,然后我们 在网上找到了 一个 c 函数 , 用来测试 内存 申请的效果 ,如下所示
编译执行,测试如下:
可以看到申请超过甚至接近 1 00 M 内存的时候,都容易 发生 OOM ,说明 cgroups 起到了限制内存使用的作用,查看 /var /log/message 中可以看到对应的日志信息,如下所示:
我们申请了 1 00 M 内存,为什么只能用到不到 9 8 M 呢,我们查看内存统计值
查看统计值发现 cache和rss仅使用了9 7 M 多一点,这基本符合我们的预期,但是剩下的内存去哪儿了?进一步查阅文献我们知道,还有一部分内存使用统计在 memory.kmem.usage_in_bytes(显示当前内核内存用量) ,而没有统计到 memory.stat ,查看结果,如下所示:
可以看到除了 9 7 M 程序运行使用的内存,还有 2M 内核多内核使用的内存,怪不得使用 9 8 M 内存时会触发 OOM 。这样一来,事情就基本符合我们的预期了。
K u bernets 中的资源限制
我们尝试 在 Kubernetes 中启动一个 deploy ment , 启动后登录到节点上查看 pod 的内存资源是如何被限制的 。
下面 的测试是使用 sealos 部署的 v1.25.9 版本的 Kubernetes 进行的。
P od 变为 Running 状态后,登录到 Pod 所在节点执行如下 shell 命令查看容器 cgroupstest 的内存限制
容器的资源限制如下所示
下面我们进一步查看限制效果,查看容器 cgroupstest 的内存限制
可以看到结果如下
可以看到当前容器 cgroupstest 的内存限制为 1 28 M 。
cgroups v 2 版本测试简单 测试使用 c groups v2
创建一个名为 test 01 的 cgroup s 组,并对内存进行资源限制,如下所示
上面的 shell 命令,我们对这个 shell 限制了 1 00 M 的内存。
使用前面测试 v 1 版本的 cg roups 时候引入 c 函数申请内存,编译执行,测试如下:
可以看到当前申请 9 7 M 内存时,就会触发 oom , cgroups v2 版本可以通过查看 memory.current 文件统计当前的内存使用。
kubernetes中 基于 cgroups v2 的 Me mory Qos
K ub ernetes提供QoS(Quality of Service,服务质量)服务质量管理,基于每个 Pod 中容器的资源请求和限制为 Pod 设置 QoS 类 ,并依赖 QoS 类来决定从遇到 节点压力 的 Node 中驱逐哪些 Pod。可选的 QoS 类有 Guaranteed、Burstable 和 BestEffort。 当一个 Node 耗尽资源时,Kubernetes 将首先驱逐在该 Node 上运行的 BestEffort Pod, 然后是 Burstable Pod,最后是 Guaranteed Pod。当这种驱逐是由于资源压力时, 只有超出资源请求的 Pod 才是被驱逐的候选对象。
下面对各级别分别进行相应说明:
Guaranteed :pod中的所有容器都必须对cpu和memory同时设置limits,如果有一个容器要设置requests,那么所有容器都要设置,并设置参数同limits一致,那么这个pod的QoS就是Guaranteed级别。注:如果一个容器只指明limit而未设定request,则request的值等于limit值。
Burstable : pod中只要有一个容器的requests和limits的设置不相同,该pod的QoS即为Burstable。
BestEffort :如果对于全部的resources来说requests与limits均未设置,该pod的QoS即为BestEffort。
Memory QoS 是 Kubernetes 在新版本里基于 cgroup v2 实现的新特性,这个特性始于 1 .22 版本,至今仍属于 Alpha 版本。该特性本质上还是封装 cgroup v2 的能力,来提供更加弹性的内存管理。 cgroups v1 实现 了 CPU 和 Memory 隔离 , CPU 属于可压缩资源,在节点 CPU 紧张时,任务只会 受到限制( throttl e ) ,不至于被 kill。但 Memory 属于 不可压缩资源,在节点内存不足时会触发 OOM ,导致容器重启,可是 这对部分 Burstable Pod 是不可接受的。而且某些内存消耗型 Pod 在创建和运行中会短时申请大量内存,导致节点内存瞬间飙升 , 节点内存紧张可能会导致内存高敏 Pod 在申请内存时 存在问题 ,从而影响服务质量。cgroups v1 无法解决此类 Memory QoS 问题。但幸运的是,cgroups v2 的 memory controller 为我们提供了参数 减缓此问题 。
我们 回顾 下 cgroup v2 中内存 限制 的配置项, cgroups v2 一共有如下四种内存分配和保护实现(即 4 个可修改的文件):
Ø memory.min :内存的硬保护机制,这是当前 cgroups 必须保留的最小内存分配,在任何情况下都不会回收这块内存。如果满足不了 memory.min 的内存分配,系统会触发 OOM 。
Ø memory.low :是一种 BestEffort 的内存保护机制,是一种软保护实现,也即不能保证这块内存一定不被回收。当无法从其他未受保护的 cgroups 中回收内存时,会回收这块的内存。
Ø memory.high :内存使用的瓶颈限制,系统会尝试将 cgroups 内存使用率控制在此阈值之下,而并不是直接 OOM Kill 。这样的设计就非常适合一些 Memory Burst 的场景,可以像 CPU 一样做一定程度的资源压缩。
Ø memory.max :内存使用的最大限制,如果 cgroups 内存使用达到这个值,会触发 OOM 。
可以看到通过设置 memory.high 的值,我们可以环节一些特殊场景下的节点内存压力, 我们需要在kubelet中加入如下启动参数,才可以启用 Memory QoS 的机制:
--feature-gates=MemoryQoS=true
在这里多提一下,我们上面的测试中一直使用centos7升级内核和systemd的方式使其支持cgroups v2,但是这里开启此特性后,测试后pod无法启动,报错如下
同样的 ku bernets 和容器运行时版本,更换 ubuntu 22 和 rockylinux8 测试,都能正常启动服务进行测试,前面我们将 systemd 升级到了 2 34 版本,但还不够高, docker 的官方文档中推荐使用 2 47 版本以上,也许 是 systemd 或者其他软件包版本的影响,笔者这里没有在 centos 7 上进一步的尝试,此处的测试使用 rockylinux8 (内核版本 4 .18 )。
我们启动一个 deploy ment ,配置资源申请如下所示:
登录到 pod 所在节点查看该 pod 包含的容器内存限制如下所示:
可以看到,此处容器的内存最大值 memory.max 为 1 28 M ,即内存的 limit 值,最小值 memory.min 为 60 M ,即内存的 request 值。
me mory . hgih 的计算公式如下:
如 limit 没有设置,则按照如下公式得出
此时配置的 memory . high 为 102 M ,是内存 limit 的 0 .8 ,符合预期。上面的 memory thrittling factor 值通过 kube let config 中的 memoryThrottlingFactor 参数配置。
总结本文对 cgroups v1 和 v2 版本 对内存资源的限制 进行了介绍和基础功能的验证,并对其在 Kubernetes 中的使用进行了一些初步的调研。
对于 Kubernetes 中的 QoS 设计,本文也未能进一步的进行了解和学习, Kubernetes 根据服务或者说 pod 的资源请求进行调度,使用 cgroups 进行 pod 占用资源的约束,根据 QoS 等级提供服务质量保障。在笔者的理解里, Qo S 是目标, cgroups 只是其中的一种工具。
最后,由于笔者能力和时间所限,难免存在一些错漏,也缺乏对一些功能的源码级分析,对于不同系统和内核版本在不同硬件和参数配置下对不同资源类型的测试也存在不充分的地方,还请谅解。
参考1) 一篇搞懂容器技术的基石: cgroup , https://zhuanlan.zhihu.com/p/434731896
2) Linux cgroups :深入理解 cgroups v1 版本 , https://www.testerfans.com/archives/linux-cgroups-learn-more
3) Linux CFS and task group , https://mechpen.github.io/posts/2020-04-27-cfs-group/index.html
4) 深入理解 Kubernetes 资源限制:CPU , https://icloudnative.io/posts/understanding-resource-limits-in-kubernetes-cpu-time/
5) CFS Bandwidth Control , https://www.kernel.org/doc/html/v5.4/scheduler/sched-bwc.html?highlight=cpu%20cfs_quota_us
6) Cgroup 详解 , https://juejin.cn/post/6921299245685276686
7) Cgroup 限制内存与节点的删除 , https://chaochaogege.com/2019/09/11/6/
8) 容器内存分析 , https://blog.csdn.net/u012986012/article/details/105291831
9) 控制组详解 , https://blog.gmem.cc/cgroup-illustrated
10) Cgroup V2 and writeback support , http://hustcat.github.io/cgroup-v2-and-writeback-support/
11) Linux CGroup 基础 , https://wudaijun.com/2018/10/linux-cgroup/
12) 详解 Cgroup V2 , https://zorrozou.github.io/docs/%E8%AF%A6%E8%A7%A3Cgroup%20V2.html
13) centos 7 升级 systemd , https://lqingcloud.cn/post/systemd-01/
14) 容器 -cgroup-blkio-cgroup , http://119.23.219.145/posts/%E5%AE%B9%E5%99%A8-cgroup-blkio-cgroup/
15) 打通 IO 栈:一次编译服务器性能优化实战 , https://mp.weixin.qq.com/s?__biz=Mzg2OTc0ODAzMw==&mid=2247502495&idx=1&sn=26950b22cba383b14052b441cd356516&source=41#wechat_redirect
16) pod 资源限制和 QoS 探索 , https://www.zerchin.xyz/2021/01/31/pod%E8%B5%84%E6%BA%90%E9%99%90%E5%88%B6%E5%92%8CQoS%E6%8E%A2%E7%B4%A2/
点击【阅读原文】到达 Kubernetes 网站。