精《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #7 Cgroup、Namespace、Linux容器

HACK #7 Cgroup、Namespace、Linux容器

本节将介绍Cgroup与Namespace以及经过这两个功能实现的容器功能。
Cgroup
Cgroup(control group)是将任意进程进行分组化管理的Linux内核功能。Cgroup自己是提供将进程进行分组化管理的功能和接口的基础结构,I/O或内存的分配控制等具体的资源管理功能是经过这个功能来实现的。这些具体的资源管理功能称为Cgroup子系统或控制器。
Cgroup子系统有控制内存的Memory控制器、控制进程调度的CPU控制器等。运行中的内核可使用的Cgroup子系统由/proc/cgroup来确认。
Cgroup提供了一个cgroup虚拟文件系统,做为进行分组管理和各子系统设置的用户接口。要使用Cgroup,必须挂载cgroup文件系统。这时经过挂载选项指定使用哪一个子系统。这里指定debug这个没有实质功能的调试用子系统来挂载。javascript

# mount -t cgroup -o debug cgroup /cgroup

注意事项:这里所说的“虚拟文件系统”,是指procfs和sysfs这种不具备物理设备的文件系统。并非用来接纳文件系统差别的内核内部层layerVFS。
小贴士:关于cgroup文件系统的标准化挂载要点,是由开发论坛进行讨论的,但目前还没有得出结论。这里是挂载到/cgroup。
挂载后,在挂载位置下应该能够看到下列几个文件。这些是Cgroup呈现出来的特殊文件。css

# ls /cgroup
    cgroup.event_control    debug.current_css_set           debug.taskcount
    cgroup.procs            debug.current_css_set_cg_links  notify_on_release
    debug.cgroup_css_links  debug.current_css_set_refcount  release_agent
    debug.cgroup_refcount   debug.releasable                tasks

文件名前缀为cgroup的以及没有前缀的文件是由Cgroup的基础结构提供的特殊文件。而前缀为debug的文件是由debug子系统提供的特殊文件。Cgroup的子系统提供的特殊文件都会像这样加上子系统的前缀。所以,根据挂载时指定的选项,即所使用的子系统不一样,存在的特殊文件也不一样。可是Cgroup的基础结构所提供的特殊文件则是不管指定哪一种子系统都一直存在的。特殊文件分为只读文件和可读写文件。只读文件是为用户提供信息的文件。可读写的特殊文件经过写入值来更改Cgroup以及Cgroup子系统设置的文件。设置的值能够经过读入特殊文件来确认。在这些特殊文件中,最重要的是tasks特殊文件。其内容能够显示以下。html

# cat /cgroup/tasks
   1 2 3 4 ... 

虽然看上去只是一些数字的排列,但其实这些是属于这个分组的线程的线程ID(TID)。在这时,系统上运行的全部线程的TID都包含在/cgroup/tasks中。这就表示所有线程都属于这个分组。那么这里出现的“分组”又是什么呢?分组,就是体现为cgroup文件系统目录的线程的集合。因为/cgroup也是目录,所以它也表示一个分组。像这样位于挂载点最上层的目录是自动生成的分组,称为根分组。在这个阶段,只有/cgroup(即根分组)是系统上存在的惟一分组。
小贴士:英语中将经过Cgroup建立的分组称作cgroup,容易与表示结构的“Cgroup”混淆,因此这里仅称为“分组”。
下面尝试建立一个分组,也就是在/cgroup下建立子目录。其内容以下所示。java

# mkdir /cgroup/test
# ls /cgroup/test
    cgroup.event_control    debug.current_css_set           debug.taskcount
    cgroup.procs            debug.current_css_set_cg_links  notify_on_release
    debug.cgroup_css_links  debug.current_css_set_refcount  tasks
    debug.cgroup_refcount   debug.releasable

虽然是新生成的目录,可是已经有文件存在。cgroup文件系统在目录生成的同时就会在其中配置特殊文件。
/cgroup/test也和/cgroup同样有tasks。其内容以下。linux

# cat /cgroup/test/tasks

