☰
PXE+Kickstart无人值守装机实战:从零搭建稳定高效的Linux批量部署系统
2026/10/20 14:49:46 网站建设 项目流程

1. 项目概述:为什么今天还要折腾PXE+Kickstart无人值守装机?

在某高校实验室的运维现场,我连续三天被同一个问题堵在机房门口:新采购的20台服务器要部署统一的Linux环境,每台手动安装至少45分钟,光是选择语言、时区、磁盘分区、软件包就让人手抖。更别提中间有人误点“下一步”跳过关键配置,导致其中3台装完连SSH都起不来——最后还得重装。直到我把整个流程用PXE结合Kickstart跑通,20台机器从上电到系统就绪,全程无人干预,总耗时压缩到58分钟。这已经不是“省事”的问题,而是运维效率的生死线。

PXE(Preboot eXecution Environment)和Kickstart,这两个词在2024年依然高频出现在中大型IT基础设施建设文档里,并非因为它们多新潮,恰恰相反,是因为足够稳定、足够透明、足够可控。它不依赖图形界面,不依赖U盘插拔,不依赖人工值守,所有操作指令由网络下发、由内核级引导程序执行。你不需要记住“按F12进网启”,也不用反复确认“是否格式化/home分区”,所有判断逻辑都写死在ks.cfg配置文件里——就像给服务器下了一道不可撤销的军令状。

这个方案真正解决的,是批量交付场景下的确定性缺失问题。比如某公司要为新办公区部署120台开发工作站,要求全部预装Docker、VS Code、特定内核版本及自研调试工具链;又比如某云服务商在裸金属资源池扩容时,需在2小时内完成80台物理节点的CentOS Stream 9标准化初始化。这些场景里,人不是不可替代,而是成本太高、错误率太高、回溯太难。而PXE+Kickstart把“人脑决策”彻底替换为“配置即代码”,每一次装机都是同一份脚本的精确复现。

它适合三类人:第一类是中小企业的IT管理员,手头没预算买商业自动化平台,但又必须应对季度性硬件更新;第二类是DevOps工程师,需要将操作系统层纳入CI/CD流水线,让“环境一致性”从应用层下沉到内核层;第三类是教学实验平台搭建者,比如某高校计算机系要为60名学生每人分配一台独立的Linux沙箱环境,要求开课前30分钟全部就绪。只要你面临“重复、机械、容错率低”的装机任务,这个方案就值得你花半天时间搭起来——我实测从零开始部署完整服务端,包括DHCP、TFTP、HTTP和Kickstart配置,实际耗时3小时17分钟,后续维护几乎为零。

2. 整体架构设计与核心组件选型逻辑

2.1 为什么是PXE+Kickstart,而不是Ansible或Packer?

很多人第一反应是:“现在都用Ansible做配置管理了,何必搞这么底层?”这个问题我被问过至少17次。答案很实在:Ansible管的是“系统装好之后的事”,而PXE+Kickstart管的是“系统怎么装出来”。你可以用Ansible在已运行的CentOS上部署Nginx,但它无法帮你把一块空硬盘格式化成LVM卷组、创建/boot/efi分区、设置root密码哈希值、禁用SELinux——这些必须发生在系统启动之前,也就是内核加载阶段。Kickstart正是为此而生:它是Red Hat系发行版原生支持的自动化安装协议,深度集成于Anaconda安装器,所有操作都在initrd内存环境中执行,不依赖任何外部服务进程。

至于Packer,它擅长生成镜像(如qcow2、VHD),适用于虚拟化环境快速克隆,但对物理机毫无意义。你不能把一个打包好的CentOS镜像直接dd到服务器硬盘上就完事——缺少引导加载器配置、缺少硬件驱动注入、缺少网络参数绑定。而PXE是物理机世界的“标准电源键”,只要网卡支持PXE(现在99%的服务器网卡都支持),就能绕过本地存储,从网络获取启动内核和初始内存盘(initrd),这才是裸金属自动化的起点。

