Proxmox VE镜像分析与定制

Proxmox VE(Proxmox Virtual Environment,简称PVE)是一个开源的服务器虚拟化环境Linux发行版,基于Debian,使用给予Ubuntu的定制内核。相比于其他虚拟化平台,PVE具有的一个显著的特点就是无需master节点,安装完成后,无需特殊配制即可将多个节点组成集群。

由于工程要求,PVE需要大规模部署在物理服务器上,所以定制镜像就显得很有必要。

定制目标包括

(1)修改initrd中init脚本的提示信息

(2)删除GRUB界面多余选项,直接进入安装界面

(3)添加预装软件

(4)在安装过程中对软件进行个性化配置

(5)修改PVE安装界面,在PVE安装界面中的所有输入框设置默认文本

Proxmox VE镜像分析

下载Proxmox VE 5.3版本镜像后挂载,观察文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ tree -L 2
.
├── boot
│   ├── boot.cat
│   ├── grub
│   ├── initrd.img
│   ├── linux26
│   └── memtest86+.bin
├── COPYING
├── COPYRIGHT
├── debian -> .
├── dists
│   └── stretch
├── efi.img
├── EULA
├── mach_kernel
├── proxmox
│   ├── country.dat
│   ├── packages
│   └── pve-base.cnt
├── pve-base.squashfs
├── pve-installer.squashfs
├── Release.txt
└── System
└── Library

9 directories, 14 files

其中:

  • grub文件夹:包含引导程序GRUB所用到的文件。

  • initrd.img:系统初始化所使用的镜像,里面包含一个最小化的系统,包含了/dev、/etc、/bin等很多基本的目录,还有关键的init程序,负责驱动的加载和文件系统的初始化。

  • linux26:Linux 2.6内核

  • efi.img:系统引导镜像,内含boot.efi、bootia32.efi、bootx64.efi。

  • proxmox文件夹:系统预安装包的存放目录

    ​ PVE的根系统默认安装包是在proxmox文件夹下的,只要不破坏其依赖关系,可以将需要预安装的包及其依赖放到这个文件夹下。

    ​ PVE预安装包时候使用的是循环读取proxmox/packages中的deb,然后使用的安装方法是先解压然后再配置,这样不会产生依赖关系而导致装不上deb的问题。

  • pve-base.squashfs:安装的根系统,也就是最终的系统

  • pve-installer.squashfs:安装时需要的系统

Proxmox VE安装流程

PVE安装流程主要分为以下4个步骤:

(1)Boot Loader:由 BIOS 加载,用于将后续的 kernel 和 initrd 的装载到内存中。(PVE安装时使用的是UEFI模式的安装,但是又不是传统意义上的UEFI,它先是使用了BIOS加载kernel和initrd到内存,然后又跳到UEFI分区执行efi.img文件,调用proxinstall进入到系统安装界面,然后是挂载pve-base.squashfs进行系统安装)

(2)kernel:为 initrd 运行提供基础的运行环境,对应boot目录下的linux26文件

(3)initrd:检测并加载各种驱动程序,并执行init,对应boot目录下的initrd.img文件

(4)rootfs:根文件系统,用户的各种操作都是基于这个被最后加载的文件系统,这里对应了pve-base.squashfs

1
2
3
4
5
6
7
8
9
st=>start: Start
uefi=>operation: UEFI
bl=>operation: Boot Loader
loadKernel=>operation: 加载kernel
initrd=>operation: 初始化initrd
installer=>operation: pve-installer.squashfs
base=>operation: pve-base.squashfs
e=>end
st->uefi->bl->loadKernel->initrd->installer->base->e

Proxmox VE镜像定制

ISO解压与压缩

使用ISO Master。

在原先使用ISO Master作为解压缩ISO的工具中,产生的ISO文件可以直接作为cdrom启动,但刻录进USB设备后缺失MBR等重要部分所以无法启动,因此改用命令行进行解压缩。

(1)ISO提取

首先挂载镜像文件。

1
$ mount -o loop $ISO_NAME $PVE_MNT_PATH

