前言
此前打算重建 OpenStack 集群,顺带想把机房的二十几台服务器从 CentOS 重装成 Ubuntu,这么多台机器一个一个插 U 盘接显示屏手动重装显然不太现实,正好借此机会研究一下 PXE,实现自动化批量安装系统。
PXE 介绍
预启动执行环境(Preboot eXecution Environment,PXE)提供了一种使用网络接口启动计算机的机制,让计算机的启动可以不依赖本地硬盘和已安装的操作系统,常用于无盘系统和通过网络安装系统。
PXE 服务器需要包含 DHCP、TFTP 和 HTTP 三个服务,启动过程如下:
- 寻找 DHCP 服务器并获得 IP 地址、PXE 服务器的地址,以及启动文件
- 从 PXE 服务器的 TFTP 服务器中获取启动文件
- 启动文件执行 Linux 引导序列,从 TFTP 服务器获取 GRUB 配置并显示菜单
- 在用户选择了菜单条目或者超时选中默认条目后,从 TFTP 服务器载入对应的内核文件
vmlinuz
与根文件系统initrd.img
引导内核 - 根据 GRUB 菜单条目中的参数,从 HTTP 服务器获取自动安装配置和 ISO 系统镜像
- 系统进入安装界面并开始安装
PXE 服务器搭建
注:以下操作均以 Ubuntu 22.04 环境为例
需要安装以下软件包:
- isc-dhcp-server(DHCP 服务器):
sudo apt install isc-dhcp-server
- tftpd-hpa(TFTP 服务器):
sudo apt install tftpd-hpa
- nginx(HTTP 服务器):
sudo apt install nginx-full
HTTP 服务器
配置 Nginx,在 /etc/nginx/sites-available
中新建配置文件,然后在 /etc/nginx/sites-enabled
中创建软链接
其中 /srv/tftp
是 TFTP 服务器的默认目录。
TFTP 服务器
Ubuntu
以 Ubuntu 22.04 为例,下载 ISO 镜像 到 /srv/tftp/iso
并挂载:sudo mount jammy-live-server-amd64.iso /media
,复制 casper/initrd
和 casper/vmlinuz
到 TFTP 目录下:
Ubuntu 使用 autoinstall 进行自动安装,需要在 TFTP 目录下建立 autoinstall
目录放置配置文件,命名为 user-data
,此外还需在同目录下创建一个 meta-data
文件,内容可以空,但必须存在。配置文件的具体格式可参照 Automated Server installer config file reference。
关于自动安装配置文件,强烈建议先手动执行一遍安装过程,然后从 /var/log/installer/autoinstall-user-data
复制生成的文件,不同硬件配置下可能会有不同的配置文件,使用 UEFI 和使用 BIOS 引导的配置也有所不同,手动编写容易出错,我会在接下来的两个部分给出示例配置,但仅供参考。
目录下剩余内容因系统引导方式而有所不同,将分别阐述。
UEFI
复制 EFI/boot/grubx64.efi
到目录下:
在 grub
目录建立 grub.cfg
配置文件:
注意:
- 将
${PXE_IP}
改为 PXE 服务器的 IP 地址 http://${PXE_IP}:3001/iso/jammy-live-server-amd64.iso
是系统镜像所在的位置http://${PXE_IP}:3001/autoinstall/
是自动安装配置所在的位置,注意需要以/
结尾initrd
和vmlinuz
的路径需与此前一致:/boot/live-server/initrd
、/boot/live-server/vmlinuz
ds="nocloud-net;s=http://${PXE_IP}:3001/autoinstall/"
这里的分号如果没有用双引号包围需要转义- 坑:不要在行尾写注释,在初始化 SubiquityServer 时会使用
shlex.split
解析命令行参数,网上找的一个例子在第 9 行后面加了一个注释# Don't forget the slash at the end.
,其中有一个单引号,触发了ValueError: No closing quotation
,直接导致安装失败……
自动安装配置文件示例如下:
配置文件包含了一些基本的配置,如系统语言、时区、主机名、用户名、密码等,注意密码为加密后的密文。此外还包含了网卡和磁盘分区的配置,网卡暂时都使用 DHCP 获取 IP 地址,待安装完成后再调整为静态 IP。
磁盘分区使用了一个简单的方案,为启动分区划分必要的空间后,将剩余空间作为一个 Logical Volume Group,其中包含一个 Logical Volume,挂载在根目录下,文件系统使用 XFS。上述的配置以手动安装后自动生成的文件为基准,将 partition-2
的 size
改为了 -1
,表示使用所有剩余空间;将 lvm_partition-0
的 size
注释掉,这样会默认使用 Logical Volume Group 的所有空间。
完成后的目录结构如下:
BIOS
从 pxelinux
包复制 pxelinux.0
:
从 syslinux-common
包复制 ldlinux.c32
、libutil.c32
和 menu.c32
:
在目录下建立 pxelinux.cfg
配置文件:
注意:
- 将
${PXE_IP}
改为 PXE 服务器的 IP 地址 http://${PXE_IP}:3001/iso/jammy-live-server-amd64.iso
是系统镜像所在的位置http://${PXE_IP}:3001/autoinstall/
是自动安装配置所在的位置,注意需要以/
结尾initrd
和vmlinuz
的路径需与此前一致:/boot/live-server/initrd
、/boot/live-server/vmlinuz
default menu.c32
是此前menu.c32
所在的位置timeout
的单位是 1/10 秒,timeout 50
意味着超时时间为 5 秒
自动安装配置文件示例如下:
同样是在生成的配置文件基础上对 Partition 和 Logical Volume Group 的大小进行了调整,可以看到配置文件的内容大致与 UEFI 相同,但在分区时并没有划分 /boot/efi
,因此并不能通用。
完成后的目录结构如下:
CentOS
以 CentOS 7 为例,下载 ISO 镜像 并挂载到 /srv/tftp/iso
,从中提取 isolinux
目录下的 vmlinuz
和 initrd.img
文件,放置到 TFTP 目录下。
pxelinux.0
、ldlinux.c32
、libutil.c32
和 menu.c32
的获取方法与 Ubuntu 相同。
CentOS 使用 Kickstart 自动化安装过程,创建 centos7.cfg
文件,内容如下:
注意:
- 将
${PXE_IP}
改为 PXE 服务器的 IP 地址 inst.repo=http://${PXE_IP}:3001/iso/
是镜像挂载后的位置- root 密码为密文,可使用 Python 生成:
不同于 Ubuntu,CentOS 提供了自动分区的选项,省去了配置磁盘分区的烦恼,但默认会将 /home
作为最大的分区,需要视情况调整。
修改 pxelinux.cfg/default
:
注意:
- 将
${PXE_IP}
改为 PXE 服务器的 IP 地址 KERNEL /vmlinuz
和initrd=/initrd.img
与此前复制的两个文件对应,均在 TFTP 根目录下inst.repo=http://${PXE_IP}:3001/iso/
是镜像挂载后的位置ks=http://${PXE_IP}:3001/centos7.cfg
是 Kickstart 自动安装配置文件的位置
相比 Ubuntu 的艰难探索,安装 CentOS 时直接采用了前人的配置,再搭配自动分区功能,省去了不少时间。UEFI 系统的安装暂未尝试,但应与 Ubuntu 类似,不再赘述。
完成后的目录结构如下:
DHCP 服务器
搭建 PXE 服务器的最后一步是配置 DHCP 服务器,编辑 /etc/dhcp/dhcpd.conf
文件,添加以下内容:
其中,subnet
和 netmask
指定了 DHCP 分配 IP 的网段,注意不要与现有网段冲突,next-server
是 PXE 服务器的 IP 地址。if option arch = 00:07
用于判断客户端使用的是 UEFI 还是 BIOS 引导,并返回对应的启动文件,使用 UEFI 的机器在 DHCP 服务器上的 vendor-class-identifer
类似于 PXEClient:Arch:00007:UNDI:003016
,使用 BIOS 的机器则类似于 PXEClient:Arch:00000:UNDI:002001
。
系统安装与故障排查
PXE 服务器搭建完毕后便可以开始安装系统了,但在安装过程中仍然可能会遇到一些问题,因此建议先拿一台机器进行测试,之后再批量安装。
将机器的下一次引导方式设置为 PXE 启动,如果是从未安装过系统的机器,PXE 启动应该已经是第一启动项,但这里我使用的是已经安装过系统的机器,因此需要手动调整启动顺序,可以借助 Intel 的 IDRAC 远程批量操控:
运行完脚本后之后机器就会重启并进入 PXE 启动,如果你的 IDRAC 有企业版许可证,可以通过 VNC 虚拟控制台查看安装过程,还可使用键盘远程操控以及重启。
不得不说这 IDRAC 虚拟控制台真是个好东西,免去了在机房里吸灰和受噪声污染的劳苦,唯一的不足就是需要购买许可证,如果没有的话可以试试 这里 的延长试用版,最多可试用 240 天(如果之前已经导入过试用版证书则无法使用),再者可以去某宝上买正式版永久授权,否则恐怕只能钻进机房接显示屏了(
进入 PXE 启动后你仍然可能会遇到各种问题,需要根据显示屏上的错误信息进行排查,基本思路如下:
- 是否成功从 DHCP 服务器获取了 IP 地址?
- DHCP 服务器是否正常工作?
- 防火墙端口是否放行?
- DHCP 服务器和 PXE 网卡是否在同一网络内?
- 是否从 TFTP 服务器获取了启动文件?
- TFTP 服务器的地址是否正确?(DHCP 服务器配置里的
next-server
) - TFTP 服务器是否正常工作?
- 防火墙端口(69 号)是否放行?
- 进行 PXE 启动的服务器能否连接到 TFTP 服务器?
- 启动文件名(DHCP 配置里的
filename
)是否正确? - TFTP 服务器的目录及文件的权限设置是否正确?
- TFTP 服务器的地址是否正确?(DHCP 服务器配置里的
- 是否进入了启动菜单?
- DHCP 服务器配置的启动文件是否正确?注意 BIOS 应使用
pxelinux.0
,UEFI 应使用grubx64.efi
- 是否从 TFTP 服务器获取了对应文件?BIOS 为
pxelinux.cfg/default
,UEFI 为grub/grub.cfg
- 配置文件的格式是否正确?
- DHCP 服务器配置的启动文件是否正确?注意 BIOS 应使用
- 是否成功获取了
vmlinuz
和initrd.img
?pxelinux.cfg/default
和grub/grub.cfg
中的文件路径是否正确?- 是否能正常连接到 TFTP 服务器?
- TFTP 服务器的目录及文件的权限设置是否正确?
- 进入内核后,是否成功下载了系统镜像文件?
pxelinux.cfg/default
和grub/grub.cfg
中的镜像地址是否正确?- 是否能正常连接到 HTTP 服务器?
- HTTP 服务器的目录及文件的权限设置是否正确?
- 是否开始自动安装?
pxelinux.cfg/default
和grub/grub.cfg
中自动安装配置文件的地址是否正确?- 内核启动的参数是否正确?
- 配置文件的格式是否正确?
- 是否能正常连接到 HTTP 服务器?
- HTTP 服务器的目录及文件的权限设置是否正确?
- 安装是否成功?
- 配置文件的格式是否正确?
- 配置是否正确?如磁盘分区时没有配置 bootloader 分区
- 安装失败时可按 Ctrl+Alt+F2 进入 Shell 查看日志
- Curtin 磁盘分区日志:
/var/log/curtin
- cloud-init 日志:
/var/log/cloud-init.log
- Subiquity 日志:
/var/log/subiquity-server.debug.log
- Curtin 磁盘分区日志:
安装完毕后,即可通过 SSH 使用 root 用户和安装配置中指定的密码登录系统。那么如何找到机器的 IP 地址呢?最简单粗暴的办法就是把 DHCP 地址段中的 IP 全部扫描一遍,但更合理的办法是查看 DHCP 的分配记录,你可以使用 systemctl status isc-dhcp-server
查看 DHCP 服务器的日志,也可以直接在 /var/lib/dhcp/dhcpd.leases
中找到全部记录。
例如以上的记录就表示 MAC 地址为 12:34:de:ad:be:ef
的机器在 2022/11/22 08:00:00 申请 IP,分配到的 IP 为 192.168.2.209
,且使用的是 BIOS 引导。
最后,如果一切顺利的话,你就能使用 root 用户和此前配置的密码登录系统了。
遗留问题
静态 IP 与主机名分配
由于各台机器 PXE 启动的时间不同,因此从 DHCP 分配的 IP 地址与机器编号不匹配,主机名也没有进行区分,有以下几种解决方法:
- 配置 DHCP 服务器将 IP 与 MAC 地址绑定
- 利用 cloud-init,根据机架号等硬件信息让每台机器获取到不同的配置文件
- 安装系统后再修改 IP 地址和主机名,使用 MAC 地址等方式匹配到机器编号,Ubuntu 的网络配置可以借助 Netplan,CentOS 可直接修改
/etc/sysconfig/network-scripts
下的配置文件,然后重启网卡
UEFI 启动顺序
设置 PXE 启动是设置的是下次引导方式,理论上在安装完系统重启后会恢复为从硬盘启动,但使用 UEFI 引导的机器在安装完 Ubuntu 系统重启后,PXE 启动仍然是第一启动项,而使用 BIOS 引导或安装 Centos 系统则不会出现此问题。
此问题的原因暂未查明,一个 workaround 是安装完系统后不要重启而是关机,待所有机器的系统都安装完毕并关机后,将 DHCP 服务器关掉,或是让 DHCP 服务器返回 BIOS 的 pxelinx.0
而不是 UEFI 的 grubx64.efi
,这样 PXE 启动时就会失败(笑),并回退到硬盘启动,最后将启动顺序批量改正即可。(注意在关机后调整启动顺序是无效的,而且此时的启动顺序仍然是正常的硬盘启动,似乎只有在进入引导后的某一时刻才会转为 PXE 启动)