Docker技术原理之Linux Cgroups(资源限制)

2024-01-20 18:32

本文主要是介绍Docker技术原理之Linux Cgroups(资源限制),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

0.前言

首先要知道一个运行的容器,其实就是一个受到隔离和资源限制的Linux进程——对,它就是一个进程。前面我们讨论了Docker容器实现隔离用到的技术Linux namespace,本篇我们来讨论容器实现资源限制的技术 Linux CGroups。

1.关于Linux CGroups

Linux Cgroups的全称是Linux Control Groups。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽等等。此外,还能够对进程进行优先级设置,以及将进程挂起和恢复等操作。

在Linux中,Cgroups给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下。在我的centos服务器下,用mount指令把它们展示出来:

//CentOS Linux release 7.5.1804
$ mount -t cgroup
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/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)

可以看到,在/sys/fs/cgroup下面有很多诸如cpuset、cpu、 memory这样的子目录,也叫子系统。这些都是我这台机器当前可以被Cgroups进行限制的资源种类。而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。比如,对CPU子系统来说,我们就可以看到如下几个配置文件:

$ ls /sys/fs/cgroup/cpu
cgroup.clone_children  cgroup.sane_behavior  cpu.rt_period_us   cpu.stat       cpuacct.usage_percpu  system.slice
cgroup.event_control   cpu.cfs_period_us     cpu.rt_runtime_us  cpuacct.stat   notify_on_release     tasks
cgroup.procs           cpu.cfs_quota_us      cpu.shares         cpuacct.usage  release_agent         user.slice

2.举个例子(CPU限制)

1) 配置你的控制组
$ cd /sys/fs/cgroup/cpu
$ mkdir testlimit
$ ls testlimit/
cgroup.clone_children  cgroup.procs       cpu.cfs_quota_us  cpu.rt_runtime_us  cpu.stat      cpuacct.usage         notify_on_release
cgroup.event_control   cpu.cfs_period_us  cpu.rt_period_us  cpu.shares         cpuacct.stat  cpuacct.usage_percpu  tasks$ cat /sys/fs/cgroup/cpu/testlimit/cpu.cfs_quota_us
-1
$ cat /sys/fs/cgroup/cpu/testlimit/cpu.cfs_period_us 
100000

你创建的这个目录testlimit就称为一个“控制组”。你会发现,操作系统会在你新创建的目录下,自动生成该子系统对应的资源限制文件。可以看到testlimit控制组里的CPU quota还没有任何限制(即:-1),CPU period则是默认的100000us

配置一个只能使用30%cpu的限制,即长度为cfs_period的一段时间内,只能被分配到总量为cfs_quota的CPU时间。

$ echo 30000 > /sys/fs/cgroup/cpu/testlimit/cpu.cfs_quota_us

至此我们的testlimit控制组就配置好了,它限制进程在100000us里只能使用30000us的cpu时间。只是目前没有将它应用于任何进程。

2) 执行脚本
$ while : ; do : ; done &
[1] 4477

该脚本执行了一个无限循环,可以把cpu吃到100%,可以看到它在后台的进程id是4477,后面限制的时候我们会用到。

通过top查看cpu使用情况:

%Cpu0  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
3) 使用cgroups限制该进程的cpu

执行如下命令,使用刚才配置好的testlimit控制组,限制上面4477号进程的cpu:

$ echo 4477 > /sys/fs/cgroup/cpu/testlimit/tasks 

再次通过top查看cpu使用情况:

%Cpu0  : 30.1 us,  3.0 sy,  0.0 ni, 65.5 id,  1.0 wa,  0.0 hi,  0.3 si,  0.0 st

可以看到使用刚才创建的testlimit控制组,将cpu被限制到了30%左右。

4) 启动一个容器加上CPU时钟周期限制

接下来,我们启动一个容器,并加上cpu限制,然后看看cgroups对应的目录里有没有该容器的限制。

$ docker run -td --cpu-period 100000 --cpu-quota 200000 busybox /bin/sh -c "while : ; do : ; done"
c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f//查看该容器的进程id: 26430
$ ps -x |grep '/bin/sh'
26430 pts/0    Rs+   20:52 /bin/sh -c while : ; do : ; done//查看cgroups
$ cat /sys/fs/cgroup/cpu/docker/c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f/cpu.cfs_period_us 
100000
$ cat /sys/fs/cgroup/cpu/docker/c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f/cpu.cfs_quota_us 
200000
$ cat /sys/fs/cgroup/cpu/docker/c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f/tasks 
26430$ top
%Cpu0  : 50.8 us, 49.2 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

如上所示,可以通过启动容器时返回的容器ID在cgroups中找到对应的限制。

3.对照Docker源码

// New creates and initializes a new containerd server
func New(ctx context.Context, config *Config) (*Server, error) {//...if err := apply(ctx, config); err != nil {return nil, err}//...
}// apply sets config settings on the server process
func apply(ctx context.Context, config *Config) error {if config.OOMScore != 0 {log.G(ctx).Debugf("changing OOM score to %d", config.OOMScore)if err := sys.SetOOMScore(os.Getpid(), config.OOMScore); err != nil {log.G(ctx).WithError(err).Errorf("failed to change OOM score to %d", config.OOMScore)}}if config.Cgroup.Path != "" {cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path))if err != nil {if err != cgroups.ErrCgroupDeleted {return err}if cg, err = cgroups.New(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path), &specs.LinuxResources{}); err != nil {return err}}if err := cg.Add(cgroups.Process{Pid: os.Getpid(),}); err != nil {return err}}return nil
}

在上面代码中,创建容器时会调用apply接口,里面的cgroups.Load调用就会去加载cgroups,cg.Add把创建的容器进程加入到cgroups task中。

4.下一代Linux Cgroups

在Kernel 3.16后,引入了一个叫__DEVEL__sane_behavior的特性(还在开发试验阶段),它可以把所有子系统都挂载到根层级下,只有叶子节点可以存在tasks,非叶子节点只进行资源控制。
The unified control group hierarchy in 3.16

参考

  • 左耳朵耗子-LINUX CGROUP
  • redhat资源管理指南
  • Fixing control groups
  • The unified control group hierarchy in 3.16

这篇关于Docker技术原理之Linux Cgroups(资源限制)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/626926

相关文章

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N