其实去年的这个时候就在玩儿这个了,那时候 BBR 还在主线,想 用 UML 来充分利用一下 OpenVZ VPS。 据说 早期的 Linode 就是用这个方法搞虚拟化卖 VPS 的。
然而就像 ArchWiki 的警告栏中描述的一样,这是个 Dead project, last activity in 2007。尽管如此,我们还是可以基于最新的 Linux 源代码运行它。
编译
AUR 里的 linux-usermode 包的 PKGBUILD 有点年久失修,本来就应该像一般的内核编译过程一样,只不过加上 ARCH=um
表示需要编译用户态内核,几行命令就够了。
export ARCH=um
make defconfig
make prepare
make vmlinux modules
但是由于 glibc 的问题,在 Arch Linux 上直接这样编译会报下面这样的错误。
arch/um/os-Linux/signal.c: In function ‘hard_handler’:
arch/um/os-Linux/signal.c:163:22: error: dereferencing pointer to incomplete type ‘struct ucontext’
mcontext_t *mc = &uc->uc_mcontext;
^~
make[1]: *** [scripts/Makefile.build:315: arch/um/os-Linux/signal.o] Error 1
make: *** [Makefile:1025: arch/um/os-Linux] Error 2
解决方法参考 这里 ,把 patch 打上就好啦。
当然你还可以试试我写的 PKGBUILD ,默认会加上 bbr 因为这才是最开始的目的 ,不想这样改的话清空掉 extra_config
就好啦。
按上面的方法编译完成的话,你会得到这两个东西。
一个是内核 vmlinux
,如果用 PKGBUILD 的话会生成一个叫 linux-usermode
的包。
另一个是可用的内核模块 modules
,对应的包是 linux-usermode-modules
。
我们通常把内核和内核模块一起打包,但是现在我们必须要分开它们,因为它们会被 安装在不同的地方。
配置
安装
其实把编译好的 vmlinux 复制到宿主机上就好啦。如果你像我一样觉得还是用包管理器更清真一些,那只需要在宿主机上安装 linux-usermode
这个包。 module 不需要安装到宿主机的文件系统。
配置 rootfs
首先得有个镜像文件作为 rootfs ,不想太占硬盘又怕分得太小不够装的话就用稀疏文件吧。 dd
和 truncate
都是很棒的工具。
$ dd of=rootfs.img bs=1M seek=20480 count=0
记录了0+0 的读入
记录了0+0 的写出
0 bytes copied, 1.8298e-05 s, 0.0 kB/s
### 或者使用 truncate
$ truncate -s 20G rootfs.img
使用 mkfs
在这块空白的镜像文件上创建你喜欢的操作系统,一般来说用 ext4
就好啦。
然后就是挂载这个镜像并装入系统啦,装系统什么的自己围观 ArchWiki 就好。其它发行版应该也都没问题。当然内核和配置启动就不需要了。
几件小事
首先 arch-chroot 进到你的 rootfs 里面。
修改启动时的 tty
当然你也可以在启动后用 screen 连上去。
systemctl enable [email protected]
systemctl disable [email protected]
重新挂载 rootfs 为可写
用户态内核会将 rootfs 挂为只读,但是我们可以加个 systemd-unit 重新挂载一下
[Unit]
Description=Remount rootfs as rw
[Service]
Type=oneshot
ExecStart=/usr/bin/mount -o remount,rw /
[Install]
WantedBy=getty.target
然后 enable 它就好。
haveged
看起来随机数生成器并不能正常工作,所以还是装上这个然后 systemctl enable haveged
。
安装模块
实际上 linux-usermode-modules
这个包是装在 rootfs 里而不是宿主机上的,这样 modprobe
才能找到内核模块并加载它们。这也就是我们必须把用户态内核与它的模块分开打包的原因。 其实没有内核模块不也一样跑。
网络
参考 Gentoo Wiki 。
其实就是简单地用 iptables 做个 NAT 。
sudo modprobe tun
sudo modprobe iptable_nat
sudo systemctl start iptables
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
记得把 eth0
换成你自己上网用的那个端口。
然后给 UML 划个 IP 段,比如我用的是 192.168.210.0/24
。然后宿主机这边的 IP 用 192.168.210.1
, UML 那边用 192.168.210.101
。
然后 arch-chroot 到 UML 的 rootfs 里加上网络配置,直接写静态 IP 就好。我习惯用 netctl ,当然 systemd-network 应该都能工作。
Description='Network for Usermode Linux with NAT'
Interface=eth0
Connection=ethernet
IP=static
Address=('192.168.210.101/24')
Gateway='192.168.210.1'
DNS=('192.168.1.1')
走之前记得顺手 enable 了它。
启动
启动自然就是一条命令的事情。
vmlinux \
ubd0=rootfs.img \
eth0=tuntap,,,192.168.210.1 \
mem=512M
配置的写法基本上就是 设备名=参数
的形式, eth0 后面接的是宿主机上打算给 NAT 用的网关 IP 。启动后会在宿主机上自动产生一个 tap0 接口并添加路由表。