Loading... ## cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如CPU、内存、IO等)的机制。 ### 01.cgroup是的作用: - 资源限制(Resource Limitation):cgroups 可以对进程组使用的资源总额进行限制。memory 子系统可以为进程组设定一个 memory 使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发 OOM(out of memory)。 - 优先级分配(Prioritization):通过分配的 CPU 时间片数量及硬盘 IO 带宽大小,实际上就相当于控制了进程运行的优先级。 - 资源统计(Accounting):cgroups 可以统计系统的资源使用量,如记录使用cpuacct子系统记录某个进程组使用的cpu时间。 - 进程控制(Control):cgroups 可以对进程组执行挂起、恢复等操作。 - 进程组隔离(isolation):使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。 ### 02.Linux CGroup核心概念: - 任务(task):在cgroup中,任务就是系统的一个进程。 - 控制组(cgroup):cgroups中的资源控制都是以控制组为单位实现的。一个进程可以加入到某个控制组中,也可以从一个进程组迁移到另一个控制组。一个进程组的进程可以使用cgroup以控制组为单位分配的资源,同时受到cgroup以控制组为单位设定的限制。 - 层级(hierarchy):控制组可以组织成层级的树状结构,其中子节点控制组继承父控制组的特定属性。 - 子系统(subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用,之后,这个层级上的所有控制组都受到这个子系统的控制。 ### 03.核心概念的相互关系: 1. 每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称 之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此 cgroup的后代)的初始成员。 2. 一个子系统(subsystem)最多只能附加到一个层级(hierarchy)。 3. 一个层级(hierarchy)可以附加多个子系统(subsystem)。 4. 一个任务(task)可以是多个cgroup的成员,但是这些cgroup必须在不同的层级(subsystem)。 5. 系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的 成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务 的cgroup。 ### 04.cgroups的子系统(subsystem)介绍: - **blkio**:为块设备设定输入/输出限制,比如物理设备(磁盘、固态硬盘、USB等)。 - **cpu** :使用调度程序提供对CPU的cgroup任务访问。 - **cpuacct** :自动生成cgroup中任务所使用的CPU报告。 - **cpuset** :为cgroup中的任务分配独立CPU(在多核系统)和内存节点。 - **devices** :可允许或者拒绝cgroup中的任务访问设备。 - **freezer** :挂起或者恢复cgroup中的任务。 - **memory** :设定cgroup中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。 - **perf_event_**:这个 subsystem 使用后使得 cgroup 中的 task 可以进行统一的性能测试。 - **net_cls** :使用等级识别符(classid)标记网络数据包,可允许Linux流量控制程序(tc)识别从具体cgroup中生成的数据包。 - **ns** :名称空间子系统。 ### 05.CGroups文件系统 ##### Linux 通过(伪)文件系统的方式将功能和配置暴露给用户,这得益于 Linux 的虚拟文件系统(VFS)。 - 使用mount挂载cgroup文件系统就可以使用配置这些controller ```shell [root@localhost ~]# 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/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/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) 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/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) ``` - 进入到controller挂载的层级目录下,就可以看到根控制组用来配置controller的文件,以及子控制组。在控制组目录下创建新的目录就相当于创建了一个子控制组。 ```shell [root@localhost ~]# ll /sys/fs/cgroup/cpu/ total 0 -rw-r--r-- 1 root root 0 Oct 28 11:19 cgroup.clone_children --w--w--w- 1 root root 0 Oct 28 11:19 cgroup.event_control -rw-r--r-- 1 root root 0 Oct 28 11:19 cgroup.procs -r--r--r-- 1 root root 0 Oct 28 11:19 cgroup.sane_behavior -r--r--r-- 1 root root 0 Oct 28 11:19 cpuacct.stat -rw-r--r-- 1 root root 0 Oct 28 11:19 cpuacct.usage -r--r--r-- 1 root root 0 Oct 28 11:19 cpuacct.usage_percpu -rw-r--r-- 1 root root 0 Oct 28 11:19 cpu.cfs_period_us -rw-r--r-- 1 root root 0 Oct 28 11:19 cpu.cfs_quota_us -rw-r--r-- 1 root root 0 Oct 28 11:19 cpu.rt_period_us -rw-r--r-- 1 root root 0 Oct 28 11:19 cpu.rt_runtime_us -rw-r--r-- 1 root root 0 Oct 28 11:19 cpu.shares -r--r--r-- 1 root root 0 Oct 28 11:19 cpu.stat drwxr-xr-x 4 root root 0 Oct 28 11:41 kubepods -rw-r--r-- 1 root root 0 Oct 28 11:19 notify_on_release -rw-r--r-- 1 root root 0 Oct 28 11:19 release_agent drwxr-xr-x 85 root root 0 Nov 2 06:53 system.slice -rw-r--r-- 1 root root 0 Oct 28 11:19 tasks ``` - 查看内核支持的控制器(controller) - /proc/cgroups 可以列出内核支持的 cgroup controllers,以及使用情况 - controller名称、挂载位置ID(在cgroup v2中都为0)、使用该controller的cgroup数量、是否启用 ```shell [root@localhost ~]# cat /proc/cgroups #subsys_name hierarchy num_cgroups enabled cpuset 6 28 1 cpu 2 112 1 cpuacct 2 112 1 memory 10 112 1 devices 7 113 1 freezer 8 28 1 net_cls 3 28 1 blkio 11 112 1 perf_event 4 28 1 hugetlb 9 28 1 pids 5 112 1 net_prio 3 28 1 ``` - 查看进程所属的 cgroup,/proc/[pid]/cgroup\ - 第一列层级ID,与/proc/cgroups第二列对应,在Cgroups v2中都为0 - 第二列为 controller 名称 - 第三列是控制组的目录路径 ```shell [root@localhost ~]# cat /proc/29525/cgroup 11:blkio:/system.slice/hostguard.service 10:memory:/system.slice/hostguard.service 9:hugetlb:/ 8:freezer:/ 7:devices:/system.slice/hostguard.service 6:cpuset:/ 5:pids:/system.slice/hostguard.service 4:perf_event:/ 3:net_prio,net_cls:/ 2:cpuacct,cpu:/system.slice/hostguard.service 1:name=systemd:/system.slice/hostguard.service ``` ### 06.Cgroups V1使用 ##### v1 中,cgroups 层级目录可以挂载一个或者多个controller。 注意:使用 CONFIG_CGROUP 选项构建的内核,才可以使用 cgroups 1. 挂载 - cgroups 是通过 tmpfs 文件系统的形式挂载到指定目录,通常会挂载到 /sys/fs/cgroup 目录 ```shell mount -t tmpfs cgroup /sys/fs/cgroup ``` - 挂载controller,挂载类型为 cgroup,-o 指定controller,可以有多个 ```shell [root@localhost ~]# mkdir -p /tmp/cgroup/cpu,cpuacct [root@localhost ~]# mount -t cgroup -o cpu,cpuacct none /tmp/cgroup/cpu,cpuacct ``` - 进入到 /tmp/cgroup/cpu,cpuacct目录下可以看到和/sys/fs/cgroup/cpu,cpuacct相同的内容,一次挂载所有controller,可以省略 -o all,挂载所有控制器是默认选项 ```shell [root@localhost ~]# mount -t cgroup -o all cgroup /tmp/cgroup ``` - 不挂载任何controller,必须提供 name,可以挂载多次,但是要求名字唯一 ```shell [root@localhost ~]# mount -t cgroup -o none,name=somename none /tmp/cgroup/none ``` - 可以使用 cgroups 的回调通知机制,来跟踪进程。Linux 5.0 后可以通过内核引导选项 cgroup_no_v1 可以禁止挂载这种命名的Cgroup V1 层级 2. 卸载 - 需要将所有子目录卸载,否则 umount 只会让挂载点不可见,并不会真正的卸载 ```shell [root@localhost ~]# umount /tmp/cgroup/cpu,cpuacct ``` 3. 创建控制组(cgroup) - 控制组是以目录形式来呈现的,/ 是控制组的根目录,注意根目录和挂载目录是两回事 - 层级可能挂载在 /sys/fs/cgroup 之类的目录下,但是无论挂载到哪里,层级中控制组的根目录都是 / - 比如 /sys/fs/cgroup/cpu/kubepods 的目录应该是 /kubepods 4. 控制进程和线程 - cgroup 目录下 cgroup.procs 记录的是 进程 ID, tasks 文件记录的是 线程 ID - 将进程 id 写入指定控制组目录下的 cgroup.procs 文件中,便将进程绑定到相应的控制组了 ```shell [root@localhost ~]# echo $$ > /sys/fs/cgroup/cpu/test1/cgroup.procs ``` - 绑定进程后,进程下的线程也会一并绑到管理组中,可以查看 tasks 文件 - cgroup.procs 文件中的进程 id 可以重复,并且没有顺序要求 - 把线程 id 写入 tasks 文件中,也可以绑定指定线程 5. 删除控制组 - 控制组下的子控制组都删除了,并且没有绑定任何进程,线程,则直接删除目录即可 6. 回调通知 - 每个控制组目录下都有两个文件 ```shell -rw-r--r-- 1 root root 0 May 11 11:33 notify_on_release -rw-r--r-- 1 root root 0 May 11 11:33 release_agent ``` - 当控制组绑定的所有进程或线程都结束后,根据 notify_on_release 文件内容,判断为否调用 release_agent 指定的命令,该命令的参数只有当前为空的控制组目录 - notify_on_release 文件内容是0或1,表示cgroup为空后,是否发出通知,1为通知 - release_agent 文件内容为通知时调用的命令路径 - release_agent 的内容也可以在挂载的时候设置 mount -o release_agent=pathname ... ### 08. subsystem 配置参数用法 #### 1.blkio - BLOCK IO资源控制 - 限额类 限额类是主要有两种策略,一种是基于完全公平队列调度(CFQ:Completely Fair Queuing )的按权重分配各个 cgroup 所能占用总体资源的百分比,好处是当资源空闲时可以充分利用,但只能用于最底层节点 cgroup 的配置;另一种则是设定资源使用上限,这种限额在各个层次的 cgroup 都可以配置,但这种限制较为生硬,并且容器之间依然会出现资源的竞争。 - 按比例分配块设备 IO 资源 - blkio.weight:填写 100-1000 的一个整数值,作为相对权重比率,作为通用的设备分配比。 - blkio.weight_device: 针对特定设备的权重比,写入格式为device_types:node_numbers weight,空格前的参数段指定设备,weight参数与blkio.weight相同并覆盖原有的通用分配比。{![查看一个设备的device_types:node_numbers可以使用:ls -l /dev/DEV,看到的用逗号分隔的两个数字就是。有的文章也称之为major_number:minor_number。]} - 控制 IO 读写速度上限 1. blkio.throttle.read_bps_device:按每秒读取块设备的数据量设定上限,格式device_types:node_numbers bytes_per_second。 2. blkio.throttle.write_bps_device:按每秒写入块设备的数据量设定上限,格式device_types:node_numbers bytes_per_second。 3. blkio.throttle.read_iops_device:按每秒读操作次数设定上限,格式device_types:node_numbers operations_per_second。 4. blkio.throttle.write_iops_device:按每秒写操作次数设定上限,格式device_types:node_numbers operations_per_second - 针对特定操作 (read, write, sync, 或 async) 设定读写速度上限 1. blkio.throttle.io_serviced:针对特定操作按每秒操作次数设定上限,格式device_types:node_numbers operation operations_per_second 2. blkio.throttle.io_service_bytes:针对特定操作按每秒数据量设定上限,格式device_types:node_numbers operation bytes_per_second - 统计与监控 以下内容都是只读的状态报告,通过这些统计项更好地统计、监控进程的 io 情况。 1. blkio.reset_stats:重置统计信息,写入一个 int 值即可。 2. blkio.time:统计cgroup对设备的访问时间,按格式device_types:node_numbers milliseconds读取信息即可,以下类似。 3. blkio.io_serviced:统计cgroup对特定设备的IO操作(包括read、write、sync及async)次数,格式device_types:node_numbers operation number 4. blkio.sectors:统计cgroup对设备扇区访问次数,格式device_types:node_numbers sector_count 5. blkio.io_service_bytes:统计cgroup对特定设备IO操作(包括read、write、sync及async)的数据量,格式device_types:node_numbers operation bytes 6. blkio.io_queued:统计cgroup的队列中对IO操作(包括 read、write、sync及async)的请求次数,格式number operation 7. blkio.io_service_time:统计cgroup对特定设备的IO操作(包括read、write、sync及async)时间 (单位为ns),格式device_types:node_numbers operation time 8. blkio.io_merged:统计cgroup将BIOS请求合并到IO操作(包括read、write、sync及async)请求的次数,格式number operation 9. blkio.io_wait_time:统计cgroup在各设备中各类型IO操作(包括read、write、sync及async)在队列中的等待时间(单位ns),格式device_types:node_numbers operation time 10. \_blkio.\_recursice\*:各类型的统计都有一个递归版本,Docker中使用的都是这个版本。获取的数据与非递归版本是一样的,但是包括cgroup所有层级的监控数据。 #### 2.cpu - CPU资源控制 - CPU 资源的控制也有两种策略,一种是完全公平调度 (CFS:Completely Fair Scheduler)策略,提供了限额和按比例分配两种方式进行资源控制;另一种是实时调度(Real-Time Scheduler)策略,针对实时进程按周期分配固定的运行时间。配置时间都以微秒(µs)为单位,文件名中用us表示。 - CFS 调度策略下的配置 - 设定CPU使用周期使用时间上限 - cpu.cfs_period_us:设定周期时间,必须与cfs_quota_us配合使用。 - cpu.cfs_quota_us:设定周期内最多可使用的时间。这里的配置指task对单个cpu的使用上限,若cfs_quota_us是cfs_period_us的两倍,就表示在两个核上完全使用。数值范围为1000-1000,000(微秒)。 - cpu.stat:统计信息,包含nr_periods(表示经历了几个cfs_period_us周期)、nr_throttled(表示task被限制的次数)及throttled_time(表示task被限制的总时长)。 - 按权重比例设定 CPU 的分配 - cpu.shares:设定一个整数(必须大于等于2)表示相对权重,最后除以权重总和算出相对比例,按比例分配CPU时间。(如cgroup A设置100,cgroup B设置300,那么cgroup A中的task运行25%的CPU时间。对于一个4核CPU的系统来说,cgroup A中的task可以100%占有某一个CPU,这个比例是相对整体的一个值。) - RT 调度策略下的配置 - 实时调度策略与公平调度策略中的按周期分配时间的方法类似,也是在周期内分配一个固定的运行时间。 - cpu.rt_period_us:设定周期时间。 - cpu.rt_runtime_us:设定周期中的运行时间。 #### 3.cpuacct - CPU资源报告 - 这个子系统的配置是cpu子系统的补充,提供CPU资源用量的统计,时间单位都是纳秒。 - cpuacct.usage:统计cgroup中所有task的cpu使用时长 - cpuacct.stat:统计cgroup中所有task的用户态和内核态分别使用cpu的时长 - cpuacct.usage_percpu:统计cgroup中所有task使用每个cpu的时长 #### 4.cpuset - CPU 绑定 - 为task分配独立CPU资源的子系统,参数较多,这里只选讲两个必须配置的参数,同时Docker中目前也只用到这两个。 - cpuset.cpus:在这个文件中填写cgroup可使用的CPU编号,如0-2,16代表0、1、2和16这4个CPU。 - cpuset.mems:与CPU类似,表示cgroup可使用的memory node,格式同上 #### 5.device - 限制task对device的使用 - 设备黑/白名单过滤 - devices.allow:允许名单,语法type device_types:node_numbers access type ; - type有三种类型:b(块设备)、c(字符设备)、a(全部设备); - access也有三种方式:r(读)、w(写)、m(创建)。 - devices.deny:禁止名单,语法格式同上。 - 统计报告 - devices.list:报告为这个cgroup中的task设定访问控制的设备 #### 6.freezer - 暂停/恢复cgroup中的task - 只有一个属性,表示进程的状态,把task放到freezer所在的cgroup,再把state改为FROZEN,就可以暂停进程。不允许在cgroup处于FROZEN状态时加入进程。 - freezer.state ,包括如下三种状态: - FROZEN 停止 - FREEZING 正在停止,这个是只读状态,不能写入这个值。 - THAWED 恢复 #### 7.memory - 内存资源管理 - 限额类 - memory.limit_bytes:强制限制最大内存使用量,单位有k、m、g三种,填-1则代表无限制。 - memory.soft_limit_bytes:软限制,只有比强制限制设置的值小时才有意义。填写格式同上。当整体内存紧张的情况下,task 获取的内存就被限制在软限制额度之内,以保证不会有太多进程因内存挨饿。可以看到,加入了内存的资源限制并不代表没有资源竞争。 - memory.memsw.limit_bytes:设定最大内存与 swap 区内存之和的用量限制。填写格式同上。 - 报警与自动控制 - memory.oom_control:改参数填0或1, 0表示开启,当cgroup中的进程使用资源超过界限时立即杀死进程,1表示不启用。默认情况下,包含memory子系统的cgroup都启用。当oom_control不启用时,实际使用内存超过界限时进程会被暂停直到有空闲的内存资源。 - 统计与监控类 - memory.usage_bytes:报告该cgroup中进程使用的当前总内存用量(以字节为单位) - memory.max_usage_bytes:报告该cgroup中进程使用的最大内存用量 - memory.failcnt:报告内存达到在 memory.limit_in_bytes设定的限制值的次数 - memory.stat:包含大量的内存统计数据。 - cache:页缓存,包括tmpfs(shmem),单位为字节。 - rss:匿名和swap缓存不包括 tmpfs(shmem),单位为字节。 - mapped_file:memory-mapped映射的文件大小包括tmpfs(shmem),单位为字节 - pgpgin:存入内存中的页数 - pgpgout:从内存中读出的页数 - swap:swap用量,单位为字节 - active_anon:在活跃的最近最少使用(least-recently-used,LRU)列表中的匿名和swap缓存,包括tmpfs(shmem),单位为字节 - inactive_anon:不活跃的LRU列表中的匿名和swap缓存,包括tmpfs(shmem),单位为字节 - active_file:活跃LRU列表中的file-backed内存,单位为字节 - inactive_file:不活跃LRU列表中的file-backed内存,单位为字节 - unevictable:无法再生的内存,单位为字节 - hierarchical_memory_limit:包含memory cgroup的层级的内存限制,单位为字节 - hierarchical_memsw_limit:包含memory cgroup的层级的内存加swap限制,单位为字节 ### 参考文献: - Docker背后的内核知识:https://www.infoq.cn/article/docker-kernel-knowledge-cgroups-resource-isolation/ - Linux Cgroups V1介绍与使用:http://icebergu.com/archives/linux-cgroups-v1 - Redhat CGroups简介:https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/6/html/resource_management_guide/index Last modification:November 2nd, 2020 at 11:57 am © 允许规范转载