tasks的内容彷佛是空的,这表示这个分组内一个线程也没有。能够将适当的线程添加到这个分组中。要将线程添加到分组中,能够在tasks中写入该线程的TID。这里以添加shell自己为例。shell

# echo 
$$

    2474 # echo $$ > /cgroup/test/tasks # cat /cgroup/test/tasks 2474 3821 

tasks的内容中包含shell的TID(也是PID,即进程ID)—2474,能够看出这个shell已经属于test分组。除此之外,这个分组内还有另外一个TID为3821的线程,这是什么呢?咱们再来看一下tasks的内容。bootstrap

# cat /cgroup/test/tasks
   2474 3822 

结果竟然发生了变化。事实上这个改变的部分,是显示了tasks内容的cat进程的TID。最初的cat(3821)和第二次的cat(3822)是不一样的进程,TID也不一样,因此结果发生了变化。可是彷佛并无将cat进程添加到test分组中。其实,属于分组的进程一旦生成子进程,其子进程就会自动属于母进程。因为cat是shell的子进程,所以前者自动属于test分组。你们应该还记得,在挂载cgroup文件系统后,系统上的全部线程是属于根分组的。也就是说,除了将明确指定为新生成分组内的进程为祖先进程之外,生成的进程都属于根分组。
这时,再显示/cgroup/tasks的内容的话,应该不会显示shell的TID(2474)。这是由于shell不属于根分组,而是属于test分组。而后,再将这个shell返回到根分组。bash

# echo 2474 > /cgroup/tasks 

这样,shell的TID(2474)就再次属于/cgroup/tasks,而/cgroup/test/tasks就变空。若是分组中一个线程也没有,能够进行撤销。删除目录就能够撤销分组。服务器

# rmdir /cgroup/test

表2-1是每一个子系统中Cgroup都会提供的特殊文件列表。
表2-1 Cgroup提供的文件种类
image网络

这里仅介绍了最基本的Cgroup使用方法,也就是分组的建立、撤销和将线程添加到分组的方法。实际使用Cgroup时,应在将线程添加到分组后,在分组内的特殊文件中设置值,来控制系统的运行。
Namespace
使用Namespace(命名空间),可让每一个进程组具备独立的PID、IPC和网络空间。
能够向clone系统调用的第3个参数flags设置划分命名空间的标志,经过执行clone系统调用能够划分命名空间。
例如,划分PID命名空间后,在新生成的PID命名空间内进程的PID是从1开始的。重新PID为1的进程fork()分叉获得的进程,被封闭到这个新的PID命名空间,与其余PID命名空间分隔开。在新建立的PID命名空间中生成的进程,其PID有可能与存在于原PID命名空间中的进程相同,但因为两者的PID命名空间划分开,就不存在相互影响。
一样,也能够用PID、网络、文件系统的挂载空间、UTS(Universal Time sharing System)为对象进行资源划分。能够在clone系统调用的第3个参数中设置资源划分的种类,如表2-2所示。
表2-2 资源划分
image

Linux 容器
使用Cgroup和Namespace就能够实现容器。容器这个技术也称为操做系统虚拟化,是将一个内核所管理的资源划分红多个分组。
在容器中,CPU和内存资源是使用Cgroup来划分的。PID、IPC、网络等资源使用Namespace来划分。
LXC
Linux中实际安装的容器有LXC(Linux Container)。本节将以Fedora 14为例介绍LXC的使用方法。

# yum install lxc

要使用网络,还须要安装bridge-utils。

# yum install bridge-utils

在使用LXC以前,必须启用cgroup文件系统。使用下列命令挂载cgroup文件系统。

# mount -t cgroup cgroup /cgroup

另外,向/etc/fstab添加下列语句,就能够在系统启动时自动挂载cgroup文件系统。

cgroup /cgroup cgroup defaults 0 0 

首先,将bash shell进程放进容器。
这里要为容器中使用的文件系统准备一个/lxc目录。

# mkdir /lxc
# cd /lxc

而后准备做为容器内的根文件系统的目录。

# mkdir rootfs
# cd rootfs

还须要准备其余必要的目录(这些目录主要使用bind mount)。

