目标:
快速建立环境用语测试 deb 包安装 / web 应用部署。
可参考 https://github.com/wolf0403/lvmvm 的 README
基础环境:
Ubuntu Natty (11.04) server.
文件系统:
测试环境的快速部署依赖 LVM (Logical Volume Management) 。安装宿主系统方法不限,留足够硬盘创建单一分区,如 /dev/sda5 。
将此分区创建为 LVM 物理卷
pvcreate /dev/sda5
在此分区之上创建 LVM 组
vgcreate data /dev/sda5
创建逻辑分区
lvcreate -n natty /dev/data -L2G
创建 文件系统
mkfs.ext4 /dev/data/natty
创建挂载点并挂载mkdir /mnt/natty
mount /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=snap1
lvcreate -s /dev/data/natty -n $VOL -l$NEWSIZE
mkdir /mnt/$VOL
mount /dev/data/$VOL /mnt/$VOL
export MP=/mnt/$VOL
touch $MP/chroot.$VOL # 盗梦的陀螺
mount --bind /dev $MP/dev
mount none $MP/dev/pts -t devpts
mount --bind /proc $MP/proc
mount --bind /sys $MP/sys
chroot /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 等脚本无法执行
#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