User Mode Linux
于是首先应该配置好 User Mode Linux, 简称 UML。
User Mode Linux 是一个对 Linux 内核到其自身的适配。它可以将一个 Linux 运行在用户空间,同时却具有几乎全部内核支持的功能,包括但不限于自己的文件系统、自己的 TCP/IP 协议栈。当使用 OpenVZ 这类的 VPS,需要自己挂载文件系统或使用 Tunnelbroker 一类的东西的时候,UML 就十分有用了。而阿里云也不让自己换内核,即使不用和其他人合租,UML 也是十分有用的东西。
因为偷懒,我在 ArchLinux AUR 上找到了一个 UML 的 PKGBUILD,从里面拿出了内核配置文件,打算直接使用。然而,这个内核配置文件存在一些问题,比如说关于 Netfilter (iptables) 的内核配置全部没有打开,文件系统的支持也不是很完全。所以,我们得手工编辑这个配置文件,打开需要的选项。当然,使用内核自带的图形化配置工具,也是可以的。
我在 kernel.org 上选择了最新的 LTS 版本 4.1.19,下载下来,解包,然后把那个改好的配置文件放了进去。然后,直接执行
make ARCH=um vmlinux
就可以获得一个名为 vmlinux 的可执行文件,这就是 User Mode Linux 的可执行文件了。
Bootstrap
然而,在使用 UML 之前,我们还必须先为 UML 配置一份自己的用户空间,即为 UML 虚拟出来的环境安装一个发行版。
由于 UML 可以从文件内的文件系统镜像启动,所以我创建了一些文件作为 虚拟磁盘 使用
fallocate -l 10G image
mkfs.xfs image
接下来就得把它挂载到一个临时挂载点
mount image /mnt
cd /mnt
以安装 ArchLinux 为例,首先得下载它的 bootstrap 压缩包然后解压。对应的 bootstrap 包可以在各大 ArchLinux 镜像里面找到,往往和 ISO 文件放在一起,如 http://mirror.rackspace.com/archlinux/iso/2016.03.01/archlinux-bootstrap-2016.03.01-i686.tar.gz。
tar zxvf bootstrap_file.tar.gz
mv root.x86_64/* ./
如果要使用 Debian, 则上述过程都可以通过 debootstrap 脚本来完成,只要指定目标目录是挂载出来的 /mnt 即可。
此时需要将必须目录映射进挂载出来的磁盘
mount --rbind /dev /mnt/dev
mount --rbind /sys /mnt/sys
mount --rbind /proc /mnt/proc
mount --rbind /tmp /mnt/tmp
然后执行更新软件包之类的操作。接着需要配置好 root 密码、配置好 locale 等。在编辑 fstab 时,请注意,根磁盘应该是 /dev/ubda,这将在稍后的 UML 启动命令行中体现。
这里有一个大坑:千万不要在 UML 内系统的 fstab 里添加 swapfile! 这会导致 udev 在启动时卡死。如果一定要用 swap,请写一个开机启动的服务来启用。
Networking
UML 的网络配置也是一个坑。要对 UML 启用网络,我们必须使用 tap 设备。例如,我们可以创建一个 tap0:
ip tuntap add tap0 mode tap
ip addr add 192.168.1.1/24 dev tap0
ip link set tap0 up
注意,在阿里云上,只有 192.168.0.0/16 这个内网网段是可用的。这里我们把主机 IP 设为 192.168.1.1, 假设我们将把 UML 客户机的 IP 设为 192.168.1.2
稍后启动 UML 时,我们将通过这个参数 (假设 MAC 地址设为 23:33:33:44:66:66)
eth0=tuntap,tap0,23:33:33:44:66:66,192.168.1.2
来将网络设备映射到 UML 内部。
为了使 UML 能访问网络,我们得添加 MASQUERADE 规则。在阿里云上,公网设备是 eth1
iptables -t nat -A POSTROURING -o eth1 -j MASQUERADE
我们还需要映射一定范围的端口到 UML 内部,以便外网访问。
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 10000:20000 -j DNAT --to-destination 192.168.1.2
UDP 也同理。请记得把 UML 内部的 sshd 监听的端口改到这个范围之内!
对于 80 和 443 端口,如果只有一个人使用,那么把它映射进 UML 是没有问题的。如果要多人使用,那就只能依靠主机 haproxy,按照域名来区分了。这要参考 haproxy 的 SNI 配置,我不再赘述。有一个小小的 Tip,那就是在 haproxy 配置中可以使用 hdr_end 或 -m end 指令匹配域名尾部以达到泛域名匹配的效果。
建议将这些网络设备的启动脚本加入主机的启动服务。在 UML 中,只需要配置 eth0 上的网络,把网关设为 192.168.1.1 就可以了。
[Match]
Name=eth0
[Network]
Address=192.168.1.2/24
Gateway=192.168.1.1
这是 systemd-networkd 的配置。
Boot up
现在已经可以启动 UML 了。首先我们需要把刚刚挂载出来的目录全部卸载,这一步推荐通过重启完成。然后可以启动
/path/to/vmlinux ubd0=/path/to/image eth0=tuntap,tap0,23:33:33:44:66:66,192.168.1.2 mem=512m con=pts
内存被限制在了 512m。如果你的 UML 中运行了 sshd, 那么你已经可以用暴露的端口访问内部运行的系统了。如果出现问题,你也可以直接通过 screen /dev/pts/X (X 是一个数字,自行 ls 查看) 来获得一个客户机的 Login shell
当然,我比较推荐的是把这些过程全部写成一个脚本,并且让它能够读取配置文件,这样再将它包装成一个 systemd 服务,就可以方便地管理多用户和开机启动了。
流量监控
在多人共享阿里云时,流量是个大问题,因为阿里云按照流量计费。因而,我们需要一个好用的流量统计工具。
我看中的是 vnstat, 因为它可以单独统计每个网络接口的流量。而 UML 的每个实例又单独享有一个 tap 接口,这就使流量统计非常方便了。
在 CentOS 上安装和启用非常简单
yum install vnstat
systemctl start vnstat
systemctl enable vnstat
过一段时间以后,使用 vnstat -q 即可查看统计数据。当然,为了方便其他用户查看,我还利用 bashttpd 搭建了一个服务器,调用 vnstat -q -i tapN 来返回各个接口的统计数据。这十分方便。
在 bashttpd.conf 中只需要这几行
serve_vnstat() {
add_response_header “Content-Type” “text/plain”
send_response_ok_exit <<<"$(vnstat -q -i tap$2)"
}
on_uri_match '^/tap([0-9])$' serve_vnstat
然后按照官方文档使用 socat 启动服务,即可在 YOUR_URL/tapN 访问到对应设备的流量统计。