我们前面几节已经详细介绍了基于 UbuntuCentOS 构建内存镜像的方案,其中 ~/work/initramfs/init 脚本内容大致如下:

#!/bin/busybox sh

# Dump to sh if something fails
error() {
    echo "Jumping into the shell..."
    setsid cttyhack sh
}

# Populate /bin with binaries from busybox
/bin/busybox --install /bin

mkdir -p /proc
mount -t proc proc /proc

mkdir -p /sys
mount -t sysfs sysfs /sys

mkdir -p /sys/dev
mkdir -p /var/run
mkdir -p /dev

mkdir -p /dev/pts
mount -t devpts devpts /dev/pts

# Populate /dev
echo /bin/mdev > /proc/sys/kernel/hotplug
mdev -s

echo "mount rootfs... "
mkdir -p /newroot
mount -t tmpfs -o size=80% tmpfs /newroot || error

xz -d -c -f rootfs.tar.xz | tar -x -f - -C /newroot || error

mount --move /sys /newroot/sys
mount --move /proc /newroot/proc
mount --move /dev /newroot/dev

exec switch_root /newroot /sbin/init || error

启动过程中, 我们需要对根文件系统进行解压, 物理服务器因为大多CPU性能都非常好, 同时内存也完全足够,这块的性能表现不明显. 如果在CPU性能较差, 内存也受限的情况下,这个解压过程将非常缓慢, 同时在计算性能明显不足的环境, 构建这个压缩包的过程也将非常慢, 即使可以使用并行压缩选项, 也依然非常慢. 所以一直在尝试将这块逻辑进行优化, 调研过各个发行版本的iso的启动流程, 尤其是gentoo的iso的启动流程,提供了一些参考, 可以使用device mapper/aufs/overlayfs等技术来解决这个问题,根文件系统大多都统一存放在squashfs只读文件系统中, 在分层文件系统的上层构建一个可写层, 保证最终的根文件系统的挂载点可读可写.

在这里, 我们选择使用overlayfs方案来实现这个分层文件系统.首先你需要知道pxe启动时的内核版本,目前看到的情况是各个发行版本的pxe 内核没有集成 overlayfs 文件系统作为内建模块,所以需要将overlayfs的内核模块拷贝到 initramfs 中的对应位置.

我们这里使用 Ubuntu 16.04 的 netboot 下的内核, 位置在 /ubuntu/dists/xenial-updates/main/installer-amd64/current/images/netboot/目录下, 下载下来后可以使用 file 命令确认下具体版本

file vmlinuz

vmlinuz: Linux kernel x86 boot executable bzImage, version 4.4.0-142-generic (buildd@lgw01-amd64-033) #168-Ubuntu SMP Wed Jan 16 21:00:45 UTC 2019, RO-rootFS, swap_dev 0x6, Normal VGA

进一步我们需要找到和上述内核匹配的 overlayfs 内核模块, 我们从 linux-image-4.4.0-142-generic 文件中获取该内核模块文件, 将 /lib/modules/4.4.0-142-generic/kernel/fs/overlayfs/overlay.ko 拷贝到 ~/work/initramfs/lib/modules/4.4.0-142-generic/kernel/fs/overlayfs 目录下路径中的内核版本和内核自身的版本必须严格匹配. 如果是其他发行版本, 找不到官方编译好的内核包, 就需要手动从 kernel.org 官网下载对应版本的内核文件,手动编译内核, 再将 overlayfs 模块拷贝到目标目录下.

准备工作我们就完成了,接下来优化 init 脚本, 重新定义挂载路径:

mkdir -p /mnt/lower
mkdir -p /mnt/upper
mkdir -p /mnt/work
mkdir -p /mnt/newroot

挂载 squashfs 根文件系统到 /mnt/lower 目录

mount /filesystem.squashfs /mnt/lower

挂载 overlayfs 文件系统

mount -t overlay overlay -olowerdir=/mnt/lower,upperdir=/mnt/upper,workdir=/mnt/work /mnt/newroot

使用 switch_root 切换到 /mnt/newroot

exec switch_root /mnt/newroot /sbin/init || error

完整的 init 脚本文件如下:

#!/bin/busybox sh

