1. 为什么我最终选择了PXE批量装机方案
机房上架三十台新服务器,如果一台一台插U盘、选启动项、点下一步,光是重复劳动就能把人耗到怀疑人生。我试过用U盘批量克隆,也试过用脚本半自动化,但真正让我觉得“这才叫干活”的,还是PXE批量装机。PXE全称Preboot Execution Environment,直译过来叫预启动执行环境,说白了就是让网卡在操作系统还没起来之前,先从网络里“要”一个启动文件,然后顺着这个文件把整个系统装好。整个过程不需要U盘、不需要光驱、不需要人守在机器旁边,插上网线、开机、按个F12选网络启动,剩下的交给服务器端自动完成。
这套方案解决的核心问题就一个:把重复的装机动作标准化、自动化、集中化。它适合谁呢?如果你手里只有一两台机器要装系统,PXE的搭建成本反而比U盘高,没必要折腾。但如果你面对的是十台以上的物理机、虚拟机模板批量部署、实验室机房定期重装、或者需要频繁切换不同操作系统版本的测试环境,那PXE带来的效率提升是数量级的。我实测下来,三十台机器从裸机到全部进入系统桌面,总耗时可以压到四十分钟以内,其中大部分时间还是在等系统文件传输和自动配置,人只需要做一件事:开机、选网络启动。
这篇文章我会把PXE批量装机的完整链路拆开讲清楚,包括服务端怎么搭、客户端怎么配、启动文件怎么准备、自动化应答怎么设计、以及我在实际部署中踩过的那些坑。不管你是刚接触批量部署的新手,还是想优化现有流程的老手,都能从里面找到可以直接抄作业的步骤和参数。
2. PXE批量装机的整体架构与核心组件拆解
2.1 从开机到系统装完,数据到底怎么流动的
理解PXE的工作流程,比死记配置命令重要得多。你可以把整个过程想象成一次“网络点餐”:客户端开机后,网卡先广播喊一句“我没系统,谁给我个启动文件”,网络里的DHCP服务器听到后回一句“你的IP是这些,启动文件在TFTP服务器上,地址是那个”,然后客户端去TFTP服务器把启动文件拿下来,这个文件通常是一个小型的引导程序,比如pxelinux.0或者GRUB的引导镜像。引导程序跑起来之后,它会根据配置文件去加载内核和初始化内存盘,接着内核启动安装程序,安装程序再通过HTTP或者FTP去下载完整的系统镜像和自动化应答文件,最后按照应答文件里的指示把系统写到本地硬盘上。
整个链路里涉及四个核心服务:DHCP负责分配IP和告诉客户端去哪里找启动文件,TFTP负责传输小体积的引导文件,HTTP或者FTP负责传输大体积的系统镜像和应答文件,DNS在某些场景下也会参与,但大多数内网环境直接用IP就能跑通。这四个服务可以跑在同一台物理机上,也可以拆开部署,我个人的习惯是全部塞在一台配置还不错的Linux服务器上,省得网络策略来回调。
2.2 为什么选DHCP+TFTP+HTTP这套组合
有人会问,为什么不用FTP传镜像?FTP当然可以,但HTTP在传输大文件时的稳定性和并发能力更好,尤其是同时有几十台客户端在拉取镜像的时候,HTTP服务的吞吐表现明显更稳。TFTP之所以只用来传引导文件,是因为它基于UDP,传输小文件快但传大文件容易丢包,所以只让它干最轻的活。DHCP则是整个流程的入口,没有它客户端连自己该拿哪个启动文件都不知道。
这套组合的另一个好处是组件都是Linux系统自带或者一条命令就能装上的,不需要额外购买商业软件。我用的服务端是常见的Linux发行版,DHCP服务用isc-dhcp-server,TFTP用tftpd-hpa,HTTP用nginx或者apache都行,看个人习惯。下面这张表是我在不同规模场景下的组件选型参考:
| 客户端数量 | DHCP方案 | TFTP方案 | 镜像传输方案 | 备注 |
|---|---|---|---|---|
| 1-10台 | 现有路由器DHCP | 单机tftpd | HTTP单节点 | 小规模够用 |
| 10-50台 | 独立DHCP服务 | 独立tftpd | HTTP+缓存 | 注意并发 |
| 50台以上 | 独立DHCP+中继 | 多TFTP节点 | HTTP集群 | 需要网络规划 |
2.3 网络环境的前置条件与规划要点
PXE对网络环境有硬性要求,不是随便插上网线就能跑的。首先,客户端和PXE服务器必须在同一个二层网络里,或者中间的网络设备支持DHCP中继。如果跨网段,DHCP广播过不去,客户端就找不到服务器。其次,网络里不能有另一个DHCP服务器在乱发地址,否则客户端可能拿到错误的网关和启动文件路径。我遇到过好几次装到一半失败的情况,排查半天发现是路由器自带的DHCP没关,客户端从路由器拿了IP,自然找不到PXE服务器。
另外,网卡的PXE功能必须在BIOS或者UEFI里开启。有些服务器出厂默认是关闭的,需要手动进设置打开。还有一点容易被忽略:如果客户端和服务器之间有过防火墙或者ACL策略,需要放行DHCP的67/68端口、TFTP的69端口、以及HTTP的80或自定义端口。我一般会在部署前先用一台测试机跑通全流程,确认网络策略没问题再批量上架。
3. 服务端搭建的完整实操步骤
3.1 基础环境准备与依赖安装
我习惯用一台干净的Linux服务器来做PXE服务端,系统版本选长期支持版,避免用太新的滚动发行版,省得踩依赖坑。装好系统后第一件事是配静态IP,因为DHCP和TFTP的配置里都要写服务器自己的地址,用动态IP会把自己坑死。假设服务器内网地址是192.168.100.10,网卡是eth0,那就在网络配置里把它固定下来。
接下来安装四个核心组件,不同发行版的包名略有差异,但大体思路一致。以常见的Debian系为例,一条命令把dhcp、tftp、http服务都装上:
apt update apt install isc-dhcp-server tftpd-hpa nginx syslinux pxelinux -y这里解释一下为什么装syslinux和pxelinux:这两个包提供了PXE启动所需的引导文件,比如pxelinux.0、ldlinux.c32、menu.c32等。没有这些文件,TFTP目录里就是空的,客户端拿不到引导程序。装完之后,TFTP的根目录通常在/srv/tftp或者/var/lib/tftpboot,具体路径可以用dpkg -L tftpd-hpa查一下配置文件里的TFTP_DIRECTORY。
3.2 DHCP服务的关键参数配置
DHCP的配置文件是/etc/dhcp/dhcpd.conf,这个文件里每一行都关系到客户端能不能正常启动。我一般会先备份原文件,然后写一个最小可用的配置。核心参数包括subnet网段、range地址池、next-server指向TFTP服务器地址、filename指定引导文件名。下面是我常用的配置模板:
subnet 192.168.100.0 netmask 255.255.255.0 { range 192.168.100.100 192.168.100.200; option routers 192.168.100.1; option domain-name-servers 192.168.100.10; next-server 192.168.100.10; filename "pxelinux.0"; }这里有几个细节值得展开说。next-server必须写PXE服务器的IP,不能写主机名,因为客户端在PXE阶段还没有DNS解析能力。filename写的是相对于TFTP根目录的路径,如果引导文件放在子目录里,这里也要带上子目录名。另外,如果网络里已经有其他DHCP服务器,要么把PXE服务器的DHCP配成 authoritative 模式,要么把其他DHCP关掉,否则地址分配会乱。
配完之后重启DHCP服务,用systemctl restart isc-dhcp-server,然后看日志确认没有报错。我习惯用journalctl -u isc-dhcp-server -f实时盯一下,有客户端请求的时候能看到分配记录,方便排查。
3.3 TFTP目录结构与引导文件部署
TFTP根目录的结构直接决定了客户端能不能找到启动文件。我一般会按操作系统和版本分目录,比如:
/srv/tftp/ ├── pxelinux.0 ├── ldlinux.c32 ├── menu.c32 ├── pxelinux.cfg/ │ └── default ├── images/ │ ├── ubuntu-2204/ │ │ ├── vmlinuz │ │ └── initrd │ └── centos-7/ │ ├── vmlinuz │ └── initrdpxelinux.0和相关的c32文件从syslinux包里复制过来,路径通常在/usr/lib/PXELINUX/或者/usr/lib/syslinux/modules/bios/。复制的时候注意权限,TFTP服务通常以tftp用户运行,目录和文件的可读权限要给足。我一般直接chmod -R 755 /srv/tftp,省得权限问题导致客户端下载失败。
pxelinux.cfg/default是引导菜单的配置文件,里面定义了每个启动项对应的内核路径和启动参数。下面是一个多系统菜单的示例:
DEFAULT menu.c32 PROMPT 0 TIMEOUT 300 LABEL ubuntu-2204-auto MENU LABEL Ubuntu 22.04 Auto Install KERNEL images/ubuntu-2204/vmlinuz APPEND initrd=images/ubuntu-2204/initrd auto=true url=http://192.168.100.10/preseed/ubuntu.cfg LABEL centos-7-auto MENU LABEL CentOS 7 Auto Install KERNEL images/centos-7/vmlinuz APPEND initrd=images/centos-7/initrd ks=http://192.168.100.10/ks/centos.cfgTIMEOUT设成300表示30秒,单位是0.1秒。如果设成0,菜单会立刻跳过,适合全自动场景。APPEND行里的url或者ks参数指向自动化应答文件,这是实现无人值守的关键。
3.4 HTTP服务与自动化应答文件托管
HTTP服务负责两件事:一是提供系统安装镜像的下载,二是提供自动化应答文件。nginx的默认站点目录是/var/www/html,我一般会在下面建两个子目录:/var/www/html/iso放镜像文件,/var/www/html/preseed或者/var/www/html/ks放应答文件。
镜像文件不需要解压,直接把ISO挂载或者复制到目录里就行。但要注意,有些发行版的安装程序需要从ISO里读取文件,所以最好把ISO挂载到本地目录,然后通过HTTP暴露挂载点。比如:
mkdir -p /var/www/html/iso/ubuntu-2204 mount -o loop ubuntu-22.04-server.iso /var/www/html/iso/ubuntu-2204自动化应答文件是PXE装机的灵魂。Ubuntu系用preseed,CentOS系用kickstart,核心思路都是把安装过程中所有需要人工选择的选项提前写好,让安装程序照着执行。preseed文件里至少要定义语言、键盘、时区、分区方案、用户账号、软件包选择、以及安装后的重启动作。kickstart文件类似,但语法不同。我一般会准备一个基础模板,然后根据项目需求改分区和软件包列表。
4. 客户端配置与无人值守安装实现
4.1 BIOS与UEFI两种启动模式的适配
现在的服务器大多支持UEFI启动,但有些老设备还是传统BIOS。这两种模式对PXE的要求不一样。传统BIOS用的是pxelinux.0,UEFI用的是grubx64.efi或者shimx64.efi。如果网络里两种设备都有,TFTP目录里就得同时准备两套引导文件,DHCP配置里也要根据客户端架构分发不同的filename。
DHCP里可以用class匹配来区分:
class "pxeclients" { match if substring (option vendor-class-identifier, 0, 9) = "PXEClient"; if option arch = 00:07 { filename "grubx64.efi"; } else { filename "pxelinux.0"; } }option arch的值00:07表示UEFI x64,00:00表示传统BIOS。这样配置之后,不同架构的客户端会自动拿到对应的引导文件,不用手动切换。我实测下来,混合环境下这套配置很稳,前提是TFTP目录里两套文件都齐全。
4.2 自动化应答文件的核心参数详解
以Ubuntu的preseed为例,有几个参数是必须写对的,写错了安装过程会卡住或者装完进不去系统。分区部分我一般用lvm方案,灵活且容易扩展:
d-i partman-auto/method string lvm d-i partman-auto-lvm/guided_size string max d-i partman-auto/choose_recipe select atomic d-i partman-partitioning/confirm_write_new_label boolean true d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true用户和密码部分,密码要写成加密后的哈希值,不能写明文。可以用mkpasswd -m sha-512生成。网络配置如果想让客户端自动获取,就写dhcp;如果要固定IP,就在preseed里写静态地址。安装后的脚本可以用late_command来执行,比如装一些基础软件、配置监控agent、或者加入域。
CentOS的kickstart文件结构类似,但分区写法不同:
part /boot --fstype=xfs --size=1024 part pv.01 --size=1 --grow volgroup vg0 pv.01 logvol / --fstype=xfs --name=root --vgname=vg0 --size=10240 logvol swap --fstype=swap --name=swap --vgname=vg0 --size=4096这里有个经验:swap大小不要设太大,现在服务器内存普遍很大,设个4G到8G足够了,设太大浪费磁盘空间。根分区给10G到20G,剩下的空间留给数据盘或者单独挂载。
4.3 安装后自动配置与首次启动脚本
系统装完重启之后,还有一些收尾工作可以自动化。比如配置NTP时间同步、设置主机名、注册到监控系统、拉取配置管理工具的agent。这些动作可以写在preseed的late_command里,也可以写成一个首次启动脚本,放在镜像里或者通过HTTP下载。
我习惯在late_command里做几件事:把root的SSH公钥写进去,这样装完就能直接登录;配置apt或者yum的本地源,加速后续软件安装;设置一个首次启动的systemd服务,跑完就自禁用。这样每台机器装完都是统一的状态,不需要人工再登上去改配置。
5. 批量部署中的常见问题与排查技巧
5.1 客户端拿不到IP或者找不到启动文件
这是最常见的问题,排查思路从DHCP开始。先在服务端看DHCP日志,确认有没有收到客户端的请求。如果日志里什么都没有,说明客户端的PXE功能没开,或者网络不通。如果日志里有分配记录但客户端还是报错,检查next-server和filename是否写对,以及TFTP服务是否正常运行。
TFTP的排查可以用tftp 192.168.100.10然后get pxelinux.0测试,如果能下载说明TFTP没问题。如果下载失败,检查防火墙和目录权限。我遇到过SELinux阻止TFTP访问文件的情况,临时用setenforce 0测试一下,如果好了就去调SELinux策略或者直接关掉。
5.2 安装过程中断或卡在某个阶段
安装中途卡住的原因很多,常见的有:镜像文件损坏、网络传输不稳定、应答文件语法错误、磁盘分区失败。排查的时候先看客户端的屏幕输出,通常会提示具体哪一步出错。如果是下载镜像卡住,检查HTTP服务的并发连接数和带宽;如果是分区失败,检查应答文件里的分区方案是否和实际磁盘匹配。
我踩过一个坑:preseed里写了partman-auto/disk string /dev/sda,但有些服务器的系统盘是/dev/nvme0n1,导致分区找不到磁盘。后来改成不指定具体磁盘,让安装程序自动选择第一块盘,问题就解决了。
5.3 多台机器同时安装时的性能瓶颈
三十台机器同时拉镜像,网络带宽和服务器IO都会成为瓶颈。我实测下来,千兆网络下同时装十台左右比较稳,再多就要考虑升级到万兆或者做多节点分发。HTTP服务可以开缓存,把常用的镜像文件缓存在内存里,减少磁盘IO。TFTP服务本身压力不大,因为只传小文件,但如果并发太高也会响应慢,可以多起几个TFTP实例做负载。
下面这张表是我整理的问题速查表,遇到问题可以按图索骥:
| 现象 | 可能原因 | 排查方法 | 解决方式 |
|---|---|---|---|
| 客户端无DHCP请求 | PXE未开启/网线不通 | 检查BIOS和交换机 | 开启PXE,换网线 |
| 拿到IP但无启动文件 | next-server/filename错误 | 看DHCP日志 | 修正配置 |
| TFTP下载失败 | 防火墙/权限/SELinux | 手动tftp测试 | 放行端口,调权限 |
| 安装卡在分区 | 磁盘设备名不匹配 | 看客户端屏幕 | 改用自动选盘 |
| 多台同时装很慢 | 带宽/IO瓶颈 | 看服务器负载 | 限流或加节点 |
5.4 几个让我少走弯路的实操心得
第一个心得:先在虚拟机里把整套流程跑通,再上物理机。虚拟机可以随便折腾,快照一恢复就能重来,比在物理机上反复重启省时间得多。第二个心得:TFTP目录和HTTP目录做好版本管理,每次改配置之前先备份,出问题能快速回滚。第三个心得:给每台客户端在DHCP里做静态绑定,虽然麻烦一点,但装完之后IP是固定的,省得后面还要改。第四个心得:如果机房里有不同批次的服务器,网卡型号不一样,PXE的兼容性也会有差异,最好先用每批次的代表机型测试一遍。
6. 进阶优化与规模化扩展思路
6.1 用配置管理工具接管安装后阶段
PXE只负责把系统装好,装完之后的配置管理可以交给更专业的工具。比如用Ansible做批量配置下发,用SaltStack做状态管理,或者用Puppet做声明式配置。这样PXE的应答文件只需要保证系统能起来、网络能通、SSH能连,剩下的角色分配、软件安装、服务配置全部由配置管理工具完成。分层之后,PXE的模板会变得很轻,维护成本也低很多。
我现在的做法是:PXE装一个最小化系统,late_command里只做三件事——写SSH公钥、配好软件源、装一个配置管理agent。agent起来之后自动向服务端注册,然后拉取对应的配置。这样新增一台机器,从装机到完全就绪,全程不需要人工干预。
6.2 多版本镜像共存与菜单动态生成
当需要同时维护多个操作系统版本时,手动改pxelinux.cfg/default会很烦。我写了一个小脚本,扫描TFTP目录下的images文件夹,自动生成菜单项。每次新增一个镜像,只需要把内核和initrd放进去,跑一下脚本,菜单就更新了。这样既不容易出错,也省去了反复编辑配置文件的麻烦。
脚本的逻辑很简单:遍历images下的子目录,读取每个目录里的元数据文件(比如一个简单的ini文件,记录显示名称和启动参数),然后拼成pxelinux.cfg/default的格式。用Python或者Shell都能写,几十行代码的事。
6.3 镜像制作与快照回滚的配合
PXE装完的系统,如果后续还要批量更新,可以结合镜像制作工具做快照。比如装好一台基准机,配置好所有软件,然后用工具把它打包成新的镜像,放回PXE目录里。下次装机直接装这个新镜像,省去装完再配置的时间。这种思路在需要频繁重装的环境里特别有用,比如测试实验室或者培训机房。
不过要注意,镜像里的硬件驱动要尽量通用,不然换一批服务器可能网卡或者磁盘驱动对不上。我一般会在基准机里装好常见的驱动包,确保兼容性。
6.4 日志集中收集与装机结果审计
批量装机最怕的是装完了不知道哪台成功哪台失败。我的做法是在late_command里加一个上报动作,把装机结果发到日志服务器或者写入一个共享目录。这样装完之后看一眼汇总表,就知道哪些机器需要重装。上报的内容包括主机名、MAC地址、IP、安装的镜像版本、时间戳。简单用curl发个HTTP请求就能实现,不需要复杂的agent。
这个习惯帮我省了很多事。有一次三十台机器里有五台因为网卡驱动问题没装成功,但屏幕上没报错,就是进不去系统。因为有了上报记录,我直接定位到那五台,换了驱动重新装,其他二十五台完全没受影响。
7. 我个人在实际操作中的体会
PXE批量装机这件事,搭建阶段确实有点繁琐,DHCP、TFTP、HTTP、应答文件、引导菜单,每个环节都有细节要注意。但一旦跑通,后面就是复制粘贴的快乐。我现在的习惯是每到一个新环境,先花半天把PXE服务端搭好,后面不管装多少台机器,都是开机选网络启动然后去喝杯咖啡的事。
最后分享一个小技巧:如果你不确定某个参数该怎么写,先去翻官方文档的示例,再结合自己的环境改。不要在网上随便抄一段配置就用,因为网络环境、硬件型号、系统版本都不一样,别人的配置在你这里可能跑不通。我踩过的坑里,有一半是因为直接抄了别人的配置没改IP和路径。另外,测试阶段一定要用一台机器反复验证,确认全流程没问题再批量上,这样即使出问题也只影响一台,排查起来快得多。