容器可以提供輕量級的虛擬化,以便隔離進程和資源,而且不需要提供指令解釋機制以及全虛擬化的其他複雜性。本文循序漸進地介紹容器工具 Linux Containers(LXC)。作者在文中示範如何設定和使用它們。
容器有效地將由單個作業系統管理的資源劃分到孤立的組中,以更好地在孤立的組之間平衡有衝突的資源使用需求。與虛擬化相比,這樣既不需要指令級類比,也不需要即時編譯。容器可以在核心 CPU 本地運行指令,而不需要任何專門的解釋機制。此外,也避免了准虛擬化(paravirtualization)和系統調用替換中的複雜性。
通過提供一種建立和進入容器的方式,作業系統讓應用程式就像在獨立的機器上運行一樣,但又能共用很多底層的資源。例如,可以有效地共用公用檔案(比如 glibc)的頁緩衝,因為所有容器都使用相同的核心,而且所有容器還常常共用相同的 libc 庫(取決於容器配置)。這種共用常常可以擴充到目錄中其他不需要寫入內容的檔案。
容器在提供隔離的同時,還通過共用這些資源節省開銷,這意味著容器比真正的虛擬化的開銷要小得多。
容器技術早就出現。例如,Solaris Zones 和 BSD jails 就是非 Linux 作業系統上的容器。用於 Linux 的容器技術也有豐富的遺產,例如 Linux-Vserver、OpenVZ 和 FreeVPS。雖然這些技術都已經成熟,但是這些解決方案還沒有將它們的容器支援整合到主流 Linux 核心。(要瞭解更多關於這些技術的資訊,請查看 參考資料 小節)。
相比之下,Linux Resource Containers 項目(見 參考資料)則通過為主流 Linux 核心作貢獻來實現容器。與此同時,這些貢獻可能對成熟的 Linux 容器解決方案有用處 — 為更成熟的容器項目提供公用後端。本文簡要介紹如何使用由 LXC 項目建立的工具。
為了充分利用本文,您應該熟悉使用命令列運行程式,例如 make、gcc 和 patch。此外,還應該熟悉 tarball(*.tar.gz 檔案)的解壓。
擷取、構建和安裝 LXC
LXC 項目由一個 Linux 核心補丁和一些 userspace 工具組成。這些 userspace 工具使用由補丁增加的核心新特性,提供一套簡化的工具來維護容器。
在使用 LXC 之前,首先需要下載 Linux 核心原始碼,應用適當的 LXC 補丁,然後構建、安裝和啟動它。最後再下載、構建和安裝 LXC 工具。
我使用一個打了補丁的 Linux 2.6.27 核心(見 參考資料)。雖然 2.6.27 Linux 核心的 lxc 補丁可能不適用於您喜歡的發行版的核心原始碼,但是 2.6.27 以後的 Linux 版本可能已經包含該補丁提供的大部分功能。所以,強烈建議使用最新的補丁和主流核心原始碼。而且,除了下載核心原始碼並添加補丁外,還可以使用 git
擷取代碼:
git clone git://git.kernel.org/pub/scm/linux/kernel/git/daveh/linux-2.6-lxc.git |
在 kernelnewbies.org 可以找到關於如何為核心添加補丁,如何配置、構建安裝和啟動核心的指導說明(見 參考資料)。
LXC 需要一些特定的核心配置。為 LXC 適當配置核心的最容易的方式是使用 make menuconfig
,然後選擇 Container support。取決於核心所支援的特性,這樣做會進一步選擇一組其他配置選項。
可用的 LXC 環境
除了一個支援容器的核心外,還需要一些工具才能夠簡單地啟動和管理容器。本文的容器管理工具來自 liblxc(從 參考資料 擷取連結。另外,還可以使用 libvirt)。這個小節討論:
- liblxc 工具
- iproute2 工具
- 如何配置網路
- 如何填充一個容器檔案系統(構建定製的 Debian 容器,還是運行 ssh 容器)
- 如何串連到容器檔案系統(SSH、VNC、VT: tty、VT: GUI)
工具:liblxc
下載並解壓縮 liblxc (見 參考資料),然後從 liblxc 目錄中:
./configure --prefix=/makemake install |
如果您習慣於構建源 RPM,可以從網上下載一個(見 參考資料)。
工具:iproute2
為了在容器中管理網路介面,需要 2.6.26 或更高版本的 iproute2 包(見 參考資料)。如果您的 Linux 發行版沒有這個包,請下載並按照說明配置和安裝它。
配置網路
很多實用的容器的另一個關鍵區段是網路訪問。目前,橋接(串連多個乙太網路區段,使它們成為一個單獨的乙太網路區段)是將一個容器串連到網路的最佳方法。為了準備使用 LXC,我們將建立一個橋(見 參考資料),並使用它將我們真正的網路介面與容器的網路介面串連起來。
建立一個名為 br0 的橋:
brctl addbr br0brctl setfd br0 0 |
用一個已有網路介面中的 IP(在本例中是 10.0.2.15
)串連橋介面:ifconfig br0 10.0.2.15 promisc up
。將已有的網路介面(在本例中是 eth0
)添加到橋,並取消它與它的 IP 位址的直接關聯:
brctl addif br0 eth0ifconfig eth0 0.0.0.0 up |
任何添加到橋 br0
的介面都將對那個 IP 位址作出響應。最後,確保預設的路由用 route add -net default gw 10.0.2.2 br0
將資料包發送到網關。以後,在配置容器時,指定 br0
作為通往外界的連結。
填充容器檔案系統
除了網路外,容器常常需要它們自己的檔案系統。取決於您的需要,有幾種填充容器檔案系統的方法。我將討論其中兩種:
- 構建一個定製的 Debian 容器
- 運行一個 ssh 容器
使用 debootstrap
命令構建一個定製的 Debian 容器 非常簡單:
debootstrap sid rootfs http://debian.osuosl.org/debian/ |
如果要構建大量的容器,首先將包下載到一個 tarball 中可以節省時間,例如 debootstrap --make-tarball sid.packages.tgz sid http://debian.osuosl.org/debian/
。這將產生一個 .tar 檔案,這個檔案約 71MB(壓縮了 52MB),而一個根目錄約 200MB。然後開始在 rootfs 中構建根目錄:debootstrap --unpack-tarball sid.packages.tgz sid rootfs
。(debootstrap
首頁上有更多關於構建更小的或更適合的容器的資訊)。
這將產生一個宿主容器高度冗餘的環境(見 參考資料)。
運行 ssh 容器 可以大大減少容器檔案系統佔用的磁碟空間。例如,這種方法僅僅使用數 KB 就能在不同容器的 22 連接埠上運行多個 ssh 守護進程(參考資料 中提供了一個例子)。容器通過使用關鍵根目錄來實現這一點,例如 /bin、/sbin 和 /lib 等的唯讀綁定掛載共用來自已有 Linux 系統的 sshd 包內容。這裡使用一個網路名稱空間,並建立基本的讀寫內容。
用於產生那些輕量級容器的方法與用於產生 chroot 環境的方法基本一樣。不同之處在於唯讀綁定掛載和使用名稱空間增強 chroot 環境的隔離性,使之成為有效容器。
接下來,需要選擇一種串連到容器的方法。
串連到容器
接下來的步驟是串連到容器。根據配置容器的不同方式,有幾種方法可供選擇:
- SSH
- VNC(GUI)
- VT: tty(文本)
- VT: X(GUI)
如果不需要用於容器的 GUI 介面,那麼 通過 SSH 串連 就可以了。在此情況下,一個簡單的 ssh 串連就足夠了(參見上面的 “運行一個 ssh 容器”)。這種方法的優點是依靠 IP 定址來支援建立任意數量的容器。
如果 ssh 串連花很長的時間才到達密碼提示,那麼在 DNS 尋找期間 Avahi multicast DNS/Service Discovery 守護進程就可能逾時。
通過 Virtual Network Computing(VNC)串連,這種方法可以為容器增加一個 GUI 介面。
使用 vnc4server 啟動一個 X 伺服器,該伺服器只為 VNC 客戶機服務。需要安裝 vnc4server,以便從容器的 /etc/rc.local 檔案運行它,如下所示:echo '/usr/bin/vnc4server :0 -geometry 1024x768 -depth 24' >> rootfs/etc/rc.local
。當容器啟動時,將建立一個解析度為 1024×768 的 24 位色的 X 螢幕。接下來的串連很簡單,如下所示:
如果容器與它的宿主共用 tty,那麼 通過 VT: tty(文本)串連 就很有用。在這情況下,可以使用 Linux Virtual Terminals(VT)串連到容器。使用 VT 的簡單用法是登入 tty 裝置之一,然後這個 tty 裝置將與 Linux VT 通訊。登入進程被稱作 getty
。使用 VT 8:
echo '8:2345:respawn:/sbin/getty 38400 tty8' >> rootfs/etc/inittab |
一旦容器被啟動,它將在 tty8 上運行 getty
,以允許使用者登入到容器中。可以通過類似的技巧,使用 LXC 工具重新啟動容器。
這種方法不支援容器的圖形化介面。而且,由於每次只有一個進程可以串連到 tty8,若要啟用多個容器,則需要進一步配置。
通過 VT: X 串連 讓您可以運行一個 GUI。在 VT 9 上運行 GNOME Display Manager(gdm),然後編輯 rootfs/usr/share/gdm/defaults.conf,將 FirstVT=7
替換為 FirstVT=9
,以及將 VTAllocation=true
替換為 VTAllocation=false
。
雖然這樣便可以使用一個圖形化介面,但是仍然只能使用有限的幾種 Linux 虛擬終端之一。
運行 LXC 工具
至此,您正在運行一個適當的核心,安裝了 LXC 公用程式,並且有了一個可用的環境,接下來便可以學習管理該環境的執行個體了。(提示:LXC README 中更加詳細地描述了這方面的大部分內容)。
LXC 使用 cgroup 檔案系統來管理容器。在使用 LXC 之前,首先必須掛載這個檔案系統:mount -t cgroup cgroup /cgroup
。可以將 cgroup 檔案系統掛載到任何地方。LXC 將使用 /etc/mtab 中掛載的第一個 cgroup 檔案系統。
本文的後面將展示一些 LXC 基礎知識和雜項內容,並討論低級訪問。
LXC 基礎知識
對於使用 LXC 工具的基礎知識,我們將看看:
- 建立容器
- 獲得(或列出)關於已有容器的資訊
- 啟動系統和應用程式容器
- 向容器中啟動並執行進程發訊號
- 暫停、恢複、停止和銷毀容器
建立容器就是將一個名稱與一個設定檔關聯起來。該名稱將用於管理容器:
lxc-create -n name -f configfile |
這使得多個容器可以同時使用相同的設定檔。在設定檔中,可以指定容器的屬性,例如它的主機名稱、網路、root 檔案系統和 fstab。運行 lxc-sshd 指令碼(該指令碼建立一個配置)之後,ssh 容器配置如下所示:
lxc.utsname = my_ssh_containerlxc.network.type = vethlxc.network.flags = uplxc.network.link = br0lxc.network.ipv4 = 10.0.2.16/24lxc.network.name = eth0lxc.mount = ./fstablxc.rootfs = ./rootfs |
無論設定檔如何,用 LXC 工具啟動的容器有自己的系統進程視圖,以及自己的掛載樹和可用的處理序間通訊(IPC)資源檢視。
除了這些以外,當一個容器啟動時,配置中未提到的任何類型的資源都被認為是與主機共用。這使管理員可以簡潔地指定容器與其主機之間的關鍵不同點,並且使配置具有可移植性。
列出關於已有容器的資訊對於管理已有容器非常重要。顯示一個特定容器的狀態:
顯示屬於一個容器的進程:
啟動
LXC 根據容器類型的不同而有所不同:一種是系統容器,一種是應用程式容器。系統容器類似於虛擬機器。與真正的虛擬化相比,雖然它們的隔離性要低一些,但是開銷也降低了。直接原因是每個容器使用相同的 Linux 核心。為了類似於虛擬機器,系統容器和 Linux 發行版一樣在同一個地方啟動,即通過運行 init 程式:
與系統容器相比,應用程式容器只是建立用於隔離一個應用程式的單獨的名稱空間。啟動一個應用程式容器:
發訊號
將一個訊號發送到在一個容器中啟動並執行所有進程:
lxc-kill -n name -s SIGNAL |
暫停
暫停一個容器在概念上類似於將 SIGSTOP
訊號發送到一個容器中的所有進程。但是,發送虛假的 SIGSTOP
訊號可能會迷惑一些程式。所以,LXC 通過 cgroup 介面使用 Linux 進程凍結器(process freezer):
恢複
要恢複一個被凍結的容器:
停止
停止一個容器將導致該容器中啟動的所有進程全體死亡,並且清理容器:
銷毀
銷毀容器是指刪除通過 lxc-create
步驟與名稱關聯的設定檔和中繼資料:
雜項
下面是您可能想知道的一些其他內容(有些與監視有關)。
查看和調整容器的優先順序:
lxc-priority -n namelxc-priority -n name -p priority |
持續觀察容器的狀態和優先順序變化:
按 Ctrl-C 停止監視容器。
還可以等待容器進入以 |
分隔的一組狀態之一:
lxc-wait -n name -s states |
等待除了 RUNNING
之外的所有狀態:
lxc-wait -n name -s 'STOPPED|STARTING|STOPPING|ABORTING|FREEZING|FROZEN' |
當然,這樣將會立即返回。如果沒有遇到意外錯誤,您應該期望只有當容器進入給定的狀態時 lxc-wait
才返回。
低級訪問
LXC 使用 cgroup 檔案系統管理容器。可以通過 LXC 讀和操縱 cgroup 檔案系統的一些部分。要管理每個容器對 cpu 的使用,則可以通過讀取和調整容器的 cpu.shares 來進行,如下所示:
lxc-cgroup -n name cpu.shareslxc-cgroup -n name cpu.shares howmany |