
做运维的最烦的一类重复劳动是什么我个人的答案不是故障排查也不是上线变更而是装系统。故障排查好歹有挑战性上变更也算有点仪式感装系统纯粹是体力活。尤其是有几十台裸机需要交付的时候光盘加U盘一台一台怼一下午下来人都是麻的而且手动操作最容易出岔子机器配置稍有不同分区方案和基础设置就容易弄乱。后来我把目光转向PXE批量装机才算是把这个环节真正从“人肉模式”切换到了“半自动流水线”。这篇文章就把我折腾PXE的完整过程、踩过的坑、以及最终沉淀下来的方案一次性说清楚。PXEPreboot eXecution Environment预启动执行环境简单讲就是让机器通过网卡引导系统不需要任何本地存储介质介入。配合DHCP自动分配地址、TFTP传输引导文件、HTTP或NFS提供安装源、Kickstart应答文件自动填表一套批量装机系统就能在半小时内完成从规划到上线。整个过程的核心思路是只在一台服务器上做一次配置后面所有客户端机器只要设好网络启动选项就能全自动完成系统的部署、分区、配置和基础软件安装。这篇文章适合所有承担过批量交付任务的运维工程师、网管、以及想深入理解Linux网络引导原理的学习者。不会只讲命令怎么敲我会把每个关键组件“为什么这么设计”也一并讲透并附上实操中真正会遇到的故障和对应排查方法。1. 内容整体设计与思路拆解1.1 装机这件事为什么要“网络化”先从需求说起。假设你面前有30台配置相近的服务器需要统一安装某个Linux发行版并预设同样的主机名规则、分区结构、网络参数和初始化用户。传统手工装机的流程是制作启动盘、逐台插拔、选择镜像、分区、写配置、装系统、然后手工执行后续脚本。操作周期的重复度非常高人工介入越久出现偏差的概率越大比如分区时手滑选错盘或者软件包列表没勾全都可能在交付后暴露成线上问题。用PXE之后流程变成了所有机器上电开机网卡主动广播DHCP请求获得IP地址和引导文件位置再通过TFTP把引导程序拉下来引导程序去读取指定安装源按Kickstart文件里的答案自动完成整个安装。人只需要在控制台提前确认好默认引导菜单剩下的全部交给链路自动执行。这解决了三个关键问题规模化交付、配置一致性、无人值守。从原理上看PXE起始于客户端的网卡固件。网卡固件在启动阶段启用PXE协议栈发出DHCP广播DHCP服务器不仅返回IP地址还带上引导信息比如文件名和服务器IP。随后客户端通过TFTP从引导服务器拉取引导文件这个文件通常是pxelinux.0或grub相关的引导器。引导器运行后读取菜单配置文件决定从哪个镜像启动最终加载内核和解耦然后挂载安装源开始系统安装。1.2 方案选型一体化还是分离式搭建PXE环境有两条主流路线。一条是用dnsmasq把所有服务合成一个进程顺带把DHCP和TFTP都干了安装源则用一个HTTP服务承载。另一条是分离部署DHCP用dhcpd、TFTP用tftp-hpa、HTTP用nginx或httpd各司其职。一开始我图省事用了一体化方案确实上手快但等环境复杂到要管理多网段、多系统镜像时单进程配置就变得臃肿排障也要从一堆日志里捞。后来生产环境我全部改成了分离方案。分离方案的好处有很多。DHCP作为整个PXE链路的入口配置相对独立调试时不会牵连其他服务TFTP服务只在引导阶段工作传输量小独立部署可以单独调整超时和并发参数HTTP服务是安装源的核心独立出来天然支持多目录、多版本镜像并行。而且三个服务进程独立重启不会出现一个配置错了全家起不来的尴尬局面。控制服务器建议用长期支持版本的系统比如Rocky Linux 9或Debian稳定版分配一个静态内网IP。所有PXE相关的流量都建议在独立管理网段中进行避免和业务流量混跑。安全问题也不要忽略内网虽然默认可信但TFTP本身没有认证机制HTTP端口也经常是弱防护区所以管理网段建议与业务网段物理或逻辑隔离至少要做好对应的网络访问控制。1.3 链路阶段拆解从网卡启动到系统交付完整的PXE装机链路在原理上可以分为五个阶段。第一阶段是预启动环境也就是网卡固件发起DHCP广播这一步需要客户端在BIOS或UEFI中开启网络启动选项并设置正确的引导顺序。第二阶段是引导加载阶段客户端通过TFTP获取引导器引导器再加载内核和解耦文件。第三阶段是启动安装程序阶段内核挂载initrd后安装程序开始运行并通过引导参数中的路径去获取应答文件。第四阶段是系统安装阶段安装程序根据应答文件访问HTTP安装源完成文件拷贝、引导安装和基础配置。第五阶段是首次启动初始化安装完成后重启系统首次启动执行cloud-init、自定义脚本等收尾动作。这里面任何一个环节出问题表现都可能极其相似比如卡在某个界面不动、报超时、或者直接拿到一个空内核环境。所以做PXE排障必须牢牢记住这条链路顺序配合抓包和服务日志定位才不会乱猜原因。2. 核心组件解析与配置要点2.1 DHCPPXE链路的入口总开关DHCP是PXE启动的起点。客户端上电后网卡固件发出DHCP Discover服务器需要提供两组信息一组是常规IP参数另一组是PXE引导参数。常规参数包括IP地址、子网掩码、网关和DNS。PXE引导参数则通过DHCP Option 66TFTP服务器地址和Option 67引导文件名传递或者在新版协议中使用Option 150等扩展字段。简单起见配置时直接把next-server指向TFTP服务器IP把filename写成引导文件名。dhcpd配置里相对容易踩的坑是子网声明和网卡接口绑定。一台PXE控制服务器往往有多个网口务必确认DHCP服务监听的网卡就是通向客户端网段的那个网卡。启动服务后第一步不要急着连客户端测试先在本机跑一下DHCP请求模拟验证确认服务能正常分配地址再进入下一步。这个习惯能省下大量神经。配置DHCP时还有一个容易被新手忽略的细节如果目标网段存在其他DHCP服务器一定要在配置里严格限定子网作用域甚至用allow unknown-clients结合MAC白名单来控制。PXE环境讲究的是可控如果混进来一个非法DHCP服务器所有客户端引导都会乱套。2.2 TFTP经典小步快跑瓶颈也在小TFTP的作用范围很有限只在客户端获取引导器的初始阶段用到。因为这个阶段网络栈还很简单无法承载复杂协议所以选用TFTP这种基础文件传输协议。TFTP默认基于UDP 69端口数据包小且没有复杂的握手和状态机实现简化到了极致。协议的代价是传输效率低尤其是默认块大小512字节时传一个几兆字节的引导文件都会有明显延迟。解决效率问题有两个办法。一是调整引导器的pxelinux.0加载后的内核和解耦文件传送这是不可避免走TFTP的但引导器本身足够小。二是让引导器后期从HTTP获取内核和initrd绕过TFTP的低效率。SYSLINUX系列引导器支持菜单配置里直接用HTTP URL加载内核。我生产环境就是用HTTP承载内核和initrdTFTP只保底传pxelinux.0和菜单文件实测安装速度比纯TFTP快不少。TFTP配置上不建议用xinetd管理的版本直接安装独立的tftpd-hpa在配置文件中明确指定blocksize 1468乃至更大值减少包往返次数。同时把超时时间调大一些否则几十台客户端同时广播请求时偶发的瞬时丢包会触发重传超时直接表现为客户端拿不到菜单。2.3 HTTP安装源镜像、仓库与目录规划PXE引导成功后系统的安装数据就全部从安装源获取。安装源用HTTP承载比用NFS省心得多不依赖特定协议栈浏览器、CIFS、普通抓包工具都能访问排查也直观。我通常用nginx作为安装源服务器配置非常简单设置一个根目录把各类发行版的ISO镜像挂在对应子目录同时目录结构保持清晰可读。镜像的加载方式有两个选择直接把整个ISO文件放在磁盘上然后通过loop挂载或者把ISO内容解压到目录中通过目录访问。前者省磁盘空间但每次访问都需要内核处理loop设备后者浪费点空间但访问效率高、部署直观。我推荐把ISO解压到目录然后软链或nginx alias指向它。需要注意挂载ISO时用mount -o loop解压时尤其不要遗漏隐藏文件因为Linux ISO中有些配置文件和校验文件是以点开头的。HTTP服务的响应速度和并发也值得关注。默认nginx配置就行不必额外调优。但如果客户端数量很大比如同时有几十上百台机器在拉包可以考虑把HTTP的日志级别降低减少磁盘写日志带来的性能损耗。还有一点确认HTTP服务对安装源目录没有做目录列表限制或鉴权拦截否则安装程序会在从源读取元数据时报403。2.4 Kickstart无人值守的关键开关Kickstart是一种自动化应答文件安装程序按它的内容自动回答原本必须人工交互的每一项问题。文件包括磁盘分区方案、文件系统类型、软件包列表、语言与键盘布局、时区、root密码、网络配置、安全策略、以及首次启动要执行的脚本。这个文件是整个PXE方案里最能体现设计水平的地方。一份好的Kickstart文件应该把“通用的安装逻辑”和“每台机器的差异项”分开。通用的部分比如基础软件包组、分区模板、时区、语言统一写在ks文件里。有差异的地方比如主机名、IP地址、部分服务开关通过引导参数传递再用%pre脚本动态处理。这样一套配置文件就能管住一个机房的环境而不是为每种硬件规格写一份单独的ks。分区方案建议采用LVM预留足够的/home或/data空间给业务数据/boot独立出来swap大小按物理内存的1到2倍规划。对于服务器场景根分区容量可以适当给足后续扩容少折腾。软件包选择上用最小化安装加必要工具组宁可装少了后期再补也别一上来把整组图形化都装上。2.5 网络规划与安全边界PXE依赖网络的程度比日常运维高一个量级对网络环境的要求也苛刻一些。首先是交换机端口客户端机器所在的交换机下联口不建议开启端口隔离或个别准入策略至少要让DHCP Discover、TFTP客户端请求、HTTP请求都能顺利穿过。其次是VLAN划分管理网段和业务网段分开PXE服务只监听管理网段避免误用DHCP干扰业务地址池。最后是网关和广播域DHCP Discover是广播报文客户端和服务器必须在同一个二层广播域里跨三层就需要配置DHCP中继代理。安全方面TFTP默认不限制访问目录建议在TFTP目录内部做好读写权限控制只开放必要的子目录。HTTP服务如果没有特殊需求不建议直接绑定到公网网口。PXE环境如果可能接触不受信任的客户端考虑在DHCP服务中启用动态分配合约或IP/MAC绑定保证只有已知的客户端才能获得引导资格。3. 实操全流程从零搭建一套可用的PXE环境3.1 准备工作与基础环境初始化我以一套Rocky Linux 9服务器作为控制端客户端统一安装同版本系统为例。控制服务器的IP规划为192.168.110.10客户端网段192.168.110.0/24。数据库术语上我们称这台服务器为PXE控制器其他所有机器都作为PXE客户端。开始之前先把本机系统更新好关闭或放行对应的防火墙端口并安装所需软件包。涉及的核心服务端口有三类DHCP使用UDP 67、TFTP使用UDP 69、HTTP使用TCP 80。如果控制端启用了防火墙需要提前放行这组端口。同时要确认SELinux不会拦截HTTP服务读取挂载目录最简单的方式是把安装源目录的标准上下文手动设置一下或者临时调整SELinux布尔值我建议按目录标准设定上下文而不是直接关闭SELinux。# 安装所需软件包 dnf install -y dhcp-server tftp-server syslinux-tftpboot nginx3.2 DHCP配置明确引导入口编辑/etc/dhcp/dhcpd.conf定义子网和PXE引导参数。这里核心是next-server和filename两个选项。一个基础配置模板如下subnet 192.168.110.0 netmask 255.255.255.0 { range 192.168.110.100 192.168.110.200; option routers 192.168.110.1; option domain-name-servers 192.168.110.10; option subnet-mask 255.255.255.0; next-server 192.168.110.10; filename pxelinux.0; default-lease-time 3600; max-lease-time 7200; }next-server告诉你TFTP服务器在哪里filename告诉该取什么引导文件。配置好之后启动服务并检查日志确认没有语法错误。有一点我后来又改进了公司内部有多个网段PXE客户端跨越了VLAN这就需要在每个VLAN的网关设备上配置DHCP中继或者用DHCP服务端同时监听多个子网有条件可以配置基于端口的动态分配。3.3 TFTP引导环境放置引导器和菜单安装syslinux-tftpboot后把pxelinux.0、菜单模块、内核和解耦文件放到TFTP根目录。这里的常规TFTP根目录是/var/lib/tftpboot实际路径看发行版差异。拷贝引导器cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/ mkdir -p /var/lib/tftpboot/pxelinux.cfg创建/var/lib/tftpboot/pxelinux.cfg/default作为默认引导菜单。我先让客户端启动到安装程序所有具体的镜像选择都放在这个菜单里。默认菜单内容如下DEFAULT menu.c32 PROMPT 0 MENU TITLE PXE Boot Menu TIMEOUT 100 LABEL linux MENU LABEL Install Rocky Linux 9 x86_64 KERNEL images/rocky9/vmlinuz APPEND initrdimages/rocky9/initrd.img kshttp://192.168.110.10/ks/ks.cfg这段配置里KERNEL指向内核文件在TFTP根下的相对路径APPEND里的initrd参数告诉引导器加载哪个解耦文件ks参数用HTTP URL指定Kickstart文件位置。注意如果使用UEFI引导环境图形化菜单模块选用menu.c32可能不生效需要根据启动模式针对性配置。TFTP根目录、权限和SELinux上下文都要配套。TFTP服务默认对目录有读取权限要求如果用tftp-hpa默认用户是tftp需要保证引导文件对tftp用户可读使用chmod 644即可。如果启用了SELinuxtftp服务有对应的布尔值开关我因为图省事踩过一整晚的坑最后是检查/var/log/messages才定位到SELinux拦截了TFTP读取。3.4 HTTP安装源挂载ISO并配置访问安装源放在nginx目录下。先把ISO镜像拷贝到控制服务器或者直接挂载然后创建软链接指向解压目录。我的做法是统一放在/data/pxe/iso每个镜像一个目录同时给客户端访问路径以简洁的URL展示。mkdir -p /data/pxe/iso mount -o loop rocky9.iso /mnt/rocky9 cp -a /mnt/rocky9/. /data/pxe/iso/rocky9/nginx配置指向这个目录server { listen 80 default_server; root /data/pxe/iso; autoindex on; }autoindex on是必要的虽然Kickstart不一定需要目录列表但调试时可以先用浏览器访问目录确认镜像文件是否完整可见。安装时安装程序会访问repodata目录抓取软件包元数据所以目录里必须有这个文件如果ISO拷贝不完整经常会在安装进度走到一半时报仓库加载失败。3.5 Kickstart应答文件编写兼顾通用与个性Kickstart文件放在HTTP服务下路径/ks/ks.cfg对应磁盘路径/data/pxe/ks/。下面这个脚本是适应多机器的通用模板已在多台物理机上跑通过。# 版式Kickstart 格式 install url --urlhttp://192.168.110.10/rocky9/ lang en_US.UTF-8 keyboard us timezone Asia/Shanghai rootpw --iscrypted $6$... authconfig --enableshadow --passalgosha512 selinux --enforcing firewall --disabled firstboot --disable # 磁盘与引导配置 zerombr clearpart --all --initlabel part /boot --fstypexfs --size1024 part pv.01 --size1 --grow volgroup vg0 pv.01 logvol / --fstypexfs --nameroot --vgnamevg0 --size40960 logvol swap --fstypeswap --nameswap --vgnamevg0 --size4096 logvol /data --fstypexfs --namedata --vgnamevg0 --size1 --grow # 软件包配置 %packages core vim wget %end # 首次启动自定义 %post --log/root/pxe-post.log echo PXE install completed /root/pxe-post.log %end这个文件的关键在于分区策略和软件包配置。分区统一走LVM后续扩展不再需要停机调整物理分区。%post脚本用于安装完成后做定制操作比如写主机名、配置SSH key、添加用户等。我是习惯在%post里通过读取DHCP的hostname或预设参数来生成每台机器的主机名这样就不需要每台机器手工改主机名了。第一段代码里的root密码以密文形式存放。生成方法可以用openssl passwd -6或者grub-crypt绝对不要写明文密码。整个Kickstart文件在HTTP目录中时确认权限尽量收紧比如只用root可读写但如果需要远程修改也可以另设专用账号。3.6 客户端启动验证与参数传递技巧配置全部就位后在一台测试机上将其启动方式设置为网络启动开始观察安装流程。理想情况下控制台会依次出现PXE准备阶段、引导菜单、内核加载、安装程序初始化然后进入无人值守安装。全程大约10分钟期间不需要任何人工干预。如果希望客户端能自动区分不同主机名或不同分区配置可以使用Kickstart里的%pre脚本配合引导参数实现。引导器APPEND里可以带自定义参数比如hostnameweb01然后在%pre中解析这些参数并写入对应配置文件。这种方式比客户端都读同一个模块更灵活也方便和资产管理平台打通。%pre --log/tmp/ks-pre.log hostname$(find /proc/cmdline -o -print | grep hostname | cut -d -f2) echo $hostname /etc/hostname %end还有一个小技巧是把menu.c32作为默认引导菜单同时键盘快捷键可以快速进入shell方便调试。实际使用中我在APPEND里经常额外加一个ipdhcp参数确保安装程序正确获取网络配置避免因为网卡名混乱导致连不上安装源。4. 常见问题与排查技巧实录4.1 客户端卡在“PXE-E51: No DHCP or proxyDHCP offers”这个报错说明DHCP阶段就没通。排查顺序很明确看看DHCP服务是否在跑监听端口是否是67确认客户端和服务器在同一二层网络检查防火墙是否丢弃了UDP 67报文。如果服务正常再用tcpdump在服务器侧抓包观察DHCP Discover是否能收到。这类问题九成是网络隔离或服务监听错了接口。4.2 客户端能拿到IP但TFTP下载引导文件超时此时DHCP已经成功问题出在引导文件获取阶段。常见原因包括TFTP服务器没放行UDP 69端口、pxelinux.0文件不存在、或文件权限不对。另外一个很容易忽略的是filename里如果写了路径比如filename pxelinux.0TFTP服务会把它当作相对路径拼接在根目录之后路径写错会直接404。建议先用tftp命令行手动测试一遍文件下载能手动下载成功再谈客户端引导。tftp 192.168.110.10 -c get pxelinux.04.3 引导菜单正常显示但内核启动后黑屏或Kernel Panic这类问题一般和引导参数有关。最常见的是APPEND里没有指定正确initrd或者vmlinuz和initrd.img与系统架构不匹配。另一个常见坑是UEFI机器加载了BIOS版引导文件导致启动协议不符。当前架构统一使用UEFI引导我配置时都会放两套引导文件按启动模式提供不同文件名从一开始避免混用。4.4 安装程序已启动但在获取仓库或安装软件包阶段失败安装程序能跑起来说明引导链路OK之后的失败通常都和HTTP源有关。建议先访问http://192.168.110.10确认目录列表能看到ISO内容。如果目录能打开但安装报404请看Kickstart里url --url写的具体路径是否与nginx目录的alias或软链路径对应。还有一个容易踩的坑如果通过mount -o loop临时挂载ISO服务器一重启挂载点就消失。我的建议是把ISO解压到目录后长期保留避免循环挂载依赖进程状态。4.5 Kickstart文件无法读取如果引导器能找到kshttp://...但安装程序提示解析错误或访问失败先用浏览器或curl确认该URL可以正常返回内容再检查文件格式。Kickstart文件其实对行尾和编码有要求Windows下编辑过再传到服务器很容易栽在回车符差异上。另一个隐蔽的问题是文件中出现空行、注释错位或者使用了不支持的指令建议安装前用ksvalidator做一次语法验证能省去大量现场调试时间。4.6 大规模并发安装时的延时与资源瓶颈同时引导几十台客户端时DHCP、TFTP、HTTP三个服务都可能成为瓶颈。DHCP层面可以把default-lease-time缩短让地址尽早释放TFTP层面引导器文件本身传输尽力减小必要时仅传输引导器内核和initrd走HTTPHTTP层面建议把所有安装源目录的冗余日志级别关闭同时nginx开启sendfile和aio并考虑采用proxy_cache做仓库包缓存。实测中同样的配置优化HTTP路径后并发20台以上也能稳定交付。4.7 安装完成后首次启动进不了系统这类问题多出在引导加载器安装和分区设计上。如果/boot分区存在但内核没写入引导项检查Kickstart里是否包含bootloader --locationmbr或UEFI相关参数。此外部分机器因固件原因在PXE安装后还保留PXE启动顺序导致重启又回到网络启动菜单。处理办法是在Kickstart%post阶段写一段脚本将启动顺序调整为本机磁盘优先或者依靠UEFI的引导管理器设置。5. 扩展场景与现实经验PXE不止能装单机系统稍微扩展一下逻辑就能覆盖多种运维场景。比如配合模板镜像可以用PXE引导一个内存系统然后在内存系统里执行克隆、备份或抽检硬件固件信息再比如把Kickstart文件里的软件包列表换成某一业务组的常用软件集合就相当于批量交付了统一基线还能和监控平台打通让安装完成后的系统第一时间自动上报到CMDB。我在实际落地中还总结了一条经验PXE的搭建代码相对固定难的不是写配置而是对网络细节的理解和异常处理。每次导入一批新机型我都会先拿一台样机完整跑一遍安装观察日志里是否有新硬件导致的异常再决定是否批量执行。如果机器网卡型号较新引导阶段可能还需要嵌入厂商网卡驱动否则会出现内核无法识别网卡而无法进入安装程序的怪问题。后来说个非常实用的小技巧建议把控制服务器上的配置做版本管理至少对dhcpd.conf、nginx配置和Kickstart脚本打上git标签。出现配置回退时几秒钟就能恢复。做PXE的乐趣就在于前期配置虽然琐碎但换来的是一劳永逸的交付能力这种投入非常值得。祝各位顺利跑通自己的PXE链路。