本節将介紹cgroup與namespace以及通過這兩個功能實作的容器功能。
cgroup
cgroup(control group)是将任意程序進行分組化管理的linux核心功能。cgroup本身是提供将程序進行分組化管理的功能和接口的基礎結構,i/o或記憶體的配置設定控制等具體的資源管理功能是通過這個功能來實作的。這些具體的資源管理功能稱為cgroup子系統或控制器。
cgroup子系統有控制記憶體的memory控制器、控制程序排程的cpu控制器等。運作中的核心可以使用的cgroup子系統由/proc/cgroup來确認。
cgroup提供了一個cgroup虛拟檔案系統,作為進行分組管理和各子系統設定的使用者接口。要使用cgroup,必須挂載cgroup檔案系統。這時通過挂載選項指定使用哪個子系統。這裡指定debug這個沒有實質功能的調試用子系統來挂載。
注意事項:這裡所說的“虛拟檔案系統”,是指procfs和sysfs這種不具有實體裝置的檔案系統。并不是用來接納檔案系統差異的核心内部層layervfs。
小貼士:關于cgroup檔案系統的标準化挂載要點,是由開發論壇進行讨論的,但目前尚未得出結論。這裡是挂載到/cgroup。
挂載後,在挂載位置下應該可以看到下列幾個檔案。這些是cgroup呈現出來的特殊檔案。
檔案名字首為cgroup的以及沒有字首的檔案是由cgroup的基礎結構提供的特殊檔案。而字首為debug的檔案是由debug子系統提供的特殊檔案。cgroup的子系統提供的特殊檔案都會像這樣加上子系統的字首。是以,根據挂載時指定的選項,即所使用的子系統不同,存在的特殊檔案也不同。但是cgroup的基礎結構所提供的特殊檔案則是無論指定哪種子系統都一直存在的。特殊檔案分為隻讀檔案和可讀寫檔案。隻讀檔案是為使用者提供資訊的檔案。可讀寫的特殊檔案通過寫入值來更改cgroup以及cgroup子系統設定的檔案。設定的值可以通過讀入特殊檔案來确認。在這些特殊檔案中,最重要的是tasks特殊檔案。其内容可以顯示如下。
雖然看上去隻是一些數字的排列,但其實這些是屬于這個分組的線程的線程id(tid)。在這時,系統上運作的所有線程的tid都包含在/cgroup/tasks中。這就表示全部線程都屬于這個分組。那麼這裡出現的“分組”又是什麼呢?分組,就是展現為cgroup檔案系統目錄的線程的集合。由于/cgroup也是目錄,是以它也表示一個分組。像這樣位于挂載點最上層的目錄是自動生成的分組,稱為根分組。在這個階段,隻有/cgroup(即根分組)是系統上存在的唯一分組。
小貼士:英語中将通過cgroup建立的分組稱做cgroup,容易與表示結構的“cgroup”混淆,是以這裡僅稱為“分組”。
下面嘗試建立一個分組,也就是在/cgroup下建立子目錄。其内容如下所示。
雖然是新生成的目錄,但是已經有檔案存在。cgroup檔案系統在目錄生成的同時就會在其中配置特殊檔案。
/cgroup/test也和/cgroup一樣有tasks。其内容如下。
tasks的内容似乎是空的,這表示這個分組内一個線程也沒有。可以将适當的線程添加到這個分組中。要将線程添加到分組中,可以在tasks中寫入該線程的tid。這裡以添加shell本身為例。
tasks的内容中包含shell的tid(也是pid,即程序id)—2474,可以看出這個shell已經屬于test分組。除此以外,這個分組内還有另一個tid為3821的線程,這是什麼呢?我們再來看一下tasks的内容。
結果居然發生了變化。事實上這個改變的部分,是顯示了tasks内容的cat程序的tid。最初的cat(3821)和第二次的cat(3822)是不同的程序,tid也不同,是以結果發生了變化。但是似乎并沒有将cat程序添加到test分組中。其實,屬于分組的程序一旦生成子程序,其子程序就會自動屬于母程序。由于cat是shell的子程序,是以前者自動屬于test分組。大家應該還記得,在挂載cgroup檔案系統後,系統上的所有線程是屬于根分組的。也就是說,除了将明确指定為新生成分組内的程序為祖先程序以外,生成的程序都屬于根分組。
這時,再顯示/cgroup/tasks的内容的話,應該不會顯示shell的tid(2474)。這是因為shell不屬于根分組,而是屬于test分組。然後,再将這個shell傳回到根分組。
這樣,shell的tid(2474)就再次屬于/cgroup/tasks,而/cgroup/test/tasks就變空。如果分組中一個線程也沒有,可以進行撤銷。删除目錄就可以撤銷分組。
表2-1是每個子系統中cgroup都會提供的特殊檔案清單。
表2-1 cgroup提供的檔案種類

