从零开始写 Docker(十九)---增加 cgroup v2 支持

feat-cgroup-v2.png

本文为从零开始写 Docker 系列第十九篇,添加对 cgroup v2 的支持。


完整代码见:https://github.com/lixd/mydocker
欢迎 Star

推荐阅读以下文章对 docker 基本实现有一个大致认识:

  • 核心原理:深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs
  • 基于 namespace 的视图隔离:探索 Linux Namespace:Docker 隔离的神奇背后
  • 基于 cgroups 的资源限制
    • 初探 Linux Cgroups:资源控制的奇妙世界
    • 深入剖析 Linux Cgroups 子系统:资源精细管理
    • Docker 与 Linux Cgroups:资源隔离的魔法之旅
  • 基于 overlayfs 的文件系统:Docker 魔法解密:探索 UnionFS 与 OverlayFS
  • 基于 veth pair、bridge、iptables 等等技术的 Docker 网络:揭秘 Docker 网络:手动实现 Docker 桥接网络

开发环境如下:

root@mydocker:~# lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.2 LTS
Release:	20.04
Codename:	focal
root@mydocker:~# uname -r
5.4.0-74-generic

注意:需要使用 root 用户

1. 概述

本篇主要添加对 cgroup v2 的支持,自动识别当前系统 cgroup 版本。

2. 实现

判断 cgroup 版本

通过下面这条命令来查看当前系统使用的 Cgroups V1 还是 V2

stat -fc %T /sys/fs/cgroup/

如果输出是cgroup2fs 那就是 V2,就像这样

root@tezn:~# stat -fc %T /sys/fs/cgroup/
cgroup2fs

如果输出是tmpfs 那就是 V1,就像这样

[root@docker cgroup]# stat -fc %T /sys/fs/cgroup/
tmpfs

Go 实现如下:

const (unifiedMountpoint = "/sys/fs/cgroup"
)var (isUnifiedOnce sync.OnceisUnified     bool
)// IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode.
func IsCgroup2UnifiedMode() bool {isUnifiedOnce.Do(func() {var st unix.Statfs_terr := unix.Statfs(unifiedMountpoint, &st)if err != nil && os.IsNotExist(err) {// For rootless containers, sweep it under the rug.isUnified = falsereturn}isUnified = st.Type == unix.CGROUP2_SUPER_MAGIC})return isUnified
}

cgroup v2 支持

使用 cgroup v2 过程和 v1 基本一致

  • 1)创建子 cgroup
  • 2)配置 cpu、memory 等 Subsystem
  • 3)配置需要限制的进程
创建子 cgroup

创建子 cgroup,则是在 cgroup 根目录下创建子目录即可,对 cgroup v2 来说,根目录就是 /sys/fs/cgroup

const UnifiedMountpoint = "/sys/fs/cgroup"// getCgroupPath 找到cgroup在文件系统中的绝对路径
/*
实际就是将根目录和cgroup名称拼接成一个路径。
如果指定了自动创建,就先检测一下是否存在,如果对应的目录不存在,则说明cgroup不存在,这里就给创建一个
*/
func getCgroupPath(cgroupPath string, autoCreate bool) (string, error) {// 不需要自动创建就直接返回cgroupRoot := UnifiedMountpointabsPath := path.Join(cgroupRoot, cgroupPath)if !autoCreate {return absPath, nil}// 指定自动创建时才判断是否存在_, err := os.Stat(absPath)// 只有不存在才创建if err != nil && os.IsNotExist(err) {err = os.Mkdir(absPath, constant.Perm0755)return absPath, err}return absPath, errors.Wrap(err, "create cgroup")
}
配置 Subsystem

以 cpu 为例,只需要在 cpu.max 中添加具体限制即可,就像这样:

echo 5000 10000 > cpu.max

含义是在10000的CPU时间周期内,有5000是分配给本cgroup的,也就是本cgroup管理的进程在单核CPU上的使用率不会超过50%

具体实现如下:

