1.1 先认识两台机器
整个部署过程涉及两台机器:
角色 它是谁 它有什么
PXE 服务器 我们搭建的那台 CentOS 7,IP 固定为 192.168.145.21 上面跑了三个服务(DHCP、TFTP、HTTP),还存着 CentOS 的 ISO 安装镜像和一份 ks.cfg 应答文件
客户机 需要被安装系统的那台空白机器 什么都没有------没有系统、没有 IP。只有一块网卡,支持从网络启动
1.2 PXE 服务器上的三个服务分别干什么
PXE 服务器本质上是把一张 CentOS 安装光盘(ISO)通过网络喂给客户机。这个过程中用到了三个服务:
服务 端口 它提供给客户机的东西 类比
DHCP UDP 67 分配给客户机的 IP 地址 + 告诉客户机"引导文件在 TFTP 那里" 前台,告诉你工位在哪、去哪领资料
TFTP UDP 69 启动内核 vmlinuz + 初始化内存盘 initrd.img + PXE 引导程序 pxelinux.0 传达室,你领走第一个包裹
HTTP TCP 80 完整安装镜像(ISO 里的全部 RPM 包)+ 无人值守应答文件 ks.cfg 仓库,你后续大量物资都从这拿
DHCP 和 TFTP 只负责启动阶段,传的文件很小(内核几十 MB)。HTTP 负责安装阶段,传的文件很大(几 GB 的 RPM 包)。
1.3 客户机从开机到装完的完整过程
以下是客户机从一片空白到安装完成的全部对话过程。每一步都标注了"谁跟谁说话"、"说了什么":
第 1 步:客户机向 DHCP 要一个 IP
客户机开机,BIOS 选择网络启动。客户机的网卡在局域网里广播一条消息:
"有人在吗?我是 00:0c:29:16:b6:37,请给我一个 IP 地址和启动文件!"
PXE 服务器上的 DHCP 服务 收到后,回复:
"给你 IP 192.168.145.100,你要的引导文件叫 pxelinux.0,它在 TFTP 服务器 192.168.145.21 上。"
至此客户机有了自己的 IP,还知道了下一步该找谁、要什么文件。
第 2 步:客户机通过 TFTP 下载引导程序和内核
客户机拿着 DHCP 给的信息,向 PXE 服务器的 TFTP 服务发起请求,依次下载三个文件:
文件 是什么
pxelinux.0 引导程序,约 27KB。它负责在屏幕上显示"Install PXE+Kickstart"菜单,然后启动后面的内核
vmlinuz Linux 内核,约 6MB。这就是最终要装到客户机上的 CentOS 内核
initrd.img 初始化内存盘,约 40MB。内核启动时需要的一个临时小文件系统,里面装着驱动(网卡驱动、硬盘驱动等)和安装引导工具
这三个文件下载完成后,pxelinux.0 把控制权交给 vmlinuz(内核),并把 PXE 菜单里 append 行上写的所有参数一起传给内核。
第 3 步:内核启动,dracut 解析内核参数
内核 vmlinuz 在客户机内存中启动后,第一个运行的程序叫 dracut。
dracut 的工作是:在这个临时的小系统里,把网卡驱动起来,连上网,然后去下载真正的安装程序。
dracut 会读取 PXE 菜单 append 行上的参数。其中有两条最关键:
inst.repo=http://192.168.145.21/pub/
ks=http://192.168.145.21/ks/ks.cfg
inst.repo= → dracut 自己用。它告诉 dracut:"安装源在 HTTP 服务器 192.168.145.21 的 /pub/ 目录下,去那里下载 LiveOS/squashfs.img。"
ks= → dracut 先记下来,不自己用,留给后面的 Anaconda。
dracut 根据 inst.repo= 的指示,通过 HTTP 下载 LiveOS/squashfs.img(约 400MB)。这个文件叫 stage2 镜像,里面就是 Anaconda 安装程序本体。
第 4 步:Anaconda 启动,解析 ks.cfg
stage2 下载完成后,Anaconda(CentOS 的安装程序)启动。
Anaconda 读取 dracut 留给它的 ks= 参数,通过 HTTP 向 http://192.168.145.21/ks/ks.cfg 发起请求,下载应答文件。
ks.cfg 里写了:
安装源在哪(url --url=http://192.168.145.21/pub/)--- 从这里下载所有 RPM 包
要清除哪块磁盘(clearpart)--- 对 nvme0n1 操作
怎么分区(part /boot、logvol /...)
root 密码是什么(rootpw --plaintext 1)
装哪些软件(%packages 段落)
装完要不要重启(reboot)
Anaconda 逐行执行 ks.cfg 里的指令:清磁盘 → 分区 → 从 HTTP 下载 RPM 包 → 安装 → 设置 root 密码 → 重启
第 5 步:重启,安装完成
Anaconda 执行完所有步骤后,客户机自动重启。此时客户机的硬盘上已经有了一套完整的 CentOS 7 系统,root 密码是 1,root 可以直接登录。
1.4 最容易踩的坑:inst.repo 和 ks.cfg 里的 url --url 不是一回事
很多人认为:只要 ks.cfg 里写了 url --url=http://...,dracut 就知道从哪下载 stage2。这是错的。
原因很简单------看时间顺序:
第 3 步:dracut 需要 inst.repo= 来找安装源下载 stage2
↑
此时 Anaconda 还没启动!ks.cfg 文件还没被读取!
↑
第 4 步:Anaconda 启动后,才解析 ks.cfg 里的 url --url=
dracut 阶段:Anaconda 不存在。内核参数是唯一的信息来源。所以必须在 PXE 菜单的 append 行写上 inst.repo=。
Anaconda 阶段:Anaconda 已经启动了,它会解析 ks.cfg,这时候 url --url= 才生效。
参数 谁读 什么时候读 用来干什么 写在哪
inst.repo= dracut 内核刚启动时(第 3 步) 下载 stage2 安装程序镜像 LiveOS/squashfs.img PXE 菜单 append 行
ks= Anaconda 安装程序启动后(第 4 步) 获取 ks.cfg 应答文件 PXE 菜单 append 行
url --url= Anaconda ks.cfg 被解析后(第 4 步内部) 获取 RPM 安装包 ks.cfg 文件内
两者都必须写,各管各的阶段,不可互相替代。
第二部分:搭建步骤
环境
项目 值
PXE 服务器 IP 192.168.145.21
网段 192.168.145.0/24 (VMware NAT)
操作系统 CentOS 7.9.2009
ISO 路径 /home/pxe/iso/CentOS-7-x86_64-DVD-2009.iso
步骤 1:关闭防火墙和 SELinux
这一步在干什么:CentOS 默认开启的防火墙和 SELinux 会拦截外来网络请求。PXE 服务器需要对外提供 DHCP、TFTP、HTTP 三个服务,客户机会主动连接这些端口。如果不关,客户机的 DHCP 请求、TFTP 下载、HTTP 访问都可能被拦截,导致 PXE 启动流程在第一步就失败。
影响谁:PXE 服务器自身的安全策略。
systemctl stop firewalld
systemctl disable firewalld
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
步骤 2:挂载 ISO 并配置本地 yum 源
这一步在干什么:ISO 文件就像一个压缩包,只有"挂载"之后里面的文件才能被系统访问。挂载后 ISO 的内容出现在 /mnt/cdrom 目录下,CentOS 安装光盘里的所有 RPM 包都在这里。然后配置 yum 源,告诉 yum:"安装软件的时候从这个本地目录找,不要上网找。"因为这台 PXE 服务器可能连不上外网,如果不禁用 base/extras/updates 三个联网源,yum 会反复尝试连接外网镜像站然后超时,导致后续安装 DHCP/TFTP/HTTP 时每步都卡很久。
影响谁:PXE 服务器自己装软件(后面步骤 3 需要 yum 安装 dhcp、tftp-server、syslinux、httpd)。
注意:yum repo 的 development 这个名字不能乱改------如果用 system-config-kickstart GUI 工具,它硬编码了只认这个 repo 名。
挂载 ISO
mkdir -p /mnt/cdrom
mount /home/pxe/iso/CentOS-7-x86_64-DVD-2009.iso /mnt/cdrom
配置 yum 源(注意:repo id 必须是 development)
cat > /etc/yum.repos.d/dvd.repo << 'EOF'
development
name=CentOS Local DVD
baseurl=file:///mnt/cdrom
gpgcheck=0
enabled=1
EOF
禁用外网源(连不上外网会导致 yum 超时)
yum-config-manager --disable base extras updates
验证
yum clean all
yum repolist
应显示: development CentOS Local DVD 4,070
步骤 3:安装所需服务(一次性安装)
这一步在干什么:从刚刚配好的本地 yum 源,一次性安装 PXE 服务器需要的所有软件包。
包名 提供的东西 对应原理中的哪个服务
dhcp DHCP 服务程序 原理第 1 步:给客户机分配 IP
tftp-server TFTP 服务程序 原理第 2 步:传 pxelinux.0 / vmlinuz / initrd.img 给客户机
xinetd 超级守护进程,管理 TFTP TFTP 不自带服务管理,由 xinetd 代为监听 UDP 69 端口
syslinux PXE 引导程序 pxelinux.0 原理第 2 步:客户机下载的第一个引导文件
httpd Apache HTTP 服务程序 原理第 3/4 步:供客户机下载 stage2 镜像和全部 RPM 包
影响谁:PXE 服务器自身------这些包装在服务器上,后面几步才配置它们。
yum install dhcp tftp-server xinetd syslinux httpd -y
步骤 4:配置 DHCP
这一步在干什么:告诉 DHCP 服务:"当有客户机请求 IP 时,给它们分配 192.168.145.100~199 范围内的地址,同时告诉它们引导文件名是 pxelinux.0,去 192.168.145.21 的 TFTP 服务上找。"
每一行的作用:
配置行 含义
subnet 192.168.145.0 netmask 255.255.255.0 这个 DHCP 服务于 192.168.145.0/24 网段
range 192.168.145.100 192.168.145.199 从 100 到 199 里挑一个空闲 IP 分配给客户机
option routers 192.168.145.1 客户机的网关(VMware NAT 网关)
next-server 192.168.145.21 告诉客户机:TFTP 服务器的地址是 192.168.145.21
filename "/pxelinux.0" 告诉客户机:你要的引导文件叫这个名字
影响谁:客户机------客户机开机时通过 DHCP 拿到 IP 和下一步方向。没有 DHCP 客户机连 IP 都没有,整个流程无法开始。
cat > /etc/dhcp/dhcpd.conf << 'EOF'
subnet 192.168.145.0 netmask 255.255.255.0 {
range 192.168.145.100 192.168.145.199;
option subnet-mask 255.255.255.0;
option routers 192.168.145.1;
default-lease-time 21600;
max-lease-time 43200;
next-server 192.168.145.21;
filename "/pxelinux.0";
}
EOF
systemctl start dhcpd
systemctl enable dhcpd
步骤 5:配置 TFTP
这一步在干什么:TFTP(简单文件传输协议)是客户机从服务器下载第一个引导文件的通道。安装的 tftp-server 由 xinetd 管理,默认是关闭的。这一步把 /etc/xinetd.d/tftp 里的 disable = yes 改成 disable = no,然后启动 xinetd------xinetd 会代为监听 UDP 69 端口,有请求时自动启动 TFTP 服务。
为什么是 TFTP 而不是 HTTP? 因为客户机网卡的 PXE 固件只支持 TFTP 协议。TFTP 极其简单,没有认证、没有目录列表,专用于这种"我只下载一个文件"的场景。后续大量数据传输才走 HTTP。
影响谁:客户机------客户机拿到 DHCP 信息后,必须通过 TFTP 下载 pxelinux.0。
启用 TFTP(xinetd 管理)
sed -i 's/disable.*=.*yes/disable = no/' /etc/xinetd.d/tftp
systemctl start xinetd
systemctl enable xinetd
验证 UDP 69 端口
netstat -ntulp | grep 69
步骤 6:部署 PXE 引导文件
这一步在干什么:把客户机 TFTP 阶段需要的文件部署到 /var/lib/tftpboot/ 目录(TFTP 服务的根目录)。共有三样东西:
文件 来源 是什么 大小
pxelinux.0 syslinux 包(/usr/share/syslinux/) PXE 引导程序,BIOS 网卡固件加载的第一个程序 ~27KB
vmlinuz ISO 里(/mnt/cdrom/isolinux/) CentOS 7 安装内核 ~6MB
initrd.img ISO 里(/mnt/cdrom/isolinux/) 初始化内存盘,含网卡/磁盘驱动和 dracut 工具 ~40MB
目录创建的位置有讲究:pxelinux.0 被加载后,它会自动在同目录下找 pxelinux.cfg/default 文件作为启动菜单。
影响谁:客户机------客户机通过 TFTP 下载的就是这个目录里的文件。没有这些文件,TFTP 端口开着也没东西可传。
syslinux 提供 pxelinux.0
cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/
从 ISO 复制内核和初始化内存盘
cp /mnt/cdrom/isolinux/* /var/lib/tftpboot/
创建 PXE 启动菜单目录
mkdir -p /var/lib/tftpboot/pxelinux.cfg
验证文件
ls /var/lib/tftpboot/
应包含: pxelinux.0 vmlinuz initrd.img pxelinux.cfg/
步骤 7:配置 HTTP 安装源
这一步在干什么:客户机前几步通过 TFTP 只能拿到几十 MB 的启动文件。真正的 CentOS 系统有几千个 RPM 包,加起来几 GB,必须通过 HTTP 传输------因为 HTTP 比 TFTP 快得多,而且支持断点续传和大文件传输。
/mnt/cdrom 是步骤 2 挂载的 ISO 内容(所有 RPM 包都在里面)。用 mount --bind 把同一个内容绑定到 httpd 的网页目录 /var/www/html/pub/,这样客户机访问 http://192.168.145.21/pub/ 就等于直接访问 ISO 里的文件。
同时创建 /var/www/html/ks/ 目录,用于存放步骤 8 生成的 ks.cfg 应答文件。
为什么用 bind mount 而不是复制:ISO 有 4.4GB,复制一份浪费磁盘。bind mount 不占额外空间。
影响谁:客户机------dracut 阶段通过 inst.repo=http://... 从这里下载 stage2 镜像,Anaconda 阶段通过 url --url=http://... 从这里下载全部 RPM 包。
mkdir -p /var/www/html/pub
mkdir -p /var/www/html/ks
把 ISO 内容通过 HTTP 发布(bind mount,不占额外磁盘)
mount --bind /mnt/cdrom /var/www/html/pub
systemctl start httpd
systemctl enable httpd
验证
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1/pub/
应返回: 200
步骤 8:编写 Kickstart 配置脚本
这一步在干什么:安装源(RPM 包)已经有了,但 Anaconda 还需要知道怎么装------装到哪块磁盘、怎么分区、root 密码是什么、装哪些软件包。这些全部写在 ks.cfg 文件里。下面的脚本 /root/gen_kscfg.sh 把所有配置项变成开头的变量,改完变量执行脚本就能自动生成 ks.cfg,避免手写。
脚本里的变量按 GUI 工具的 11 个配置页分组:
变量组 对应 GUI 页面 控制的内容
KS_LANG、KS_KEYBOARD... 基本配置 语言、键盘、时区、root 密码、是否自动重启
KS_INSTALL_METHOD、KS_HTTP_SERVER... 安装方式 从 HTTP/FTP/NFS/CDROM 安装
KS_BOOTLOADER_* 引导器 GRUB 装在哪、内核参数
KS_DISK、KS_PART_* 分区 目标磁盘名、各分区大小
KS_ETH、KS_HOSTNAME... 网络 网卡名、DHCP/静态IP、主机名
KS_AUTH、KS_AUTH_PASSALGO 认证 密码加密算法
KS_FIREWALL、KS_SELINUX 安全 防火墙和 SELinux 开关
KS_X11 显示 装完用文本还是图形界面
KS_PACKAGES 数组 软件包 要装哪些 RPM 包和包组
KS_PRE_SCRIPT 安装前脚本 分区完成后、装包前执行的命令
KS_POST_SCRIPT 安装后脚本 装完系统后、重启前执行的命令(chroot 环境)
影响谁:客户机------Anaconda 读取这个文件来决定安装过程中的所有操作。每次改完 gen_kscfg.sh 必须执行一次,才会更新 /var/www/html/ks/ks.cfg。
#!/bin/bash
============================================================
Kickstart 配置生成脚本
修改下面变量后执行: sh /root/gen_kscfg.sh
============================================================
---- 步骤 1: Basic Configuration (基本配置) ----
KS_LANG="en_US.UTF-8" # 语言
KS_KEYBOARD="us" # 键盘
KS_TIMEZONE="Asia/Shanghai" # 时区
KS_ROOTPW="1" # root 密码 (明文)
KS_REBOOT="yes" # 安装后自动重启
---- 步骤 2: Installation Method (安装方式) ----
KS_INSTALL_METHOD="http" # http | ftp | nfs | cdrom
KS_HTTP_SERVER="192.168.145.21"
KS_HTTP_DIR="/pub/"
---- 步骤 3: Boot Loader (引导器) ----
KS_BOOTLOADER_LOCATION="mbr" # mbr | partition | none
KS_BOOTLOADER_APPEND="crashkernel=auto"
---- 步骤 4: Partition (分区) ----
KS_DISK="nvme0n1" # 目标磁盘 (nvme0n1 / sda / vda)
KS_PART_BOOT=500 # /boot (MB)
KS_PART_ROOT=10240 # / (MB)
KS_PART_SWAP=2048 # swap (MB)
KS_PART_HOME=2048 # /home (MB)
KS_USE_LVM="yes"
KS_VG_NAME="centos"
---- 步骤 5: Network (网络) ----
KS_ETH="eth0"
KS_NET_BOOTPROTO="dhcp"
KS_HOSTNAME="localhost.localdomain"
---- 步骤 6: Authentication (认证) ----
KS_AUTH="enableshadow"
KS_AUTH_PASSALGO="sha512"
---- 步骤 7: Firewall & SELinux (安全) ----
KS_FIREWALL="disabled" # enabled | disabled
KS_SELINUX="disabled" # enforcing | permissive | disabled
---- 步骤 8: Display (显示) ----
KS_X11="--defaultdesktop=GNOME" # 留空 = 文本模式
---- 步骤 9: Packages (软件包) ----
KS_PACKAGES=(
"@^minimal"
"@core"
"chrony"
"kexec-tools"
"net-tools"
"vim"
"wget"
"bash-completion"
)
---- 步骤 10: Pre-Install Script (安装前脚本) ----
KS_PRE_SCRIPT=""
---- 步骤 11: Post-Install Script (安装后脚本) ----
KS_POST_SCRIPT=""
KS_POST_SCRIPT='
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
useradd -m -s /bin/bash admin
echo "admin:123456" | chpasswd
'
============================================================
以下为生成逻辑,不需要修改
============================================================
OUTPUT_FILE="/var/www/html/ks/ks.cfg"
mkdir -p "(dirname"(dirname "(dirname"OUTPUT_FILE")"
构建条件行
-n "$KS_ROOTPW_ENCRYPTED" && ROOTPW_LINE="rootpw --iscrypted $KS_ROOTPW_ENCRYPTED"
|| ROOTPW_LINE="rootpw --plaintext KSROOTPW""KS_ROOTPW" \[ "KSROOTPW"\["KS_REBOOT" = "yes" && REBOOT_LINE="reboot" || REBOOT_LINE=""
-n "$KS_X11" && X11_LINE="xconfig $KS_X11" || X11_LINE=""
构建安装方式
case "KSINSTALLMETHOD"inhttp)INSTALLLINE="url−−url=h¨ttp://KS_INSTALL_METHOD" in http) INSTALL_LINE="url --url=\"http://KSINSTALLMETHOD"inhttp)INSTALLLINE="url−−url=h¨ttp://{KS_HTTP_SERVER}KSHTTPDIR"¨;;ftp)INSTALLLINE="url−−url=f¨tp://{KS_HTTP_DIR}\"" ;; ftp) INSTALL_LINE="url --url=\"ftp://KSHTTPDIR"¨;;ftp)INSTALLLINE="url−−url=f¨tp://{KS_HTTP_SERVER}KSHTTPDIR"¨;;nfs)INSTALLLINE="nfs−−server={KS_HTTP_DIR}\"" ;; nfs) INSTALL_LINE="nfs --server=KSHTTPDIR"¨;;nfs)INSTALLLINE="nfs−−server={KS_HTTP_SERVER} --dir=${KS_HTTP_DIR}" ;;
cdrom) INSTALL_LINE="cdrom" ;;
esac
cat > "KaTeX parse error: Expected 'EOF', got '#' at position 23: ...FILE" << KSEOF #̲version=DEVEL a...{KS_AUTH} --passalgo=${KS_AUTH_PASSALGO}
INSTALLLINEfirstboot−−enableignoredisk−−only−use={INSTALL_LINE} firstboot --enable ignoredisk --only-use=INSTALLLINEfirstboot−−enableignoredisk−−only−use={KS_DISK}
keyboard --vckeymap=KSKEYBOARD−−xlayouts=′{KS_KEYBOARD} --xlayouts='KSKEYBOARD−−xlayouts=′{KS_KEYBOARD}'
lang KSLANGnetwork−−bootproto={KS_LANG} network --bootproto=KSLANGnetwork−−bootproto={KS_NET_BOOTPROTO} --device=KSETH−−onboot=yes−−ipv6=auto−−activatenetwork−−hostname={KS_ETH} --onboot=yes --ipv6=auto --activate network --hostname=KSETH−−onboot=yes−−ipv6=auto−−activatenetwork−−hostname={KS_HOSTNAME}
${ROOTPW_LINE}
services --enabled="chronyd"
timezone ${KS_TIMEZONE} --isUtc
REBOOTLINEbootloader−−append="{REBOOT_LINE} bootloader --append="REBOOTLINEbootloader−−append="{KS_BOOTLOADER_APPEND}" --location=KSBOOTLOADERLOCATION−−boot−drive={KS_BOOTLOADER_LOCATION} --boot-drive=KSBOOTLOADERLOCATION−−boot−drive={KS_DISK}
clearpart --all --initlabel --drives=KSDISKpart/boot−−fstype="xfs"−−ondisk={KS_DISK} part /boot --fstype="xfs" --ondisk=KSDISKpart/boot−−fstype="xfs"−−ondisk={KS_DISK} --size=KSPARTBOOTpartpv.01−−fstype="lvmpv"−−ondisk={KS_PART_BOOT} part pv.01 --fstype="lvmpv" --ondisk=KSPARTBOOTpartpv.01−−fstype="lvmpv"−−ondisk={KS_DISK} --size=1 --grow
volgroup KSVGNAME−−pesize=4096pv.01logvol/−−fstype="xfs"−−size={KS_VG_NAME} --pesize=4096 pv.01 logvol / --fstype="xfs" --size=KSVGNAME−−pesize=4096pv.01logvol/−−fstype="xfs"−−size={KS_PART_ROOT} --name=root --vgname=KSVGNAMElogvolswap−−fstype="swap"−−size={KS_VG_NAME} logvol swap --fstype="swap" --size=KSVGNAMElogvolswap−−fstype="swap"−−size={KS_PART_SWAP} --name=swap --vgname=KSVGNAMElogvol/home−−fstype="xfs"−−size={KS_VG_NAME} logvol /home --fstype="xfs" --size=KSVGNAMElogvol/home−−fstype="xfs"−−size={KS_PART_HOME} --name=home --vgname=KSVGNAMEfirewall−−{KS_VG_NAME} firewall --KSVGNAMEfirewall−−{KS_FIREWALL}
selinux --${KS_SELINUX}
${X11_LINE}
KSEOF
追加软件包(数组在 heredoc 里处理不了,单独写)
cat >> "$OUTPUT_FILE" << PKGEOF
%packages
(forpkgin"(for pkg in "(forpkgin"{KS_PACKAGES@}"; do echo "$pkg"; done)
%end
%addon com_redhat_kdump --enable --reserve-mb=auto
%end
PKGEOF
-n "KaTeX parse error: Expected 'EOF', got '\&' at position 18: ..._PRE_SCRIPT" &̲& { echo "%pre"...KS_PRE_SCRIPT"; } >> "$OUTPUT_FILE"
cat >> "$OUTPUT_FILE" << ANACEOOF
%anaconda
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
%end
ANACEOOF
-n "KaTeX parse error: Expected 'EOF', got '\&' at position 19: ...POST_SCRIPT" &̲& { echo "%post...KS_POST_SCRIPT"; } >> "$OUTPUT_FILE"
echo ""
echo " ks.cfg 生成完毕: OUTPUT_FILE"
echo " 大小: (wc−c\<"(wc -c \< "(wc−c\<"OUTPUT_FILE") bytes"
echo " 访问: http://{KS_HTTP_SERVER}/ks/ks.cfg"
echo ""
echo " 磁盘: KSDISK分区:/boot={KS_DISK} 分区: /boot=KSDISK分区:/boot={KS_PART_BOOT}M /=${KS_PART_ROOT}M"
echo " 网络: {KS_ETH} dhcp 主机: {KS_HOSTNAME}"
echo " 软件包: ${KS_PACKAGES\*}"
部署并执行:
将上述脚本保存为 /root/gen_kscfg.sh
chmod +x /root/gen_kscfg.sh
sh /root/gen_kscfg.sh
步骤 9:编写 PXE 启动菜单
这一步在干什么:pxelinux.0 加载后会读取 pxelinux.cfg/default 来决定显示什么菜单、用哪个内核启动、传什么参数。这个文件的 append 行是整个 PXE 流程中最关键的一行------内核和 dracut 收到的所有指令都在这里。
逐行说明:
配置行 含义
default linux 默认选中 label linux 菜单项,超时后自动执行
timeout 60 菜单等待 6 秒,无操作则自动启动
kernel vmlinuz 告诉 pxelinux.0:用 TFTP 目录下的 vmlinuz 作为内核
append initrd=initrd.img ... 传给内核的参数,每个都用空格分隔
append 行上每个参数的作用(结合原理第 3/4 步):
参数 谁读 作用
initrd=initrd.img Linux 内核 指定初始化内存盘文件
inst.repo=http://192.168.145.21/pub/ dracut 去哪下载 stage2 安装程序镜像。不能省略
ks=http://192.168.145.21/ks/ks.cfg Anaconda 应答文件的位置
ksdevice=eth0 dracut 用哪块网卡下载
net.ifnames=0 biosdevname=0 内核 强制网卡命名为 eth0 而非 ens33/enp0s3
影响谁:客户机------pxelinux.0 读取这个菜单,决定加载哪个内核、传什么参数。参数里 inst.repo= 和 ks= 缺一不可。
cat > /var/lib/tftpboot/pxelinux.cfg/default << 'EOF'
default linux
timeout 60
display boot.msg
label linux
menu label ^Install PXE+Kickstart_RedHat7
kernel vmlinuz
append initrd=initrd.img inst.repo=http://192.168.145.21/pub/ ks=http://192.168.145.21/ks/ks.cfg ksdevice=eth0 net.ifnames=0 biosdevname=0
EOF
inst.repo= 不能省略! 它是 dracut 阶段获取 stage2 镜像的唯一来源。ks.cfg 里的 url --url= 只在 Anaconda 阶段生效。
步骤 10:全面验证
这一步在干什么:搭建完成后做一次完整检查,逐项确认每个服务都在运行、每个文件都在正确的位置、HTTP 安装源和 ks.cfg 都能正常访问。三项检查分别对应客户机 PXE 流程的三个阶段:
验证项 对应原理阶段 通过标准
DHCP 服务 running 第 1 步:客户机获取 IP systemctl is-active dhcpd → active
TFTP 端口 69 监听 + boot 文件存在 第 2 步:客户机下载引导文件 pxelinux.0、vmlinuz、initrd.img 都在 /var/lib/tftpboot/
HTTP pub 和 ks.cfg 返回 200 第 3/4 步:dracut 下载 stage2 + Anaconda 下载应答文件 curl 返回 HTTP 200
验证服务
systemctl is-active dhcpd && echo "DHCP OK" || echo "DHCP FAIL"
systemctl is-active httpd && echo "HTTP OK" || echo "HTTP FAIL"
systemctl is-active xinetd && echo "TFTP OK" || echo "TFTP FAIL"
netstat -ntulp | grep -q 69 && echo "Port 69 OK" || echo "Port 69 FAIL"
验证 HTTP 可访问
curl -s -o /dev/null -w "pub: %{http_code}\n" http://127.0.0.1/pub/
curl -s -o /dev/null -w "ks: %{http_code}\n" http://127.0.0.1/ks/ks.cfg
验证 TFTP 文件
ls /var/lib/tftpboot/pxelinux.0 /var/lib/tftpboot/vmlinuz /var/lib/tftpboot/initrd.img
第三部分:生产环境部署清单
从测试环境搬到生产环境,需要修改以下三处配置。
清单 1:DHCP 配置 --- /etc/dhcp/dhcpd.conf
subnet 192.168.145.0 netmask 255.255.255.0 { # ★ 改成生产网段
range 192.168.145.100 192.168.145.199; # ★ 改 IP 池, 决定同时装多少台
option subnet-mask 255.255.255.0; # ★ 子网掩码
option routers 192.168.145.1; # ★ 生产网关 IP
default-lease-time 21600;
max-lease-time 43200;
next-server 192.168.145.21; # ★ PXE 服务器 IP
filename "/pxelinux.0";
}
参数 作用 怎么改
subnet ... netmask ... 声明网段 改成生产网络的网段,必须和 PXE 服务器 IP 在同一网段
range ... 可分配的 IP 范围 范围大小 = 同时 PXE 启动的最大台数。生产建议 200-500 个
option routers 客户机网关 改成生产网络的网关 IP
next-server TFTP 服务器地址 必须和 PXE 服务器 IP 一致
filename 引导文件名 固定 /pxelinux.0,不用改
清单 2:PXE 启动菜单 --- /var/lib/tftpboot/pxelinux.cfg/default
default linux
timeout 60 # ★ 菜单等待秒数, 批量部署改成 1
display boot.msg
label linux
menu label ^Install PXE+Kickstart_RedHat7
kernel vmlinuz
append initrd=initrd.img inst.repo=http://192.168.145.21/pub/ ks=http://192.168.145.21/ks/ks.cfg ksdevice=eth0 net.ifnames=0 biosdevname=0
★ 把 IP 换成 PXE 服务器 IP ★ 同上
参数 作用 怎么改
timeout 菜单等待秒数后自动启动 批量部署改 1(1秒自动进安装,不等人);调试阶段改 600(10分钟,留时间排查)
inst.repo= dracut 下载 stage2 的地址 IP 换成 PXE 服务器实际 IP,路径 /pub/ 不变
ks= Anaconda 下载 ks.cfg 的地址 IP 同上,路径 /ks/ks.cfg 不变
ksdevice=eth0 强制用 eth0 网卡 如果有双网卡且不是 eth0,改成实际卡名,或者删掉让系统自动选择
net.ifnames=0 biosdevname=0 强制网卡命名为 eth0 生产物理机如果有多网卡建议保留此参数,保证一致性
清单 3:Kickstart 脚本 --- /root/gen_kscfg.sh
脚本顶部有 ★ 标记的全是生产需要改的参数,这里挑最重要的列出来:
参数 作用 测试环境值 生产环境应改成
KS_HTTP_SERVER PXE 服务器 IP(用于 url --url= 下载 RPM 包) 192.168.145.21 生产 PXE 服务器的实际 IP
KS_DISK 目标磁盘设备名 nvme0n1 先装一台测试机进 shell 敲 lsblk 确认(物理机多数是 sda)
KS_PART_ROOT / 根分区大小 (MB) 10240 生产至少 50G (51200),按实际需求
KS_ROOTPW root 密码(明文) 1 生产必须用强密码,或用 KS_ROOTPW_ENCRYPTED 填加密后的密码
KS_HOSTNAME 主机名 localhost.localdomain 建议留空或用前缀(如 node-),配合 DHCP/DNS
KS_FIREWALL 防火墙 disabled 生产改 enabled
KS_SELINUX SELinux disabled 生产改 enforcing
KS_X11 图形界面 --defaultdesktop=GNOME 服务器留空(文本模式即可)
KS_PACKAGES 要装的软件包 见默认值 加 openssh-clients、ntp、监控 agent、公司要求的包
KS_POST_SCRIPT 装完后执行的脚本 空 注入 SSH 公钥、配 DNS、加监控、初始化脚本等
生产部署前自检清单
1. 确认 PXE 服务器 IP
ip a | grep inet
2. DHCP 配置的网段和 IP 池是否正确
grep -E "subnet|range|routers" /etc/dhcp/dhcpd.conf
3. PXE 菜单里两个 URL 的 IP 是否正确
grep "inst.repo|ks=" /var/lib/tftpboot/pxelinux.cfg/default
4. gen_kscfg.sh 里的 KS_HTTP_SERVER 是否正确
grep "^KS_HTTP_SERVER" /root/gen_kscfg.sh
5. 三个服务都在跑
systemctl is-active dhcpd httpd xinetd
6. ks.cfg 可以从 HTTP 访问
curl -sf http://127.0.0.1/ks/ks.cfg > /dev/null && echo "ks OK" || echo "ks FAIL"
第四部分:问题解决
问题 1:dracut-initqueue timeout → /dev/root does not exist
现象(dracut 紧急 shell):
dracut-initqueue730: Warning: dracut-initqueue timeout
dracut-initqueue730: Warning: Could not boot.
dracut-initqueue730: Warning: /dev/root does not exist
原因:PXE 菜单缺少 inst.repo=
dracut 加载完内核和 initrd 后,需要从网络下载 LiveOS/squashfs.img(stage2 安装程序镜像)。没有 inst.repo= 它就不知道该去哪获取,反复超时后放弃。
解决:检查 /var/lib/tftpboot/pxelinux.cfg/default 的 append 行:
append initrd=initrd.img inst.repo=http://192.168.145.21/pub/ ks=http://...
验证方法:在 dracut 紧急 shell 中执行:
ip a # 检查客户端是否拿到 IP
ping -c 2 192.168.145.21 # 检查到 PXE 服务器的连通性
curl -I http://192.168.145.21/pub/ # 检查 HTTP 安装源是否可达
如果三个都正常但还报这个错,就是缺 inst.repo=。
问题 2:No space left on device(dracut 阶段)
现象(dracut 紧急 shell):
OSError: Errno 28 No space left on device
/lib/anaconda-lib.sh: line 217: /tmp/ks.info: No such file or directory
原因:客户机内存不足
CentOS 7.3+ 的 Kickstart 要求客户机至少 2GB 内存。dracut 的 /tmp 运行在 tmpfs(内存)上,内存不够时解析 ks.cfg 就会写爆。
解决:VMware 中将客户机内存调到 ≥ 2048 MB
问题 3:Disk "xxx" given in ignoredisk command does not exist
现象(Anaconda 报错界面):
The following problem occurred on line 12 of the kickstart file:
Disk "sda" given in ignoredisk command does not exist.
原因:客户机磁盘设备名不是 sda
不同虚拟化平台的磁盘命名:
平台/接口 磁盘名
VMware SATA/SCSI sda
NVMe nvme0n1
VirtIO (KVM) vda
Xen xvda
解决:在 Anaconda Shell(Alt+F2)中执行 lsblk 确认实际磁盘名,然后修改 /root/gen_kscfg.sh 中的 KS_DISK 变量:
方式一:改脚本变量后重新生成(推荐)
KS_DISK="nvme0n1" # 改为实际磁盘名
sh /root/gen_kscfg.sh
方式二:直接全局替换(临时)
sed -i 's/sda/nvme0n1/g' /var/www/html/ks/ks.cfg
问题 4:yum repolist 显示 0(Package Selection 空白)
现象:system-config-kickstart 的 Package Selection 页面空白,或 yum repolist 显示 0
原因:yum 缓存被连不上外网的基础源污染
解决:
yum clean all
yum --disablerepo=base,extras,updates repolist
应显示: development CentOS Local DVD 4,070
说明:system-config-kickstart 的 Package Selection 在 CentOS 7.9 上有 comps.xml 解析 bug,即使 yum 正常也可能不显示。不建议依赖 GUI,直接编辑 /root/gen_kscfg.sh 中的 KS_PACKAGES 数组更可靠。
问题 5:重启后 ISO 挂载丢失
现象:curl http://192.168.145.21/pub/ 返回空或 404
原因:mount 命令不会持久化,重启后失效
解决:
mount /home/pxe/iso/CentOS-7-x86_64-DVD-2009.iso /mnt/cdrom
mount --bind /mnt/cdrom /var/www/html/pub
systemctl restart httpd
如需永久生效,写入 /etc/fstab:
/home/pxe/iso/CentOS-7-x86_64-DVD-2009.iso /mnt/cdrom iso9660 loop,ro 0 0
/mnt/cdrom /var/www/html/pub none bind 0 0
问题 6:客户机 IP 不在 DHCP 池范围内
现象:客户机 PXE 启动时 DHCP 拿不到 IP
原因:客户机网段和 DHCP 配置的 subnet 不一致
检查:
在 PXE 服务器上查看 DHCP 日志
journalctl -u dhcpd -f
确认 DHCP 配置中的 subnet 和你 VMware NAT 网段一致
grep subnet /etc/dhcp/dhcpd.conf