veth

Posted by 夏泽民

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: mtu 65536 qdisc noop state DOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 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 配置network namespace的网口 # ip netns exec sunldnamespace01 ifconfig sunldveth01 100.2.96.2/16 up # ip netns exec sunldnamespace01 ip addr list 显示信息如下 1: lo: mtu 65536 qdisc noop state DOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 21: sunldveth01: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 link/ether da:a1:36:d1:3b:36 brd ff:ff:ff:ff:ff:ff inet 100.2.96.2/16 brd 100.2.255.255 scope global sunldveth01 valid_lft forever preferred_lft forever 开启空间脚本 #可以使用这条命令开启一个 ns0 的 shell ip netns exec ns0 sh network namespace 创建network namespace # ip netns add blue # ip netns list blue 添加网口到namespace 先创建veth # ip link add veth0 type veth peer name veth1 在当前namespace可以看到veth0和veth1 # ip link list 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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 qlen 1000 link/ether 00:0c:29:b2:cf:72 brd ff:ff:ff:ff:ff:ff 3: veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether ae:0d:00:e1:11:38 brd ff:ff:ff:ff:ff:ff 4: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 42:e7:50:d4:bb:c5 brd ff:ff:ff:ff:ff:ff 将veth1加到namespace “blue” # ip link set veth1 netns blue 此时,当前namepapce只能看到veth0。 通过如下命令可以查看blue namespace的网口 # ip netns exec blue ip link list 配置network namespace的网口 通过ip netns exec可以配置namespace的网口 # ip netns exec blue ifconfig veth1 172.17.42.2/16 up network namespace的网口与物理网卡的通信通过bridge来实现。 # add the namespaces ip netns add ns1 ip netns add ns2 # create the veth pair ip link add tap1 type veth peer name tap2 # move the interfaces to the namespaces ip link set tap1 netns ns1 ip link set tap2 netns ns2 # bring up the links ip netns exec ns1 ip link set dev tap1 up ip netns exec ns2 ip link set dev tap2 up



linux namespace

Posted by 夏泽民

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//uid_map 和 /proc//gid_map 这两个文件。这两个文件的格式为: ID-inside-ns ID-outside-ns length 其中: 第一个字段ID-inside-ns表示在容器显示的UID或GID, 第二个字段ID-outside-ns表示容器外映射的真实的UID或GID。 第三个字段表示映射的范围,一般填1,表示一一对应。 比如,把真实的uid=1000映射成容器内的uid=0 $ cat /proc/2465/uid_map 0 1000 1 再比如下面的示例:表示把namespace内部从0开始的uid映射到外部从0开始的uid,其最大范围是无符号32位整形 $ cat /proc/$$/uid_map 0 0 4294967295 另外,需要注意的是: 写这两个文件的进程需要这个namespace中的CAP_SETUID (CAP_SETGID)权限(可参看Capabilities) 写入的进程必须是此user namespace的父或子的user namespace进程。 另外需要满如下条件之一:1)父进程将effective uid/gid映射到子进程的user namespace中,2)父进程如果有CAP_SETUID/CAP_SETGID权限,那么它将可以映射到父进程中的任一uid/gid。 当你启动一个Docker容器后,你可以使用ip link show或ip addr show来查看当前宿主机的网络情况(我们可以看到有一个docker0,还有一个veth22a38e6的虚拟网卡——给容器用的) # 把容器里的 veth-ns1改名为 eth0 (容器外会冲突,容器内就不会了) ip netns exec ns1 ip link set dev veth-ns1 name eth0 # 为容器中的网卡分配一个IP地址,并激活它 ip netns exec ns1 ifconfig eth0 192.168.10.11/24 up # 上面我们把veth-ns1这个网卡按到了容器中,然后我们要把lxcbr0.1添加上网桥上 brctl addif lxcbr0 lxcbr0.1 # 为容器增加一个路由规则,让容器可以访问外面的网络 ip netns exec ns1 ip route add default via 192.168.10.1 # 在/etc/netns下创建network namespce名称为ns1的目录, # 然后为这个namespace设置resolv.conf,这样,容器内就可以访问域名了 mkdir -p /etc/netns/ns1 echo "nameserver 8.8.8.8" > /etc/netns/ns1/resolv.conf 上面基本上就是docker网络的原理了,只不过, Docker的resolv.conf没有用这样的方式,而是用了Mount Namesapce的那种方式 另外,docker是用进程的PID来做Network Namespace的名称的。



docker_net

Posted by 夏泽民

由于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容器通过独立IP暴露给局域网的方法

Posted by 夏泽民

Docker容器非常轻量,系统开销非常少,比VMware或者VirtualBox用起来方便,部署起来也非常容易。官方推荐我们通过端口映射的方式把Docker容器的服务提供给宿主机或者局域网其他容器使用。一般过程是: 1、Docker进程通过监听宿主机的某个端口,将该端口的数据包发送给Docker容器 2、宿主机可以打开防火墙让局域网其他设备通过访问宿主机的端口进而访问docker的端口



linux cgroup

Posted by 夏泽民

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中:

echo 3529 » /sys/fs/cgroup/cpu/haoel/tasks

然后,就会在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 然后,在我们另外一边:

创建memory cgroup

$ mkdir /sys/fs/cgroup/memory/haoel $ echo 64k > /sys/fs/cgroup/memory/haoel/memory.limit_in_bytes

把上面的进程的pid加入这个cgroup

$ 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以下



Search

Popular posts

Anything in here will be replaced on browsers that support the canvas element

Recent posts

This blog is maintained by 夏泽民

Get in touch with me at 465474307@qq.com

Subscribe to our mailing list

* indicates required