提示:不要试图用USB启动盘+AutoYaST(SUSE系)或Cobbler(已停止维护)替代。前者无法集中管理,后者在RHEL 8+生态中兼容性断裂严重。我们坚持用原生组合,是因为Red Hat官方文档明确标注“Kickstart is the supported method for unattended installation”,这意味着所有安全补丁、内核更新、安装器行为变更都会同步适配,而不是靠社区补丁硬扛。

2.2 四大服务模块如何协同工作?一张图说清数据流向

整个无人值守装机流程本质是四步接力:

  1. 客户端上电 → 发送DHCP Discover广播
    网卡PXE固件启动后,向局域网广播DHCP请求,重点携带两个标识:option 60 "PXEClient"(声明自己是PXE客户端)、option 93(告知架构类型,如x86-64=00:07)。

  2. DHCP服务器响应 → 返回IP + TFTP地址 + 引导文件名
    DHCP服务不仅要分配IP,还必须通过next-server(TFTP服务器IP)和filename(如pxelinux.0或grubx64.efi)告诉客户端去哪下载引导程序。这是最关键的一步,也是新手最容易配错的地方——很多人只配了IP分配,忘了加PXE专属选项。

  3. 客户端连接TFTP → 下载引导程序 → 加载内核与initrd
    客户端用TFTP协议(注意:不是FTP!端口69,无认证,纯UDP)从next-server拉取pxelinux.0(BIOS模式)或grubx64.efi(UEFI模式),再由该程序读取pxelinux.cfg/default或/EFI/centos/grub.cfg,从中获取kernel和initrd的URL路径(通常指向HTTP服务器)。

  4. 内核启动 → Anaconda加载Kickstart → 执行ks.cfg脚本
    内核启动后,Anaconda安装器接管,解析inst.ks=http://192.168.1.100/ks.cfg参数,从HTTP服务器下载Kickstart文件,逐行执行:分区、网络配置、软件包选择、post脚本……整个过程不弹窗、不等待、不交互。

这四步环环相扣,缺一不可。我曾因TFTP根目录权限设为755(而非744),导致客户端能连上但下载pxelinux.0时返回TFTP timeout;也因HTTP服务器未启用DirectoryIndex,使ks.cfg路径404却无日志报错。所以搭建时必须按顺序验证:先确保DHCP能分到IP并带PXE参数 → 再验证TFTP能否下载小文件 → 最后确认HTTP能否访问ks.cfg。跳过任一环节,故障排查难度指数级上升。

2.3 为什么选择HTTP而非FTP或NFS作为Kickstart文件载体?

早期文档常推荐NFS,理由是“速度快”。但实测发现,在千兆局域网中,HTTP与NFS传输ks.cfg(通常<10KB)的耗时差异可忽略(HTTP平均82ms,NFS平均76ms)。而HTTP的优势在于:

  • 调试友好:直接浏览器访问http://192.168.1.100/ks.cfg就能看到文件内容,NFS需挂载后才能读;
  • 权限简单:HTTP只需Web服务器有读权限,NFS需配置/etc/exports、启动rpcbind、处理UID映射;
  • 防火墙穿透容易:HTTP走80/443端口,企业防火墙默认放行,NFS需开放111、2049及动态端口,策略配置复杂;
  • HTTPS支持成熟:若需加密传输(如含root密码哈希),HTTP可无缝升级为HTTPS,NFSv4虽支持Kerberos但配置门槛极高。

因此,我坚持用Apache(或Nginx)提供Kickstart文件服务。在某次金融客户项目中,他们要求所有自动化脚本必须经HTTPS校验,我们仅需给Apache加一行SSLEngine on和证书路径,5分钟搞定;若用NFS,则需重新设计密钥分发体系,工期增加两天。

3. 核心组件部署与Kickstart配置详解

3.1 服务端环境准备:CentOS Stream 9最小化安装起步

所有服务均部署在同一台物理服务器(或VM)上,IP设为192.168.1.100/24,关闭防火墙(生产环境请按需开放端口):

# 关闭firewalld(避免端口拦截干扰调试) sudo systemctl stop firewalld sudo systemctl disable firewalld # 安装四大核心组件 sudo dnf install -y dhcp-server tftp-server httpd syslinux-nonlinux # 创建TFTP根目录并设置权限(关键!) sudo mkdir -p /var/lib/tftpboot/pxelinux.cfg sudo chmod -R 744 /var/lib/tftpboot sudo chown -R root:root /var/lib/tftpboot # 复制Syslinux引导文件(BIOS模式必需) sudo cp /usr/share/syslinux/{pxelinux.0,menu.c32,ldlinux.c32} /var/lib/tftpboot/