# mkdir bin dev etc lib lib64 proc sbin sys usr var 

而后还要生成LXC的配置文件lxc.conf以及引用的fstab。

# vi /lxc/lxc.conf
lxc.utsname = lxc
lxc.rootfs = /lxc/rootfs lxc.mount = /lxc/fstab # vi /lxc/fstab /bin /lxc/rootfs/bin none ro,bind 0 0 /sbin /lxc/rootfs/sbin none ro,bind 0 0 /lib /lxc/rootfs/lib none ro,bind 0 0 /lib64 /lxc/rootfs/lib64 none ro,bind 0 0 /etc /lxc/rootfs/etc none ro,bind 0 0 /usr /lxc/rootfs/usr none ro,bind 0 0 /dev /lxc/rootfs/dev none rw,bind 0 0 /dev/pts /lxc/rootfs/dev/pts none rw,bind 0 0 /proc /lxc/rootfs/proc proc defaults 0 0 /sys /lxc/rootfs/sys sysfs defaults 0 0 

准备工做完成后,使用lxc-create命令生成名为lxc的容器。

# lxc-create -n lxc -f /lxc/lxc.conf

使用lxc-ls命令能够确认容器列表。

# lxc-ls
lxc

使用lxc-create命令在lxc容器内执行bash。

# lxc-execute -n lxc bash

小贴士:在笔者的环境下,出现了终端的按键输入不显示的状况。发生这种状况时能够执行reset命令,终端的操做就会恢复。
bash 4.1# reset(不显示在画面上)
执行ps命令,就能够发现PID是从1开始的,除lxc容器之外看不到其余进程。

bash-4.1# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.1 0.1 14688 664 pts/0 S 21:34 0:00 /usr/lib64/lxc/lxc-init -- bash root 2 0.3 0.3 108440 1760 pts/0 S 21:34 0:00 bash root 6 0.0 0.2 108120 1104 pts/0 R+ 21:34 0:00 ps aux 

另外,生成的容器可使用lxc-destroy命令来撤销。

# lxc-destroy -n lxc

