pxe场景下引导内核的更换
最近在搞一个pxe的项目,当前系统对服务器进行两次pxe,第一次是用clonezallia获取服务器信息,例如网卡,内存等,第二次pxe是真正安装系统。主要是特定物理服务器不支持pxe,需要修改两次pxe的引导内核以及系统内核。因此对pxe相关的东西做了一个了解,知识相对琐碎,串起来之后再理解相对容易。
initrd.img与vmlinux
vmlinuz指的是内核,作用:进程管理、内存管理、文件管理、驱动管理、网络管理。
initrd.img是一个小的映象,包含一个最小的linux系统,放的是和启动相关的驱动模块。
通常的步骤是先启动内核,然后内核挂载initrd.img,并执行里面的脚本来进一步挂载各种各样的模块,其中最重要的就是根文件系统驱动模块,有了它才能挂载根文件系统(rootfs),继而运行用户空间的第一个应用程序init或者systemd,完成系统后续的启动。
initrd.img当然是可选的了,如果没有initrd.img,内核就试图直接挂载root分区。 之所以要有initrd,那是为了启动的时候有更大的灵活性。比如,你把ext3支持编译成模块了。偏偏你的root分区又是ext3的。这下就麻烦了。因为内核需要挂载root分区之后才能加载ext3支持。但是没有ext3支持就没法挂载root分区。initrd就是用来解决这个问题的。 类似的用这个东西还可以做其他的事情,比如从usb盘启动linux也会面临上面类似的问题。用initrd就能搞定了。 甚至,我想在有些嵌入式设备里面都不需要真正的root分区,用initrd就足够搞定一切了。
而对于pxe系统而言,initrd.img则是必须的,用于引导加载系统,进而进行安装系统到硬盘。
注意:vmlinuz的版本与initrd.img中的内核模块版本(modules)必须对应,这样才能找到相应的驱动模块,挂载设备。
pxe工作原理
基于Client/Server的网络模式,支持远程主机通过网络从远端服务器下载映像,并由此支持通过网络启动操作系统;PXE可以引导和安装Windows,linux等多种操作系统。
PXE工作原理:
- Client向PXE Server上的DHCP发送IP地址请求消息,DHCP检测Client是否合法(主要是检测Client的网卡MAC地址),如果合法则返回Client的IP地址,同时将启动文件pxelinux.0的位置信息一并传送给Client
- Client向PXE Server上的TFTP发送获取pxelinux.0请求消息,TFTP接收到消息之后再向Client发送pxelinux.0大小信息,试探Client是否满意,当TFTP收到Client发回的同意大小信息之后,正式向Client发送pxelinux.0
- Client执行接收到的pxelinux.0文件(用于网络启动的小程序,具有“兼容性、小巧、高效”特点)
- Client向TFTP Server发送针对本机的配置信息文件(在TFTP 服务的pxelinux.cfg目录下),TFTP将配置文件发回Client,继而Client根据配置文件执行后续操作。
- Client向TFTP发送Linux内核请求信息,TFTP接收到消息之后将内核文件发送给Client
- Client向TFTP发送根文件请求信息,TFTP接收到消息之后返回Linux根文件系统
- Client启动Linux内核
- Client下载安装源文件,读取自动化安装脚本
注意:如果要是用PXE远程安装操作系统,网卡必须支持PXE(即网卡中包含tftp的客户端)
Cobbler
pxe需要配置httpd、dhcp、tftp、ks文件等服务或文件,内容繁琐复杂,而cobbler则是基于PXE技术的工作原理的二次封装,通过命令的方式简化了PXE配置过程。
Cobbler是一个Linux服务器快速网络安装的服务,由python开发,小巧轻便(15k行python代码),可以通过PXE的方式来快速安装、重装物理服务器和虚拟机,同时还可以管理DHCP,DNS,TFTP、RSYNC以及yum仓库、构造系统ISO镜像。
Cobbler可以使用命令行方式管理,也提供了基于Web的界面管理工具(cobbler-web),还提供了API接口,可以方便二次开发使用。
Cobbler工作流程
- client裸机配置了从网络启动后,开机后会广播包请求DHCP服务器 (cobbler server)发送其分配好的一个IP
- DHCP服务器(cobbler server)收到请求后发送responese,包括其ip地址
- client裸机拿到ip后再向cobbler server发送请求OS引导文件的请求
- cobbler server告诉裸机OS引导文件的名字和TFTP server的ip和 port
- client裸机通过上面告知的TFTP server地址通信,下载引导文件
- client裸机执行执行该引导文件,确定加载信息,选择要安装的os, 期间会再向cobbler server请求kickstart文件和os image
- cobbler server发送请求的kickstart和os iamge
- client裸机加载kickstart文件
- client裸机接收os image,安装该os image
Cobbler集成(接管)的服务
- PXE服务支持
- DHCP服务管理
- DNS服务管理(可选bind,dnsmasq)
- 电源管理
- Kickstart服务支持
- YUM仓库管理
- TFTP(PXE启动时需要)
- Apache(提供kickstart的安装源,并提供定制化的kickstart配置)
Clonezilla
Clonezilla 是一个很好的系统克隆工具,即不仅支持对整个系统进行克隆,而且也可以克隆单个的分区,这种灵活性可能更能适应备份者的需要。
Clonezilla是一个pe(Preinstallation Environment)系统,说白了就是基于某个liunx内核版本做一些修改,让其支持不用安装系统直接管理计算机,类似于常用的windows pe。在我们项目中即用Clonezilla系统用于收集物理主机的相关信息。
Clonezilla与pxe集成
引用官网的说明:
Edit your PXElinux config file /tftpboot/nbi_img/pxelinux.cfg/default, and append the following:
-----------
label Clonezilla-live
MENU LABEL Clonezilla Live (Ramdisk)
KERNEL vmlinuz
APPEND initrd=initrd.img boot=live username=user union=overlay config components quiet noswap edd=on nomodeset nodmraid locales= keyboard-layouts= ocs_live_run="ocs-live-general" ocs_live_extra_param="" ocs_live_batch=no net.ifnames=0 nosplash noprompt fetch=tftp://$serverIP/filesystem.squashfs
fetch后面是squashfs,包含于下载的clonezilla软件中,在pxe安装后会挂载filesystem.squashfs,进而会生成一个可读写的ramdisk系统。
内核替换
从上述pxe服务器的过程来看,识别特定服务器的关键点是能够引导系统,系统厂商给了基于特定版本的内核补丁以及kernel包,即安装后直接可以得到vmlinux与modules,因此有两种方案:
- 基于pxe所用的引导内核重新制作内核
- 将特定版本的kernel包直接替换掉pxe所用引导内核
考虑到系统厂家的补丁是基于特定内核版本,在不同kernel版本上的更改需要适配,并且可用性还需要二次验证,因此采用第二种方案,将系统厂商给定的kernel包安装后,得到vmlinux,直接替换pxe所用引导内核,并修改initrd.img所用的lib/modules,方便描述简称vmlinux-new以及modules-new。
Clonezilla内核替换
进入clonezilla引导文件目录:
cd /var/lib/tftpboot/images/clonezilla-live
ls ./
initrd.img vmlinuz
-
将vmlinux-new直接替换为vmliunz直接替换即可
-
initrd.img的更改
initrd.img的更改主要是更改lib/modules,将原来的modules文件夹替换为新的modules-new。
initrd.img默认是xz压缩文件,先进行解压,解压后其实ASCII cpio archive归档文件,将归档文件抽取,修改后重新制作为归档即可,不需要再进行压缩,引导内核可以识别。
file initrd.img XZ compressed data mv initrd.img initrd.xz xz -d initrd.xz file initrd initrd.img: ASCII cpio archive (SVR4 with no CRC) mkdir test cd test cpio -i -F ../initrd(抽取) ll bin conf etc init lib lib64 run sbin scripts usr 可以看到原本内核用的是 ll lib/modules/4.11.0-1-amd64/ 替换掉该modules rm -rf lib/modules/* cp -r modules-new lib/modules/ 重新打包成归档文件(非压缩方式) find . | cpio -o -H newc > ../initrd.img 或者带有压缩的方式(默认kernel不识别crc64,要手动指定--check=crc32) find . | cpio -c -o | xz -9 --format=xz --check=crc32 > ../initrd.img
将打包的initrd.img直接替换改initrd.img即可。
替换后重启主机执行pxe,系统卡在安装页面(换内核还是有风险,谨慎执行)
分析clonezilla的执行过程:
-
vmlinuz内核挂载initrd.img后执行init脚本,init脚本中会解析pxe default文件中的选项,
init脚本–》. /scripts/${BOOT}–》bin/live-boot 会执行lib/live/boot/下面的脚本
-
挂载filesystem.squashfs,即fetch选项后面的squashfs。
修改default文件,去掉fetch,即先进入initrd文件系统,并在pxe default配置中加入debug选项方便调试(在init脚本中可以看到改选项),这样可以根据生成的日志就行追踪定位。
label Clonezilla-live
MENU LABEL Clonezilla Live (Ramdisk)
KERNEL vmlinuz
APPEND initrd=initrd.img boot=live username=user union=overlay config components quiet noswap edd=on nomodeset nodmraid locales= keyboard-layouts= ocs_live_run="ocs-live-general" ocs_live_extra_param="" ocs_live_batch=no net.ifnames=0 nosplash noprompt debug
问题记录:
-
驱动没有加载
lsmod 回显为空
将strace可执行文件复制到initrd.img,并重新制作,方便定位
strace modprobe loop write(2, "modprobe: ERROR: ../libkmod/libk"..., 144modprobe: ERROR: ../libkmod/libkmod.c:586 kmod_search_moddep() could not open moddep file '/lib/modules/3.10.0-327.1.fh.x86_64/modules.dep.bin'
clonezallia自带的modprobe版本不支持打开替换的modules,替换为当前系统版本(安装补丁内核的系统)。
cp /bin/kmod ./bin/ cp /usr/lib64/liblzma.so.5 usr/lib/x86_64-linux-gnu/ 注:so.5是kmod所依赖的
-
驱动加载不全
init脚本中会执行加载conf/modules配置的模块,缺少的模块直接在文件末尾加入即可。我是用虚拟机模拟的,真实物理机所需要的modules要看具体情况而定,例如添加scsi_transport_sas、igb、i40e等。
vim conf/modules 添加 squashfs virtio_pci virtio_net
-
驱动版本不匹配
在加载squashfs之前会检测/dev/loop* ,而loop驱动已经加载,只是没有生成/dev/loop*,手动执行参数加载loop驱动。
vim etc/modprobe.d/loop.conf options loop max_loop=5
多次更改initrd.img,最终会挂载squashfs,进而生成ramddisk,当然如果有必要还需要更改fetch选项中的filesystem.squashfs。
squashfs
嵌入式操作系统开发中,Linux 操作系统通常被压缩成 Image 后存放在 Flash 设备中。在系统启动过程中,这些 Image 被直接挂载到根文件系统, 然而这时的根文件系统是只读的, 用户不能在这个文件系统中进行任何写的操作。 如果把 Image 解压后直接拷贝到内存中,也可以实现写的功能,但是嵌入式系统一直存在内存大小方面的限制,所以将整个 Linux 系统拷入内存是不可取的。
SquashFS 也是一个只读的文件系统,它可以将整个文件系统压缩在一起,存放在某个设备,某个分区或者普通的文件中。如果您将其压缩到一个设备中,那么您可以将其直接 mount 起来使用,而如果它仅仅是个文件的话,您可以将其当为一个 loopback 设备使用。
squashfs文件系统制作与修改:
-
下载安装squashfs-tools
yum install squashfs-tools
-
解压filesystem.squashfs
unsquashfs filesystem.squashfs ll squashfs-root
-
更改squashfs-root的内容
rm -rf lib/modules/* cp -r modules-new lib/modules/
-
压缩成filesystem.squashfs
mksquashfs squashfs-root filesystem.squashfs
将制作好的squashfs替换到相应目录即可。
Centos内核替换
第二次pxe是用于安装centos,因此与clonezallia稍微不同,从cobbler可以看到,整个的安装文件在/var/www/cobbler/ks_mirror/CentOS-7-x86_64/中。
cobbler distro report
Name : CentOS-7-x86_64
Architecture : x86_64
TFTP Boot Files : {}
Breed : redhat
Comment :
Fetchable Files : {}
Initrd : /var/www/cobbler/ks_mirror/CentOS-7-x86_64/images/pxeboot/initrd.img
Kernel : /var/www/cobbler/ks_mirror/CentOS-7-x86_64/images/pxeboot/vmlinuz
Kernel Options : {}
Kernel Options (Post Install) : {}
Kickstart Metadata : {'tree': 'http://@@http_server@@/cblr/links/CentOS-7-x86_64'}
Management Classes : []
OS Version : rhel7
Owners : ['admin']
Red Hat Management Key : <<inherit>>
Red Hat Management Server : <<inherit>>
Template Files : {}
与clonezallia的内核替换类似,替vmlinuz以及重新制作initrd.img
ll /var/lib/tftpboot/images/CentOS-7-x86_64
initrd.img vmlinuz
替换后重启主机执行pxe,系统依旧卡在安装页面,提示:
ValueError: new value non-exist xfs filesystem is not valid as a default fs type
大致猜到是xfs的驱动没有加载,同样使用strace命令定位,切换到命令行页面(alt+ tab,安装页面有提示):
看到modprobe加载时用的modules并不是initrd系统中的,那这个是哪个文件系统中的?
losetup -a可以看到所有的挂载,会挂载LiveOS/rootfs.img。
再回头看下centos镜像中各个文件夹中的作用:
-rw-r--r-- 1 root root 14 Aug 15 18:57 CentOS_BuildTag
drwxr-xr-x 3 root root 2048 Aug 15 18:57 EFI(UEFI启动)
-rw-r--r-- 1 root root 227 Aug 15 18:57 EULA
-rw-r--r-- 1 root root 18009 Aug 15 18:57 GPL
drwxr-xr-x 3 root root 2048 Aug 15 18:57 images(efiboot与pxe安装)
drwxr-xr-x 2 root root 2048 Aug 15 18:57 isolinux(本地安装)
drwxr-xr-x 2 root root 2048 Aug 15 18:57 LiveOS(?)
drwxr-xr-x 2 root root 92160 Aug 15 19:22 Packages(安装包)
drwxr-xr-x 2 root root 4096 Aug 15 19:45 repodata
-rw-r--r-- 1 root root 1690 Aug 15 18:57 RPM-GPG-KEY-CentOS-7
-rw-r--r-- 1 root root 1690 Aug 15 18:57 RPM-GPG-KEY-CentOS-Testing-7
-r--r--r-- 1 root root 2883 Aug 15 19:45 TRANS.TBL
pxe安装系统时images/pxeboot/vmlinuz 引导initrd.img ,进而加载LiveOS/squashfs.img,挂载改系统后再执行系统安装,因此这个必须更squashfs.img中的modules。
对于squashfs,上文已经说了,不过与fetch选项中的squashfs不同的是,打开之后只有一个文件,这个文件其实是个rootfs。
ll squashfs-root/LiveOS/
total 1296388
-rw-r--r-- 1 root root 2147483648 Aug 15 02:22 rootfs.img
file rootfs.img
rootfs.img: Linux rev 1.0 ext4 filesystem data, UUID=e2fddbe2-a003-4b98-b272-3defe7b377c4, volume name "Anaconda" (extents) (64bit) (huge files)
rootfs
Linux系统中的根文件系统,Root FileSystem,简称为rootfs;
说白了就是能让操作系统运行起来的,一堆文件夹以及文件的集合,平时进入的liunx的根目录就是一个rootfs。
squashfs文件系统修改与制作:
修改比较简单,直接挂载,然后把相关文件复制进去,再卸载就ok了
-
挂载rootfs
mount rootfs.img /mnt/test/ umount /mnt/test
-
修改rootfs
cd /mnt/test/ rm -rf lib/modules/* cp -r modules-new lib/modules/
-
umount目录
umount /mnt/test
制作rootfs也比较简单
-
制作空盘
dd if=/dev/zero of=rootfs.img bs=1M count=100
-
格式化为你需要的文件系统,比如 ext3,ext4等
mkfs.ext4 -m 0 -O none -F rootfs.img
-
挂载rootfs,并把相应的文件都复制进去
mount rootfs.img /mnt/test1 cp -r * /mnt/test1/
-
卸载目录
umount /mnt/test1
将制作好的squashfs替换到相应目录即可。
结尾
通过更换内核会遇到各种问题,多数都是和驱动有关。
clonezilla的脚本基本都是sh脚本,在. /scripts/可以找到整个加载squashfs过程。
cobbler工具比较好用,命令可以通过help查看,常用cobbler sync。
参考文档:
kali linux中filesystem.squashfs文件更改
2 ways to update and rebuild initrd image in CentOS/RHEL 7 and 8