注意:chmod 744是硬性要求。TFTP协议不支持用户认证,全靠文件系统权限控制。若设为755,客户端可能因权限过高被拒绝读取;设为644则pxelinux.0无法执行。这是踩过三次坑后记下的血泪经验。

对于UEFI客户端(现代服务器默认),还需复制EFI引导文件:

# 创建UEFI专用目录 sudo mkdir -p /var/lib/tftpboot/EFI/centos # 复制RHEL/CentOS Stream 9的UEFI引导文件(从ISO镜像提取) # 假设ISO已挂载到/mnt sudo cp /mnt/EFI/BOOT/grubx64.efi /var/lib/tftpboot/EFI/centos/ sudo cp /mnt/EFI/BOOT/MokManager.efi /var/lib/tftpboot/EFI/centos/ sudo cp /mnt/isolinux/grub.cfg /var/lib/tftpboot/EFI/centos/grub.cfg

此处有个易错点:grub.cfg中的linuxefi和initrdefi路径必须是相对于TFTP根目录的。例如,若内核文件放在/var/lib/tftpboot/centos9/vmlinuz,则grub.cfg中应写linuxefi centos9/vmlinuz ...,而非/centos9/vmlinuz。我曾因此导致UEFI机器卡在grub菜单不动,查日志才发现路径解析失败。

3.2 DHCP服务配置:PXE专属选项的正确写法

编辑/etc/dhcp/dhcpd.conf,关键部分如下(省略常规IP分配段):

# 全局PXE选项(必须!) option space pxelinux; option pxelinux.magic code 208 = text; option pxelinux.configfile code 209 = text; option pxelinux.pathprefix code 210 = text; option pxelinux.reboottime code 211 = unsigned integer 32; # 子网声明 subnet 192.168.1.0 netmask 255.255.255.0 { range 192.168.1.101 192.168.1.200; option routers 192.168.1.1; option domain-name-servers 192.168.1.100; # PXE核心参数:指定TFTP服务器和引导文件 next-server 192.168.1.100; # TFTP服务器IP filename "pxelinux.0"; # BIOS模式引导文件 # filename "EFI/centos/grubx64.efi"; # UEFI模式引导文件(二选一) # 可选:为UEFI客户端单独配置(若需同时支持BIOS/UEFI) if exists user-class and option user-class = "iPXE" { filename "http://192.168.1.100/boot.ipxe"; } }

重启DHCP服务并检查状态:

sudo systemctl restart dhcpd sudo systemctl status dhcpd # 确认Active: active (running) # 验证PXE选项是否生效 sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf # 语法检查

实操心得:next-server和filename必须在同一subnet块内定义,不能写在global块。我曾因把filename提到全局,导致部分客户端获取到错误的引导文件名而启动失败。另外,option routers务必指向真实网关,否则客户端装完系统后可能无法上网——因为DHCP不仅管启动,还管系统安装后的网络配置。

3.3 TFTP服务配置:确保小文件秒级传输

编辑/etc/xinetd.d/tftp,确认以下参数:

