从运维角度查看linux中的cgroup(容器的基础)
一、linux cgroup相关运维操作
本机环境
[root@ht10 /sys/fs/cgroup]#uname -a [root@ht10 /sys/fs/cgroup]#docker -v |
1、linux系统显示全路径的配置
修改/etc/profile文件 |
2、cgroup子系统
redhat默认支持10个子系统,但默认只挂载了8个子系统,ubuntu 12.04 默认支持8个子系统,但默认只挂载了5个子系统。 centos 默认支持15个子系统 |
3、查看内核是否开启cgroup支持
[root@ht10 /sys/fs/cgroup]#more /boot/config-`uname -r` | grep -i cgroup CONFIG_CGROUPS=y # CONFIG_CGROUP_DEBUG is not set CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_HUGETLB=y CONFIG_CGROUP_PERF=y CONFIG_CGROUP_SCHED=y CONFIG_BLK_CGROUP=y # CONFIG_DEBUG_BLK_CGROUP is not set CONFIG_NETFILTER_XT_MATCH_CGROUP=m CONFIG_NET_CLS_CGROUP=y CONFIG_NETPRIO_CGROUP=y //当然我们可以修改linux启动配置文件来关闭它 |
4、查看cgroup版本(v1和v2)
[root@ht10 /sys/fs/cgroup]#grep cgroup /proc/filesystems nodev cgroup //v1 |
5、/proc/cgroups可以查看当前系统挂载了多少子系统,每个子系统的cgroup个数
[root@ht10 /sys/fs/cgroup]#cat /proc/cgroups [root@ht10 /sys/fs/cgroup]#ll 查看当前挂载的控制组 [root@ht10 /sys/fs/cgroup/cpu]# mount|awk '$5=="cgroup" {print $0}' |
6、可以手工挂载点,直接创建 /mytest控制组
[root@ht10 /sys/fs/cgroup/cpu]#mkdir mytest //自动生成相关的cpu控制住的基本内容 [root@ht10 /sys/fs/cgroup/cpu]#ll mytest [root@ht10 /sys/fs/cgroup/cpu/mytest]# mkdir mytest2 [root@ht10 /sys/fs/cgroup/cpu/mytest]#ll 1) 要想设定 /mytest 这个控制组的 cpu.shares 参数。 2) 要把一个进程加入到mytest控制组,你只需把进程 pid 写入到mytest目录下的 tasks 文件即可 [root@ht10 /proc/33]#cat /proc/33/cgroup //语法 /proc/pid/cgroup [root@ht10 /sys/fs/cgroup/cpu/mytest]#cd mytest2 举例: [root@ht10 /sys/fs/cgroup/cpu/mytest/mytest2]#cat cpuacct.stat [root@ht10 /sys/fs/cgroup/cpu/mytest]#cat cpu.cfs_period_us [root@ht10 /sys/fs/cgroup/cpu/mytest]#cat cpu.cfs_quota_us 然后如果哪个程序不想让他占用太多cpu时间,则把进程id [root@ht10 /sys/fs/cgroup/cpu/mytest]# echo 33 > /sys/fs/cgroup/cpu/mytest/tasks 就可以了 这个进程使用的cpu时间不会太长,因为mytest容器已经做了限制了 |
7、一个挂载都会生成的几个公共文件 cgroup.clone_children、cgroup.event_control、cgroup.procs、cgroup.sane_behavior、release_agent、task、notify_on_release
下面做下比较 [root@ht10 /sys/fs/cgroup/cpu]#ll
[root@ht10 /sys/fs/cgroup/cpu/mytest]#ll |
8、查看cgroup挂载点(lssubsys -am 可以查看各子系统的挂载点,需要安装libcgroup-tools,我下面有介绍安装libcgroup-tools)
安装tools以后才会有lssubsys命令,注意
[root@ht10 /proc/33]#lssubsys -am |
9、lsgroup命令(也来自libcgroup-tools包)
lscgroup(1) —— lscgroup 指令用于将层级中的 cgroups 列出。 [root@ht10 /sys/fs/cgroup/pids]#lscgroup | grep mytest |
[root@ht10 /sys/fs/cgroup/cpu/mytest/mytest2]#mount -t cgroup |
9、查看当前的存在的cpu子系统
[root@ht10 /sys/fs/cgroup/cpu]#ll |
二、cgroup理论阐述
什么是cgroup
cgroups,其名称源自控制组群(control groups)的简写,是Linux内核的一个功能,用来限制、控制与分离一个进程组的资源(如CPU、内存、磁盘输入输出等) |
cgroups的一个设计目标是为不同的应用情况提供统一的接口,从控制单一进程到操作系统层虚拟化(像OpenVZ,Linux-VServer,LXC)。
简介
Google的工程师(主要是Paul Menage和Rohit Seth)于2006年以“过程容器”的名义开始了这项功能的研究。在2007年末,为了避免在Linux内核上下文中由术语“ 容器 ” 的多种含义引起的混淆,术语改为“控制组” ,并且控制组功能在内核2.6版中合并到Linux内核主线中.24,于2008年1月发布。此后,开发人员添加了许多新功能和控制器,例如2014年对kernfs的支持,防火墙,和统一的层次结构。 |
为什么需要cgroup
在 Linux 里,一直以来就有对进程进行分组的概念和需求,比如 session group, progress group 等,后来随着人们对这方面的需求越来越多,比如需要追踪一组进程的内存和 IO 使用情况等,于是出现了 cgroup,用来统一将进程进行分组,并在分组的基础上对进程进行监控和资源控制管理等。 Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的cpuset等子系统,也为未来开发新的子系统提供接口。现在的cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。
Cgroups提供了以下功能:
限制进程组 | 1.限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会触发OOM(out of memory)。 |
进程组的优先级控制 | 2.进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share。 |
记录进程组 | 3.记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间 |
进程组隔离 | 4.进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。 |
进程组控制 | 5.进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。 |
相关概念术语
task |
在cgroups中,task(任务)就是系统的一个进程 |
control group (控制组) |
cgroups 中的资源控制都以cgroup为单位实现。cgroup表示按某种资源控制标准划分而成的任务组,包含一个或多个子系统。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup |
hierarchy (层级树) |
hierarchy由一系列cgroup以一个树状结构排列而成,每个hierarchy通过绑定对应的subsystem进行资源调度。hierarchy中的cgroup节点可以包含零或多个子节点,子节点继承父节点的属性。整个系统可以有多个hierarchy |
subsytem (子系统) |
一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制 |
Cgroup 提供了一个 CGroup 虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用 Cgroup,必须挂载 CGroup 文件系统。这时通过挂载选项指定使用哪个子系统。
Cgroup的子系统
blkio | 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等) |
devices | 进程范围设备权限 |
cpuset | 分配进程可使用的 CPU数和内存节点 |
cpu | 控制CPU占有率 |
cpuacct | 统计CPU使用情况,例如运行时间,throttled时间 |
memory |
这个子系统设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告 |
freezer | 暂停 Cgroup 中的进程 |
net_cls | 配合 tc限制网络带宽 |
net_prio | 设置进程的网络流量优先级 |
huge_tlb | 限制 HugeTLB 的使用 |
perf_event | 允许 Perf 工具基于 Cgroup 分组做性能检测 |
ns | 名称空间子系统 |
pids | 控制进程数 |
rdma | |
参考: https://man7.org/linux/man-pages/man7/cgroups.7.html |
Cgroup的子系统详细介绍
1、cpu 子系统
cpu 子系统用于控制 cgroup 中所有进程可以使用的 cpu 时间片。cpu 子系统主要涉及 5 个参数:cpu.cfs_period_us,cpu.cfs_quota_us,cpu.shares,cpu.rt_period_us,cpu.rt_runtime_us
。
查看linux 下的目录(注意cpu和cpuacct在同一个目录下)
[root@ht10 /sys/fs/cgroup/cpu]#ll |
cpu 参数文件详细说明
参数文件 | 说明 |
---|---|
cpu.cfs_period_us | 用来设置一个 CFS 调度时间周期长度,默认值是 100000us(100ms),一般 cpu.cfs_period_us 作为系统默认值我们不会去修改它。系统总 CPU 带宽:cpu 核心数 * cfs_period_us 运行样例 [root@ht10 /sys/fs/cgroup/cpu]#cat cpu.cfs_period_us |
cpu.cfs_quota_us | 用来设置在一个 CFS 调度时间周期 (cfs_period_us) 内,允许此控制组执行的时间。默认值为-1 表示限制时间。cfs_quota_us 的最小值为 1ms(1000),最大值为 1s。通过 cfs_period_us 和 cfs_quota_us 可以以绝对比例限制 cgroup 的 cpu 使用,即 cfs_quota_us/cfs_period_us 等于进程可以利用的 cpu cores,不能超过这个数值。使用 cfs_quota_us/cfs_period_us,例如 20000us/100000us=0.2,表示允许这个控制组使用的 CPU 最大是 0.2 个 CPU,即限制使用 20%CPU。如果 cfs_quota_us/cfs_period_us=2,就表示允许控制组使用的 CPU 资源配置是 2 个。 运行样例 [root@ht10 /sys/fs/cgroup/cpu]#cat cpu.cfs_quota_us |
cpu.shares | 用来设置 cpu cgroup 子系统对于控制组之间的 cpu 分配比例。默认值是 1024。cpu.shares 以相对的比例限制 cgroup 的 cpu。例如:在两个 cgroup 中都将 cpu.shares 设定为 1024 的任务将有相同的 CPU 时间,但在 cgroup 中将 cpu.shares 设定为 2048 的任务可使用的 CPU 时间是在 cgroup 中将 cpu.shares 设定为 1024 的任务可使用的 CPU 时间的两倍。 运行样例 [root@ht10 /sys/fs/cgroup/cpu]#cat cpu.shares |
cpu.rt_runtime_us | 以微秒(µs,这里以“us”代表)为单位指定在某个时间段中 cgroup 中的任务对 CPU 资源的最长连续访问时间。建立这个限制是为了防止一个 cgroup 中的任务独占 CPU 时间。如果 cgroup 中的任务应该可以每 5 秒中可有 4 秒时间访问 CPU 资源,请将 cpu.rt_runtime_us 设定为 4000000(4秒),并将 cpu.rt_period_us 设定为 5000000(5秒)。 一秒的一百万分之一,即1秒=100百万微秒 运行样例 [root@ht10 /sys/fs/cgroup/cpu]#cat cpu.rt_runtime_us |
cpu.rt_period_us | 以微秒(µs,这里以“us”代表)为单位指定在某个时间段中 cgroup 对 CPU 资源访问重新分配的频率。如果某个 cgroup 中的任务应该每 5 秒钟有 4 秒时间可访问 CPU 资源,则请将 cpu.rt_runtime_us 设定为 4000000,并将 cpu.rt_period_us 设定为 5000000。 运行样例 [root@ht10 /sys/fs/cgroup/cpu]#cat cpu.rt_period_us |
|
2、cpuacct 子系统
cpuacct 子系统(CPU accounting)会自动生成报告来显示 cgroup 中任务所使用的 CPU 资源。报告有两大类:cpuacct.stat 和 cpuacct.usage。
查看linux 下的目录
[root@ht10 /sys/fs/cgroup/cpuacct]#ll |
cpuacct 参数文件详细说明
参数 | 说明 |
---|---|
cpuacct.stat | cpuacct.stat 记录 cgroup 的所有任务(包括其子孙层级中的所有任务)使用cpu的user和system的时间累计。 运行样例 [root@ht10 /sys/fs/cgroup/cpuacct]#cat cpuacct.stat |
cpuacct.usage |
cpuacct.usage 记录这个 cgroup 中所有任务(包括其子孙层级中的所有任务)使用CPU的总时间数(单位是纳秒)。 [root@ht10 /sys/fs/cgroup/cpuacct]#cat cpuacct.usage |
cpuacct.usage_percpu | cpuacct.usage_percpu 记录 cgroup控制住中每一个任务(包括其子控制组中的所有任务)在每个 cpu 中使用的 cpu时间(以纳秒为单位)。 [root@ht10 /sys/fs/cgroup/cpuacct]#cat cpuacct.usage_percpu |
cpuset 子系统
cpuset 主要是为了 numa 使用的,numa 技术将 CPU 划分成不同的 node,每个 node 由多个 CPU 组成,并且有独立的本地内存、I/O 等资源 (硬件上保证)。可以使用 numactl 查看当前系统的 node 信息。
参数 | 说明 |
---|---|
cpuset.cpus | cpuset.cpus 指定允许这个 cgroup 中任务访问的 CPU。这是一个用逗号分开的列表,格式为 ASCII,使用小横线("-")代表范围。 |
cpuset.mems | cpuset.mems 指定允许这个 cgroup 中任务可访问的内存节点。这是一个用逗号分开的列表,格式为 ASCII,使用小横线("-")代表范围。 |
memory 子系统
linux所挂载目录
[root@ht10 /sys/fs/cgroup/memory]#ll |
memory 子系统自动生成 cgroup 任务使用内存资源的报告,并限定这些任务所用内存的大小。
参数 | 说明 |
---|---|
memory.limit_in_bytes | 用来设置用户内存的最大使用。如果没有指定单位,则该数值将被解读为字节。但是可以使用后缀代表更大的单位 ,例如k 或者 K 代表千字节,m 或者 M 代表兆字节 ,g 或者 G 代表千兆字节。 不能使用 memory.limit_in_bytes 限制 root cgroup;只能对层级中较低的控制组使用这些值。 在 memory.limit_in_bytes 中写入 -1 则可以移除全部已有限制。(-1表示不限制) |
memory.memsw.limit_in_bytes | 用来设置内存与 swap 用量之和的最大值。如果没有指定单位,则该值将被解读为字节。但是可以使用后缀代表更大的单位 —— k 或者 K 代表千字节,m 或者 M 代表兆字节,g 或者 G 代表千兆字节。您不能使用 memory.memsw.limit_in_bytes 来限制 root cgroup;您只能对层级中较低的群组应用这些值。在 memory.memsw.limit_in_bytes 中写入 -1 可以删除已有限制。 |
memory.oom_control | 1)用来设置当控制组中所有进程达到可以使用内存的最大值时,也就是发生 OOM(Out of Memory) 时是否触发 linux 的 OOM killer 杀死控制组内的进程。包含一个标志(0 或 1)来开启或者关闭 cgroup 的 OOM killer,默认的配置是开启 OOM killer 的。 2)如果 OOM killer 关闭,那么进程尝试申请的内存超过允许,那么它就会被暂停 (就是 hang 死),直到额外的内存被释放。memory.oom_control 文件也在 under_oom 条目下报告当前 cgroup 的 OOM 状态。如果该 cgroup 缺少内存,则会暂停它里面的任务。under_oom 条目报告值为 1。 |
memory.usage_in_bytes | 这个参数是只读的,它里面的数值是当前控制组里所有进程实际使用的内存总和,主要是 RSS 内存和 Page Cache 内存的和。准确的内存使用量计算公式(memory.kmem.usage_in_bytes 表示该 memcg 内核内存使用量):memory.usage_in_bytes = memory.stat[rss] + memory.stat[cache] + memory.kmem.usage_in_bytes。 |
memory.stat | 保存内存相关的统计数据,可以显示在当前控制组里各种内存类型的实际的开销。想要判断容器真实的内存使用量,我们不能用 Memory Cgroup 里的 memory.usage_in_bytes,而需要用 memory.stat 里的 rss 值。 |
memory.swappiness | 可以控制这个 Memroy Cgroup 控制组下面匿名内存和 page cache 的回收,取值的范围和工作方式和全局的 swappiness 差不多。这里有一个优先顺序,在 Memory Cgorup 的控制组里,如果你设置了 memory.swappiness 参数,它就会覆盖全局的 swappiness,让全局的 swappiness 在这个控制组里不起作用。不同于 /proc 文件系统下全局的 swappiness,当 memory.swappiness = 0 的时候,对匿名页的回收是始终禁止的,也就是始终都不会使用 Swap 空间。因此,我们可以通过 memory.swappiness 参数让需要使用 Swap 空间的容器和不需要 Swap 的容器,同时运行在同一个宿主机上。 |
memory.memsw.usage_in_bytes | 该 cgroup 中进程当前所用的内存量和 swap 空间总和(以字节为单位)。 |
memory.max_usage_in_bytes | cgroup 中进程所用的最大内存量(以字节为单位)。 |
memory.memsw.max_usage_in_bytes | 该 cgroup 中进程的最大内存用量和最大 swap 空间用量(以字节为单位)。 |
memory.failcnt | 内存达到 memory.limit_in_bytes 设定的限制值的次数。 |
memory.memsw.failcnt | 内存和 swap 空间总和达到 memory.memsw.limit_in_bytes 设定的限制值的次数。 |
memory.force_empty | 当设定为 0 时,该 cgroup 中任务所用的所有页面内存都将被清空。这个接口只可在 cgroup 没有任务时使用。如果无法清空内存,请在可能的情况下将其移动到父 cgroup 中。移除 cgroup 前请使用 memory.force_empty 参数以免将废弃的页面缓存移动到它的父 cgroup 中。 |
memory.use_hierarchy | 包含标签(0 或者 1),它可以设定是否将内存用量计入 cgroup 层级的吞吐量中。如果启用(1),内存子系统会从超过其内存限制的子进程中再生内存。默认情况下(0),子系统不从任务的子进程中再生内存。 |
三、libcgroup-tools工具包做进程的限制和子系统测试
说明:从centos7开始,已经默认不再使用libcgroup套件,所以需要安装
包内的相关命令:
cgclassify | cgclassify命令是用来将运行的任务移动到一个或者多个cgroup |
cgclear | cgclear 命令是用来删除层级中的所有cgroup(清除所有挂载点内部文件) |
cgconfig.conf | 在/etc/cgconfig.conf文件中定义cgroup |
cgconfigparser | cgconfigparser命令解析cgconfig.conf文件和并挂载层级 |
cgcreate | cgcreate在层级中创建新cgroup |
cgdelete |
cgdelete命令删除指定的cgroup |
cgexec | cgexec命令在指定的cgroup中运行任务 |
cgget | cgget命令显示cgroup参数 |
cgred.conf | cgred.conf是cgred服务的配置文件 |
cgrules.conf | cgrules.conf 包含用来决定何时任务术语某些 cgroup的规则 |
cgrulesengd | cgrulesengd 在 cgroup 中发布任务 |
cgset | cgset 命令为 cgroup 设定参数 |
lscgroup | lscgroup 命令列出层级中的 cgroup |
lssubsys | lssubsys 命令列出包含指定子系统的层级 |
安装libcgroup-tools包
[root@blog ~]# yum install libcgroup-tools -y
[root@ht10 /sys/fs/cgroup/cpu]#lssubsys -a //列出所有的子系统
[root@ht10 /sys/fs/cgroup/cpu]#lssubsys -m //列出所在的目录
pidstat主要用于监控全部或指定进程占用系统资源的情况,如CPU,内存、设备IO、任务切换、线程等
[root@ht10 /sys/fs/cgroup/pids]#pidstat -r | grep docker
05:06:20 PM 0 1781 0.50 0.00 4865332 158220 0.06 dockerd
05:06:20 PM 0 1926 0.10 0.00 5138572 71256 0.03 docker-containe
05:06:20 PM 0 3583 0.04 0.00 2835656 31628 0.01 docker
05:06:20 PM 0 3654 0.40 0.00 7640 3004 0.00 docker-containe
05:06:20 PM 0 5051 0.37 0.00 7640 3392 0.00 docker-containe
05:06:20 PM 0 5260 0.38 0.00 7896 3272 0.00 docker-containe
05:06:20 PM 0 5357 0.40 0.00 9048 3536 0.00 docker-containe
05:06:20 PM 0 5428 0.37 0.00 7640 3188 0.00 docker-containe
05:06:20 PM 0 5982 0.41 0.00 9112 3300 0.00 docker-containe
05:06:20 PM 0 6201 0.37 0.00 7640 3024 0.00 docker-containe
05:06:20 PM 0 6582 0.38 0.00 7640 3320 0.00 docker-containe
05:06:20 PM 0 6597 0.34 0.00 7640 3464 0.00 docker-containe
05:06:20 PM 0 6886 0.38 0.00 7640 3432 0.00 docker-containe
05:06:20 PM 0 7430 0.33 0.00 7640 3292 0.00 docker-containe
05:06:20 PM 0 7588 0.37 0.00 9048 3408 0.00 docker-containe
05:06:20 PM 0 8212 0.03 0.00 2704072 30700 0.01 docker
05:06:20 PM 0 8456 0.37 0.00 7640 3528 0.00 docker-containe
05:06:20 PM 0 8745 0.36 0.00 7704 3696 0.00 docker-containe
05:06:20 PM 0 55647 0.12 0.00 9048 3296 0.00 docker-containe
05:06:20 PM 0 55910 0.11 0.00 9112 5028 0.00 docker-containe
四、分析docker
可以参看
这里我从不同的角度来分析一下,我们会看到kubernetes kubepods和docker以及组件kube-proxy 都使用了cgroup
cpu |
[root@ht10 /sys/fs/cgroup/cpu]#ll |
memory |
[root@ht10 /sys/fs/cgroup/memory]#ll |
cpuset |
[root@ht10 /sys/fs/cgroup/cpu]#ll ../cpuset |
devices |
[root@ht10 /sys/fs/cgroup]#ll devices/ |
pids |
[root@ht10 /sys/fs/cgroup]#ll pids |
systemd |
[root@ht10 /sys/fs/cgroup]#ll systemd/ |