Cgroup 目录
1 Cgroup基础... 1
1.1:Cgroups是什么?... 1
1.2:Cgroups可以做什么?... 2
1.3 Cgroups相关概念... 2
1.4 Cgroups子系统... 2
1.4.1 子系统基本介绍... 2
1.4.2子系统关系... 3
1.4.3子系统相应参数介绍... 3
1.5 Cgroups文件系统... 3
2:cgroup基本管理及应用... 3
2.1 cgroup的管理... 3
2.2cgroup的测试... 3
3:结语... 3
4:常见问题... 3
1 Cgroup基础
1.1:Cgroups是什么?Cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:cpu,memory,IO等等)的机制。最初由google的工程师提出,后来被整合进Linux内核。Cgroups也是LXC为实现虚拟化所使用的资源管理手段,可以说没有cgroups就没有LXC。 我们可以对 Cgroups 进行监控,禁止 Cgroups 控制下的进程访问某些资源,还可以在一个运行中的系统中对 Cgroups 动态地进行配置。cgconfig ( control group config ) 是一项系统服务,它可以根据配置文件创建 Cgroups,我们可以通过它在每次重启操作系统之后保持一致的 Cgroups 配置。
1.2:Cgroups可以做什么?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子系统可以将进程组挂起和恢复。
1.3 Cgroups相关概念
1.任务(task)。在cgroups中,任务就是系统的一个进程。
2.控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。
3.层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。
4.子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制
1.4 Cgroups子系统
1.4.1 子系统基本介绍
blkio -- 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。
cpu -- 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。
cpuacct -- 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。
cpuset -- 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。
devices -- 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。
freezer -- 这个子系统挂起或者恢复cgroup 中的任务。
memory -- 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。
net_cls -- 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
net_prio -- 这个子系统提供了一种动态控制每个网卡流量优先级的功能
ns -- 名称空间子系统
1.4.2子系统关系
1: 系统中第一个被创建的cgroup被称为root cgroup,该cgroup的成员包含系统中所有的进程
2:一个层级中不能出现重复的子系统。
3:进程与Cgroup属于多对多的关系。
4:一个进程创建了子进程后,该子进程默认为父进程所在cgroup的成员。
5:一个任务不能同时属于同一个层次结构中的两个 cgroup。
1.4.3子系统相应参数介绍
通用项目:
tasks 属于该group的进程ID
cgroup.procs 属于该group的线程ID cgroup.event_control 属于cgroup的通知API,允许改变cgroup的状态 notify_on_release 布尔值 是否启用客户端通知,启用时,内核执行release_agent时,cgroup不在包含任何任务(去清空tasks内容)。提供了一个清空group的方法。 注意:root的group默认是0,非root的group和其父group一样 release_agent:仅适用于root group;当notify on release被触发时,执行该文件命令;当一个gorup进程全部清空,并且启用了notify_on_release。
cpu
cpu子系统用于控制cgroup中所有进程可以使用的cpu时间片。附加了cpu子系统的hierarchy下面建立的cgroup的目录下都有一个cpu.shares的文件,对其写入整数值可以控制该cgroup获得的时间片。例如:在两个 cgroup 中都将 cpu.shares 设定为 1 的任务将有相同的 CPU 时间,但在 cgroup 中将 cpu.shares 设定为 2 的任务可使用的 CPU 时间是在 cgroup 中将 cpu.shares 设定为 1 的任务可使用的 CPU 时间的两倍。
cpu子系统是通过Linux CFS调度器实现的。按照作者Ingo Molnar的说法:"CFS百分之八十的工作可以用一句话概括:CFS在真实的硬件上模拟了完全理想的多任务处理器"。
cpu子系统调度CPU的访问控制,这里有2种调度模式
CFS(Completely Fair Scheduler):
基于linux的CFS及各group的cpu的权重,在croup的task间分配CPU
在CFS中,如果CPU空闲较多,那么group的task可能可以获得额外的CPU资源
cpu.cfs_period_us:该group的cpu分配周期(微妙),如果想让该group在1s内能又0.5秒的cpu使用时间设置cpu.cfs_period_us为1000000,设置cpu.cfs_quota_us为500000;如果想让该group使用2个CPU,设置cpu.cfs_quota_us
为2000000,设置cpu.cfs_period_us为1000000
cpu.cfs_quota_us:该指为-1时表示不限制CPU使用
cpu.stat:统计CPU的使用状态
nr_periods:CPU的使用周期(有多少个cpu.cfs_period_us)
nr_throttled :超出CPU限制的次数
throttled_time:被KILL的任务的cpu使用时间
cpu.shares:一个整数值(大于等于2),指定了使用CPU的权重(相对于系统上的
所有CPU),2048的group可使用的cpu资源位1024的2倍。
如果当前group的有闲置的cpu资源,那么这些资源可以被分配给其他的group使用
RTS(Real-Time scheduler):直接通过tasks的cpu使用时间来调度CPU
cpu.rt_period_us:(仅适用于实时任务调度)该参数指定了一段时间内风给该group的CPU使用时间(微秒)。如果该值设置为200000,cpu.rt_period_us设置为1000000,那么每秒该group有0.2秒的cpu使用时间
cpu.rt_runtime_us:(仅适用于实时任务调度)指定了该group可以持续使用CPU的最长时间。如果想让该group在1秒内有0.2秒的cpu使用时间,设置cpu.rt_runtime_us为200000,设置cpu.rt_period_us为1000000;如果想让该group有2个cpu的资源设置改制为2000000,设置cpu.rt_period_us为1000000。
cpuacct cpuacct.usage:该group及其子group的cpu总使用时间(纳秒),内容为0时,充值cpuacct的数据 cpuacct.stat:该group及其子group的cpu的用户和和内核态的分别使用时间 单位为$USER_HZ cpuacct.usage_percpu:该group及其子group的cpu分别使用时间(纳秒)
cpuset cpuset.cpus:绑定该group的cpu节点,如绑定该进程可以使用4,5,6,17,18 5
个cpu,格式如下:
4-6,17,18 cpu.mems:绑定该group的内存节点,格式如上 cpuset.memory_migrate:布尔值,默认0,指定当内存节点变化是,原内存页面是否迁移到新的内存节点上。 cpuset.cpu_exclusive:布尔值,默认0,指定该group的子group是否可以共享该group的cpu cpuset.mem_exclusive:布尔值,默认0,指定该group的子group是否可以共享该group的内存 cpuset.mem_hardwall:布尔值,默认0,内核为该group分配的进程是否应该仅仅在指定的内存节点上。 cpuset.memory_pressure:统计了该group内存压力的平均值(仅在cpuset.memory_pressure_enabled启用是有效) 该值为:每秒该group试图尝试回收内存的次数*1000 cpuset.memory_pressure_enabled:布尔值,默认0 cpuset.memory_spread_page:布尔值,默认0,是否均衡使用该group的内存节点 cpuset.memory_spread_slab:布尔值,默认0,是否均衡使用该group的cpu节点 cpuset.sched_load_balance:布尔值,默认1,是否平均分配该group的cpu负载到该group的节点上,注意,如果父group启用了这项,那么当前项就不在有效。 cpuset.sched_relax_domain_level:一个-1至5;代表系统试图进行负载均衡的类型(仅在 cpuset.sched_load_balance启用时有效) l -1:使用系统默认值 l 0:定期负载均衡 l 1:实时在同一内核线程间进行负载均衡 l 2:实时在同一内核包间负载均衡 l 3:实时在同一cpu节点或者刀片上负载均衡 l 4:实时在多个CPU(NUMA)节点负载均衡 l 5:实时在所有cpu间负载均衡
memory
memory 子系统可以设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。memory子系统是通过linux的resource counter机制实现的。
memory.stat:统计内存使用状态 memory.usage_in_bytes: 当前cgroup的内存使用情况
memory.memsw.usage_in_bytes: 当前group的内存+swap的内存使用情况
memory.limit_in_bytes: 设定最大的内存使用量,可以加单位(k/K,m/M,g/G) 注意:1:不能现在root的group 2:子group的限制要小于父group 3:-1是不限制 memory.memsw.limit_in_bytes:设定内存和swap总和的最大使用量,其他同上 注意:设置memory.memsw.limit_in_bytes前要设置memory.limit_in_bytes memory.failcnt:统计达到内存限制(memory.limit_in_bytes)的次数 memory.memsw.failcnt:统计达到内存+swap限制(memory.memsw.limit_in_bytes)的次数 memory.force_empty:当设置为0时,情况该group的所有内存页;该选项只有在当前group没有tasks才可以使用 memory.swappiness:针对该group的交换分区的优先级,类似vm.swappiness 注意:1: 不能调整root的group的swappiness 2: 不能调整有子group的swappiness memory.use_hierarchy:布尔值,默认0;指定是否在整个group层限制内存 memory.oom_control:布尔值;默认0;指定是否杀掉超出内存使用限制的进程。0:kill哪些超出内存使用范围的进程,1:暂停哪些超出内存使用范围的进程,指定有多余的内存。 该项也指出了是否有进程因为内存使用过多被暂停,under_oom为1 blkio 该子系统提供了2中方式来控制IO 1:基于权重 每个group都可以设置一个数值,根据数值的不通,系统分配相应的IO blkio.weight:一个100-1000的数值 echo 1000 > blkio.weight blkio.weight_device:一个100-1000的数值,指定设备的IO权重 echo "8:0 500" > blkio.weight_device 以上2个值同一个group只能存在一个 8 block SCSI disk devices (0-15) 0 = /dev/sda First SCSI disk whole disk 16 = /dev/sdb Second SCSI disk whole disk 32 = /dev/sdc Third SCSI disk whole disk ... 240 = /dev/sdp Sixteenth SCSI disk whole disk Partitions are handled in the same way as for IDE disks (see major number 3) except that the limit on partitions is 15. 2:基于速度: 每个group都有一个最大的速度,该group的进程IO不能大于这个速度 主要控制项: blkio.throttle.read_bps_device:指定该设备上的最大读速度(bytes/s) echo "8:0 10485760" > blkio.throttle.read_bps_device blkio.throttle.read_iops_device:制定该设备上的最大读IO(IO read/s) echo "8:0 10" > blkio.throottle.read_iops_device 下面2个跟上面2个类似 blkio.throttle.write_bps_device blkio.throttle.write_iops_device 主要记录项: blkio.throttle.io_serviced:记录设备IO操作总数: 8:0 Read 172227 8:0 Write 120543 8:0 Sync 261857 8:0 Async 30913 8:0 Total 292770 blkio.throttle.io_service_bytes:记录设备读取总数: 8:0 Read 3339509760 8:0 Write 7702714368 8:0 Sync 4183794688 8:0 Async 6858429440 8:0 Total 11042224128 其他配置项: blkio.reset_stats:对当前文件写入一个整数可重置当前所有数据 blkio.time:指定设备的cgroup控制的IO访问时间(ms) blkio.sectors:指定设备的扇区操作数 blkio.io_service_time:制定设备的IO工作时间(ns) blkio.io_wait_time:Cgroup等待IO的时间 blkio.io_merged:被合并的IO请求 blkio.io_queued:被cgroup放到队列的IO请求 注意如果不启用增强版的IO隔离,cgroup的IO隔离仅仅对顺序IO有效,启用后对逻辑IO也有效,默认启用 echo 1 > /sys/block/<disk_device>/queue/iosched/group_isolatio devices devices子系统是通过提供device whilelist 来实现的,devices子系统通过在内核对设备访问的时候加入额外的检查来实现;而devices子系统本身只需要管理好可以访问的设备列表就行了。 devices.allow:指定该group各设备的访问权限,有4个字段 type: a:所有设备 b:块设备 c:字符型 major,minor:主副设备号 access: r:读 w:写 m:创建新设备 devices.allow:指定该group各设备的不可访问权限,语法同上 devices.list:统计该group所有权限 freezer 该文件可能读出的值有三种,其中两种就是前面已提到的FROZEN和THAWED,分别代表进程已挂起和已恢复(正常运行),还有一种可能的值为FREEZING,显示该值表示该cgroup中有些进程现在不能被frozen。当这些不能被frozen的进程从该cgroup中消失的时候,FREEZING会变成FROZEN,或者手动将FROZEN或THAWED写入一次。
freezer.state:(仅对非root的group有效)
FROZEN:挂起进程 FREEZING:显示该值表示有些进程不能被frozen;当不能被挂起的进程从cgroup消失时,变成FROZEN,或者受到改为FROZEN或THAWED THAWED :恢复进程 ns ns子系统是一个比较特殊的子系统。ns子系统没有自己的控制文件,而且没有属于自己的状态信息。
ns子系统实际上是提供了一种同命名空间的进程聚类的机制。具有相同命名空间的进程会在相同cgroup中。
net_cls net_cls.classid :控制该control的类ID(基于tc) 格式如下:0xAAAABBBB A代码主类编号,B代码副类编号,如果没有就写0,并且0是可以省略的 0x10001=0x0000100001=0x1:1 net_prio net_prio.prioidx:只读文件,包含内核中用来表示该group的唯一整数值 net_prio.ifpriomap:指定各个网卡的该group的优先级格式: eth0 2 注意,1:子group默认使用父group的优先级 :2:值越大优先级越低
1.5 Cgroups文件系统
Cgroup提供了一个伪文件系统来对上面的各个子系统及参数进行管理
Cgroups用户空间的管理是通过cgroup文件系统实现的。当创建一个cgroup实例时至少指定一种子系统,这样新建的进程组在访问子系统对应的子资源时,就有了相应的限制。 Cgroup的文件系统实现类似linux的VFS Cgroup中进程组的层级关系与linux中进程的层级关系类似,在linux中一个进程通过fork()调用创建了一个子进程,这2个进程间存在父子的关系,并且子进程可以继承父进程的一些资源,系统中的进程形成一个树形的等级关系,每个进程的位置也都是唯一的。对于Cgroup来说,cgroup实例间也是有具体级别关系的,子group会继承父group对资源的控制属性,该层级关系是为了更细粒度的进程资源控制
比如创建一个层级:
mount -t cgroup -o cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem
创建一个cgroup:
cd /cgroup/cpu_and_mem
mkdir foo
通过以上两个命令,我们就在刚才创建的层级下创建了一个叫foo的cgroup。
你再cd foo,然后ls,直接就能看到刚刚创建的文件夹下就已经有了一些东西
2:cgroup基本管理及应用
2.1 cgroup的管理
redhat通过了一个管理套件:libcgroup
安装:yum install libcgroup 该管理套件提供了一个服务:cgconfig,配置文件为:/etc/cgconfig.conf 改配置文件主要分为2快:mount 和 group mount 确定每个子系统的挂载位置 mount { cpuset = /cgroup/red; #多个子系统以冒号隔开,并用行分离 } 等于: mkdir /cgroup/red mount -t cgroup -o cpuset red /cgroup/red group 管理cgroup实体和相应子系统的参数 风格如下: group <name> { perm { #权限这块是可选的 task { uid = <task user>, gid = <task gropu>, } admin { uid = root, gid = root, } } <controller>{ <param name> = <param value>, .... } .... } 示例: group test {
perm { admin { #指定了那个用户可以调整限制的参数
uid = root;
gid = root;
} task { #指定了那个用户可以调整限制的进程,只需将进程号输入到tasks文件中即可
uid = halfss;
gid = halfss;
}
}
memory {
memory.limit_in_bytes = 53687091;
}
} 这样的配置相当于执行了如下一些命令: mkdir /cgroup/memory/test chown -R root:root /cgroup/memory/test chown -R halfss:halfss /cgroup/memory/test/tasks echo 53687091 > /cgroup/memory/test/memory.limit_in_bytes 注意: 1:该配置文件需要cgconfig服务重启后生效 2:重启的时候不能有用户停留在cgroup的子系统中 上述的这些功能,mount和group的管理,即可以通过刚才的配置文件也可以通过命令行。无论是配置文件还是命令行对cgroup的管理最终都是修改的cgroup的文件系统,而该文件系统是无状态的;也就意味着通过命令调整完毕后(可以通过命令生成配置文件当做快照),重启后你做的工作都没了,所以这里推荐用配置文件进行管理。 这里说下基本的命令: 1:cgroup文件系统的挂载 mount -t cgroup -o subsystem1,sub system2 name /mount_point #mkdir /cgroup/cpu_and_mem #mount -t cgroup -o cpu,cpuset,memory cpuu_and_memory /cgroup/cpu_and_mem # lssubsys -am
ns
cpuacct
devices
freezer
net_cls
blkio
perf_event
net_prio
cpuset,cpu,memory /cgroup/cpu_and_mem # umount /cgroup/cpu_and_mem
# lssubsys 返回空了 2.2cgroup的测试测试: 1:测试代码 #!/usr/bin/env python #coding=utf8 a={} i=1 while True: a=i i+=1 在不做任何限制的情况下,该脚本会无限制的占用内存。 2:测试过程: 1:$ python test.py 2:ps aux | grep test halfss 14795 64.0 2.0 277156 165924 pts/0 R+ 02:32 0:01 python test.py
echo 14795 >> /cgroup/memory/test/tasks 3:$ python test.py
Killed #这里可以看得过一会后,该进程被kill掉了,其实可以让他暂停,具体操作参加上文 3:结语了解了cgroup的基本架构,工作原理及使用方法后,剩下的就是灵活的使用cgroup来为我们服务了 4:常见问题: 问题1:echo 3 >> tasks
-bash: echo: write error: Invalid argument 原因:不能将内核进程放到cgroup里,也是内核自己限制不了自己 问题3:echo 4 >> tasks bash: echo:write error:No space left on device 原因: group os {
memory {
memory.limit_in_bytes = 1G;
}
cpuset {
cpuset.cpus = 0;
cpuset.mems = 0; 在cpuset中限制cpu的时候没有同时限制内存
}
}
|