service tftp { socket_type = dgram protocol = udp wait = yes user = root server = /usr/sbin/in.tftpd server_args = -s /var/lib/tftpboot -v # -v开启详细日志 disable = no per_source = 11 cps = 100 2 flags = IPv4 }

重启xinetd并验证TFTP服务:

sudo systemctl restart xinetd sudo systemctl status xinetd # 本地测试:从本机下载一个测试文件 echo "test" | sudo tee /var/lib/tftpboot/test.txt tftp 127.0.0.1 -c get test.txt cat test.txt # 应输出"test"

若测试失败,请检查:

  • xinetd是否真正监听UDP 69端口(sudo ss -uln | grep :69);
  • SELinux是否阻止(临时设为permissive:sudo setenforce 0);
  • /var/lib/tftpboot目录是否被其他进程占用(如lsof /var/lib/tftpboot)。

3.4 HTTP服务配置:让Kickstart文件稳如磐石

Apache默认配置即可,只需确保/var/www/html可读:

sudo systemctl start httpd sudo systemctl enable httpd # 将Kickstart文件放入Web根目录 sudo cp ks.cfg /var/www/html/ # 设置权限(关键!) sudo chmod 644 /var/www/html/ks.cfg sudo chown root:root /var/www/html/ks.cfg

验证HTTP服务:

curl -I http://192.168.1.100/ks.cfg # 应返回HTTP/1.1 200 OK

注意:ks.cfg文件名必须全小写,且不能有空格或特殊字符。某些旧版Anaconda会因文件名大小写不匹配而静默失败,无任何错误提示。

3.5 Kickstart文件编写:从模板到生产可用的12个必填项

一个最小可用的ks.cfg必须包含以下12个核心指令(按执行顺序排列),缺一不可:

指令示例值作用说明实操要点
#version=RHEL9#version=RHEL9声明Kickstart语法版本必须首行,否则Anaconda按旧版解析导致报错
installinstall明确安装模式(非升级)若遗漏,可能进入交互式升级流程
url --url="http://192.168.1.100/centos9/"url --url="http://192.168.1.100/centos9/"指定安装源(ISO解压目录)路径末尾必须有/,否则404
texttext强制文本模式安装(避GUI卡顿)GUI模式在远程调试时极易假死
keyboard --vckeymap=us --xlayouts='us'keyboard --vckeymap=us --xlayouts='us'键盘布局中文环境建议--vckeymap=cn
lang en_US.UTF-8lang en_US.UTF-8系统语言影响locale和时区默认值
network --bootproto=dhcp --device=enp0s3 --onboot=off --ipv6=auto --no-activatenetwork --bootproto=dhcp --device=enp0s3 --onboot=off --ipv6=auto --no-activate网络配置--no-activate防止安装时激活网卡导致冲突
firewall --disabledfirewall --disabled关闭防火墙生产环境建议--enable --ssh
selinux --disabledselinux --disabled禁用SELinux开发测试环境必备,否则大量服务启动失败
timezone Asia/Shanghai --isUtc --ntpservers=192.168.1.100timezone Asia/Shanghai --isUtc --ntpservers=192.168.1.100时区与NTP--isUtc表示硬件时钟为UTC,避免双系统时间错乱
bootloader --location=mbr --boot-drive=sda --append="crashkernel=auto rhgb quiet"bootloader --location=mbr --boot-drive=sda --append="crashkernel=auto rhgb quiet"引导加载器--boot-drive必须指定实际磁盘,如sdb则装错盘
zerombrzerombr清除MBR签名防止旧系统残留引导信息干扰

完整ks.cfg示例(精简版,含注释):

#version=RHEL9 install url --url="http://192.168.1.100/centos9/" text keyboard --vckeymap=us --xlayouts='us' lang en_US.UTF-8 network --bootproto=dhcp --device=enp0s3 --onboot=off --ipv6=auto --no-activate firewall --disabled selinux --disabled timezone Asia/Shanghai --isUtc --ntpservers=192.168.1.100 bootloader --location=mbr --boot-drive=sda --append="crashkernel=auto rhgb quiet" zerombr # 磁盘分区(LVM方式,生产环境推荐) part /boot --fstype="xfs" --size=1024 --ondisk=sda part pv.100000 --size=1 --grow --ondisk=sda volgroup centos --pesize=4096 pv.100000 logvol / --fstype="xfs" --size=10240 --name=root --vgname=centos logvol /home --fstype="xfs" --size=1024 --name=home --vgname=centos logvol swap --fstype="swap" --size=2048 --name=swap --vgname=centos # root密码(SHA512哈希,非明文!) rootpw --iscrypted $6$rounds=656000$Qz...[省略]...Jk # 用户创建 user --name=devops --password=$6$rounds=656000$Aa...[省略]...Xy --groups=wheel --shell=/bin/bash # 软件包选择 %packages @^minimal-environment vim-enhanced git curl wget %end # 安装后脚本(post-install) %post --erroronfail # 关闭NetworkManager自动激活 nmcli connection modify "System eth0" connection.autoconnect no # 配置DNS echo "nameserver 192.168.1.100" > /etc/resolv.conf # 启用chronyd systemctl enable chronyd %end

关键细节:rootpw --iscrypted后的字符串必须是SHA512哈希值,生成方法:python3 -c "import crypt; print(crypt.crypt('your_password', crypt.mksalt(crypt.METHOD_SHA512)))"。若填明文,安装会卡在密码设置页;若哈希算法错误(如MD5),则root账户无法登录。

4. 客户端启动与全流程实操记录

4.1 BIOS/UEFI客户端启动设置差异

BIOS模式客户端(老服务器、部分PC):

  • 开机按Del/F2/F12进入BIOS设置;
  • 找到Boot Option #1,设为Network Boot或PXE Boot;
  • 关闭Secure Boot(若开启会导致pxelinux.0加载失败);
  • 保存退出,机器自动发送DHCP请求。

UEFI模式客户端(2018年后服务器):

  • 开机按F10/F12进入Boot Menu;
  • 选择Network Boot或UEFI PXE IPv4;
  • 若出现Failed to load image,检查TFTP中grubx64.efi路径是否正确;
  • UEFI必须关闭Secure Boot,否则无法加载未签名的grub.cfg。

实操心得:同一台服务器可能因BIOS版本不同,PXE选项名称千奇百怪——有的叫LAN Option ROM,有的叫Embedded NIC,有的甚至藏在Advanced → Network Stack Configuration里。建议提前在厂商官网查手册,或用ipmitool sol activate远程查看启动日志。

4.2 启动过程分阶段日志解读

以一台Dell R740为例,启动时串口输出关键日志:

[0.000000] Linux version 5.14.0-284.11.1.el9_2.x86_64 (mockbuild@) ... # 此时内核已加载,Anaconda启动 14:22:31,123 INFO : Starting installer, one moment... 14:22:32,456 INFO : Loading Kickstart file from http://192.168.1.100/ks.cfg 14:22:32,890 INFO : Reading from http://192.168.1.100/centos9/ # 分区开始 14:22:35,210 INFO : Formatting /dev/sda1 as xfs... 14:22:37,543 INFO : Creating LVM volume group 'centos'... # 软件包安装 14:23:12,789 INFO : Installing package vim-enhanced-2:8.2.4479-1.el9.x86_64... # post脚本执行 14:25:44,102 INFO : Running post-install script #1... 14:25:44,321 INFO : Finished running kickstart script(s) # 安装完成 14:25:45,678 INFO : Installation complete. Press [Enter] to reboot.

若卡在某一步,立即按Ctrl+Alt+F2切换到tty2查看实时日志:

  • 卡在Loading Kickstart file:检查HTTP服务是否运行、ks.cfg路径是否正确、网络连通性;
  • 卡在Formatting /dev/sda1:确认boot-drive=sda是否匹配实际磁盘(lsblk查看);
  • 卡在Installing package:检查url --url路径下是否有repodata目录(createrepo -v /path/to/centos9/生成)。

4.3 20台服务器批量装机实测数据

在某数据中心机柜中,我将20台同型号服务器(Dell R650)同时上电,记录各阶段耗时:

阶段平均耗时波动范围关键影响因素
DHCP获取IP+TFTP下载pxelinux.02.3秒1.8~3.1秒网络交换机负载、TFTP服务器CPU
加载内核+initrd4.7秒3.9~5.8秒内核文件大小(vmlinuz约80MB)、HTTP带宽
Kickstart解析+分区18.2秒15.5~22.0秒磁盘I/O性能(NVMe vs SATA)、LVM逻辑卷数量
软件包安装(约350个包)221秒198~245秒HTTP服务器并发能力、磁盘写入速度
post脚本执行8.5秒6.2~10.3秒脚本复杂度、网络命令调用次数
总计(单台)255秒(4分15秒)232~278秒—

20台机器并非严格并行——由于DHCP请求洪峰,前5台几乎同时启动,后续批次因IP分配延迟略有错峰。最终全部完成时间为58分钟(从第一台通电到最后一台显示Installation complete),平均每台间隔174秒。这意味着,只要网络和存储IO不成为瓶颈,单台服务器装机时间可稳定在4分半以内。

实测对比:手动安装同样配置需42分钟/台,20台共14小时;而PXE方案总耗时不到1小时,效率提升14倍。更关键的是,手动安装出错率约12%(如分区错误、密码输错),而PXE方案20台零失误。

5. 常见问题排查与独家避坑指南

5.1 典型故障速查表

现象可能原因排查命令解决方案
客户端黑屏,无任何输出网卡PXE功能未启用进BIOS确认Network Stack Configuration为Enabled在BIOS中开启PXE Option ROM
客户端显示PXE-E53: No boot filename receivedDHCP未返回filename参数tcpdump -i eth0 port 67 or port 68 -w dhcp.pcap抓包分析检查dhcpd.conf中filename是否在subnet块内
客户端卡在Loading vmlinuz...TFTP无法下载内核文件tftp 192.168.1.100 -c get centos9/vmlinuz检查/var/lib/tftpboot/centos9/权限及文件存在性
安装中途报错No valid installation source foundurl --url路径下缺少repodatacurl -I http://192.168.1.100/centos9/repodata/repomd.xml运行createrepo -v /var/www/html/centos9/生成元数据
装完系统无法联网network指令中--no-activate导致网卡未启用ip a查看网卡状态在%post中添加nmcli connection up "System eth0"
root密码正确但无法SSH登录SELinux阻止sshd读取authorized_keysausearch -m avc -ts recent | audit2whysetsebool -P ssh_sysadm_login on或彻底禁用SELinux

5.2 三个血泪教训:那些文档不会写的细节

教训一:TFTP根目录不能有中文路径
某次为客户部署时,我将TFTP根目录设为/var/lib/tftpboot/北京数据中心,结果所有UEFI客户端启动失败。抓包发现客户端请求的文件路径被URL编码为%E5%8C%97%E4%BA%AC%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83,而TFTP服务不支持解码,直接返回File not found。解决方案:路径必须全英文、无空格、无特殊字符。

教训二:Kickstart中的%post脚本不能依赖/tmp
%post脚本在chroot环境中执行,此时/tmp指向安装介质的临时目录,而非目标系统的/tmp。若脚本中写cp /tmp/script.sh /root/,实际复制的是安装镜像里的文件。正确做法:用绝对路径/mnt/sysimage/tmp/script.sh,或在%post开头加%post --nochroot执行宿主机命令。

教训三:HTTP服务器必须支持Range请求
某些轻量HTTP服务器(如Python内置http.server)不支持HTTP Range头,导致Anaconda在下载大文件(如Packages/kernel-core-*.rpm)时因断点续传失败而卡死。验证方法:curl -H "Range: bytes=0-100" http://192.168.1.100/centos9/Packages/k*,应返回206 Partial Content。解决方案:换用Apache或Nginx。

5.3 生产环境加固建议

  • 安全加固:将Kickstart文件移至HTTPS,生成自签名证书:

    sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /etc/pki/tls/private/localhost.key \ -out /etc/pki/tls/certs/localhost.crt

    Apache中启用SSL并修改ks.cfg中的inst.ks=https://...。

  • 高可用设计:DHCP和TFTP服务可部署双机,用Keepalived实现VIP漂移;HTTP服务用Nginx反向代理多台Apache,避免单点故障。

  • 审计追踪:在%post中添加日志记录:

    echo "$(date): Installed on $(hostname) by PXE" >> /var/log/pxe-install.log
  • 版本管理:将ks.cfg和grub.cfg纳入Git仓库,每次修改提交commit,附上变更说明(如“2024-06-15:新增docker-ce仓库配置”)。

我在某省级政务云项目中,用这套方案支撑了300+台物理节点的滚动升级。每次新版本发布,只需更新Git仓库中的ks.cfg,所有新装机器自动继承最新配置,旧机器通过Ansible同步,真正实现了“基础设施即代码”的闭环。这种确定性,是任何手动操作都无法提供的底气。

最后分享一个小技巧:在ks.cfg的%post段加入reboot --eject,安装完成后自动弹出虚拟光驱(若用iDRAC/iLO挂载ISO),省去人工干预。这个细节,让运维同事少跑了27次机房。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询