PXE系列文章(10)- 构建内存 OS (基于 Ubuntu)
在前面的文章中, 我们已经提到, initrd 本身就是精简版的根盘文件系统, 主要目的是用于内核启动后 OS 环境的初始化, 比如你是 Soft Raid,需要在这个时候读取 /etc/fstab
文件,比如根盘文件在特定的 Raid 设备卷中的分区上, 需要预加载指定 Raid 驱动等等.总之这个环境主要用于真正用户 OS 启动之前的准备.
通常在各个发行版本都有各自的 initrd 的构建工具,各自的目的呢, 也仅限于我们上述提到的功能. 而我们是需要将整个 OS 塞到这个内存镜像中,所以稍微还是有区别,调研了下一些业界的实现, 这里的几个链接供参考:
- http://www.espenbraastad.no/posts/centos-7-rootfs-on-tmpfs/
- https://wiki.gentoo.org/wiki/Diskless_nodes/zh-cn
- https://www.redpill-linpro.com/sysadvent/2016/12/14/use-virt-manager-to-build-disk-images.html
上述链接可以看到, 一般是使用 https://virt-manager.org/ 和 http://libguestfs.org/ 这两个项目的工具来构建镜像.可以尝试下这个方法, 完全可行, 但是有个问题是这个流程真的太长了, 比如要按上述流程构建的话, 需要用virt-install完整安装一遍目标 OS , 继续用virt-sysprep对镜像做一遍清理, 将生成的镜像导入到 virt-builder, 继续在 KVM 虚拟机中配置一遍 OS 默认信息, 然后就可以将镜像更新到 virt-builder 基础镜像中, 然后拿到其他地方使用, 比如 Ramdisk boot/Vagrant/OpenStack/Docker中使用.
其实上述安装过程是依赖于各个发行版本的安装器, 完整走过一遍各发行版本的安装流程, 然后对磁盘内容进行整理和优化, 然后作为最终的镜像使用.但是其实我们的述求是很简单的,就是根盘目录所有的内容,一番调研之后, Debian/Ubuntu 系列 OS 提供的 debootstrap 工具用于构建完整的 OS 根目录, 而且操作及其简单, 一行命令就可以完成, 相比我们使用上面的流程将更加简便和可靠, 局限就是只能使用 Debian/Ubuntu 系列发行版. 其他发行版本经过查找, 不确定是否包含该类似工具.
好接下来,我们就介绍下 debootstrap 工具:
main # mkdir /stable-chroot
main # debootstrap stable /stable-chroot http://deb.debian.org/debian/
官方的 debootstrap 的使用教程,请参考 https://wiki.debian.org/Debootstrap
使用 debootstrap 工具我们构建了根盘, 但是还是以目录形式存在,将如何使用呢? 在使用层面, 我们和 http://www.espenbraastad.no/posts/centos-7-rootfs-on-tmpfs/ 文章所使用的逻辑保持一致.
内核启动后, 默认执行的第一个程序就是 pid=0 的 init 程序.而发行版的默认的 init 程序在各个历史阶段皆不相同, 早期大多使用 SysVInit, 较新的版本使用 Systemd 作为 init 程序, init 程序默认是放置在系统根盘(/)下, 一般是通过软链接的方式链接到/init. 而我们这里是希望在内存镜像中集成完整版本的 OS.就涉及到两个过程, 一个是内核启动后initrd
中默认的 init 程序,另一个是 initrd
中的初始化完成后, 使用 switch_root 切换到用户端 OS 的 init 程序, 我们重点关注第一个过程中 init 程序, 需要重新定义这里的初始化逻辑. 第二个过程中的 init 进程一般和 OS 的 init 程序挂钩, 早期是 SysVinit,目前更多是 Systemd.
我们需要构建一个工作目录,比如 ~/work/
,接下来构建一些必须的子目录:
mkdir -p ~/work/build # 用于存放最终的 initrd 文件
mkdir -p ~/work/initramfs # initrd 中存放的文件内容,主要包括 一阶段 init 程序和根盘压缩内容
mkdir -p ~/work/chroot #用于存放操作系统的根盘内容
初始化 OS 的根盘内容
我们这里使用 Ubuntu 18.04 作为内存 OS, 我们这里主要用于 VirtualBox 实验环境, 安装 linux-virtual
内核包就可以,如果是在物理机上真实使用, 安装 linux-generic
内核包.
debootstrap --arch amd64 bionic ~/work/chroot http://mirrors.163.com/ubuntu
chroot ~/work/chroot apt -y install linux-virtual
根盘文件系统好了之后, 需要将其压缩打包:
cd ~/work/chroot
time tar -cf - . | xz -v --threads=24 > ~/work/initramfs/rootfs.tar.xz
````2019-06-21-build_ram_os.md*
接下来我们需要下载 `busybox` 来作为执行环境, 减少 init 脚本的编写成本
```bash
wget -O ~/work/initramfs/bin/busybox https://busybox.net/downloads/binaries/1.21.1/busybox-x86_64
编写 init 文件内容(这里可以可以参考各个发行版本的 iso 中的 init 工具的编写方式, 大同小异)
vim ~/work/initramfs/init
~/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
到此整个 initramfs 的内容,我们就构建完成了.最终需要输出为cpio文档格式.
cd ~/work/initramfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ~/work/build/initramfs.gz
最终生成的 initramfs 文件为 ~/work/build/initramfs.gz
, 将其拷贝到 PXE Server 的 /var/lib/tftpboot/ubuntu-installer/amd64/
, 接下来你就可以去我们的 PXE 环境调试下, 将 PXE Server 的 `/var/lib/tftpboot/ubuntu-installer/amd64/boot-screens/txt.cfg 中的内容调整为如下:
default install
label install
menu label ^Automatically Install Ubuntu 16.04
kernel ubuntu-installer/amd64/linux
append vga=788 initrd=ubuntu-installer/amd64/initramfs.gz
然后启动一下 PXE Client 虚拟机, 看看是否启动了一个完整的 OS.