挂载点目录中的文件是只读的,所以需要同步到工作目录下。

1
2
$ cd $PVE_MNT_PATH
$ rsync -av . $PVE_ISO_PATH > /dev/null

同步之后就即可修改ISO内的文件。

1
2
$ cd $PVE_ISO_PATH
$ umount $PVE_MNT_PATH

(2)ISO压缩

使用原镜像的MBR(前512字节)作为定制镜像的MBR

1
$ dd if=proxmox-ve_5.4-1.iso bs=512 count=1 of=proxmox.mbr

打包ISO

1
$ xorriso -as mkisofs -o ../$MY_ISO -r -V 'inspur' --grub2-mbr ../proxmox.mbr --protective-msdos-label -efi-boot-part --efi-boot-image  -c '/boot/boot.cat' -b '/boot/grub/i386-pc/eltorito.img' -no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info -eltorito-alt-boot -e '/efi.img' -no-emul-boot .

修改initrd

initrd.img位于原始镜像的boot目录下,修改initrd的目的是修改安装过程中的输出文本,是一个比较特殊的部分,要从initrd引入的目的讲起。

initrd 的英文含义是 boot loader initialized RAM disk,就是由 boot loader 初始化的内存盘。initrd的最初的目的是为了把kernel的启动分成两个阶段:在kernel中保留最少最基本的启动代码,然后把对各种各样硬件设备的支持以模块的方式放在initrd中,这样就在启动过程中可以从initrd所mount的根文件系统中装载需要的模块。这样的一个好处就是在保持kernel不变的情况下,通过修改initrd中的内容就可以灵活的支持不同的硬件。在启动完成的最后阶段,根文件系统可以重新mount到其他设备上。也就是说由于initrd会在内存虚拟一个文件系统,然后可以根据不同的硬件加载不同的驱动,而不需要重新编译整个核心。所以,大部分的发行版都会通过这种方式对驱动进行加载。

initrd引入之后Linux的引导会变成如下流程。

(1)boot loader 把内核以及 initrd 文件加载到内存的特定位置。
​(2)内核判断initrd的文件格式,如果是cpio格式。
​(3)将initrd的内容释放到rootfs中。
​(4)执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。

根据核心版本的不同,initrd文件有两种格式:image和cpio。**kernel 2.4只使用image格式,而kernel 2.6可同时支持两种格式。**它们不单格式不一样,而且运作的机制和流程也完全不同,甚至制作方法也不一样。pve的kernel版本是2.6,所以在此只讲cpio格式的initrd制作。

initrd解压、修改与压缩流程:

(1)解压proxmox-ve_5.3-1.iso,boot目录下的initrd.img就是gz格式的压缩文件

(2)将initrd.img备份后重命名为initrd.org.img,并解压缩

1
$ gzip -d -S ".img" ./initrd.org.img

执行file后查看格式

1
2
$ file initrd.org
initrd.org: ASCII cpio archive (SVR4 with no CRC)

(3)创建initrd.tmp目录以存放后续还原出来的文件,然后执行cpio命令将文件还原

1
2
3
$ mkdir initrd.tmp
$ cd initrd.tmp
$ cpio -id < ../initrd.org

(4)ls查看文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
total 96
drwxr-xr-x 13 root root 4096 Dec 24 15:26 ./
dr-xr-xr-x 4 helong helong 4096 Dec 24 15:49 ../
drwxr-xr-x 2 root root 4096 Dec 24 12:14 bin/
drwxr-xr-x 8 root root 36864 Dec 24 12:14 dev/
drwxr-xr-x 2 root root 4096 Dec 24 12:14 devfs/
drwxr-xr-x 2 root root 4096 Dec 24 12:14 etc/
-rwxr-xr-x 1 root root 8051 Dec 24 15:26 init*
drwxr-xr-x 6 root root 4096 Dec 24 12:14 lib/
lrwxrwxrwx 1 root root 3 Dec 24 12:14 lib64 -> lib/
drwxr-xr-x 2 root root 4096 Dec 24 12:14 mnt/
drwxr-xr-x 2 root root 4096 Dec 24 12:14 proc/
-rw-r--r-- 1 root root 37 Dec 24 12:14 .pve-cd-id.txt
drwxr-xr-x 2 root root 4096 Dec 24 15:29 sbin/
drwxr-xr-x 2 root root 4096 Dec 24 12:14 sys/
drwxr-xr-x 2 root root 4096 Dec 24 12:14 tmp/
drwxr-xr-x 6 root root 4096 Dec 24 12:14 usr/

