Cgroups是什么?
Cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:cpu,memory,IO等等)的机制。最初由google的工程师提出,后来被整合进Linux内核。Cgroups也是LXC为实现虚拟化所使用的资源管理手段,可以说没有cgroups就没有LXC。
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子系统可以将进程组挂起和恢复。
Cgroups相关概念及其关系
相关概念
1.任务(task)。在cgroups中,任务就是系统的一个进程。
2.控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。
3.层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。
4.子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
相互关系
1.每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。
2.一个子系统最多只能附加到一个层级。
3.一个层级可以附加多个子系统
4.一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级。
5.系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的cgroup。
Cgroups子系统介绍
blkio -- 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。
cpu -- 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。
cpuacct -- 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。
cpuset -- 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。
devices -- 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。
freezer -- 这个子系统挂起或者恢复 cgroup 中的任务。
memory -- 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。
net_cls -- 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
ns -- 名称空间子系统。
PS:Linux cgroups详解系列是本人毕业论文部分章节的草稿,算是为论文打前站,欢迎方家斧正。
在CentOS上安装Linux Container
1. 准备工作
先把Linux Container依赖的一些软件包给装上,并启动相关的服务。其中git将在第二步中用来下载源代码。- yum install -y automake gcc libvirt libcgroup git
- service cgconfig start
- service libvirtd start
2. 从github中下载Linux Container的代码
- git clone https://github.com/jewes/lxc.git
上面的git地址是笔者从官方源代码中fork出来的,对centos的模板做了些小修改,使得创建CentOS的Linux Container更加简单。
3. 编译
- ./autogen.sh && ./configure && make && sudo make install
4. 设置环境变量,可以考虑将其写到/etc/bashrc中去
- export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH
经过以上几步后,Linux Container就安装到你的机器上了。下面将介绍如何使用Linux Container。
使用Linux Container
1. 创建Linux Container
Linux Container自带了命令lxc-create来创建Container。不过,在构建Hadoop集群的时候,我们有如下需求:- 需要创建若干个Container,对应于集群中节点的数量
- 每个Container要分配一个固定的IP地址
- 每个Container要配置FQDN,也就是在运行hostname -f的时候,输出的内容要带有域名的,这在配置Kerberos的Hadoop集群中是必须的。
- Container之间通过机器名能互相访问
为此,笔者在lxc-create命令的基础上写了小脚本create_containers.sh(该脚本位于源代码的根目录下),使得能用一条命令就创建出我们需要数量的Container。
使用方法举例:
- # 需要root权限,因为lxc-create需要root权限
- ./create_containers.sh -n 5 -s 111
上述命令将创建5个Container,Container的IP地址从192.168.122.111开始,相关信息如下:
hostname/container name
|
fqdn
|
ip address
|
---|---|---|
lxc111 | lxc111.example.com | 192.168.122.111 |
lxc112 | lxc112.example.com | 192.168.122.112 |
lxc113 | lxc113.example.com | 192.168.122.113 |
lxc114 | lxc114.example.com | 192.168.122.114 |
lxc115 | lxc115.example.com | 192.168.122.115 |
2. 常用的Linux Container的管理工具:
lxc-start: 启动一个Linux Container- # -d 表示以daemon来启动该Container
- lxc-start -n <container name> -d
lxc-info:显示一个Linux Container的信息
- lxc-info -n <container name>
lxc-stop: 停止一个Container
- lxc-stop -n <container name>
lxc-destroy: 销毁一个Linux Container
- lxc-destroy -n <container name>
你也可以用ssh方式登入Container,这样就和使用一台普通Linux机器没有区别了,你可以在里面用yum来安装需要的软件,当然也可以部署Hadoop了。
secure container
对于配置的权限,还是会有要求,container-executor.cfg文件需要root权限,和其父目录等向上拥有者为root,所以,可以把改文件放置到/etc/目录下满足其要求,同时方便配置同步管理,然而,container-executor的默认读取路径是$HADOOP_HOME/etc/hadoop/目录下,所以我们需要传入指定路径去重新编译container-executor,该文件权限可以设置为6050。
mvn package -Pdist,native -DskipTests -Dtar -Dcontainer-executor.conf.dir=/etc
编译完成后,就可以检查是否成功了
strings container-executor | grep etc
关于Cgroup
要开启该功能,在新机器中要确保安装开启和内核支持该功能
在yarn配置了CGroup的模式下,它是通过cpu.cfs_period_us,cpu.cfs_quota_us,cpu.shares来对container进行CPU的资源控制。
举个例子:
假设我们设置yarn.nodemanager.resource.percentage-physical-cpu-limit参数为80和yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage参数为true(假设测试虚拟机的核数为4),在该模式下所有container能使用的核数为
yarnProcessors = nodeCpuPercentage * numProcessors = 0.8 * 4 = 3.2
CPU shares为
cpuShares = CPU_DEFAULT_WEIGHT * containerVCores = 1024 * 1
其中CPU_DEFAULT_WEIGHT=1024代表一个CPU时间
即containers能使用的核数量为3.2个,然后通过公式:
containerCPU = (containerVCores * yarnProcessors) / nodeVCores
注:nodeVCore为配置NM的核数为4,containerVCores为应用为每个container申请的核数
即按照上面的公式得到containerCPU为1*3.2除以4=0.8,然后根据quotaUS = periodUS * containerCPU,即1000*1000*0.8得到800000,即cpu.cfs_quota_us的值,即该进程使用的CPU不会超过80%,当应用申请的container核数为2时,得到shares为2048,意味着其将会获得比1024多2倍的CPU时间,由此类推。
cgroup控制cpu,/proc/mounts下有一个/sys/fs/cgroup/cpu配置路径,此部分是cgroup用来控制cpu隔离的路径配置。
在/sys/fs/cgroup/cpu下添加子路径hadoop-yarn,这个路径是yarn用来控制cpu的隔离的根路径
通过yarn.nodemanager.linux-container-executor.cgroups.hierarchy配置。
每一个container启动时根据container的名称在根路径下创建子路径来控制这个container的cpu资源
yarn的cgroup控制:
cgroups 里,可以用 cpu.cfs_period_us 和 cpu.cfs_quota_us 来限制该组中的所有进程在单位时间里可以使用的 cpu 时间。
这里的 cfs 是完全公平调度器的缩写。
cpu.cfs_period_us 就是时间周期(每个cpu core),默认为 1000000,即1秒。
cpu.cfs_quota_us 就是在这期间内可使用的 cpu 时间,默认 -1,即无限制。
cpu.cfs_quota_us 的值在单core的情况下小于或等于cpu.cfs_period_us的值,在多core的情况下大于这个值。
配置项:yarn.nodemanager.resource.percentage-physical-cpu-limit,默认值100,表示不显示cpu资源。
这个配置用于限制yarn对当前的nodemanager的物理内存的使用百分比,通过这个配置与cpu的逻辑core进行乘法,得到可用的cpu的core.
如果配置有对cpu使用的阀值时,对yarn的根路径的cgroup的控制:
按1000*1000微秒为cpu的调度周期除以cpu的可用的core的个数,
得到yarn的根路径的cpu.cfs_period_us的值,这是每个core的cpu调度周期(最小调度周期为1000微秒)。
cpu.cfs_period_us与cpu.cfs_quota_us值的计算:
在yarn中按1000*1000微秒来划分一次cpu的时间片调度周期.
=>如果可使用的cpu core的个数小于1 core,这个时候cpu.cfs_period_us的值必须大于cpu.cfs_quota_us的值
cpu.cfs_period_us的值为1000*1000微秒
cpu.cfs_quota_us的值为(1000*1000微秒)/可用的cpu core的个数,如果这个值小于1000,设置这个值为1000
=>如果可使用的cpu core的个数大于或等于1 core时,
cpu.cfs_period_us的值是一个小于或等于cpu.cfs_quota_us的值,由(1000*1000微秒)/可用的cpu core的个数得到
cpu.cfs_quota_us的值为1000*1000微秒
如果计算出来cpu.cfs_period_us的值小于1000微秒时,设置cpu.cfs_period_us的值为1000,同时设置cpu.cfs_quota_us的值为-1
=>最后说下:
如果cpu core是10时,那么每个cpu core的period_us的周期为100ms,quota_us是1s,
也就是说这个任务可以在每个cpu core中跑100ms的时间周期
yarn中cgroup针对每个container的cpu控制:
cpu.shares=>用于分配cpu执行的权重,按1024*container的vcore的个数来得到cpu的执行权重
如:一个container分配2core,一个conainer分配3core,那么他们的执行权重为2:3
cpu.cfs_period_us与cpu.cfs_quota_us的值,默认使用根路径的值.
=>如果yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage配置为true时,默认为false.
这个配置用于控制是否根据nodemanager配置的vcore来计算container分配的vcore的实际可使用cpu core.
(实际可用的cpu core的个数)=计算(container分配的vcore的个数*可用的cpu的core的个数)/nm配置的vcore的个数
nm配置vcore的配置项:yarn.nodemanager.resource.cpu-vcores,默认值8(可根据实际情况来进行分配).
cpu.cfs_period_us与cpu.cfs_quota_us的计算公式
=>(实际可用的cpu core的个数)小于1 core:
cpu.cfs_period_us的值为1000*1000微秒
cpu.cfs_quota_us的值为(1000*1000微秒)/(实际可用的cpu core的个数),如果这个值小于1000,设置这个值为1000
=>(实际可用的cpu core的个数)大于或等于1 core:
cpu.cfs_period_us的值是一个小于或等于cpu.cfs_quota_us的值,由(1000*1000微秒)/(实际可用的cpu core的个数)
cpu.cfs_quota_us的值为1000*1000微秒
=>如果计算出来cpu.cfs_period_us的值小于1000微秒时:
设置cpu.cfs_period_us的值为1000,同时设置cpu.cfs_quota_us的值为-1