如果你干过运维,大概率经历过这种场景:新到了一批服务器,要装几十台系统,手边只有U盘和一堆ISO镜像,一台一台插着装,装完还要手动配置源、更新软件。纯粹靠体力活堆。后来我把YUM仓库部署、PXE远程部署、ks无人值守三样东西串在一起,才算真正把装机这件事从“重复劳动”变成“一键触发”。这篇内容不打算讲高深理论,就是把完整流程一步步拆开,讲清楚为什么要这么配置、哪些参数是不得不注意的坑,以及我实际操作里踩过的那些雷。
这篇内容更适合两类人:一类是刚接手批量装机、被半自动化配置折磨过的新手运维,另一类是已经能手动装系统、但想把YUM仓库和PXE引导玩明白的人。内容会覆盖服务端和客户端的完整链路,从YUM源搭建,到DHCP/TFTP引导,再到kickstart文件自动化应答,最后讲联调时常见的故障。看完之后,哪怕你手头只有一台能跑的服务器和几台支持PXE启动的裸机,也能把整套环境搭起来。
1. 为什么要搭这套组合:思路设计与整体规划
1.1 核心需求拆解
YUM仓库、PXE远程部署、ks无人值守,这三样东西单独看都挺常见,但组合在一起才是一套完整的批量装机方案。YUM仓库解决的是“系统装完之后软件从哪来”的问题,PXE解决的是“机器怎么通过网络引导启动”的问题,ks无人值守解决的是“安装过程中怎么自动应答”的问题。
你会发现这三者是一条业务链:客户端通电后,网卡通过PXE从服务器拉取引导文件,进入安装程序,然后ks文件告诉anaconda“你该用哪个源、怎么分区、装哪些包”,而安装时访问的软件源就是YUM仓库。再往后,系统装完重启,第一次进入系统后需要yum安装的常用软件,也全部来自同一个YUM仓库。所以这套组合既解决装机过程自动化,也顺带把后续的软件包管理基础设施搭好了。
如果你只用PXE不加ks,装到一半还是会跳出分区、手填IP这种交互界面,人还是得盯着屏幕。只有加上ks文件,才能真正无人值守。这也是很多新手搭PXE时容易忽略的点:以为能引导进anaconda就完事了,结果每台机器还是得人工点下一步。
1.2 网络拓扑与服务规划
我实际搭建时的网络规划大概是这样的:一台服务器当作“装机服务器”,IP固定为192.168.100.10,安装CentOS 7作为宿主机。这台服务器上跑三个服务:HTTP服务提供YUM仓库和ks文件,DHCP服务给客户端分配IP地址,TFTP服务提供PXE引导文件。
客户端处于同一个二层网络内,网卡支持PXE或者通过板载网卡启动。最简单的方式是把服务器接到交换机上,客户端也接同一台交换机,不要把DHCP部署在别的地方,不然会互相干扰。如果你的生产环境已经有DHCP服务器,那就需要在现有DHCP配置里加入next-server和filename这两个选项,避免起两个DHCP服务冲突。
我的习惯是把HTTP和TFTP分开,因为HTTP传大文件更稳定,TFTP只负责引导阶段的小文件。有些人图省事,pxelinux.0后面所有文件都想走TFTP传输,这样会把TFTP拖得很慢,还容易超时。实际上在进入内核阶段之后,vmlinuz和initrd.img下载完成,后续安装源就靠HTTP了,TFTP只承担引导这一小段。这样设计是有道理的。
1.3 三种安装方式的对比
在动手之前,可以对比一下手动安装、半自动PXE和PXE+ks无人值守三种方式,能更清楚这套组合的价值。
| 安装方式 | 耗时(以10台为例) | 人力成本 | 可重复性 | 依赖条件 |
|---|---|---|---|---|
| 手动U盘/光驱安装 | 每台20-40分钟 | 全程盯屏幕 | 差,人工操作容易选错 | 需要物理介质 |
| PXE半自动 | 每台15-25分钟 | 每台仍需人工点击 | 一般,后续步骤仍需要人 | 需要PXE服务器 |
| PXE+ks无人值守 | 每台10-20分钟(可并行) | 启动后不用管 | 高,同一套配置可重复使用 | 需要YUM源+DHCP+TFTP+HTTP |
从表格里能看出来,真正的收益在于“可重复性”和“人力成本”。一旦ks文件写好了,后面再装同版本系统,基本就是开机、插网线、等结果。而且如果你有10台机器同时网络启动,也不需要一台一台排队,只要网络带宽和地址池足够,可以并行安装,这个效率提升非常明显。
2. YUM仓库部署:给后续所有操作打好底子
2.1 准备基础目录与HTTP服务
YUM仓库是整个链条里最基础的一环,也是网络源、ks文件提供方。我先说最简单的实现方式:把一个CentOS 7镜像挂载到HTTP目录下,再配合createrepo生成repodata。生产环境里也可以用reposync同步外网源,但作为起步,用本地ISO更可靠,速度也最快。
先安装必要的软件:
yum install -y httpd createrepo然后创建存放仓库的目录。我的习惯是/var/www/html/centos/7/,下面按照base、extras、updates分开目录,方便后续扩展:
mkdir -p /var/www/html/centos/7/base mkdir -p /var/www/html/centos/7/extras mkdir -p /var/www/html/centos/7/updates接着把ISO挂载到base目录,利用镜像里自带的基础包:
mount -o loop CentOS-7-x86_64-DVD-2009.iso /var/www/html/centos/7/base这里注意,如果你把ISO直接挂载到某个目录,里面的repodata是镜像自带的一个子集,但有时候anaconda在安装时只认这个目录下的安装源,所以这个目录结构高概率是能用的。如果你想同步外网源,例如同步extras:
reposync -p /var/www/html/centos/7/extras --repoid=extras这个过程会比较久,一般我建议用cron定期做增量更新。不过第一版环境,直接把ISO挂载起来作为base仓库就够了,后续再做外网源更新。
启动httpd并设置开机自启:
systemctl enable --now httpd systemctl status httpd访问 http://192.168.100.10/centos/7/base/ 如果能列出目录,说明HTTP服务正常。
2.2 制作仓库元数据
如果你只是挂载ISO,那目录下本身就有repodata目录,理论上不需要重新createrepo。但我仍然建议你执行一遍createrepo,一是为了确保元数据完整,二是如果你后续添加了RPM包到目录里,必须更新repodata才能被yum识别。
进入base目录并生成元数据:
cd /var/www/html/centos/7/base createrepo -v .执行完之后,你会看到repodata目录下生成了一堆xml.gz文件。这里的-v参数能显示详细过程,方便排查哪些包读取失败。如果目录里已经有旧的repodata,重新生成时建议用:
createrepo --update .这样做的好处是只需要重新扫描变动的RPM包,速度会快很多,尤其源仓库很大时效果明显。
为什么这一步重要?因为YUM客户端在解析一个仓库时,必须拿到repodata目录里的repomd.xml文件,通过这个文件找到组信息和包列表。如果缺了repodata,客户端就会报“Could not resolve host”或者“Cannot retrieve repository metadata”之类的错误。我见过很多人搭YUM源时直接把RPM文件拷进去了,却没有执行createrepo,结果一直失败。
2.3 客户端验证与开通防火墙
仓库服务端配置完成后,要在一台客户端机器上测试。在客户端的/etc/yum.repos.d/目录下创建一个repo文件,比如local.repo:
cat > /etc/yum.repos.d/local.repo <<EOF [local] name=Local YUM Repo baseurl=http://192.168.100.10/centos/7/base/ enabled=1 gpgcheck=0 EOF然后执行:
yum clean all yum makecache看到“metadata cache created”之类的输出说明仓库已经生效。以后客户端安装软件就直接来源这个仓库了。
服务端到这里还没完,别忘了防火墙和SELinux。CentOS 7默认可能开着firewalld,需要放行HTTP端口:
firewall-cmd --permanent --add-service=http firewall-cmd --reloadSELinux也容易让httpd无法读取挂载目录下面的文件。我通常把SELinux设为permissive或直接关闭(如果是内网测试环境)。如果你不想关闭,可以给目录打上正确的etiquete:
chcon -R -t httpd_sys_content_t /var/www/html/centos/这一步是很多人拿HTTP端口访问正常、但yum下载包始终404的根源之一。目录权限、SELinux标签、暂时防火墙没放行,三板斧排查完基本都能解决。
3. PXE引导服务:让服务器开机自动进安装程序
3.1 DHCP服务配置
PXE的核心思路是:客户端不知道从哪里下载引导文件,所以它要先通过DHCP拿到IP地址,同时从DHCP报文里取出“谁提供引导文件”和“引导文件名是什么”这两个关键信息。
在CentOS 7服务端安装DHCP服务:
yum install -y dhcp这里有人喜欢用dnsmasq,它一个服务同时提供DHCP、DNS、TFTP,配置更简单,但排错时不如独立服务清晰。我建议第一次搭建还是老老实实用DHCP+TFTP+HTTP三个独立服务,哪里出问题就查哪里,不会一锅乱炖。
编辑/etc/dhcp/dhcpd.conf:
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; default-lease-time 600; max-lease-time 7200; next-server 192.168.100.10; filename "pxelinux.0"; }这个配置里,next-server指定TFTP服务器地址,filename指定客户端要下载的引导文件。如果客户端不设置filename,它一般会默认请求pxelinux.0,但最好显式写出来。
启动DHCP服务:
systemctl enable --now dhcpd注意:启动dhcpd之前要确保服务端这台机器自己的IP地址在所属子网内,并且网卡没有配置DHCP,否则dhcpd很可能启动失败,报“Not configured to listen on any interfaces”。
3.2 TFTP服务与引导文件放置
TFTP服务是PXE引导阶段的核心。安装tftp-server:
yum install -y tftp-serverCentOS 7下tftp可以通过xinetd托管,也可以直接用socket激活。修改/etc/xinetd.d/tftp,把disable改成no,然后重启xinetd:
systemctl enable --now xinetd或者更省事的方式,直接启用tftp.socket:
systemctl enable --now tftp.socket默认情况下,TFTP根目录是/var/lib/tftpboot。现在需要把PXE引导文件放进去。syslinux包提供pxelinux.0:
yum install -y syslinux cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/然后从CentOS 7镜像的isolinux目录拷贝内核和initrd文件到tftpboot根目录:
mkdir -p /var/lib/tftpboot/centos7 cp /var/www/html/centos/7/base/isolinux/vmlinuz /var/lib/tftpboot/centos7/ cp /var/www/html/centos/7/base/isolinux/initrd.img /var/lib/tftpboot/centos7/如果你的TFTP根目录下文件比较多,建议单独建一个centos7子目录放引导内核,不让根目录太乱。注意TFTP传输是没有认证和加密的,文件名路径必须和配置文件里一致,大小写也很敏感。
3.3 PXE默认菜单配置
在/var/lib/tftpboot/pxelinux.cfg/目录下创建default文件。这个文件名本身就有讲究:PXE客户端在启动时会按特定顺序查找配置文件,先找与MAC地址对应的文件,找不到再找IP地址相关的文件,最后才找default。你可以先用default,这样所有客户端都用同一套菜单。
cd /var/lib/tftpboot/pxelinux.cfg cat > default <<EOF default vesamenu.c32 prompt 0 timeout 100 label linux menu label ^1) Install CentOS 7 with ks kernel centos7/vmlinuz append initrd=centos7/initrd.img ks=http://192.168.100.10/ks/ks7.cfg EOF注意,我特意把ks参数的地址指向HTTP而不是TFTP,因为ks文件通常比pxelinux.0大一些,TFTP传大文件效率太低。HTTP传输速度更快,而且方便你在服务端查看日志、修改内容。
如果你想要一个图形化菜单,还需要vesamenu.c32这个文件,同样从syslinux或isolinux里拷贝到TFTP根目录。不加也行,纯黑底白字的命令行菜单也足够用了。
3.4 PXE引导链路原理复盘
客户端从开机到进入安装程序,其实是这样一个流程:BIOS/UEFI中启用网卡启动,网卡发DHCP discovery包,DHCP服务器返回IP、next-server和filename,客户端立刻通过TFTP下载pxelinux.0到内存执行。pxelinux.0读取pxelinux.cfg/default,再通过TFTP下载vmlinuz和initrd.img。这两个文件下载完,内核就被加载起来了,接下来内核通过append参数访问HTTP地址的ks文件,然后进入anaconda安装界面。
我把这个流程讲清楚,是为了方便你去排查问题。例如,如果客户端停在“PXE-E53: no boot filename received”,说明DHCP没有正确返回filename字段;如果下载pxelinux.0之后提示找不到配置文件,说明default文件的路径不对或者TFTP服务有问题;如果已经进入anaconda但找不到安装源,那就是HTTP仓库路径配置有误,跟TFTP已经没有关系了。
每次排查的时候,先在服务端确认三个端口都在监听:DHCP使用UDP 67,TFTP使用UDP 69,HTTP使用TCP 80。这三个缺一不可。
防火墙放行命令:
firewall-cmd --permanent --add-service=dhcp firewall-cmd --permanent --add-service=tftp firewall-cmd --permanent --add-service=http firewall-cmd --reload4. ks无人值守文件:从“半自动”到“全自动”
4.1 Kickstart文件的核心字段
ks文件本质上就是一段按固定语法组织的文本,anaconda在安装时逐条读取执行。它至少有四个段:安装语言、键盘等基础设置;安装源指向;分区策略;软件包选择。还可以加%pre和%post脚本来做安装前后自定义操作。
我先给一个最精简的ks示例:
#version=RHEL7 install url --url="http://192.168.100.10/centos/7/base/" lang en_US.UTF-8 keyboard us network --bootproto=dhcp rootpw --iscrypted $6$xxxxxxxxx timezone Asia/Shanghai zerombr clearpart --all --initlabel part /boot --fstype=xfs --size=1024 part / --fstype=xfs --grow --size=1 bootloader --location=mbr selinux --permissive firewall --disabled %packages --nobase @core %end %post --log=/root/ks-post.log cat > /etc/yum.repos.d/local.repo <<EOF [local] name=Local YUM Repo baseurl=http://192.168.100.10/centos/7/base/ enabled=1 gpgcheck=0 EOF %end这几个字段的优先级很高:url指定了安装源,也就是前面搭建的YUM仓库目录;clearpart和part负责清空磁盘并重建分区;rootpw是加密后的root密码;%packages里可以用@core这种方式安装包组。注意,url的路径必须指向一个包含repodata的目录,否则anaconda会直接报“找不到安装源”。
有人会问,为什么rootpw不是明文?因为ks文件可能被局域网内任何机器访问,明文密码很容易泄露。可以用下面命令生成加密字符串:
openssl passwd -1 '你要设置的密码'把输出替换到ks文件中的rootpw那行,前面保留--iscrypted。
4.2 快速生成与校验ks文件
手写ks文件容易出错,我的建议是先在现有系统上参考生成的anaconda-ks.cfg文件。如果你在某个CentOS机器上手动安装过系统,/root/anaconda-ks.cfg里就有当前系统安装时的完整应答配置,这可以作为模板。把里面的分区、语言、网络配置调整成目标环境就行。
另一种方式是安装system-config-kickstart这个图形化工具来生成基础模板,虽然CentOS 7仓库里这个工具比较老,但生成基础框架还是比纯手写快。
写完ks文件之后,用ksvalidator校验一下语法:
yum install -y pykickstart ksvalidator ks7.cfg如果没有输出错误,就可以放到HTTP可访问的目录下,比如/var/www/html/ks/ks7.cfg:
mkdir -p /var/www/html/ks cp ks7.cfg /var/www/html/ks/4.3 与PXE菜单联动实现全自动安装
这一步其实前面已经做好了,就是default菜单里append行的ks参数:
append initrd=centos7/initrd.img ks=http://192.168.100.10/ks/ks7.cfg当客户端通过PXE引导时,内核启动后会把这个URL下载成临时文件,然后交给anaconda解析。此时安装过程就完全不再需要人工干预了。除非你在ks文件里设置了交互参数,比如auth --enableshadow --passalgo=sha512之外还有交互行为,或者分区选择有误,anaconda才会重新回到交互界面。
我最早搭PXE时,因为ks文件里的url写成了/files而不是/files/,导致anaconda报了找不到仓库。这个问题排查了很久,后来才发现在HTTP路径中,如果末尾缺少反斜杠,浏览器可以智能补全,但anaconda严格解析,不会帮你补。所以这里的坑在于路径的完整性和一致性,建议用curl在服务端试一遍:
curl -I http://192.168.100.10/centos/7/base/确保返回200,目录列表正常。
4.4 %post脚本的实战应用
ks文件里最灵活的是%post段,它会在系统安装完成、首次重启前执行。我通常会在里面做三件事:配置YUM源;安装常用工具;调整SSH配置。这样装出来的机器就是“开箱即用”的状态。
上面的例子里已经通过%post写了一个YUM源配置文件。如果你想安装常用软件,可以继续在%post里加:
%post --log=/root/ks-post.log yum install -y vim wget telnet net-tools systemctl enable sshd %end注意,%post脚本执行时,目标是新系统的chroot环境,所以好多在正常系统里的操作在这里都能用,但是没有图形界面、没有交互,网络是否可用取决于ks文件里的network配置。我在实际使用中遇到过%post里yum源没生效的情况,原因是系统还在chroot状态时,resolv.conf可能没有配置DNS。如果ks里配置了静态IP+网关,这个问题比较少;如果完全DHCP,偶尔会慢。稳妥一点的做法是在%post里先写死DNS:
echo "nameserver 192.168.100.10" >> /etc/resolv.conf或者在ks的network字段里带上网关和DNS参数:
network --bootproto=static --ip=192.168.100.101 --netmask=255.255.255.0 --gateway=192.168.100.1 --nameserver=192.168.100.10这里的参数要按实际环境调整,不然装出来的机器网络配置会是错的。
5. 全流程联调与问题排查
5.1 从客户端开机到安装完成的一次完整巡检
所有服务配置完成后,我习惯在真正批量装机前,先用一台机器完整跑一遍流程。具体过程是这样的:
- 客户端插上网线,开机,进入引导菜单,选择“PXE Boot”(不同厂商BIOS快捷键不一样,常见的有F12、F8、Esc等)。
- 屏幕上出现DHCP获取IP的日志,然后开始从TFTP下载pxelinux.0。
- 如果一切正常,看到一行“Trying to load: pxelinux.cfg/default”,接着出现菜单,自动选中默认label。
- 之后加载vmlinuz和initrd.img,这个过程可能几秒钟。
- 进入anaconda图形或者字符安装界面,这时你可以按Ctrl+Shift+F3或F4切换终端,看到后台日志,确认ks文件被下载、分区已执行、包安装进度。
- 等待安装完成,系统自动重启,进入GRUB,最终登录到shell。
在这个流程的每一个节点,我都会在服务端同步观察日志。比如查看HTTP访问日志确认ks文件被拉取:
tail -f /var/log/httpd/access_log看到类似“GET /ks/ks7.cfg”的记录,说明客户端确实拿到了ks。如果始终没有这条记录,那问题就出在PXE引导阶段或网络链路。
5.2 常见问题速查表与避坑指南
我把实际部署中容易踩的问题整理成一个速查表,方便你直接对照。
| 现象 | 常见原因 | 排查/解决方式 |
|---|---|---|
| 客户端卡在PXE-E53 | DHCP没有返回filename,或TFTP服务未启动 | 检查dhcpd.conf是否有next-server和filename,检查UDP69端口 |
| TFTP下载pxelinux.0后提示找不到配置文件 | pxelinux.cfg目录不存在或default文件路径错误 | 确认目录在tftp根目录下,文件名拼写正确 |
| 引导成功但anaconda提示找不到安装源 | ks文件里的url路径不正确,或HTTP目录没有repodata | 用curl验证url是否能访问,检查createrepo是否执行 |
| 安装过程弹出分区或语言选择界面 | ks文件没有正确被读取 | 确认ksURL可达、ksvalidator校验通过 |
| YUM仓库客户端makecache报错 | 防火墙/SELinux/权限问题 | 查看httpd日志,检查目录标签,放行80端口 |
| DHCP服务启动失败 | IP规划错误或接口未监听子网 | 检查dhcpd.conf中subnet与服务端IP是否一致 |
这里再补充两个独家心得。第一,如果客户端数量超过10台,TFTP的并发能力可能不够,引导阶段会出现部分机器超时。解决办法是不要所有机器同时开机,可以分批启动,或者把引导阶段的小文件尽量控制在几百KB以内。第二,HTTP仓库承载整个安装过程的RPM包传输,建议把服务端网卡打成千兆或万兆,否则并发安装时带宽会成为瓶颈。
5.3 利用系统日志提高排错效率
在整个PXE+ks联调过程中,充分利用日志能省下大量时间。TFTP服务虽然默认不记录访问日志,但如果你有xinetd管理,可以在/etc/xinetd.d/tftp里加上log_type file /var/log/tftp.log,这样每一次TFTP请求都会被记下来。不过CentOS 7的tftp.socket方式默认不一定记录,可以通过tcpdump抓包确认:
tcpdump -i eth0 -nn port 69如果看到客户端不断发TFTP Read Request,服务端却没有应答,基本可以判定是tftp服务状态或防火墙问题。
客户端侧如果需要看anaconda日志,可以在安装界面切到tty3或tty4,屏幕上会滚动输出很多关键信息。比如“Failed to read ks”这种错误,几乎一眼就能定位是ks访问失败了。这些细节在实际排错时比Google一顿乱搜更有效。
6. 一些额外建议与个人体会
这套环境搭过之后,我再装机基本都是直接PXE引导、ks自动应答,基本不用再碰物理介质了。不过有几点还是想提醒一下。
第一,YUM仓库不是一套就完事的。系统的base包虽然能通过ISO覆盖,但extras和updates源如果长期不更新,新装出来的系统会带着一堆旧版本软件,存在安全隐患。建议配置cron定期执行reposync同步外网源,只同步需要的repo即可,否则磁盘占用会很大。比如extras源有时要好几十GB,同步之前先看下磁盘剩余空间。
第二,ks文件应该纳入版本管理。我见过不少人在/var/www/html/ks/下面堆了一堆ks6.cfg、ks7-new.cfg、ks-final.cfg,时间一长自己也分不清哪个是最新的。后来我把ks文件统一放在一个目录,文件名带日期或版本号,改动前先备份,这样不管过多久都能追溯。
第三,如果你要支持UEFI启动,上面的BIOS模式pxelinux.0方案是不行的,需要额外配置grub2-efi引导文件和EFI菜单。如果你的机器全是传统BIOS,那本文这套流程完全够用;如果网卡默认UEFI且不支持CSM,记得在BIOS设置里打开传统引导模式,不然PXE客户端根本不会走到pxelinux.0。
最后再分享一个小技巧:如果你不想让PXE服务器在整个网络里随意给机器分配IP,可以在dhcpd.conf里用host MAC做绑定,只给需要装机的机器分配IP并返回引导文件。这样能避免生产环境里一些不该被重装的机器意外进入PXE安装流程。毕竟无人值守意味着只要满足条件,机器就真的会自动重装,这个风险一定得控制好。