func (s *CpuSubSystem) Set(cgroupPath string, res *resource.ResourceConfig) error {if res.CpuCfsQuota == 0 {return nil}subCgroupPath, err := getCgroupPath(cgroupPath, true)if err != nil {return err}// cpu.cfs_period_us & cpu.cfs_quota_us 控制的是CPU使用时间,单位是微秒,比如每1秒钟,这个进程只能使用200ms,相当于只能用20%的CPU// v2 中直接将 cpu.cfs_period_us & cpu.cfs_quota_us 统一记录到 cpu.max 中,比如 5000 10000 这样就是限制使用 50% cpuif res.CpuCfsQuota != 0 {// cpu.cfs_quota_us 则根据用户传递的参数来控制,比如参数为20,就是限制为20%CPU,所以把cpu.cfs_quota_us设置为cpu.cfs_period_us的20%就行// 这里只是简单的计算了下,并没有处理一些特殊情况,比如负数什么的if err = os.WriteFile(path.Join(subCgroupPath, "cpu.max"), []byte(fmt.Sprintf("%s %s", strconv.Itoa(PeriodDefault/Percent*res.CpuCfsQuota), PeriodDefault)), constant.Perm0644); err != nil {return fmt.Errorf("set cgroup cpu share fail %v", err)}}return nil
}
配置需要限制的进程

只需要将 pid 写入 cgroup.procs 即可

echo 1033 > cgroup.procs

Go 实现如下:

func (s *CpuSubSystem) Apply(cgroupPath string, pid int) error {return applyCgroup(pid, cgroupPath)
}func applyCgroup(pid int, cgroupPath string) error {subCgroupPath, err := getCgroupPath(cgroupPath, true)if err != nil {return errors.Wrapf(err, "get cgroup %s", cgroupPath)}if err = os.WriteFile(path.Join(subCgroupPath, "cgroup.procs"), []byte(strconv.Itoa(pid)),constant.Perm0644); err != nil {return fmt.Errorf("set cgroup proc fail %v", err)}return nil
}
移除

删除 cgroup 下的子目录即可移除

func (s *CpuSubSystem) Remove(cgroupPath string) error {subCgroupPath, err := getCgroupPath(cgroupPath, false)if err != nil {return err}return os.RemoveAll(subCgroupPath)
}

兼容V1和V2

只需要在创建 CgroupManager 时判断当前系统 cgroup 版本即可

func NewCgroupManager(path string) CgroupManager {if IsCgroup2UnifiedMode() {log.Infof("use cgroup v2")return NewCgroupManagerV2(path)}log.Infof("use cgroup v1")return NewCgroupManagerV1(path)
}

3. 测试

cgroup v1

到 cgroup v1 环境进行测试