接下来尝试启用网络,并启动sshd进程。而后,建立用来链接分配到容器的网络接口的网桥(关于网桥请参考Hack #24)。
在这个例子中将IP地址设置为192.168.20.254。

# brctl addbr br0
# ifconfig br0 192.168.20.254 

而后,修改lxc.conf,添加网络设置。

# vi /lxc/lxc.conf
lxc.utsname = lxc
lxc.rootfs = /lxc/rootfs lxc.mount = /lxc/fstab lxc.network.type = veth lxc.network.flags = up lxc.network.link = br0 lxc.network.name = eth0 lxc.network.ipv4 = 192.168.20.1/24 

启动sshd时须要下列目录,要事先建立。当该目录不存在时,从lxc-execute启动sshd时就会失败。

# mkdir -p rootfs/var/empty/sshd 

接下来生成容器。

# lxc-execute -n lxc /usr/sbin/sshd

这时打开其余终端,确认SSH服务器是否正在运行。
首先,使用ping命令确认网络是否已链接。

# ping 192.168.20.1 PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data. 64 bytes from 192.168.20.1: icmp_req=1 ttl=64 time=0.899 ms 64 bytes from 192.168.20.1: icmp_req=2 ttl=64 time=0.174 ms ^C 

使用ssh命令,链接分配到容器的IP地址192.168.20.1。

# ssh 192.168.20.1 root@192.168.20.1's password: 

SSH链接已创建,输入密码后就成功登陆。
经过ps命令所显示的进程来确认资源是否已被容器隔离。

-bash-4.1# ps auxw USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 14688 660 pts/0 S+ 21:43 0:00 /usr/lib64/lxc/ lxc-init -- /usr/sbin/sshd root 3 0.0 0.2 75104 1116 ? Ss 21:43 0:00 /usr/sbin/sshd root 4 1.4 0.8 108816 4068 ? Ss 21:47 0:00 sshd: root@pts/3 root 6 1.6 0.3 108440 1904 pts/3 Ss 21:47 0:00 -bash root 19 0.0 0.2 108124 1104 pts/3 R+ 21:47 0:00 ps auxw -bash-4.1# ifconfig eth0 eth0 Link encap:Ethernet HWaddr 3A:A1:C2:A0:6F:1B inet addr:192.168.20.1 Bcast:192.168.20.0 Mask:255.255.255.0 inet6 addr: fe80::38a1:c2ff:fea0:6f1b/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:280 errors:0 dropped:0 overruns:0 frame:0 TX packets:259 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:31389 (30.6 KiB) TX bytes:31373 (30.6 KiB) -bash-4.1# exit logout 

能够在其余终端执行lxc-stop,来关闭装有sshd的容器。

# lxc-stop -n lxc

而后运行debian。
使用debootstrap建立用来启动debian的根文件系统。deboot strap使用yum来安装。

# yum install debootstrap

建立debian的根文件系统,须要准备/debian。

# mkdir /debian
# cd /debian

如今执行debootstrap,生成debian lenny的文件系统。

# debootstrap - -arch=amd64 lenny lenny

而后,准备LXC用的设置。

# vi /debian/lenny.conf
lxc.utsname = lenny
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.name = eth0
lxc.network.ipv4 = 192.168.20.2/24 lxc.rootfs = /debian/lenny lxc.mount = /debian/lenny.fstab # vi /debian/lenny.fstab devpts /debian/lenny/dev/pts devpts defaults 0 0 proc /debian/lenny/proc proc defaults 0 0 sysfs /debian/lenny/sys sysfs defaults 0 0 

建立名称为lenny的容器。

# lxc-create -n lenny -f lenny.conf

所建立的容器能够经过lxc-start来启动。只到这一步的话,init虽然启动,但不能进行任何操做。这是由于刚执行debootstrap后,安装的仅是最低限度须要的数据包。这里为了让外部可以链接到容器,须要安装sshd。

# lxc-start -n lenny bash

debian环境的shell就会在容器内启动。而后使用apt-get安装openssh-server。

lenny:~# apt-get install openssh-server

另外,为了在SSH上进行登陆,还须要设置root的密码。

lenny:~# passwd

在容器内启动lenny。

# lxc-start -n lenny
INIT: version 2.86 booting Setting the system clock. Cannot access the Hardware Clock via any known method. Use the --debug option to see the details of our search for an access method. Unable to set System Clock to: Fri Dec 10 22:59:45 UTC 2010 (warning). Activating swap...done. Setting the system clock. Cannot access the Hardware Clock via any known method. Use the --debug option to see the details of our search for an access method. Unable to set System Clock to: Fri Dec 10 22:59:46 UTC 2010 (warning). Cleaning up ifupdown.... Loading kernel modules...FATAL: Could not load /lib/modules/2.6.35.9-64.fc14.x86_64/modules.dep: No such file or directory Checking file systems...fsck 1.41.3 (12-Oct-2008) done. Setting kernel variables (/etc/sysctl.conf)...done. Mounting local filesystems...done. Activating swapfile swap...done. Setting up networking.... Configuring network interfaces...done. INIT: Entering runlevel: 2 Starting enhanced syslogd: rsyslogd. Starting OpenBSD Secure Shell server: sshd. Starting periodic command scheduler: crond. 

咱们尝试从其余终端使用SSH来链接。

# ssh 192.168.20.2 root@192.168.20.2's password: lenny:~# 

在debian环境下实施关闭(shutdown)的话,容器结束。

lenny:~# shutdown -h now

按照前面所述方法使用LXC就能够简单地建立容器。
小结
本节介绍了Linux内核的资源划分功能:划分CPU、内存空间、I/O等的Cgroup,以及划分PID、IPC、网络、mount命名空间的Namespace。另外,还介绍了实际安装上述资源划分功能的容器LXC。
参考文献
man 2 clone
man 2 unshare
LXC
http://lxc.sourceforge.net/
http://www.ibm.com/developerworks/jp/linux/library/l-lxc-containers/
debootstrap
http://www.debian.org/releases/stable/i386/apds03.html.ja—Munehiro IKEDA, Hiroshi Shimamoto

相关文章
相关标签/搜索