Linux container 中用到一个叫做veth的东西,这是一种新的设备,专门为 container 所建。veth 从名字上来看是 Virtual ETHernet 的缩写,它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace。veth 设备是成对的,一个是 container 之中,另一个在 container 之外,即在真实机器上能看到的。
VETH设备总是成对出现,送到一端请求发送的数据总是从另一端以请求接受的形式出现。创建并配置正确后,向其一端输入数据,VETH会改变数据的方向并将其送入内核网络子系统,完成数据的注入,而在另一端则能读到此数据。(Namespace,其中往veth设备上任意一端上RX到的数据,都会在另一端上以TX的方式发送出去)veth工作在L2数据链路层,veth-pair设备在转发数据包过程中并不串改数据包内容。
显然,仅有veth-pair设备,容器是无法访问网络的。因为容器发出的数据包,实质上直接进入了veth1设备的协议栈里。如果容器需要访问网络,需要使用bridge等技术,将veth1接收到的数据包通过某种方式转发出去
创建veth的命令如下:
ip link add name veth0 type veth0 peer name veth1
veth设备特点
veth和其它的网络设备都一样,一端连接的是内核协议栈
veth设备是成对出现的,另一端两个设备彼此相连
一个设备收到协议栈的数据发送请求后,会将数据发送到另一个设备上去
常用命令
创建network namespace
# ip netns add sunldnamespace01
# ip netns list
sunldnamespace01
创建veth
# ip link add sunldveth01 type veth peer name sunldveth02
# ip link list
显示信息如下:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:2d:d4:23 brd ff:ff:ff:ff:ff:ff
20: sunldveth02: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether c6:bb:c0:d0:54:71 brd ff:ff:ff:ff:ff:ff
21: sunldveth01: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether da:a1:36:d1:3b:36 brd ff:ff:ff:ff:ff:ff
添加网卡到namespace
# ip link set sunldveth01 netns sunldnamespace01
查看当前namespace中的veth,只有sunldveth02
# ip link list
显示信息如下
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:2d:d4:23 brd ff:ff:ff:ff:ff:ff
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether ba:d9:d4:48:55:65 brd ff:ff:ff:ff:ff:ff
20: sunldveth02: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether c6:bb:c0:d0:54:71 brd ff:ff:ff:ff:ff:ff
通过命令查看sunldnamespace01中的veth
# ip netns exec sunldnamespace01 ip link list
显示信息如下
1: lo:
Linux Namespace是Linux提供的一种内核级别环境隔离的方法。不知道你是否还记得很早以前的Unix有一个叫chroot的系统调用(通过修改根目录把用户jail到一个特定目录下),chroot提供了一种简单的隔离模式:chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上,提供了对UTS、IPC、mount、PID、network、User等的隔离机制。
举个例子,我们都知道,Linux下的超级父亲进程的PID是1,所以,同chroot一样,如果我们可以把用户的进程空间jail到某个进程分支下,并像chroot那样让其下面的进程 看到的那个超级父进程的PID为1,于是就可以达到资源隔离的效果了(不同的PID namespace中的进程无法看到彼此)
Linux Namespace 有如下种类,官方文档在这里《Namespace in Operation》
分类 系统调用参数 相关内核版本
Mount namespaces CLONE_NEWNS Linux 2.4.19
UTS namespaces CLONE_NEWUTS Linux 2.6.19
IPC namespaces CLONE_NEWIPC Linux 2.6.19
PID namespaces CLONE_NEWPID Linux 2.6.24
Network namespaces CLONE_NEWNET 始于Linux 2.6.24 完成于 Linux 2.6.29
User namespaces CLONE_NEWUSER 始于 Linux 2.6.23 完成于 Linux 3.8)
主要是三个系统调用
clone() – 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述参数达到隔离。
unshare() – 使某进程脱离某个namespace
setns() – 把某进程加入到某个namespace
User Namespace主要是用了CLONE_NEWUSER的参数。使用了这个参数后,内部看到的UID和GID已经与外部不同了,默认显示为65534。那是因为容器找不到其真正的UID所以,设置上了最大的UID(其设置定义在/proc/sys/kernel/overflowuid)。
要把容器中的uid和真实系统的uid给映射在一起,需要修改 /proc/
由于Boot2Docker的存在,造成了三层Mac->VirtualBox->Docker网络,由VirtualBox到Docker的映射可以通过run容器的时候指定-p参数实现,而从宿主机到VirtualBox端口映射需要通过下述方法实现: 查询虚拟机网络: VBoxManagelistvms查询虚拟机网络状态,默认虚拟机名为′default′ VBoxManage showvminfo “default” | grep NIC 2.关闭运行中的虚拟机 由于Boot2Docker会自动运行VirtualBox中的虚拟机,所以在设置网络映射时必须先关闭运行中的虚拟机。否则,将出现The machine ‘boot2docker’ is already locked for a session (or being unlocked)的错误提示 $ VBoxManage controlvm “default” poweroff 修改虚拟机与Mac系统的网络映射 根据实际需要进行网络映射,其中 rulename: 自定义规则名称 hostip: Mac访问地址,可不填 hostport: Mac映射端口 guestip: 虚拟机访问地址,可不填 guestport: 虚拟机映射端口 $ VBoxManage modifyvm “dufault” –natpf1 “,
Docker容器非常轻量,系统开销非常少,比VMware或者VirtualBox用起来方便,部署起来也非常容易。官方推荐我们通过端口映射的方式把Docker容器的服务提供给宿主机或者局域网其他容器使用。一般过程是: 1、Docker进程通过监听宿主机的某个端口,将该端口的数据包发送给Docker容器 2、宿主机可以打开防火墙让局域网其他设备通过访问宿主机的端口进而访问docker的端口
Namespace解决的问题主要是环境隔离的问题,这只是虚拟化中最最基础的一步,我们还需要解决对计算机资源使用上的隔离。也就是说,虽然你通过Namespace把我Jail到一个特定的环境中去了,但是我在其中的进程使用用CPU、内存、磁盘等这些计算资源其实还是可以随心所欲的。所以,我们希望对进程进行资源利用上的限制或控制。这就是Linux CGroup出来了的原因。
Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。这个项目最早是由Google的工程师在2006年发起(主要是Paul Menage和Rohit Seth),最早的名称为进程容器(process containers)。在2007年时,因为在Linux内核中,容器(container)这个名词太过广泛,为避免混乱,被重命名为cgroup,并且被合并到2.6.24版的内核中去。然后,其它开始了他的发展。
Linux CGroupCgroup 可让您为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。
主要提供了如下功能:
Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。
Accounting: 一些审计或一些统计,主要目的是为了计费。
Control: 挂起进程,恢复执行进程。
使用 cgroup,系统管理员可更具体地控制对系统资源的分配、优先顺序、拒绝、管理和监控。可更好地根据任务和用户分配硬件资源,提高总体效率。
在实践中,系统管理员一般会利用CGroup做下面这些事(有点像为某个虚拟机分配资源似的):
隔离一个进程集合(比如:nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。
为这组进程 分配其足够使用的内存
为这组进程分配相应的网络带宽和磁盘存储限制
限制访问某些设备(通过设置设备的白名单)
Linux把CGroup这个事实现成了一个file system,你可以mount。在我的Ubuntu 14.04下,你输入以下命令你就可以看到cgroup已为你mount好了。
$ mount -t cgroup
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu type cgroup (rw,relatime,cpu)
cgroup on /sys/fs/cgroup/cpuacct type cgroup (rw,relatime,cpuacct)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,relatime,memory)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,relatime,freezer)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,relatime,blkio)
cgroup on /sys/fs/cgroup/net_prio type cgroup (rw,net_prio)
cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,net_cls)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,relatime,hugetlb)
或者使用lssubsys命令:
$ lssubsys -m
cpuset /sys/fs/cgroup/cpuset
cpu /sys/fs/cgroup/cpu
cpuacct /sys/fs/cgroup/cpuacct
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
blkio /sys/fs/cgroup/blkio
net_cls /sys/fs/cgroup/net_cls
net_prio /sys/fs/cgroup/net_prio
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb
我们可以看到,在/sys/fs下有一个cgroup的目录,这个目录下还有很多子目录,比如: cpu,cpuset,memory,blkio……这些,这些都是cgroup的子系统。分别用于干不同的事的。
如果你没有看到上述的目录,你可以自己mount,下面给了一个示例:
mkdir cgroup
mount -t tmpfs cgroup_root ./cgroup
mkdir cgroup/cpuset
mount -t cgroup -ocpuset cpuset ./cgroup/cpuset/
mkdir cgroup/cpu
mount -t cgroup -ocpu cpu ./cgroup/cpu/
mkdir cgroup/memory
mount -t cgroup -omemory memory ./cgroup/memory/
一旦mount成功,你就会看到这些目录下就有好文件了,比如,如下所示的cpu和cpuset的子系统:
$ ls /sys/fs/cgroup/cpu /sys/fs/cgroup/cpuset/
/sys/fs/cgroup/cpu:
cgroup.clone_children cgroup.sane_behavior cpu.shares release_agent
cgroup.event_control cpu.cfs_period_us cpu.stat tasks
cgroup.procs cpu.cfs_quota_us notify_on_release user
/sys/fs/cgroup/cpuset/:
cgroup.clone_children cpuset.mem_hardwall cpuset.sched_load_balance
cgroup.event_control cpuset.memory_migrate cpuset.sched_relax_domain_level
cgroup.procs cpuset.memory_pressure notify_on_release
cgroup.sane_behavior cpuset.memory_pressure_enabled release_agent
cpuset.cpu_exclusive cpuset.memory_spread_page tasks
cpuset.cpus cpuset.memory_spread_slab user
cpuset.mem_exclusive cpuset.mems
你可以到/sys/fs/cgroup的各个子目录下去make个dir,你会发现,一旦你创建了一个子目录,这个子目录里又有很多文件了。
hchen@ubuntu:/sys/fs/cgroup/cpu$ sudo mkdir haoel
[sudo] password for hchen:
~:/sys/fs/cgroup/cpu$ ls ./haoel
cgroup.clone_children cgroup.procs cpu.cfs_quota_us cpu.stat tasks
cgroup.event_control cpu.cfs_period_us cpu.shares notify_on_release
好了,我们来看几个示例。
CPU 限制
假设,我们有一个非常吃CPU的程序,叫deadloop,其源码如下:
DEADLOOP.C
int main(void)
{
int i = 0;
for(;;) i++;
return 0;
}
用sudo执行起来后,毫无疑问,CPU被干到了100%(下面是top命令的输出)
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3529 root 20 0 4196 736 656 R 99.6 0.1 0:23.13 deadloop
然后,我们这前不是在/sys/fs/cgroup/cpu下创建了一个haoel的group。我们先设置一下这个group的cpu利用的限制:
# cat /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us
-1
# echo 20000 > /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us
我们看到,这个进程的PID是3529,我们把这个进程加到这个cgroup中:
然后,就会在top中看到CPU的利用立马下降成20%了。(前面我们设置的20000就是20%的意思)
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3529 root 20 0 4196 736 656 R 19.9 0.1 8:06.11 deadloop
然后,在我们另外一边:
$ mkdir /sys/fs/cgroup/memory/haoel $ echo 64k > /sys/fs/cgroup/memory/haoel/memory.limit_in_bytes
$ echo [pid] > /sys/fs/cgroup/memory/haoel/tasks
你会看到,一会上面的进程就会因为内存问题被kill掉了。
磁盘I/O限制
我们先看一下我们的硬盘IO,我们的模拟命令如下:(从/dev/sda1上读入数据,输出到/dev/null上)
sudo dd if=/dev/sda1 of=/dev/null
我们通过iotop命令我们可以看到相关的IO速度是55MB/s(虚拟机内):
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8128 be/4 root 55.74 M/s 0.00 B/s 0.00 % 85.65 % dd if=/de~=/dev/null…
然后,我们先创建一个blkio(块设备IO)的cgroup
mkdir /sys/fs/cgroup/blkio/haoel
并把读IO限制到1MB/s,并把前面那个dd命令的pid放进去(注:8:0 是设备号,你可以通过ls -l /dev/sda1获得):
# echo ‘8:0 1048576’ > /sys/fs/cgroup/blkio/haoel/blkio.throttle.read_bps_device
# echo 8128 > /sys/fs/cgroup/blkio/haoel/tasks
再用iotop命令,你马上就能看到读速度被限制到了1MB/s左右。
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8128 be/4 root 973.20 K/s 0.00 B/s 0.00 % 94.41 % dd if=/de~=/dev/null…
CGroup的子系统
好了,有了以上的感性认识我们来,我们来看看control group有哪些子系统:
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 — 这个子系统用来设计网络流量的优先级
hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。
注意,你可能在Ubuntu 14.04下看不到net_cls和net_prio这两个cgroup,你需要手动mount一下:
$ sudo modprobe cls_cgroup
$ sudo mkdir /sys/fs/cgroup/net_cls
$ sudo mount -t cgroup -o net_cls none /sys/fs/cgroup/net_cls
$ sudo modprobe netprio_cgroup
$ sudo mkdir /sys/fs/cgroup/net_prio
$ sudo mount -t cgroup -o net_prio none /sys/fs/cgroup/net_prio
关于各个子系统的参数细节,以及更多的Linux CGroup的文档,你可以看看下面的文档:
Linux Kernel的官方文档
Redhat的官方文档
CGroup的术语
CGroup有下述术语:
任务(Tasks):就是系统的一个进程。
控制组(Control Group):一组按照某种标准划分的进程,比如官方文档中的Professor和Student,或是WWW和System之类的,其表示了某进程组。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组。而资源的限制是定义在这个组上,就像上面示例中我用的haoel一样。简单点说,cgroup的呈现就是一个目录带一系列的可配置文件。
层级(Hierarchy):控制组可以组织成hierarchical的形式,既一颗控制组的树(目录结构)。控制组树上的子节点继承父结点的属性。简单点说,hierarchy就是在一个或多个子系统上的cgroups目录树。
子系统(Subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多,也在不断增加中。
下一代的CGroup
上面,我们可以看到,CGroup的一些常用方法和相关的术语。一般来说,这样的设计在一般情况下还是没什么问题的,除了操作上的用户体验不是很好,但基本满足我们的一般需求了。
不过,对此,有个叫Tejun Heo的同学非常不爽,他在Linux社区里对cgroup吐了一把槽,还引发了内核组的各种讨论。
对于Tejun Heo同学来说,cgroup设计的相当糟糕。他给出了些例子,大意就是说,如果有多种层级关系,也就是说有多种对进程的分类方式,比如,我们可以按用户来分,分成Professor和Student,同时,也有按应用类似来分的,比如WWW和NFS等。那么,当一个进程即是Professor的,也是WWW的,那么就会出现多层级正交的情况,从而出现对进程上管理的混乱。另外,一个case是,如果有一个层级A绑定cpu,而层级B绑定memory,还有一个层级C绑定cputset,而有一些进程有的需要AB,有的需要AC,有的需要ABC,管理起来就相当不易。
层级操作起来比较麻烦,而且如果层级变多,更不易于操作和管理,虽然那种方式很好实现,但是在使用上有很多的复杂度。你可以想像一个图书馆的图书分类问题,你可以有各种不同的分类,分类和图书就是一种多对多的关系。
所以,在Kernel 3.16后,引入了unified hierarchy的新的设计,这个东西引入了一个叫__DEVEL__sane_behavior的特性(这个名字很明显意味目前还在开发试验阶段),它可以把所有子系统都挂载到根层级下,只有叶子节点可以存在tasks,非叶子节点只进行资源控制。
我们mount一下看看:
$ sudo mount -t cgroup -o __DEVEL__sane_behavior cgroup ./cgroup
$ ls ./cgroup
cgroup.controllers cgroup.procs cgroup.sane_behavior cgroup.subtree_control
$ cat ./cgroup/cgroup.controllers
cpuset cpu cpuacct memory devices freezer net_cls blkio perf_event net_prio hugetlb
我们可以看到有四个文件,然后,你在这里mkdir一个子目录,里面也会有这四个文件。上级的cgroup.subtree_control控制下级的cgroup.controllers。
cgroup只有上线控制下级,无法传递到下下级。所以,C和D中没有memory的限制,E中没有blkio和memory的限制。而本层的cgroup.controllers文件是个只读的,其中的内容就看上级的subtree_control里有什么了。
任何被配置过subtree_control的目录都不能绑定进程,根结点除外。所以,A,C,D,E可以绑上进程,但是B不行。
我们可以看到,这种方式干净的区分开了两个事,一个是进程的分组,一个是对分组的资源控制(以前这两个事完全混在一起),在目录继承上增加了些限制,这样可以避免一些模棱两可的情况。
当然,这个事还在演化中,cgroup的这些问题这个事目前由cgroup的吐槽人Tejun Heo和华为的Li Zefan同学负责解决中。总之,这是一个系统管理上的问题,而且改变会影响很多东西,但一旦方案确定,老的cgroup方式将一去不复返。
Cgroup是进行分组化管理的Linux内核功能,具体的资源管理是通过子系统来完成的。可以理解为子系统就是资源控制器,每种子系统就是一个资源的分配器,比如cpu子系统是控制cpu时间分配的,使用方式如下
安装(ubuntu)
#apt-get install cgroup-bin
基本命令
cgclassify – cgclassify命令是用来将运行的任务移动到一个或者多个cgroup。
cgclear – cgclear 命令是用来删除层级中的所有cgroup。
cgconfig.conf – 在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 命令列出包含指定子系统的层级
子系统说明
可以使用lssubsys -a来列出系统支持多少种子系统,和:比如cpu是控制cpu时间片的,memory是控制内存使用的
#lssubsys -a
cpuset
cpu,cpuacct
memory
devices
freezer
net_cls,net_prio
blkio
perf_event
hugetlb
主要的几种子系统说明如下:
blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
cpu 这个子系统使用调度程序为cgroup任务提供cpu的访问。
cpuacct 产生cgroup任务的cpu资源报告。
cpuset 如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
devices 允许或拒绝cgroup任务对设备的访问。
freezer 暂停和恢复cgroup任务。
memory 设置每个cgroup的内存限制以及产生内存资源报告。
net_cls 标记每个网络包以供cgroup方便使用。
ns 名称空间子系统
perf_event: 增加了对每group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程
要为Cgroup分配限制的资源,首先要挂载子系统,然后才有控制组,比如想要对目标程序进行内存限制,那就需要挂载memory子系统
使用lssubsys -am来显示已经挂载的子系统
#lssubsys -am
cpuset /sys/fs/cgroup/cpuset
cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
net_cls,net_prio /sys/fs/cgroup/net_cls,net_prio
blkio /sys/fs/cgroup/blkio
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb
可以手动挂载或者卸载子系统,如执行umount /sys/fs/cgroup/memory,memory子系统就被卸载了,这时候手动执行# mount -t cgroup -o memory memory /sys/fs/cgroup/memory就又挂载上了。
要确保需要的子系统都挂上了,不然创建控制组的时候会报错 is not mounted
#cgcreate -g memory,cpu:/hzmali_test
cgcreate: can’t create cgroup /hzmali_test: Cgroup one of the needed subsystems is not mounted
如何创建control group(即需要资源管理的组)呢, 这里用cgcreate命令,当然也有其他方法, 如cgconfig.conf等
#cgcreate -g memory,cpu:/hzmali_test
这里有个重要特性:一个组可以同时做多个资源的限制,如这里我同时限制了memory和cpu,然后memory和cpu子系统目录下会自动生成这个组的目录和些文件,如memory
#/sys/fs/cgroup/memory/hzmali_test$ ls -lrt
文件很多,选几个重要的讲下:
tasks 可以将想要限制资源的进程都加到这个文件中
memory.max_usage_in_bytes内存的最大使用量,用来限制资源
-memory.soft_limit_in_bytes 和 memory.limit_in_bytes 的差异是,这个限制并不会阻止进程使用超过限额的内存,只是在系统内存不足时,会优先回收超过限额的进程占用的内存,使之向限定值靠拢。
memory.oom_control
包含一个标志(0或1)来开启或者关闭cgroup的OOM killer。如果开启(1),任务如果尝试申请内存超过允许,就会被系统OOM killer终止。OOM killer在每个使用cgroup内存子系统中都是默认开启的。如果需要关闭,则可以向memory.oom_control文件写入1:
# echo 1 > /sys/fs/cgroup/memory.oom_control
如果OOM killer关闭,那么进程尝试申请的内存超过允许,那么它就会被暂停,直到额外的内存被释放
memory.mem.usage_in_bytes 当前进程内存用量,因为现在还没有进程加到组里,就是0了
memory.mem.failcnt显示内存达到限制值的次数
Cgroup文档
Cgroup的使用细节,子系统和参数设置都可以可以在https://www.kernel.org/doc/Documentation/cgroups/中找到,继承等特性由于篇幅所限,可以看下文档
Cgroup实战
内存限制测试
用控制组限制目标程序内存使用为1000000 byte,当然,需要root执行
echo “1000000” >memory.limit_in_bytes
一般更推荐用cgset来设置数值
cgset -r memory.limit_in_bytes=1000000 hzmali_test
然后构造一个吃内存的程序,每运行一次内存使用就大幅增加
如果我不想把机器跑死,这里想要限制组里的进程的CPU使用,有2种做法
1.在cpu子系统中控制cpu调度的配额
先看下当前cpu分配情况
cat /sys/fs/cgroup/cpu/hzmali_test/cpu.cfs_quota_us
-1
cat /sys/fs/cgroup/cpu/hzmali_test/cpu.cfs_period_us
100000
-1表示无限制,这里改为50000,即相对于cpu.cfs_period_us 来说为50000/100000约占1个核50%的cpu时间
#./cpu_test.sh &
[1] 17709
# echo 17709 >/sys/fs/cgroup/cpu/hzmali_test/tasks
或者直接使用命令cgexec执行
cgexec -g cpu:hzmali_test ./cpu_test.sh
top了下基本上就是在50%的cpu占用
%Cpu0 : 50.5 us, 0.0 sy, 0.0 ni, 49.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 17709 root 20 0 25368 2020 1764 R 50.2 0.1 1:14.74 bash
2.在cpuset控制物理cpu的分配
当前使用了上面的方法后,我们发现进程的CPU使用都在Cpu0上,这次希望只用Cpu1来跑这个小程序
所以把控制组也加到cpuset
# cgcreate -g cpuset:/hzmali_test
看一下现在使用的cpu的设置
# cat /sys/fs/cgroup/cpuset/hzmali_test/cpuset.cpus
0-1
改为只用Cpu1,输入以下命令
# echo 1 > /sys/fs/cgroup/cpuset/hzmali_test/cpuset.cpus
# echo 17709 > /sys/fs/cgroup/cpuset/hzmali_test/tasks
或用命令
# cgset -r cpuset.cpus=’1’ hzmali_test
# cgclassify -g cpu,cpuset:hzmali_test 17709
top一下,内存的使用从CPU0到CPU1了
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
%Cpu1 : 50.3 us, 0.0 sy, 0.0 ni, 49.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
17709 root 20 0 25368 2108 2076 R 50.1 0.1 8:56.78 bash
IO限制测试
用dd对硬盘进行写操作
# dd if=/dev/sda of=/dev/null &
打开iotop看下IO速度
Total DISK READ : 100.37 M/s | Total DISK WRITE : 0.00 B/s
Actual DISK READ: 100.37 M/s | Actual DISK WRITE: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
18081 be/4 root 100.37 M/s 0.00 B/s 0.00 % 1.34 % dd if=/dev/sda of=/dev/null
为了控制IO速度,在blkio上创建控制组
# cgcreate -g blkio:/hzmali_test
查看下硬盘号
# ls -l /dev/sda
brw-rw—- 1 root disk 8, 0 Jul 25 22:46 /dev/sda
设置硬盘号和对应的读取速度限制,然后执行同样的命令
# cgset -r blkio.throttle.read_bps_device=”8:0 1000000” hzmali_test
# cgexec -g blkio:hzmali_test “dd if=/dev/sda of=/dev/null”
用iotop查看下,速度果然就降到1M以下