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工作原理:

img

  1. Client向PXE Server上的DHCP发送IP地址请求消息,DHCP检测Client是否合法(主要是检测Client的网卡MAC地址),如果合法则返回Client的IP地址,同时将启动文件pxelinux.0的位置信息一并传送给Client
  2. Client向PXE Server上的TFTP发送获取pxelinux.0请求消息,TFTP接收到消息之后再向Client发送pxelinux.0大小信息,试探Client是否满意,当TFTP收到Client发回的同意大小信息之后,正式向Client发送pxelinux.0
  3. Client执行接收到的pxelinux.0文件(用于网络启动的小程序,具有“兼容性、小巧、高效”特点)
  4. Client向TFTP Server发送针对本机的配置信息文件(在TFTP 服务的pxelinux.cfg目录下),TFTP将配置文件发回Client,继而Client根据配置文件执行后续操作。
  5. Client向TFTP发送Linux内核请求信息,TFTP接收到消息之后将内核文件发送给Client
  6. Client向TFTP发送根文件请求信息,TFTP接收到消息之后返回Linux根文件系统
  7. Client启动Linux内核
  8. 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工作流程

img

  1. client裸机配置了从网络启动后,开机后会广播包请求DHCP服务器 (cobbler server)发送其分配好的一个IP
  2. DHCP服务器(cobbler server)收到请求后发送responese,包括其ip地址
  3. client裸机拿到ip后再向cobbler server发送请求OS引导文件的请求
  4. cobbler server告诉裸机OS引导文件的名字和TFTP server的ip和 port
  5. client裸机通过上面告知的TFTP server地址通信,下载引导文件
  6. client裸机执行执行该引导文件,确定加载信息,选择要安装的os, 期间会再向cobbler server请求kickstart文件和os image
  7. cobbler server发送请求的kickstart和os iamge
  8. client裸机加载kickstart文件
  9. client裸机接收os image,安装该os image

Cobbler集成(接管)的服务

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,因此有两种方案:

考虑到系统厂家的补丁是基于特定内核版本,在不同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
  1. 将vmlinux-new直接替换为vmliunz直接替换即可

  2. 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的执行过程:

  1. vmlinuz内核挂载initrd.img后执行init脚本,init脚本中会解析pxe default文件中的选项,

    init脚本–》. /scripts/${BOOT}–》bin/live-boot 会执行lib/live/boot/下面的脚本

  2. 挂载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

问题记录:

  1. 驱动没有加载

    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所依赖的
    
  2. 驱动加载不全

    init脚本中会执行加载conf/modules配置的模块,缺少的模块直接在文件末尾加入即可。我是用虚拟机模拟的,真实物理机所需要的modules要看具体情况而定,例如添加scsi_transport_sas、igb、i40e等。

    vim conf/modules
    添加
    squashfs
    virtio_pci
    virtio_net
    
  3. 驱动版本不匹配

    在加载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文件系统制作与修改:

  1. 下载安装squashfs-tools

    yum install squashfs-tools
    
  2. 解压filesystem.squashfs

    unsquashfs filesystem.squashfs
    ll
    squashfs-root
    
  3. 更改squashfs-root的内容

    rm -rf lib/modules/*
    cp -r modules-new lib/modules/
    
  4. 压缩成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,系统依旧卡在安装页面,提示:

1567135102323

ValueError: new value non-exist xfs filesystem is not valid as a default fs type

大致猜到是xfs的驱动没有加载,同样使用strace命令定位,切换到命令行页面(alt+ tab,安装页面有提示):

1567135308979

看到modprobe加载时用的modules并不是initrd系统中的,那这个是哪个文件系统中的?

1567135564049

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了

  1. 挂载rootfs

    mount rootfs.img /mnt/test/
       
    umount /mnt/test
    
  2. 修改rootfs

    cd /mnt/test/
    rm -rf lib/modules/*
    cp -r modules-new lib/modules/
    
  3. umount目录

    umount /mnt/test
    

制作rootfs也比较简单

  1. 制作空盘

    dd if=/dev/zero of=rootfs.img bs=1M count=100
    
  2. 格式化为你需要的文件系统,比如 ext3,ext4等

    mkfs.ext4 -m 0 -O none -F rootfs.img
    
  3. 挂载rootfs,并把相应的文件都复制进去

    mount rootfs.img /mnt/test1
    cp -r * /mnt/test1/
    
  4. 卸载目录

    umount /mnt/test1
    

将制作好的squashfs替换到相应目录即可。

结尾

通过更换内核会遇到各种问题,多数都是和驱动有关。

clonezilla的脚本基本都是sh脚本,在. /scripts/可以找到整个加载squashfs过程。

cobbler工具比较好用,命令可以通过help查看,常用cobbler sync。

参考文档:

Centos7 下cobbler安装及配置

kali linux中filesystem.squashfs文件更改

linux rootfs.img的制作

2 ways to update and rebuild initrd image in CentOS/RHEL 7 and 8

Table of Contents