Linux 容器的使用
Linux 容器在 v2.6.29版本之後就加入到核心之中了, 之前雖然也聽說過, 但一直沒有太留心, 一直使用 KVM 來建立虛擬機器.
直至最近 Docker 大出風頭, 才開始關注. 想了解一下 Linux 容器究竟是什麼? 與現有虛擬機器技術(Xen, KVM等)有什麼區別?
Linux 容器技術出現的很早, 其實也是一直虛擬化技術, 但似乎一直沒有 Xen, KVM 這些來的出名.
同時, 在實現原理上, 和Xen, KVM之類的也是有很大區別的.
下面簡單說明下目前4類虛擬技術的區別: (下面說明中, VM:虛擬機器, HOST:主機, 即安裝虛擬機器的機器)
傳統的虛擬化技術 (VirtualBox, VMware)
通過在Linux上安裝虛擬化軟體, 然後通過虛擬化軟體來安裝虛擬機器系統, 大致結構如下:VM1 VM2 VM3 ... ... VirtualBox or VMWare or ... Linux Kernel 硬體 VM是由虛擬化軟體(VirtualBox, VMWare…)來管理的, Linux Kernel不能直接管理到各個VM.
Xen (半虛擬化)
Xen是Linux上歷史比較長的虛擬化技術, 它的虛擬化結構大致如下:Linux Kernel VM1 VM2 VM3 ... ... Xen 硬體 Xen的虛擬化原理是在 Linux Kernel和硬體之間加入一層 Xen程式碼, 有Xen來管理Linux Kernel和其它的VM.
KVM (最新的虛擬化技術)
相比其它的虛擬化技術, KVM是比較新的, 它需要CPU的支援. 它的虛擬化結構大致如下:VM1 VM2 VM3 ... ... KVM (由核心管理) Linux Kernel 硬體 這個結構和傳統的虛擬化技術很類似, 有一點不同的是, KVM和Linux Kernel是緊密結合的,
所以Linux Kernel能夠更好的管理 VMs, VM的效能會比傳統的虛擬化技術更好.Linux 容器 (LXC - linux container)
LXC 是非常輕量級的, 它將 VM 的程式也偽裝成 HOST 的程式. 大致的結構如下:p1(HOST), p2(VM), p3(VM), p4(HOST)...... Linux Kernel 硬體 那麼, 對於某些系統程式, PID是固定的, 比如 init程式的PID=1, VM中的 init程式的PID是如何處理的呢?
原來, VM的 init程式的PID在 HOST的程式表中會顯示成其它PID(>1).從上面可以看出, LXC這種虛擬化, VM的程式就像HOST的程式一樣執行, 管理, 所以建立和銷燬都是非常快速的.
注: 參考 http://veck.logdown.com/posts/200566-compare-of-kvm-and-lxc
Linux 容器相關的2個重要概念
Linux容器功能是基於 cgroups 和 Namespace 來實現的. 所以要了解 Linux 容器必須先了解 cgroup 和 Namespace.
cgroups
cgroups 是將任意程式進行分組化管理的 Linux 核心功能.
通過cgroups可以有效的隔離各類程式, 同時還可以控制程式的資源佔用(CPU, 記憶體等等)情況.
使用示例: (debian v7.6 x86_64)
mount -t tmpfs cgroup_root /sys/fs/cgroup
mkdir /sys/fs/cgroup/test
mount -t cgroup -ocpuset test /sys/fs/cgroup/test
此時, test目錄就是一個 cgroup, 這裡 -o 指定了 cpuset, cpuset是Linux中既定的一種cgroup, 後面有時間重新寫部落格詳細介紹.
test 目錄有cgroup必須的各個檔案
cd /sys/fs/cgroup/test
ls -l
total 0
-rw-r--r-- 1 root root 0 Aug 14 14:34 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 14 14:34 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 14 14:34 cgroup.procs
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.cpus
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_migrate
-r--r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.mems
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 Aug 14 14:34 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 Aug 14 14:34 notify_on_release
-rw-r--r-- 1 root root 0 Aug 14 14:34 release_agent
-rw-r--r-- 1 root root 0 Aug 14 14:34 tasks
其中部分檔案介紹.
檔名 | R/W | 用途 |
---|---|---|
release_agent | RW | 刪除分組時執行的命令. 這個檔案只存在於根分組 |
notify_on_release | RW | 設定是否執行 release\_agent. 為1時執行 |
tasks | RW | 屬於分組的執行緒 TID 列表 |
cgroup.procs | R | 屬於分組的程式 PID 列表. 僅包括多執行緒程式的執行緒leader的TID, 這點與 tasks 不同 |
cgroup.event_control | RW | 監視狀態變化的分組刪除事件的配置檔案 |
在cgroup中還可以建立子cgroup, 建立的方法很簡單, 只要建立資料夾即可.
cd /sys/fs/cgroup/test
mkdir test-child
ls -l test-child # 建立了資料夾之後, 自動生成cgroup需要的檔案
total 0
-rw-r--r-- 1 root root 0 Aug 14 15:10 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 14 15:10 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 14 15:10 cgroup.procs
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.cpus
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_migrate
-r--r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.mems
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 Aug 14 15:10 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 Aug 14 15:10 notify_on_release
-rw-r--r-- 1 root root 0 Aug 14 15:10 tasks
注意, 刪除子cgroup的時候, 要用 rmdir 來刪除資料夾, 用 rm -rf 的方法無法刪除
cd /sys/fs/cgroup/test
rmdir test-child
注: 參考核心文件 Documentation/cgroups/cgroups.txt
Namespace (名稱空間)
使用Namespace, 可以讓每個程式組有獨立的PID, IPC和網路空間.
Namespace的生效主要是通過 clone系統呼叫來實現的.
clone系統呼叫的第3個引數flags就是通過設定Namespace來劃分資源的.
引數種類如下:
名稱 | 說明 |
---|---|
CLONE_NEWIPC | 劃分IPC(程式間通訊)名稱空間, 訊號量(semaphore), 共享記憶體, 訊息佇列等程式間通訊用的資源 |
CLONE_NEWNET | 劃分網路名稱空間. 分配網路介面 |
CLONE_NEWNS | 劃分掛載的名稱空間. 與chroot同樣分配新的根檔案系統 |
CLONE_NEWPID | 劃分 PID 名稱空間. 分配新的程式ID空間 |
CLONE_NEWUTS | 劃分 UTS(Universal Time sharing System)名稱空間. 分配新的 UTS 空間 |
Linux 容器的使用方法 (以下命令基於 debian v7.5)
安裝 LXC
apt-get install lxc lxc-checkconfig # 安裝完成後, 用這個命令檢查系統是否可以使用 lxc # 我的debian系統上有個 missing Cgroup namespace: CONFIG_CGROUP_NSmissing # 對於這個missing, 可能是由於系統中沒有掛載cgroup導致的, 掛載一個cgroup即可 mount -t cgroup cgroup /mnt/cgroup
建立容器
從現有模板建立容器, 比較慢, 需要下載# 建立一個 debian 系統 lxc-create -n test -t debian
這樣建立的容器預設在 /var/lib/lxc/test 中, 為了將容器建立在我們指定的位置, 可以寫個簡單的配置檔案
lxc.conf, 裡面只需要一句lxc.rootfs = /home/lxc/test
然後,
lxc-create -n test -t debian -f /path/to/lxc.conf
這樣, 就把容器建立在了 /home/lxc/test 中了, /var/lib/lxc/test 中只有一個 config檔案(這個config檔案可以作為 lxc-create 命令 -f 引數對應配置檔案的參考)
啟動容器
啟動後就進行入了虛擬機器的控制檯了. (果然像傳說一樣, 幾秒就啟動完成了 ^_^)lxc-start -n test
停止容器
在主機中輸入停止的命令.lxc-stop -n test
銷燬容器
銷燬之前, 可以通過 lxc-ls 來檢視有幾個容器lxc-ls test lxc-destroy -n test lxc-ls
注: 參考URL - http://obdnmagazine.blogspot.com/2013/07/tested-lxc-080-rc1-debian-wheezyax3a6.html
容器示例 - 配置python uliweb 開發環境
嘗試在容器配置一次開發環境, 然後通過複製容器, 形成多個虛擬機器.
# 主機中
root@debian-113:~# uliweb # 主機中沒有安裝uliweb 軟體包
-bash: uliweb: command not found
root@debian-113:~# lxc-start -n test
# 虛擬機器登入介面, 輸入使用者名稱和密碼
# 虛擬機器中
root@test:~# apt-get install python
root@test:~# apt-get install python-pip
root@test:~# pip install Uliweb
root@test:~# uliweb --version
Uliweb version is 0.3.1
主機中設定網橋, 虛擬機器用橋接方式上網, 確保每個虛擬機器有獨立的IP
# 主機中
root@debian-113:~# lxc-stop -n test
root@debian-113:~# apt-cache search bridge-utils
root@debian-113:~# brctl addbr br0
# 配置主機的網橋
root@debian-113:/var/lib/lxc/test# cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
#auto eth0
iface lo inet loopback
# 追加的網橋配置
auto br0
iface br0 inet static
address 192.168.1.113
netmask 255.255.255.0
gateway 192.168.1.1
bridge_ports eth0
bridge_stp on
bridge_fd 0
root@debian-113:/var/lib/lxc/test# /etc/init.d/networking restart
配置容器的網路(也是在主機中修改容器的配置檔案)
root@debian-113:/var/lib/lxc/test# cat /var/lib/lxc/test/config
... ... (很多預設生成的配置)
# network <-- 這個 network 相關的是要追加的
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.name = eth0
啟動Linux容器, 進入虛擬機器
root@debian-113:/var/lib/lxc/test# lxc-start -n test
# 登入進入虛擬機器, 確認虛擬機器的IP
root@test:~# cat /etc/network/interfaces <-- 預設是自動獲取IP
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
root@test:~# ifconfig <-- 我的機器自動分配的 192.168.1.167
# 建立一個簡單的uliweb工程
root@test:~# cd /home/
root@test:/home# mkdir CM-web
root@test:/home# cd CM-web/
root@test:/home/CM-web# uliweb makeproject test
root@test:/home/CM-web# cd test/
root@test:/home/CM-web/test# uliweb makeapp first_app
root@test:/home/CM-web/test# uliweb runserver -h 0.0.0.0
啟動Web服務後, 就可以在主機的瀏覽器中 通過 http://192.168.1.167:8000/ 來訪問虛擬機器中的web服務了.
最後, 複製一個新的容器, 也就是再重新生成一個上面的 python uliweb 開發環境
# 在主機中
root@debian-113:~# cd /var/lib/lxc
root@debian-113:/var/lib/lxc# cp -r test test2
# 修改 test2/config 如下
lxc.utsname = test2 <-- 修改名稱
xc.rootfs = /home/lxc/test2 <-- 修改 rootfs位置
... ... <-- 其它部分不用修改, 和 test 一樣就行
root@debian-113:/var/lib/lxc# cd /home/lxc/
root@debian-113:/home/lxc# cp -r test test2 <-- 重新複製一份 rootfs
root@debian-113:/home/lxc# lxc-start -n test2 <-- 啟動 test2 虛擬機器, 其中環境和 test一樣, IP會不一樣, 自動獲取的
# 進入 test2 虛擬機器中, 可以直接啟動之前的 uliweb 測試工程, 也可以從主機中訪問其web服務.