建立掛載點並掛載mkdir /mnt/nattymount /dev/data/natty /mnt/natty
建立測試環境
debootstrap natty /mnt/natty
完成之後的 /mnt/natty 為一個最基礎的 Ubuntu Natty 環境(debootstrap 中可以指定其他版本,如 oneiric 等。將 /mnt/natty 卸載作為範本。
umount /mnt/natty
可以壓縮分區以節約磁碟空間:將檔案系統減到最小
resize2fs -M /dev/data/natty # 會要求先執行 e2fsck -f /dev/data/natty 等
然後根據檔案系統大小相應壓縮邏輯卷。$NEW_SIZE 為上一條操作執行後檔案系統的大小;應該留有適當餘地,否則可能損壞檔案系統。或使用 -r 參數。
lvresize -L $NEW_SIZE /dev/data/natty
建立新測試環境
使用 LVM 的 snapshot 功能建立測試環境:
NEWSIZE=`lvdisplay /dev/data/natty | grep 'Current LE' | egrep -o '[[:digit:]]+'`VOL=snap1lvcreate -s /dev/data/natty -n $VOL -l$NEWSIZEmkdir /mnt/$VOLmount /dev/data/$VOL /mnt/$VOL
export MP=/mnt/$VOLtouch $MP/chroot.$VOL # 盜夢的陀螺mount --bind /dev $MP/dev
mount none $MP/dev/pts -t devpts
mount --bind /proc $MP/proc
mount --bind /sys $MP/syschroot /mnt/$VOL
網路此時建立的測試環境雖然檔案系統獨立,但其他均為共用。如果同時建立多個環境運行網路服務(如 web server 或 fastcgi 等)則可能出現連接埠衝突。解決方案是通過 veth 給每個環境分配獨立的網路。
參考:http://lxc.sourceforge.net/index.php/about/kernel-namespaces/network/configuration/
攻略:
宿主環境開啟路由和 arp 代理
echo 1 > /proc/sys/net/ipv4/ip_forward echo 1 > /proc/sys/net/ipv4/conf/eth0/proxy_arp
建立虛擬網卡對
ip link add type veth
為網卡對的主機端指定地址。不應與 ethX 衝突
ifconfig veth0 192.168.3.101/24 up
為虛擬網卡開啟 arp 轉寄
echo 1 > /proc/sys/net/ipv4/conf/veth0/proxy_arp
為虛擬網卡對端指定路由(這裡 192.168.3.102 是虛擬環境使用的 IP 位址)
route add -host 192.168.3.102 dev veth0
另開一個會話,chroot 進入虛擬環境。用 unshare 命令隔離與宿主的網路空間
unshare -mun chroot /mnt/$VOL
這時執行 ifconfig 應該看不見網卡,ifconfig lo0 可以看見 loopback 網卡,但是沒有 IP 位址。在這個 shell 執行
echo $$
得到 PID
在第一個會話中(宿主環境)執行
ip link set veth1 netns $PID
將 veth1 劃入虛擬環境
在虛擬環境 chroot shell 中 ifconfig 應該可以看到 veth1 。將 veth1 的地址指定為前述(路由指向的)IP 位址
ifconfig veth1 192.168.3.102
在虛擬環境中測試監聽
nc -vv -l 10888
在主機中測試
nc -vv localhost 10888
失敗,但
nc -vv 192.168.2.102 10888
成功,說明網路隔離室成功的。
CPU、記憶體資源測試利用 cgroups 限定可用記憶體和 CPU
通過 cgcreate -g memory:Name 建立分組,然後在 /sys/fs/cgroup/memory/Name/memory.limit_in_bytes 中進行設定,如 echo 10M > memory.limit_in_bytes 等。
FIXME: 通過 for (;;) malloc (1024 * 1024) 並未測得相應的結果,記憶體增長(top 中 VIRT / RES / SHR)與實際佔用( /sys/fs/cgroup/Name/memory.usage_in_bytes)並不對應。
問題Ubuntu 虛擬環境中 MySQL 等指令碼無法執行Ubuntu 8.04 之後的系統啟動指令碼由 Sysvinit 替換成了 Upstart。Sysvinit 是一組簡單的 shell 指令碼,由 /sbin/init 建立進程並獨立執行,因此相對簡單。Ubuntu 使用的 upstart 系統( http://upstart.ubuntu.com )依賴 dbus 通訊,因此指令碼的執行依賴於 /sbin/init 的執行。而 Ubuntu 的 /sbni/init 又強制要求自己的 PID 為 1,否則直接退出。因為以上的 unshare 命令目前只能處理網路、System
V IPC 等名字空間,但是空間仍然是共用的,無法在虛擬環境中正常執行 /sbin/init。解決方案 1:對於依賴 upstart 的服務,手工啟動(如手工執行 mysqld 等)解決方案 2:利用 LD_PRELOAD 劫持 getpid 返回 1 強制啟動虛擬環境的 /sbin/init (http://blog.csdn.net/Wolf0403/article/details/389276)#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <dlfcn.h>#include <stdio.h>#include <stdlib.h>#include <stdarg.h>pid_t getpid (void) { char *pidstr = getenv ("HJPID"); if (pidstr && pidstr[0] == '1' && pidstr[1] == '\0') { return (pid_t) 1; } void *dlh = dlopen ("libc.so.6", RTLD_LAZY); if ( !dlh ) { exit (1); } pid_t (* glibc_getpid) (); glibc_getpid = dlsym ( dlh, "getpid" ); if ( ! glibc_getpid ) { exit (2); } pid_t r = glibc_getpid (); dlclose ( dlh ); return r;}
編譯
cc -shared -fPIC getpid.c -o libpid.so -ldl
然後用 env LD_PRELOAD=libpid.so HJPID=1 方式插入 /sbin/init 這樣可以啟動 /sbin/init,但是 MySQL 啟動仍然有問題。FIXME整理目前測試成功的是在虛擬環境中執行 nginx / php-fcgi,MySQL 可以在 chroot 中執行相關指令碼:https://github.com/wolf0403/lvmvm關於 PID namespace 的問題,Linux 的 clone(2) 系統調用提供了新的 CLONE_NEWPID 參數用於隔離 PID 名稱空間。http://linux.die.net/man/2/clone 可以嘗試整合到 unshare 中(TODO)。資源Upstart /sbin/init PID = 1: http://linux-vserver.org/Upstart_issuesLinux unshare(1) 命令:http://linux.die.net/man/1/unshare