做运维的人,迟早会遇到批量装机这件事。一天一两台机器,手工插U盘还能忍,可一旦面对几十上百台的物理机或虚拟机,一个人通宵装系统,装到后面手指头都是机械动作,甚至还会因为某个镜像版本漏了更新,把整批机器装成旧版本,第二天又要推倒重来。我的解决办法是搭一套PXE批量装机环境,让客户端开机时从网卡引导,自动完成系统安装。这篇文章就从原理、部署、配置到踩坑,完整拆一遍PXE批量装机,适合正在做Linux运维、网络管理,或者负责机房/实验室环境交付的工程师。
1. 为什么需要PXE批量装机
1.1 传统装机方式的痛点
手工装机的方式看着简单,实际上一旦规模上来,问题非常多。
拿U盘安装来说,你需要先准备一个安装盘,一台一台插上去,选择启动项,等待复制文件,然后手工分区、设置密码、配置网络。如果是同样的系统装十台,重复劳动就非常明显。更难受的是,机器数量一多,很容易搞混哪台装了哪个版本、哪台配了哪组网段。我见过有同事在新一批机器上,因为U盘镜像版本不一致,装出来的系统底层驱动都不一样,最后排查环境问题时浪费了大半天时间。
光盘装机更不用提,还得一台台换盘,光驱故障率也不低,盘片损坏、读取变慢都是实际会遇到的事。远程机房场景下尤其麻烦,不可能为了重新装一台机器专门跑一趟现场,哪怕有远程管理卡,能不能挂载ISO还要看硬件环境。
PXE批量装机解决的正是这类问题:客户端只要支持网络启动(现在主流服务器和多数虚拟化平台都支持),开机后就能从网络自动获取引导文件,走完安装流程,全程不需要人工干预。整个过程中,人的工作被集中到“准备服务端”这一步,之后只要批量开机就行。
1.2 PXE的工作流程
PXE全称是Preboot eXecution Environment,本质是Intel定义的一套网络引导标准。客户端在BIOS/UEFI阶段,如果配置了网卡启动,它会向网络发DHCP请求,拿到IP之后还会拿到两个关键参数:一个是TFTP服务器的地址,另一个是要下载的引导文件名称。
整个流程拆开看是这样:
- 客户端开机,网卡固件发出DHCP广播。
- DHCP服务器分配IP,并同时返回next-server和filename字段。
- 客户端从TFTP服务器下载PXE引导程序(传统BIOS下通常是pxelinux.0,UEFI下是grubx64.efi之类)。
- 引导程序读取TFTP根目录下的配置文件(如pxelinux.cfg/default),里面写明了要加载哪个内核、initrd以及启动参数。
- 内核和initrd下载完成后,内核启动,挂载HTTP/NFS服务器上的系统镜像源。
- 安装程序(以Linux为例是Anaconda)开始工作,如果配置了kickstart文件,就能全自动安装,否则会进入手动安装界面。
理解了这条链路,后面的配置就有方向了。PXE不是一个独立服务,而是DHCP、TFTP、HTTP/NFS、引导程序、安装镜像这几个组件互相配合的结果。
1.3 适用场景判断
PXE不是所有场景都适合。我的经验是,以下情况收益最大:
- 同一型号或同一批硬件规格的机器数量较多,系统版本要求一致。
- 无人值守场景,比如机房巡检后需要批量重装,或者实验环境需要快速恢复。
- 多台机器需要同时安装不同系统版本,可以通过启动菜单选择,仍然比插盘高效。
- 远程机房,物理接触困难,但网卡启动和服务器电源管理可用。
如果只是单台机器急需重装,或者网络环境极不稳定、交换机端口有安全限制,那就老老实实用U盘/ISO,没必要为了单台机器搭一套PXE。另外,PXE依赖广播和DHCP,如果网络里存在严格的端口隔离或DHCP Snooping策略,需要先协调好网络配置,否则会出现各种“找不到启动服务”的问题。
2. 环境准备与组件选型
2.1 最小化组件清单
在动手之前,先把需要准备的东西列全。基于我实际搭建的最小体系,以下组件缺一不可:
| 组件 | 作用 | 常见实现 |
|---|---|---|
| DHCP服务 | 分配IP,同时告知引导文件位置 | dnsmasq、isc-dhcp-server |
| TFTP服务 | 提供pxelinux.0、内核、initrd等小文件 | tftp-hpa、dnsmasq内置tftp |
| HTTP服务 | 提供系统镜像和kickstart文件 | nginx、apache、lighttpd |
| 引导程序 | 负责读取菜单并加载内核 | syslinux(BIOS)、grub2-efi(UEFI) |
| 系统镜像 | 安装源,可以是ISO挂载目录,也可以解包后的树 | CentOS/Debian/Ubuntu的ISO |
额外建议准备一台测试用的虚拟机或物理机,配置成“磁盘启动失败后自动回退到网络启动”,这样不影响已有系统,可以反复验证。
我比较推荐的组合是:DHCP用dnsmasq或isc-dhcp-server,TFTP用tftp-hpa,HTTP用nginx。如果你只是为了快速测试,也可以用dnsmasq同时承担DHCP和TFTP,进一步减少服务数量。不过正式环境我更倾向于拆开,便于日志排查和故障隔离,这个问题下面会详细说。
2.2 网络规划与IP分配策略
PXE环境里的网络规划需要提前想好,否则后面会非常痛苦。
我通常用一个独立的管理网段来跑PXE,比如模拟项目环境里用192.168.30.0/24这个网段。服务器IP固定为192.168.30.10,网关是192.168.30.1,DHCP地址池设为192.168.30.100到192.168.30.200,池子外面再预留一些IP给管理口、网络设备和其他固定主机。
地址池大小的估算逻辑是:如果单次批量装50台,池子最好至少能有2倍数量的地址,避免客户端因租约问题反复冲突。DHCP租约时间在PXE安装场景下可以缩短,比如600秒,因为安装完成后系统就不再用这个临时租约了。
如果你的环境是跨VLAN部署,需要在三层交换机上配置DHCP Relay,DHCP服务器还要能识别来自不同VLAN的请求,并且返回对应的TFTP服务器地址。这部分配置相对复杂,建议第一次搭PXE时先在同网段跑通,再考虑跨VLAN。
2.3 传输协议的选型思路
PXE引导阶段,小文件用的是TFTP,这点由PXE标准决定。但进入安装程序后,系统镜像源的访问协议是可以自由选择的,常见的有NFS、HTTP、FTP。
我更推荐用HTTP做安装源,理由有几个:
- HTTP的端口固定是80/8080,防火墙策略简洁,不容易被中间设备拦截。
- HTTP是基于TCP的,传输大文件时比NFS和TFTP更稳定,特别在并发客户端多的时候不会像TFTP那样频繁丢包重传。
- 权限配置简单,只要nginx或apache目录可读即可,不需要考虑NFS的exports、SELinux布尔值、客户端挂载选项等一堆细节。
NFS也不是不能用,早期很多教程喜欢用NFS,因为它挂载后可以直接读取镜像目录。但实际用下来,NFS对网络抖动更敏感,且NFS服务端配置稍有不慎就会出现nobody权限问题,客户端挂载后看到目录但无法读取文件。HTTP则没有这个烦恼,你只需要确保目录索引可读,安装器直接从URL拉文件就行。
3. 核心服务部署:DHCP、TFTP、HTTP与PXE引导
3.1 DHCP地址池与引导参数配置
以isc-dhcp-server为例,核心配置写在/etc/dhcp/dhcpd.conf里。针对PXE,关键不是地址池,而是next-server和filename这两个参数,它们是客户端找到引导程序的钥匙。
subnet 192.168.30.0 netmask 255.255.255.0 { range 192.168.30.100 192.168.30.200; option routers 192.168.30.1; option domain-name-servers 192.168.30.1; default-lease-time 600; max-lease-time 7200; next-server 192.168.30.10; filename "pxelinux.0"; }next-server告诉客户端“你去192.168.30.10下载引导文件”,filename告诉它“要下载的文件名是pxelinux.0”。这两个字段协同工作,缺一不可。
如果你的网络里已经有其他DHCP服务器,比如路由器或防火墙自带DHCP,一定要避免冲突。同一个广播域里出现多个DHCP服务器,客户端获得的IP可能随机来自其中任何一个,PXE引导也会变得不稳定。我的做法是:在DHCP服务器上配置ignore unknown-clients,或者直接把PXE专用DHCP独立部署在测试VLAN里,不让它和日常办公网络混在一起。
3.2 TFTP服务与pxelinux.0
TFTP的部署不复杂,但有几个坑需要注意。
先装好tftp-hpa和syslinux:
yum install -y tftp-server syslinux # 或是 apt install -y tftpd-hpa pxelinux syslinux-commonsyslinux软件包里提供pxelinux.0引导文件。传统BIOS模式下,把它复制到TFTP根目录。TFTP根目录我习惯设置为/var/lib/tftpboot。
mkdir -p /var/lib/tftpboot/pxelinux.cfg cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/这里有个容易踩的坑:systemd管理的tftp服务默认使用socket激活模式,也就是有请求时才启动,响应会有一点延迟。批量安装时几十台机器同时请求,socket模式很容易出现部分客户端连接超时。建议禁用tftp.socket,只启动tftp.service,让进程常驻:
systemctl disable tftp.socket systemctl enable tftp.service systemctl restart tftp.service接下来是引导菜单文件/var/lib/tftpboot/pxelinux.cfg/default。一个最基础的菜单如下:
DEFAULT menu.c32 PROMPT 0 TIMEOUT 60 MENU TITLE PXE Boot Menu LABEL linux MENU LABEL Install CentOS 7 x86_64 KERNEL vmlinuz APPEND initrd=initrd.img inst.repo=http://192.168.30.10/ISO/CentOS7 ks=http://192.168.30.10/ks/ks.cfg这里KERNEL指向的vmlinuz和APPEND里的initrd.img,都是从系统镜像的isolinux目录里拷贝出来的。注意它们的路径是相对TFTP根目录的,所以直接放在根目录即可。
menu.c32来自syslinux,它让菜单有基本的图形渲染效果。如果没有这个文件,可以只保留DEFAULT和APPEND配置,也能启动,只是界面是文本列表。
3.3 HTTP镜像源和内核启动参数
HTTP服务我选择nginx,配置也比较省心。把ISO解压到网站目录,或者直接挂载ISO到目录也完全可以。
mkdir -p /var/www/html/ISO/CentOS7 mount -o loop CentOS-7-x86_64-Minimal.iso /var/www/html/ISO/CentOS7这时候客户端在启动参数里写inst.repo=http://192.168.30.10/ISO/CentOS7,安装程序就会从这个HTTP目录里读取安装包元数据和软件包。
这里有一个重要细节:Anaconda安装程序从内核启动参数获取repo源和kickstart源。如果只给repo不给ks,安装程序会进入交互界面询问你语言、键盘、磁盘等设置,顶多省去镜像介质步骤。要实现真正无人值守,必须用kickstart文件,也就是在上面的APPEND里同时指定ks参数。
另外,如果客户端是UEFI启动,pxelinux.0是不能用的。UEFI环境需要对应的EFI引导文件,比如grubx64.efi或者shimx64.efi,配置文件也变成grub.cfg。检测方法很简单:进入启动菜单如果报“文件找不到”,而你又确定pxelinux.0位置没问题,那就要确认机器是不是UEFI模式。现在新出的服务器和笔记本默认UEFI居多,所以我在搭建PXE时会同时准备BIOS和UEFI两套引导文件。
4. 自动化无人值守:kickstart配置解密
4.1 ks.cfg核心段
kickstart会自动弹出的安装界面翻译成一份配置文件,Anaconda读过之后按文件内容执行。它本质上就是一个文本文件,任何人拿到模板都能看懂。
一个最小可用的CentOS 7 ks.cfg大致是这样:
#version=DEVEL install url --url=http://192.168.30.10/ISO/CentOS7 lang en_US.UTF-8 keyboard us network --bootproto=dhcp rootpw --iscrypted $1$somehash... authconfig --enableshadow --passalgo=sha512 selinux --disabled firewall --disabled clearpart --all part /boot --fstype=xfs --size=1024 part / --fstype=xfs --size=1 --grow part swap --size=4096 bootloader --location=mbr %packages @core wget vim %end各段的逻辑很清楚:url指定安装源,network指定网卡获取IP方式,clearpart和part负责分区,rootpw设置加密后的root密码,%packages里写需要安装的软件包组。
要注意rootpw字段里的加密hash不能直接放明文密码,Anaconda在生成ks时可以通过grub-md5-crypt或openssl生成。实际运维中,我习惯先把明文密码写在一个临时文件里,用openssl passwd -6一次性生成,再粘到ks中,这样内容不会泄露。
4.2 从手动到无人值守
有人可能觉得,既然有了ks文件,是不是所有情况都能无人值守了?并不是。ks配置里如果缺少某些必填项或者写了无效参数,Anaconda会弹回交互界面。比如network段如果同时写了多个网卡且没有指定哪块,安装器可能还是会问你网络配置。
我的建议是:第一次测试时不要把ks配置写得太复杂,先用最简模板跑通,再逐步增加软件包、分区、网络、自定义脚本。让问题减到最少,才方便定位是PXE链路的问题还是ks本身的问题。
在启动参数里加上ks=http://192.168.30.10/ks/ks.cfg后,客户端安装过程完全不需要键盘鼠标。如果你在一个物理机房间里同时开50台机器,只需要保证所有机器都从网卡启动,然后等待即可。安装完的系统会自动重启,到时候根据预设策略就能进入正式系统了。
4.3 批量场景中的参数化技巧
批量装机真正的难点不在“装完”,而在“每台机器拿到符合预期的配置”。如果50台机器都通过DHCP拿到临时IP,安装出来的系统也都用DHCP,看起来可以,但生产环境通常希望服务器分配固定IP,或者主机名按照一定规则生成。
一个很实用的技巧是用%pre脚本在系统安装前执行一段shell,然后动态生成一个包含网络配置的ks片段,再通过%include引入。
比如我在HTTP服务器上放一个mac_ip.txt文件,每行格式是“MAC地址 IP地址”,%pre脚本里用客户端自身的MAC匹配行,把IP写入临时文件/tmp/network.ks:
%pre MAC=$(cat /sys/class/net/$(ls /sys/class/net | grep -E '^e|^en' | head -1)/address) IP=$(curl -s http://192.168.30.10/ks/mac_ip.txt | grep "$MAC" | awk '{print $2}') echo "network --bootproto=static --ip=$IP --netmask=255.255.255.0 --gateway=192.168.30.1 --nameserver=192.168.30.1 --hostname=node-${IP##*.}" > /tmp/network.ks %end %include /tmp/network.ks这样每台机器在开始分区前就已经确定了IP和主机名,后续安装完可以直接投入使用。如果一台机器临时换了网卡,MAC对不上,脚本会生成空文件,安装就会失败或退回交互界面,这反而会提醒我们“这台机器的资产信息要更新了”。
5. 完整安装流与故障排查手册
5.1 端到端启动流程演示
把前面所有配置串起来,完整的装机流程如下:
- 在PXE服务器上启动nginx、tftp、dhcp服务。
- 确保系统镜像目录可访问,在测试机浏览器或curl验证http://192.168.30.10/ISO/CentOS7/repodata/repomd.xml返回正常。
- 测试机开机,进入BIOS/UEFI启动菜单,选择网络启动(通常名字里有PXE或Network Boot字样)。
- 网卡从DHCP拿IP,再通过TFTP下载引导文件。
- 屏幕上出现PXE菜单,默认配置指向Linux安装项。
- 内核和initrd加载,Anaconda启动。
- 安装程序读取ks.cfg,自动分区、安装软件包、设置root密码。
- 安装完成后机器自动重启,此时要提前设置下一次启动从硬盘启动,避免再次进入PXE循环。
这里特别强调第八步:PXE装完重启后再进PXE的话,系统会重复安装。解决办法有三个:一是控制启动顺序,让硬盘启动优先于网络启动;二是在ks最后加一个reboot命令,并且把dhcp的租约时间设短,让客户端重启时已经拿不到原地址;三是用Cobbler这类系统自动处理。最推荐第一种,因为最可控。
5.2 五个高频问题速查表
这些年排查PXE问题,我总结了一些固定套路。下面这张表可以直接作为排查手册用:
| 现象 | 常见原因 | 处理方法 |
|---|---|---|
| 客户端提示PXE-E61: Media test failure | 网卡没有获取到DHCP地址,或网线/交换机端口问题 | 确认DHCP服务运行、交换机端口UP、VLAN没配错 |
| 能获取IP但下载pxelinux.0超时 | TFTP服务没开或防火墙拦截 | 检查tftp进程和防火墙,禁用tftp.socket,直接常驻服务 |
| 提示找不到配置文件或文件格式错误 | pxelinux.0路径不对,或pxelinux.cfg/default缺失 | 核对TFTP根目录结构,并用tftp客户端在本机测试下载文件 |
| 引导菜单能显示但加载内核卡住 | vmlinuz/initrd.img文件缺失或路径错误 | 确认文件在TFTP根目录,且文件名大小写一致 |
| 安装开始后报“找不到安装源” | HTTP目录无法访问,或inst.repo参数写错 | 用curl验证URL,检查nginx配置,核对镜像挂载路径 |
| UEFI机器无法通过PXE启动BIOS菜单 | 缺少EFI引导文件,或DHCPfilename指向pxelinux.0 | 改用grubx64.efi引导,并为UEFI客户端准备grub.cfg |
5.3 从PXE到更完整的批量装机系统
PXE只是批量装机的地基,真正生产级的环境往往需要在此基础上增加更多能力,比如资产登记、版本管理、装机任务编排、事后审计等。我见过不少团队在PXE上面再封装一层Web界面,让操作人员不用直接编辑DHCP和TFTP配置文件,而是通过界面提交装机任务,后端自动生成对应菜单项并记录MAC/IP的对应关系。
这个方向有两个收益:一是减少误操作,因为所有配置都从模板生成,不会再出现手误删掉一个参数导致整批失败的情况;二是装机状态可视化,每台机器处于“待装中、安装中、已完成、失败”的哪个状态,有没有进入安装界面,都能通过日志和API汇总。
如果你只是个人维护几十台机器,写几个shell脚本也能做到类似效果。核心把下面几个动作自动化起来就不会差:
- 根据MAC地址自动生成IP和主机名。
- 根据主机名自动选择对应软件包配置。
- 每一次装机都打印完整日志,方便回看失败原因。
PXE不会替代所有装机方式,但它是批量运维体系里不可回避的基础能力。我自己的体会是,第一次完整跑通PXE链路后,再去接触更复杂的自动化运维系统,很多概念都会豁然开朗。因为那些系统再怎么封装,底层仍然是DHCP、TFTP、HTTP、引导文件和kickstart这些组件在协同工作。
如果看完这篇文章想动手验证,建议先用虚拟机做一套最小环境,把服务端和客户端放在同一个虚拟网络里,用snapshot随时回滚,试错成本几乎为零。等你在虚拟环境把流程走顺了,再找一台空闲物理机做真机验证,最后再考虑推广到批量场景。这个小项目最大的价值不是炫技,而是让你真正理解“网络引导装系统”这条链路的每一环,遇到问题时脑子里能直接浮现出对应的服务日志。