(5)编辑init

(6)更改后,使用cpio重新压缩

1
$ find . | cpio -H newc -o > ../initrd

(7)gzip压缩initrd

1
$ gzip -9 -S ".img" initrd

去除GRUB界面

pve在安装时使用了GRUB2,所以想要去除掉GRUB界面需要找到原始镜像中boot/grub/grub.cfg文件,添加set timeout=0,就可以直接进入默认选项Install Proxmox VE模式。如果有需要我们也可以修改默认选项来实现直接进入其他模式的功能。

定制预装软件

Proxmox VE所有的预装软件都以deb包的形式存放在镜像的proxmox/packages下,并将在安装pve的过程中统一安装这些软件包,全部安装完成之后再进行配置,这样可以避免依赖关系出现问题。

所以定制预装软件只需要在proxmox/packages目录下放入需要的deb包,pve将会自动安装并进行默认配置。

配置预装程序

​ pve在配置软件是只会按照默认的配置,如果希望将软件配置成我们想要的形式,则只需要修改pve-installer.squashfs里的usr/bin/proxinstall文件。pve-installer.squashfs是pve安装时由initrd加载的系统,安装过程中proxinstall负责所有业务逻辑,其中配置软件部分的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
# needed for postfix postinst in case no other NIC is active
syscmd("chroot $targetdir ifup lo");

my $cmd = "chroot $targetdir dpkg $dpkg_opts --force-confold --configure -a";
$count = 0;
run_command ($cmd, sub {
my $line = shift;
if ($line =~ m/Setting up\s+(\S+)/) {
update_progress ((++$count)/$pkg_count, 0.75, 0.95,
"configuring $1");
}
});

1
2
3
4
5
6
7
8
9
10
11
12
# set apt mirror
if (my $mirror = $cmap->{country}->{$country}->{mirror}) {
my $fn = "$targetdir/etc/apt/sources.list";
syscmd ("sed -i 's/ftp\\.debian\\.org/$mirror/' '$fn'");
}

# create extended_states for apt (avoid cron job warning if that
# file does not exist)
write_config ('', "$targetdir/var/lib/apt/extended_states");

# allow ssh root login
syscmd(['sed', '-i', 's/^#\?PermitRootLogin.*/PermitRootLogin yes/', "$targetdir/etc/ssh/sshd_config"]);

可以看出pve也是对部分程序进行了个性化的配置,所以对配置文件的编辑的代码只需要仿照后者,使用syscmd函数,将修改的命令作为参数,写在前者之后即可。

定制安装界面

pve-installer.squashfs里的usr/bin/proxinstall文件中,有create_main_window函数,这个函数的功能是创建图形界面窗口里的各种组件,通过分析这个函数我们可以得到安装UI的结构。

顶部的image、中心的htmlview窗口以及下方的cmdbox构成了我们所看到的外观。在此只修改image和htmlview。

顶部的image是在1785行加载pve-installervar/lib/pve-installer/pve-banner.png来完成的,所以只需要用一个尺寸同样为1024X164的图像替代。

中心的htmlview是通过在每个create_*函数中调用display_html函数来加载,加载的html文件都位于var/lib/pve-installer/html文件夹下,对应的只需要修改每个html文件就可以实现外观上的替换。

另外由于窗口运行环境openbox的语言设置默认不是中文,所以使用中文字符展示会出现乱码,因此可以由html加载含中文的图片,以此来展示中文。

默认输入信息的修改就只需要在proxinstall中找到对应的输入框,修改预设文本。

参考