序言
由于之前笔者所在公司的一款虚拟化平台产品在数据中心服务器上布署时出现不兼容现象,怀疑是安装介质中我们所订制的Linux内核与新服务器硬件不兼容造成,于是就牵连到升级安装介质中Linux内核的工作。因为这款虚拟化平台产品是在CentOS6.5的基础上订制得到,所以本质上相当于直接更新CentOS6安装介质中Linux内核。关于怎么订制一张Linux发行版光碟,以及怎样在一个完整现有的Linux系统上升级内核,网上各类文章铺天盖地几乎已成山路货。但是直接升级发行版介质中的内核却少有提到,因此我将整个工作过程记录出来,所用方法方式不一定最优,但意在抛砖引玉。
在我进行这项工作的时侯,发觉上的内核最新版本已渐次变为4.8.6linux安装内核开发包,这是一个stable版本,于是乎决定就用它了。
安装光碟目录结构剖析
CentOS6.5安装光碟目录结构如右图所示:
图1CentOS发行版ISO目录树
(1)EFI目录主要用于64位的基于EFI的系统引导。其中的BOOT目录下的BOOTX64.conf为grub的配置文件,用于显示引导菜单。
(2)images目录:包含有各类引导镜像。最重要的是引导第二阶段安装程序须要用到的镜像文件install.img(CentOS7安装盘中该文件名称是squashfs.img),该镜像文件内部文件系统类型是squashfs,未经压缩,可以直接挂载(只读),anaconda程序就在这个镜像文件中。该目录中还包含一个pxeboot目录,主要用于制做PXE安装方法引导介质。
(3)isolinux目录:有开机引导系统安装的内核(vmlinuz)及临时文件系统(initrd.img),在引导系统时会载入显存。
(4)Packages目录:包含安装所需的所有二补码RPM包。
(5)repodata目录:一个坐落光碟介质上的yum源,内部包含了软件库房所有的配置文件。
(6)TRANS.TBL文件:记录当前目录的列表,用mkisofs的-T参数重新生成,主要是为了支持长文件名称。
(7).discinfo文件是安装介质的辨识信息。.treeinfo文件记录不同安装方法安装程序所在的目录结构,如PXE方法时,内核kernel=images/pxeboot/vmlinuz,根文件系统initrd=images/pxeboot/initrd.img。
CentOS安装光碟是一张引导盘,启动时,引导程序会分别将vmlinuz和initrd.img载入显存,待内核初始化完成后,会执行initrd中的/sbin/init,/sbin/init加载/sbin/loader常用linux系统,最终加载运行install.img中的anaconda安装程序。anaconda会依照配置和用户操作分别安装Packages文件夹下的rpm包,操作系统内核也以rpm包的方式存在其中。为此,须要更新的文件主要是光碟中isolinux、image/pxeboot下的vmlinuz和initrd.img文件,Packages目录下的内核RPM包。
编译内核及模块
第一步,从上下载新版本的内核源码,linux内核版本号中的第二位(即次版本号)为质数的版本为稳定版,为质数的版本是处于开发中的非稳定版,本文考虑到更新后的安装程序须要用于生产环境,因而,选择的版本号为4.8.6,即稳定版。下载后直接用tar将源码解压到/usr/src/kernels路径下。
第二步,配置和编译内核。在配置和编译内核前,需先打算好相关工具环境,先执行yum–yinstallgccncurses-developensslcreaterepo,在源码目录下分别执行下列命令即可生成内核二补码文件:
#makemenuconfig//启动一个图形化内核配置界面,该配置工具会将当前系统内核配置作为默认配置,配置好后选择按键,会再内核源码目录中生成一个.config文件保存配置。
#makebzImage//编译内核源码,得到内核压缩文件vmlinuz
#makemodules//编译内核模块
#makemodules_install//安装内核模块
#makeinstall//安装内核
另外,还可能用到的make目标有:
#makeclean//消除建立过程中生成的中间文件和目标文件,但保留内核配置及建立版本号
#makemrproper//消除建立过程中的中间文件和目标文件,同时,消除内核配置及建立版本号
生成内核RPM包
执行:
#makerpm-pkg
可以生成内核RPM包,该命令生成的内核RPM包可在现有完整系统上直接安装,执行:
#rpm–ivhkernel-4.8.6-1.el6.x86_64.rpm
然而,假若将新生成的内核RPM包替换掉Packages目录中原有的内核RPM包,则系统在安装后启动时可能会抛出KernelPanic,缘由是内核RPM包在执行过程中须要依赖其它一些第三方工具来最终完成安装流程,这种依赖关系须要配置到RPM包中,致使在安装内核前,先安装这种被依赖的工具包,否则都会造成安装不完整。右图是原有2.6版本内核RPM包和新生成的4.8.6版本的RPM包依赖关系对比图:
图22.6.32版本内核包默认依赖
图34.8.6版本内核包默认依赖
从上述两张图中可以看出显著的差异,完成内核安装需执行module-init-tools、initscripts、grubby、dracut等几个包中的脚本,因而这几个包必须先于内核安装。为了实现这一目的,我所采用的办法是更改/usr/src/kernels/linux-4.8.6/scripts/package下的mkspec脚本,这是由于在makerpm-pkg时,会调用到该脚本来生成内核RPM的SPEC文件。最终在mkspec文件中降低如下内容:
echo "PreReq: fileutils, module-init-tools, initscripts, grubby >= 7.0.4-1, dracut-kernel,
/sbin/new-kernel-pkg, device-mapper-event-libs, device-mapper-libs,
device-mapper-multipath, device-mapper-multipath-libs, NetworkManager,
NetworkManager-glib, crypto-utils, cryptsetup-luks, cryptsetup-luks-libs, lvm2,
dmraid, dmraid-events, dracut, file, python, python-cryptsetup, sysvinit-tools"
其中,“PreReq”表示依赖类型为install前。保存mkspec文件并重新makerpm-pkg,然后生成的RPM就可以直接装入到Packages目录中用于系统全新安装了,检测新生成的内核RPM可以见到如下景色:
图4新生成的kernel-4.8.6RPM依赖项
图4中的那些依赖项是我初步整理过后的一个结果,可能有某些依赖不是必要的,但因为编译建立内核耗费时间较长,为了减低失败次数,保险起见将其加到PreReq中了。具体依赖关系还有待进一步深入剖析。
更新initrd.img
CentOS6.5安装镜像中的initrd.img文件是一个CPIO包,采用LZMA压缩算法进行了压缩,可以直接用lzma和cpio进行解压解包后得到一个完整的目录树。这个文件本质上是一个ramfs,它的地位和作用与系统中/boot/initramfs-...img文件是一致的,即:为安装程序内核提供了一个带有驱动模块的临时显存文件系统,同时initrd.img中还包含了安装程序第一阶段所须要的初始化程序/init、/sbin/loader。
更新initrd就是要将新编译下来的模块文件添加到它内部/modules中,然而,我将更新后的操作系统/lib/modules/下的内容直接添加进去却失败了,内核启动时同样出现panic,但是这些方法也造成initrd.img文件规格暴增,后来我又将编译后生成的initramfs文件中的modules添加进去,也失败了,内核启动时辨识不了网路设备,由于initramfs中的modules不完整。看来,只能通过dracut重新生成一份带完整驱动模块的initramfs文件,并从该文件中得到modules。
#dracut-f-v--hostonly-k"/lib/modules/4.8.6/"/boot/initramfs-4.8.6.img4.8.6
上述命令借助dracut生成一份用于本地使用的initramfs文件,该文件包含了当前宿主机上早已被加载的模块文件。假如想将生成的initramfs文件放在别的主机上使用,则去除“--hostonly”即可,执行:
#dracut-f-v-k"/lib/modules/4.8.6/"/boot/initramfs-4.8.6.img4.8.6
这样可以让initramfs内的驱动模块相对完整一些。但CentOS安装镜像须要在不同硬件环境下使用,因而须要将尽可能多的驱动模块包含进去linux压缩命令,dracut提供了一种配置机制让我们可以定做模块或驱动。在/etc/dracut.conf中可以见到如下内容:
图5dracut.conf文件
可以将须要添加和滤除的模块驱动追加到“dracutmodules”、“omit_dracutmodules”、“add_dracutmodules”、“add_drivers”等变量中,关于这种变量的具体涵义在其上方都有注释说明。不难看出,为了实现将尽可能完整的驱动模块打包进initramfs文件这一目的,只须要将所有待定制的驱动模块文件的文件名(包含路径且不含后缀)添加到dracutmodules这个变量中,在进行具体操作前,还有两件事情须要完成:
第一,dracut.conf是老版本dracut所使用的配置文件,按照要求新版本的配置文件须装入到/etc/dracut.conf.d/目录下,文件名可自定义,但后缀必须是conf,为此,事先将dracut.conf拷贝一份进dracut.conf.d并重命名为mydracut.conf。
第二,因为待添加的驱动模块文件数目诸多,一个个自动加到add_driver中是件苦差。我采用的办法是写一个脚本,把那些文件路径和文件名都提出来导入为一个环境变量,之后将环境变量加到mydracut.conf中,具体地,可以创建一个expmod.sh文件并添加如下内容:
#!/bin/bash
ALL_DRVS=
all_drvs_l="$(find /lib/modules/4.8.6/kernel/ -name '*.ko')" #找到所有后缀为ko的模块文件
for i in $all_drvs_l; do
ALL_DRVS=${ALL_DRVS}${i//.ko/ } #dracut要求滤掉文件后缀名ko
done
export ALL_DRVS
接着,更改/etc/dracut.conf.d/mydracut.conf中第13行为:
add_drivers+="$ALL_DRVS"
然后开始生成一个initramfs,可暂时命名为tmpinitramfs.img。
#chmodu+xexpmod.sh
#sourceexpmod.sh
#dracut-f-v-k"/lib/modules/4.8.6/"/tmp/tmpinitramfs.img
这时,生成的tmpinitramfs.img包含的驱动模块会特别完整,生成这个文件的目的其实是为了得到其中/lib/modules的内容。下边开始即将升级光碟中的initrd.img文件。
第1步,创建两个临时目录
#mkdir-pv/tmp/initrd.d/tmp/initrd.d.tmp
将原先CentOS光碟镜像中的initrd.img拷贝到/tmp/initrd.d中linux安装内核开发包,将新生成的tmpinitramfs.img拷贝到/tmp/initrd.d.tmp中。
第2步,将原有的initrd.img解压解包
#cd/tmp/initrd.d
#mvinitrd.imginitrd.img.lzma
#lzma-dinitrd.img.lzma
#cpio-ivmd<initrd.img
第3步,提取新版本(4.8.6)的modules,并将其中所有文件拷贝至第2步操作后的目录结构中
#cd/tmp/initrd.d.tmp
#mvtmpinitramfs.imgtmpinitramfs.img.gz
#gunziptmpinitramfs.img.gz
#cpio-ivmd
#cp-rlib/modules/4.8.6/tmp/initrd.d/modules
第4步,生成新的initrd.img
#cd/tmp/initrd.d
#rm-finitrd.img
#find.|cpio-o-Hnewc>initrd.img
至此,新的initrd.img生成了。
更新光碟介质中软件库房
更新Packages目录中的内核及相关工具,并生成新的yum库房。
第1步,用过yum工具更新内核工具包module-init-tools,initscripts,grubby,sysvinit-tools,事先确保系统早已安装了yum-utils
#yuminstallyum-utils
之后,下载上述工具及其依赖包
#yuminstall–ymodule-init-tools,initscripts,grubby,sysvinit-tools--downloadonly--downloaddir=./
第2步,在/tmp内构建一个临时目录作为新软件库房base目录
#cd/tmp&&cd$(mktemp-dPackages.XXXXX)
挂载原有光碟镜像,将光碟镜像中Packages目录拷贝到/tmp/Packages.XXXXX中。
#mount–tiso9660/dev/sr0/mnt
#cp-r/mnt/Packages./
第3步,将第1步下载的RPM包连同kernel-4.8.6-1.x86_64.rpm一并替换到/tmp/Packages.XXXXX中,并将原先的内核kernel-2.6.32-279.el6.x86_64.rpm、kernel-firmware-2.6.32-279.el6.x86_64.rpm,同时还应把原先的TRANS.TBL删除。
第4步,生成新的TRANS.TBL文件
#mkisofs-v-r-T-J-o./Packages.iso./Packages
这一步生成Packages.iso是为了得到其中的TRANS.TBL文件,挂载Packages.iso,将其中的TRANS.TBL拷贝进/tmp/Packages.XXXXX中
第5步,生成新的yum库房
#cp/mnt/repodata/XXXXXXXXXXXXX-comps.xml./comps.xml
#createrepo–gcomps.xml./
上述comps.xml文件是Packages中关于软件包分组的配置文件,安装程序Anaconda也会解析该文件,因为不涉及到对分组的更改,所以直接使用原先的分组配置就可以了。执行完上述命令后可以看见在当前目录下生成了新的repodata目录,上面包含了新的yum库房所有的配置信息。
更新光碟镜像
将第三节编译后得到的vmlinuz、第五节生成的initrd.img以及第六节更新后的Packages、repodata替换原先的光碟。
借助mkisofs生成新的可引导光碟镜像。
总结
此次升级基本上是用自动操作的方法一步步完成的,虽然也企图希望找寻到一些现有的工具来完成,但很遗憾没有找到。倘若各路高手有更好的方法方式,希望能得到赐教。