1. 项目缘起与整体设计思路
机房上架三十台新服务器,如果一台一台插U盘、点下一步、选时区、分区、设密码,一整天就搭进去了,而且手一抖分区选错还得重来。这种重复劳动干过一次就不想干第二次。PXE批量网络装机就是来解决这个问题的——让机器从网卡启动,自动从网络拉取安装镜像,再配合kickstart应答文件实现全自动无人值守,插上电、接上网线,剩下的它自己搞定。
这套方案的核心思路其实不复杂,拆开来看就三件事:第一,让客户端网卡能通过网络启动并拿到IP地址;第二,告诉客户端去哪里下载启动引导程序和安装镜像;第三,用应答文件把安装过程中所有需要人工填写的选项提前写好,让安装程序自己读、自己装。三件事分别对应DHCP、TFTP、HTTP(或FTP/NFS)三个服务,再加上kickstart文件,就构成了一套完整的PXE装机体系。
为什么选PXE而不是其他方案?对比几种常见的批量装机方式就清楚了。U盘批量拷贝虽然简单,但每台机器都要插拔,而且U盘寿命有限,几十台下来容易出坏块;克隆盘对拷速度快,但硬件配置稍有差异就会蓝屏或驱动不兼容;而PXE走网络,不依赖物理介质,一台服务器可以同时给几十台甚至上百台机器装机,硬件差异通过kickstart里的驱动配置也能灵活处理。更重要的是,PXE天然适合自动化,配合kickstart就是一套标准化的流水线。
这套方案适合谁?我觉着两类人最需要:一是中小企业的运维,机房规模不大但经常有新机器上架或者系统重装的需求;二是实验室或教学环境的管理员,机器数量多、配置统一、重装频率高。当然,如果你只是偶尔装一两台机器,那确实没必要折腾这套,U盘更省事。但只要批量超过五台,PXE的投入产出比就非常明显了。
整个架构里,DHCP负责分配IP并告诉客户端TFTP服务器的地址;TFTP负责传输启动引导文件(如pxelinux.0、内核、initrd);HTTP服务器负责提供完整的安装镜像和kickstart应答文件。客户端开机后,网卡PXE固件广播请求,DHCP响应并指向TFTP,客户端下载引导程序后加载内核,内核再通过HTTP拉取安装源,最后读取kickstart文件完成自动安装。整个链条环环相扣,任何一个环节配置错误都会导致装机失败,所以每一步都要仔细验证。
2. 核心组件拆解与关键配置解析
2.1 DHCP服务:不只是分配IP那么简单
很多人以为DHCP在PXE里就是发个IP地址,其实它承担了更关键的引导指向功能。普通的DHCP配置只分配IP、网关、DNS,但PXE场景下还需要额外告诉客户端:“你的启动文件在哪个TFTP服务器上,启动文件名是什么。”这就要用到DHCP的next-server和filename两个参数。
next-server指向TFTP服务器的IP地址,filename指定启动引导文件的路径。对于传统BIOS客户端,这个文件通常是pxelinux.0;对于UEFI客户端,则是grubx64.efi或shimx64.efi。如果机房里有新旧混杂的机器,就需要在DHCP配置里根据客户端架构做判断,分别指向不同的引导文件。
我常用的DHCP配置片段是这样的:
subnet 192.168.1.0 netmask 255.255.255.0 { range 192.168.1.100 192.168.1.200; option routers 192.168.1.1; option domain-name-servers 192.168.1.1; next-server 192.168.1.10; filename "pxelinux.0"; }这里有个细节要注意:range的地址池不要和静态IP冲突,next-server的地址必须是TFTP服务器的真实IP。另外,如果网络环境里有其他DHCP服务器,一定要确保PXE客户端的请求由这台DHCP响应,否则客户端可能拿到错误的引导信息。我一般会在交换机上做DHCP snooping或者直接隔离出一个装机专用VLAN,避免干扰。
还有一个容易踩的坑:某些品牌的网卡PXE固件对DHCP响应速度有要求,如果DHCP服务器响应太慢,客户端会超时重试甚至放弃。所以DHCP服务器最好和TFTP服务器在同一台物理机上,减少网络延迟。实测下来,DHCP和TFTP分开部署时,偶尔会出现客户端拿不到引导文件的情况,合并部署后这个问题基本消失。
2.2 TFTP服务:引导文件的搬运工
TFTP是个非常轻量的文件传输协议,基于UDP,没有复杂的认证和目录浏览功能,但正因为简单,它被选为PXE的标准引导传输协议。TFTP服务器的根目录通常设为/var/lib/tftpboot,里面需要放置以下文件:
- pxelinux.0:BIOS客户端的引导程序
- ldlinux.c32:pxelinux的依赖模块
- menu.c32或vesamenu.c32:图形化启动菜单模块
- pxelinux.cfg/default:启动菜单配置文件
- vmlinuz和initrd.img:安装内核和初始化内存盘
- grubx64.efi和shimx64.efi:UEFI客户端的引导程序
这些文件从哪来?pxelinux.0和菜单模块来自syslinux包,vmlinuz和initrd.img来自安装镜像的isolinux目录,UEFI引导文件来自grub2-efi包或镜像的EFI目录。我一般会先把安装镜像挂载到本地,然后从里面拷贝这些文件,确保版本匹配。
TFTP的配置文件/etc/xinetd.d/tftp里,关键参数是server_args,要指定-s /var/lib/tftpboot和-c(允许创建新文件)。disable要设为no,否则服务不启动。另外,TFTP默认的传输块大小是512字节,传输大文件时速度较慢,可以在配置里加上--blocksize 1468来提升传输效率,但要注意客户端是否支持。
注意:TFTP根目录的权限要设为755,文件权限设为644,否则客户端可能无法读取。我遇到过因为权限问题导致引导文件下载失败的情况,排查了半天才发现是SELinux在作怪,临时用setenforce 0测试后确认,然后通过chcon调整了文件上下文。
2.3 HTTP服务:安装源的载体
安装镜像动辄几个GB,用TFTP传输显然不现实,所以安装源通常通过HTTP或FTP提供。HTTP配置最简单,把安装镜像挂载或拷贝到Web根目录下即可。我一般会在/var/www/html下建一个os目录,把镜像内容放进去,然后通过http://192.168.1.10/os/访问。
为什么选HTTP而不是NFS?HTTP配置简单、兼容性好、不需要额外开放端口,而且大多数环境里Web服务已经很常见。NFS虽然性能略好,但配置防火墙和权限映射比较麻烦,除非有特殊需求,否则HTTP足够用。
安装源目录里需要包含完整的镜像内容,包括Packages、repodata、isolinux等。如果只是拷贝了部分文件,安装过程中会报“找不到包”的错误。我习惯用rsync从挂载点同步,确保文件完整性和权限一致。
2.4 kickstart文件:无人值守的灵魂
kickstart文件是一个纯文本的应答文件,里面包含了安装过程中所有需要人工选择的选项:语言、键盘、时区、分区方案、root密码、要安装的软件包、网络配置、安装后执行的脚本等。安装程序启动时通过ks=参数指定kickstart文件的URL,就会自动读取并执行。
一个典型的kickstart文件结构如下:
#version=DEVEL lang zh_CN.UTF-8 keyboard us timezone Asia/Shanghai rootpw --iscrypted $6$xxxxxx network --bootproto=dhcp part /boot --fstype=xfs --size=1024 part / --fstype=xfs --size=102400 part swap --size=8192 %packages @^minimal-environment @standard %end %post echo "Installation completed" >> /root/install.log %end这里有几个关键点:rootpw可以用明文也可以用加密串,生产环境建议用加密串;分区方案要根据实际硬盘大小调整,写死了可能导致小硬盘装不上;%post段可以放安装后要执行的脚本,比如配置yum源、安装监控agent、注册到CMDB等。
提示:kickstart文件里的磁盘设备名(如sda)在不同硬件上可能变化,建议用--ondisk=sda明确指定,或者用ignoredisk --only-use=sda排除其他磁盘。我遇到过因为机器上插了多余U盘导致安装程序选错磁盘的情况,加上ignoredisk后就稳了。
3. 完整实操流程与核心环节实现
3.1 环境准备与基础服务搭建
先明确一下实验环境:一台CentOS 7或Rocky Linux 8的服务器作为PXE服务端,IP设为192.168.1.10,关闭防火墙和SELinux(生产环境按需配置规则),客户端和服务器在同一二层网络。安装镜像用Rocky Linux 8的DVD ISO。
第一步,挂载镜像并配置本地yum源:
mkdir -p /mnt/cdrom mount -o loop /path/to/Rocky-8.iso /mnt/cdrom cat > /etc/yum.repos.d/local.repo <<EOF [local] name=Local baseurl=file:///mnt/cdrom enabled=1 gpgcheck=0 EOF第二步,安装所需服务:
yum install -y dhcp tftp-server httpd syslinux grub2-efi-x64 shim-x64这里syslinux提供BIOS引导文件,grub2-efi-x64和shim-x64提供UEFI引导文件。如果只装BIOS客户端,后两个可以不装。
第三步,配置DHCP。编辑/etc/dhcp/dhcpd.conf,加入前面提到的subnet配置。注意要注释掉或删除默认的subnet声明,否则会冲突。配置完成后启动服务:
systemctl enable --now dhcpd systemctl enable --now tftp systemctl enable --now httpd3.2 TFTP目录结构与引导文件部署
TFTP根目录设为/var/lib/tftpboot,依次创建以下结构:
mkdir -p /var/lib/tftpboot/pxelinux.cfg mkdir -p /var/lib/tftpboot/images cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/ cp /usr/share/syslinux/ldlinux.c32 /var/lib/tftpboot/ cp /usr/share/syslinux/menu.c32 /var/lib/tftpboot/ cp /mnt/cdrom/isolinux/vmlinuz /var/lib/tftpboot/images/ cp /mnt/cdrom/isolinux/initrd.img /var/lib/tftpboot/images/UEFI部分需要拷贝grubx64.efi和shimx64.efi到TFTP根目录,并在/var/lib/tftpboot/grub.cfg里配置菜单。如果暂时只处理BIOS客户端,可以先跳过UEFI配置。
接下来配置启动菜单/var/lib/tftpboot/pxelinux.cfg/default:
default menu.c32 prompt 0 timeout 300 menu title PXE Install Menu label 1 menu label ^1. Install Rocky Linux 8 (Auto) kernel images/vmlinuz append initrd=images/initrd.img ks=http://192.168.1.10/ks/rocky8-ks.cfg label 2 menu label ^2. Install Rocky Linux 8 (Manual) kernel images/vmlinuz append initrd=images/initrd.imgtimeout设为300表示30秒无操作后自动选择默认项,单位是1/10秒。ks=参数指向kickstart文件的HTTP地址。如果要做多版本共存,可以在这里加多个label,分别指向不同的内核和ks文件。
3.3 HTTP安装源与kickstart文件配置
把镜像内容拷贝到Web目录:
mkdir -p /var/www/html/os/rocky8 rsync -av /mnt/cdrom/ /var/www/html/os/rocky8/然后创建kickstart文件目录和文件:
mkdir -p /var/www/html/ks cat > /var/www/html/ks/rocky8-ks.cfg <<'EOF' #version=Rocky8 lang zh_CN.UTF-8 keyboard us timezone Asia/Shanghai --utc rootpw --iscrypted $6$rounds=4096$saltsalt$hashedpassword user --name=ops --groups=wheel --iscrypted --password=$6$rounds=4096$saltsalt$hashedpassword2 network --bootproto=dhcp --device=link --activate network --hostname=node01.example.com ignoredisk --only-use=sda clearpart --all --initlabel part /boot --fstype=xfs --size=1024 part / --fstype=xfs --size=102400 part swap --size=8192 part /data --fstype=xfs --size=1 --grow reboot --eject %packages @^minimal-environment @standard vim curl wget net-tools %end %post --log=/root/ks-post.log echo "PXE install completed at $(date)" >> /root/install.log sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config systemctl disable firewalld %end EOFrootpw的加密串可以用openssl passwd -6生成,或者用python的crypt模块。分区方案里,/data用了--grow表示占用剩余空间,适合数据盘场景。%post段里关闭SELinux和firewalld是为了后续配置方便,生产环境请根据安全策略调整。
注意:kickstart文件里的hostname如果写死,所有机器装完主机名都一样,后续管理会混乱。建议在%post段用脚本根据IP或MAC地址动态设置主机名,或者通过DHCP的host声明为每台机器分配固定IP和主机名。
3.4 客户端启动与安装过程验证
服务端配置完成后,启动一台客户端,进入BIOS设置网卡启动,或者按F12选择网络引导。客户端会依次经历以下阶段:
- 网卡PXE固件广播DHCP请求,获取IP地址和next-server信息。
- 从TFTP服务器下载pxelinux.0并执行。
- 加载pxelinux.cfg/default菜单,显示启动选项。
- 选择自动安装后,下载vmlinuz和initrd.img并启动内核。
- 内核通过ks=参数下载kickstart文件,开始自动分区、安装。
- 安装完成后自动重启,进入新系统。
整个过程无需人工干预,一台机器从启动到装完大约10-15分钟,取决于网络带宽和硬盘速度。如果同时装多台,带宽会成为瓶颈,建议用千兆网络,并考虑用多台HTTP服务器做负载。
验证安装是否成功,可以登录新系统检查分区、主机名、软件包是否符合kickstart定义。如果安装中途报错,安装界面会显示错误信息,根据提示排查即可。
4. 常见问题与排查技巧实录
4.1 客户端无法获取IP或引导文件
这是最常见的问题,表现是客户端一直显示“PXE-E51: No DHCP or proxyDHCP offers were received”或者“PXE-E32: TFTP open timeout”。排查思路如下:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 无DHCP响应 | DHCP服务未启动或配置错误 | 检查systemctl status dhcpd,查看/var/log/messages |
| 有IP但无引导 | next-server或filename配置错误 | 用tcpdump抓包看DHCP响应内容 |
| TFTP超时 | TFTP服务未启动或防火墙拦截 | 检查systemctl status tftp,临时关闭防火墙测试 |
| 引导文件下载失败 | 文件权限或SELinux问题 | 检查/var/log/messages,用setenforce 0测试 |
我踩过最坑的一次是DHCP配置里next-server写成了主机名而不是IP,客户端解析不了导致引导失败。改成IP后立刻正常。所以next-server一定要用IP地址,不要用域名。
4.2 kickstart文件读取失败
如果客户端能进入安装界面但提示“Unable to read kickstart file”,通常是URL路径错误或HTTP服务不可达。先在服务端用curl测试一下ks文件的URL是否能正常访问:
curl -I http://192.168.1.10/ks/rocky8-ks.cfg如果返回200说明文件可访问,问题可能在客户端网络配置或DNS解析。另外,kickstart文件里的语法错误也会导致读取失败,可以用ksvalidator工具检查:
yum install -y pykickstart ksvalidator /var/www/html/ks/rocky8-ks.cfg这个工具能提前发现大部分语法问题,强烈建议每次修改ks文件后都跑一遍。
4.3 分区或软件包报错
分区报错常见于硬盘大小与kickstart定义不匹配,比如定义了100GB的根分区但硬盘只有80GB。解决办法是用--grow让分区自动扩展,或者用%pre段动态计算分区大小。软件包报错通常是安装源不完整或包名拼写错误,检查HTTP目录下Packages目录是否完整,以及%packages段里的包名是否存在于该版本。
实操心得:我习惯在kickstart的%pre段加一段脚本,把客户端的磁盘信息、内存大小输出到日志,方便后续排查。比如
lsblk >> /tmp/ks-pre.log和free -h >> /tmp/ks-pre.log,安装完成后可以查看这些日志了解硬件情况。
4.4 UEFI与BIOS混合环境处理
如果机房里有新旧机器混杂,DHCP配置需要根据客户端架构分别指向不同的引导文件。ISC DHCP支持用class匹配:
option arch code 93 = unsigned integer 16; class "pxeclients" { match if substring (option vendor-class-identifier, 0, 9) = "PXEClient"; if option arch = 00:07 { filename "grubx64.efi"; } else { filename "pxelinux.0"; } }这段配置的意思是:如果客户端架构是UEFI(arch=00:07),就下发grubx64.efi;否则下发pxelinux.0。UEFI的grub.cfg配置和pxelinux.cfg类似,只是语法略有不同,需要单独维护。
4.5 安装速度优化与并发控制
同时给多台机器装机时,TFTP和HTTP的并发压力会很大。优化手段包括:把TFTP的blocksize调大、用HTTP代替TFTP传输大文件、把安装源放在SSD上、用多台HTTP服务器做负载均衡。如果网络是千兆,同时装5-8台比较合适,再多就会明显变慢。
另外,安装完成后客户端会自动重启,如果BIOS启动顺序里网络启动排在硬盘前面,重启后又会进入PXE循环。解决办法是在kickstart里加reboot --eject,或者安装完成后手动调整BIOS启动顺序。我一般会在%post段加一句efibootmgr -o 0001,0000(UEFI环境)来调整启动顺序,避免循环。
5. 进阶扩展与个人实操体会
这套PXE+kickstart的方案跑通之后,还可以做很多扩展。比如结合Ansible,在%post段调用ansible-pull拉取配置,实现装机后自动完成基础配置;或者对接CMDB,装机时自动注册资产信息;再或者用Cobbler这类工具做上层管理,简化多版本、多环境的维护。
我个人在实际操作中的体会是,PXE装机的难点不在技术本身,而在细节的打磨。DHCP的每一个参数、TFTP的每一个文件权限、kickstart的每一行语法,都可能成为拦路虎。但只要第一次跑通,后面就是复制粘贴的快乐。我建议新手先在虚拟机里搭一套环境练手,用VirtualBox或VMware的网络启动功能测试,确认流程无误后再上物理机。虚拟机里折腾坏了快照回滚就行,物理机上出问题排查起来麻烦得多。
最后分享一个小技巧:把kickstart文件放在Git仓库里管理,每次修改都有记录,出问题可以快速回滚。同时用CI工具做语法检查,避免低级错误。这套组合拳打下来,批量装机就从体力活变成了流水线作业,省下来的时间喝杯茶不香吗。