這裡僅介紹了最基本的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 資源劃分
linux 容器
使用cgroup和namespace就可以實作容器。容器這個技術也稱為作業系統虛拟化,是将一個核心所管理的資源劃分成多個分組。
在容器中,cpu和記憶體資源是使用cgroup來劃分的。pid、ipc、網絡等資源使用namespace來劃分。
lxc
linux中實際安裝的容器有lxc(linux container)。本節将以fedora 14為例介紹lxc的使用方法。
要使用網絡,還需要安裝bridge-utils。
在使用lxc之前,必須啟用cgroup檔案系統。使用下列指令挂載cgroup檔案系統。
另外,向/etc/fstab添加下列語句,就可以在系統啟動時自動挂載cgroup檔案系統。
首先,将bash shell程序放進容器。
這裡要為容器中使用的檔案系統準備一個/lxc目錄。
然後準備作為容器内的根檔案系統的目錄。
還需要準備其他必要的目錄(這些目錄主要使用bind mount)。
然後還要生成lxc的配置檔案lxc.conf以及引用的fstab。
準備工作完成後,使用lxc-create指令生成名為lxc的容器。
使用lxc-ls指令可以确認容器清單。
使用lxc-create指令在lxc容器内執行bash。
小貼士:在筆者的環境下,出現了終端的按鍵輸入不顯示的情況。發生這種情況時可以執行reset指令,終端的操作就會恢複。
bash 4.1# reset(不顯示在畫面上)
執行ps指令,就可以發現pid是從1開始的,除lxc容器以外看不到其他程序。
另外,生成的容器可以使用lxc-destroy指令來撤銷。
接下來嘗試啟用網絡,并啟動sshd程序。然後,建立用來連接配接配置設定到容器的網絡接口的網橋(關于網橋請參考hack #24)。
在這個例子中将ip位址設定為192.168.20.254。
然後,修改lxc.conf,添加網絡設定。
啟動sshd時需要下列目錄,要事先建立。當該目錄不存在時,從lxc-execute啟動sshd時就會失敗。
接下來生成容器。
這時打開其他終端,确認ssh伺服器是否正在運作。
首先,使用ping指令确認網絡是否已連接配接。
使用ssh指令,連接配接配置設定到容器的ip位址192.168.20.1。
ssh連接配接已建立,輸入密碼後就成功登入。
通過ps指令所顯示的程序來确認資源是否已被容器隔離。
可以在其他終端執行lxc-stop,來關閉裝有sshd的容器。
然後運作debian。
使用debootstrap建立用來啟動debian的根檔案系統。deboot strap使用yum來安裝。
建立debian的根檔案系統,需要準備/debian。
現在執行debootstrap,生成debian lenny的檔案系統。
然後,準備lxc用的設定。
建立名稱為lenny的容器。
所建立的容器可以通過lxc-start來啟動。隻到這一步的話,init雖然啟動,但不能進行任何操作。這是因為剛執行debootstrap後,安裝的僅是最低限度需要的資料包。這裡為了讓外部能夠連接配接到容器,需要安裝sshd。
debian環境的shell就會在容器内啟動。然後使用apt-get安裝openssh-server。
另外,為了在ssh上進行登入,還需要設定root的密碼。
在容器内啟動lenny。
我們嘗試從其他終端使用ssh來連接配接。
在debian環境下實施關閉(shutdown)的話,容器結束。
按照前面所述方法使用lxc就可以簡單地建立容器。
小結
本節介紹了linux核心的資源劃分功能:劃分cpu、記憶體空間、i/o等的cgroup,以及劃分pid、ipc、網絡、mount命名空間的namespace。另外,還介紹了實際安裝上述資源劃分功能的容器lxc。
參考文獻
man 2 clone
man 2 unshare
<a href="http://lxc.sourceforge.net/">http://lxc.sourceforge.net/</a>
<a href="http://www.ibm.com/developerworks/jp/linux/library/l-lxc-containers/">http://www.ibm.com/developerworks/jp/linux/library/l-lxc-containers/</a>
debootstrap
<a href="http://www.debian.org/releases/stable/i386/apds03.html.ja">http://www.debian.org/releases/stable/i386/apds03.html.ja</a>
—munehiro ikeda, hiroshi shimamoto