# Dump to sh if something fails
error() {
    echo "Jumping into the shell..."
    setsid cttyhack sh
}

# Populate /bin with binaries from busybox
mkdir /bin, 
/bin/busybox --install /bin

modprobe overlay
mkdir -p /proc
mount -t proc proc /proc

mkdir -p /sys
mount -t sysfs sysfs /sys目录下

mkdir -p /sys/dev
mkdir -p /var/run
mkdir -p /dev

mkdir -p /dev/pts
mount -t devpts devpts /dev/pts

# Populate /dev
echo /bin/mdev > /proc/sys/kernel/hotplug
mdev -s

mkdir -p /mnt
mount -t tmpfs -o size=80% tmpfs /mnt || error

echo "mount rootfs... "
# mount filesystem.squashfs /newroot -t squashfs -o loop
# xz -d -c -f rootfs.tar.xz | tar -x -f - -C /newroot || error
mkdir -p /mnt/lower
mkdir -p /mnt/upper
mkdir -p /mnt/work
mkdir -p /mnt/newroot

mount /filesystem.squashfs /mnt/lower
mount -t overlay overlay -olowerdir=/mnt/lower,upperdir=/mnt/upper,workdir=/mnt/work /mnt/newroot

#/usr/bin/unsquashfs -f -d /newroot /filesystem.squashfs || error

mount --move /sys /mnt/newroot/sys
mount --move /proc /mnt/newroot/proc
mount --move /dev /mnt/newroot/dev

exec switch_root /mnt/newroot /sbin/init || error

使用这种方式后, 整个启动过程, 省去了一半以上的启动时间, 个人实验环境一般是 VirtualBox, 即使在这样的虚拟化环境, 启动过程也非常快速. 之前系统启动过程中, 除了启动慢, CPU占用还异常高, 这两个问题使用上述方案都得到较好的解决.

~/work/initramfs目录下的内容如下:

(virtualenv) root@server: find .
.
./filesystem.squashfs
./lib
./lib/modules
./lib/modules/4.4.0-142-generic
./lib/modules/4.4.0-142-generic/kernel
./lib/modules/4.4.0-142-generic/kernel/fs
./lib/modules/4.4.0-142-generic/kernel/fs/overlayfs
./lib/modules/4.4.0-142-generic/kernel/fs/overlayfs/overlay.ko
./init
./bin
./bin/busybox

构建initramfs.gz文件:

cd ~/work/initramfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../build/initramfs.gz

拷贝内核到build目录:

cd ~/work/chroot
cp -rf boot/vmlinuz-4.15.0-54-generic ~/work/build/vmlinuz

最终build目录下的内容如下:

(virtualenv) root@server: find .
.
./initramfs.gz
./vmlinuz

将两个文件拷贝到你本地的 ~/.VirtualBox/TFTP/ 目录下,更新 ~/.VirtualBox/TFTP/ubuntu-installer/amd64/boot-screens/txt.cfg 文件内容如下:

default install
label install
    menu label ^Automated Boot RAM OS
    menu default
    kernel vmlinuz
    append vga=788 initrd=initramfs.gz net.ifnames=0 biosdevname=0

如果你的虚拟机名称为test的话, 确保 ~/.VirtualBox/TFTP/test.pxe 正确链接到 ~/.VirtualBox/TFTP/pxelinux.0。同时保证虚拟机是优先网络启动,网卡类型选择 网络地址转换(NAT).

接下来启动下 test 虚拟机,看看是否正常进入内存OS,也可以和之前的启动过程对比下,启动耗时。取消了根文件的解压过程,在虚拟环境表现还是非常明显的。

遗留下的一些问题是, 内存OS的根目录还显臃肿,需要削减大小,有这方面经验的同学欢迎给介绍下经验, 如果还有任何问题欢迎通过 github 或者 电子邮箱: mailto:linuxcoming@qq.com 沟通交流.

【腾讯云】境外1核2G服务器低至2折,半价续费券限量免费领取!
https://cloud.tencent.com/act/cps/redirect?redirect=1068&cps_key=e4b50f6c64a4480367f8a8d16fd07c5a&from=console

标签: pxe, kernel, init, netboot, vmlinuz, ramos, busybox, overlayfs, squashfs

添加新评论