root@mydocker:~/mydocker# ./mydocker run -mem 10m -cpu 10 -it -name cgroupv1 busybox /bin/sh
{"level":"info","msg":"createTty true","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"resConf:\u0026{10m 10 }","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"lower:/var/lib/mydocker/overlay2/3845479957/lower image.tar:/var/lib/mydocker/image/busybox.tar","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/var/lib/mydocker/overlay2/3845479957/lower,upperdir=/var/lib/mydocker/overlay2/3845479957/upper,workdir=/var/lib/mydocker/overlay2/3845479957/work /var/lib/mydocker/overlay2/3845479957/merged]","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"use cgroup v1","time":"2024-04-14T13:23:19+08:00"}
{"level":"error","msg":"apply subsystem:cpuset err:set cgroup proc fail write /sys/fs/cgroup/cpuset/mydocker-cgroup/tasks: no space left on device","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"command all is /bin/sh","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"init come on","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"Current location is /var/lib/mydocker/overlay2/3845479957/merged","time":"2024-04-14T13:23:19+08:00"}
{"level":"info","msg":"Find path /bin/sh","time":"2024-04-14T13:23:19+08:00"}

根据日志可知,当前使用的时 cgroup v1

{"level":"info","msg":"use cgroup v1","time":"2024-04-14T13:23:19+08:00"}

执行以下命令测试memory分配

yes > /dev/null

可以看到,过会就被 OOM Kill 了

/ # yes > /dev/null
Killed

执行以下命令 跑满 cpu

while : ; do : ; done &

确实被限制到 10%了

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND              
1212 root      20   0    1332     68      4 R   9.9   0.0   0:02.30 sh  

cgroup v2

到 cgroup v2 环境进行测试,或者参考以下步骤切换到 v2 版本。

切换到 cgroup v2

你还可以通过修改内核 cmdline 引导参数在你的 Linux 发行版上手动启用 cgroup v2。

如果你的发行版使用 GRUB,则应在 /etc/default/grub 下的 GRUB_CMDLINE_LINUX 中添加 systemd.unified_cgroup_hierarchy=1, 然后执行 sudo update-grub

编辑 grub 配置

vi /etc/default/grub

内容大概是这样的:

GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

对最后一行GRUB_CMDLINE_LINUX进行修改

GRUB_CMDLINE_LINUX="quiet splash systemd.unified_cgroup_hierarchy=1"

然后执行以下命令更新 GRUB 配置

sudo update-grub

最后查看一下启动参数,确认配置修改上了

cat /boot/grub/grub.cfg | grep "systemd.unified_cgroup_hierarchy=1"

然后就是重启

reboot

重启后查看,不出意外切换到 cgroups v2 了

root@cgroupv2:~# stat -fc %T /sys/fs/cgroup/
cgroup2fs
测试
./mydocker run -mem 10m -cpu 10 -it -name cgroupv2 busybox /bin/sh
root@mydocker:~/mydocker# ./mydocker run -mem 10m -cpu 10 -it -name cgroupv2 busybox /bin/sh
{"level":"info","msg":"createTty true","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"resConf:\u0026{10m 10 }","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"lower:/var/lib/mydocker/overlay2/3526930704/lower image.tar:/var/lib/mydocker/image/busybox.tar","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/var/lib/mydocker/overlay2/3526930704/lower,upperdir=/var/lib/mydocker/overlay2/3526930704/upper,workdir=/var/lib/mydocker/overlay2/3526930704/work /var/lib/mydocker/overlay2/3526930704/merged]","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"use cgroup v2","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"init come on","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"command all is /bin/sh","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"Current location is /var/lib/mydocker/overlay2/3526930704/merged","time":"2024-04-14T13:26:32+08:00"}
{"level":"info","msg":"Find path /bin/sh","time":"2024-04-14T13:26:32+08:00"}

根据日志可知,当前使用的时 cgroup v2

{"level":"info","msg":"use cgroup v2","time":"2024-04-14T13:26:32+08:00"}

执行同样的测试,效果一致,说明 cgroup v2 使用正常。

执行以下命令测试memory分配

yes > /dev/null

可以看到,过会就被 OOM Kill 了

/ # yes > /dev/null
Killed

执行以下命令 跑满 cpu

while : ; do : ; done &

确实被限制到 10%了

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND              
1212 root      20   0    1332     68      4 R   9.9   0.0   0:02.30 sh  

4. 小结

本文主要为 mydocker 添加了 cgroup v2 的支持,根据系统 cgroup 版本自适应切换。


完整代码见:https://github.com/lixd/mydocker
欢迎关注~


**【从零开始写 Docker 系列】**持续更新中,搜索公众号【探索云原生】订阅,文章。


相关代码见 feat-cgroup-v2 分支,测试脚本如下:

需要提前在 /var/lib/mydocker/image 目录准备好 busybox.tar 文件,具体见第四篇第二节。

# 克隆代码
git clone -b feat-cgroup-v2 https://github.com/lixd/mydocker.git
cd mydocker
# 拉取依赖并编译
go mod tidy
go build .
# 测试 
./mydocker run -mem 10m -cpu 10 -it -name cgroupv2 busybox /bin/sh

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

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

相关文章

微软蓝屏”事件暴露了网络安全哪些问题?

📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…

cadence SPB17.4 - allegro - 设置不同网络之间的距离规则

文章目录 cadence SPB17.4 - allegro - 设置不同网络之间的距离规则概述笔记END cadence SPB17.4 - allegro - 设置不同网络之间的距离规则 概述 插座进来的管脚,可能带来高压(有可能用户接错,或者出现浪涌,或者做ESD静电测试&a…

SpringBoot热部署重启关闭(DevTools)

一、DevTools依赖 1、DevTools简介 在Spring Boot项目中,spring-boot-devtools模块提供了多种开发时的便利功能,其中最显著的是restart和livereload特性,它们分别用于应用代码的热重启和前端资源的即时重载。 devtools依赖: &l…

如何在调整节拍时间的过程中保持生产流程的稳定性?

在快节奏的工业生产领域,节拍时间(Takt Time)——即完成一个完整产品所需的标准时间,是维持生产效率和流程稳定性的关键指标。然而,市场需求的波动、技术升级或是生产线的微调,都可能要求我们对节拍时间进行…

Redis-主从模式

目录 前言 一.主从节点介绍 二.配置redis主从结构 二.主从复制 四.拓扑结构 五.数据同步 全量复制(Full Sync Replication) 局部复制(Partial Replication) Redis的学习专栏:http://t.csdnimg.cn/a8cvV 前言 …

docker安装phpMyAdmin

直接安装phpMyAdmin需要有php环境,比较麻烦,总结了使用docker安装方法,并提供docker镜像。 1.docker镜像 见我上传的docker镜像:https://download.csdn.net/download/taotao_guiwang/89595177 2.安装 1).加载镜像 docker load …

AC/DC和DC/DC开关电源的传导和辐射原理

电磁干扰(EMI)始终是开关电源(AC/DC和DC/DC转换器)的潜在问题。如今的电源有很好的电磁发射和抗干扰的能力。但为了满足特定的应用要求,仍要有正确的滤波电路以确保满足标准的要求。 基于AC/DC和DC/DC电源模块的很佳EM…

CentOS7使用yum安装MySQL

废话不多说,直接上干货 1、CentOS7的yum源中默认是没有mysql的,我们先下载mysql的repo源 wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm 2、安装mysql-community-release-el7-5.noarch.rpm包 sudo rpm -ivh mysql-community-r…

商城购物系统

下载在最后 技术栈: ssmmysqljsp 展示: 下载地址: CSDN现在上传有问题,有兴趣的朋友先收藏.正常了贴上下载地址 备注:

Hbase简介和快速入门

一 Hbase简介 1 HBase定义 Apache HBase™ 是以hdfs为数据存储的,一种分布式、可扩展的NoSQL数据库。 2 HBase数据模型 HBase的设计理念依据Google的BigTable论文,论文中对于数据模型的首句介绍。Bigtable 是一个稀疏的、分布式的、持久的多维排序map…

Idea常用快捷键:设置自动导包

Idea设置自动导包 【File】→【Setting】(或使用快捷键【Crlt Shift S】)打开Setting设置。点击【Editor】→【General】→【Auto Import】。勾选自定导包的选项,并确定,如下: Addunambiguousimportsonthefly:添加明确的导入 …

长上下文语言模型与RAPTOR 方法

在科技领域的前沿,长上下文语言模型(Long Context LLMs)和新兴检索方法如RAPTOR 正在引发广泛关注。本文将围绕这些技术展开讨论,并探讨它们在实际应用中的创新性和科技性。 长上下文语言模型的崛起 近几周来,随着新型…

基于springboot+vue+uniapp的戏曲文化苑小程序

开发语言:Java框架:springbootuniappJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包&#…

Java两表查询的方法(一对一,一对多,多对多)

一、配置环境&#xff1a; 首先我们需要Maven环境; 源码&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"…

yandex图标点选验证码YOLOV8识别案例

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 某yandex图标点选验证码如下: 使用过yolov8的小伙伴可能都知道,这种直接打个标注,基本上就可以了,至于问题图片由于不能很好的切割做分类,所以干脆也做成目标…

基于图卷积神经网络(GCN)的高光谱图像分类详细教程(含python代码)

目录 一、背景 二、基于卷积神经网络的代码实现 1、安装依赖库 2、建立图卷积神经网络 3、建立数据的边 4、训练模型 5、可视化 三、项目代码 一、背景 图卷积神经网络&#xff08;Graph Convolutional Networks, GCNs&#xff09;在高光谱图像分类中是一种有效的方法…

CSS实现文本溢出处理

1.单行文本溢出 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…

Dependency Injection: 如何解决依赖注入失败问题

Dependency Injection: 如何解决依赖注入失败问题 &#x1f489; **Dependency Injection: 如何解决依赖注入失败问题 &#x1f489;**摘要引言正文内容1. 依赖注入的基础概念代码示例&#xff1a;构造函数注入 2. 依赖注入失败的常见原因2.1 未能找到依赖的实例2.2 循环依赖2.…

App测试分发的秘密:如何让你的应用程序快速上线

App测试分发的重要性 在移动应用程序的开发过程中&#xff0c;测试分发是一个非常重要的环节。它可以帮助开发者快速地将应用程序推广到目标用户手中&#xff0c;收集反馈&#xff0c;进行bug修复和优化&#xff0c;从而提高应用程序的质量和用户体验。但是&#xff0c;测试分…

linux脚本:自动检测的bash脚本,用于检查linux的系统性能

目录 一、要求 二、脚本介绍 1、脚本内容 2、解释 3、使用方法&#xff1a; &#xff08;1&#xff09;脚本文件 &#xff08;2&#xff09;赋予权限 &#xff08;3&#xff09;执行结果 三、相关命令介绍 1、top &#xff08;1&#xff09;定义 &#xff08;2&…