PXE系列文章(13)- 优化内存镜像的init脚本逻辑
我们前面几节已经详细介绍了基于 Ubuntu
和 CentOS
构建内存镜像的方案,其中 ~/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 沟通交流.