第一部分:原理
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"菜单,然后启动后面的内核 vmlinuzLinux 内核,约 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 服务器自身的安全策略。
bash
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 名。
bash
# 挂载 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 服务器自身------这些包装在服务器上,后面几步才配置它们。
bash
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 都没有,整个流程无法开始。
bash
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。
bash
# 启用 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 端口开着也没东西可传。
bash
# 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 包。
bash
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。
bash
#!/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 "$OUTPUT_FILE")"
# 构建条件行
[ -n "$KS_ROOTPW_ENCRYPTED" ] && ROOTPW_LINE="rootpw --iscrypted $KS_ROOTPW_ENCRYPTED" \
|| ROOTPW_LINE="rootpw --plaintext $KS_ROOTPW"
[ "$KS_REBOOT" = "yes" ] && REBOOT_LINE="reboot" || REBOOT_LINE=""
[ -n "$KS_X11" ] && X11_LINE="xconfig $KS_X11" || X11_LINE=""
# 构建安装方式
case "$KS_INSTALL_METHOD" in
http) INSTALL_LINE="url --url=\"http://${KS_HTTP_SERVER}${KS_HTTP_DIR}\"" ;;
ftp) INSTALL_LINE="url --url=\"ftp://${KS_HTTP_SERVER}${KS_HTTP_DIR}\"" ;;
nfs) INSTALL_LINE="nfs --server=${KS_HTTP_SERVER} --dir=${KS_HTTP_DIR}" ;;
cdrom) INSTALL_LINE="cdrom" ;;
esac
cat > "$OUTPUT_FILE" << KSEOF
#version=DEVEL
auth --${KS_AUTH} --passalgo=${KS_AUTH_PASSALGO}
${INSTALL_LINE}
firstboot --enable
ignoredisk --only-use=${KS_DISK}
keyboard --vckeymap=${KS_KEYBOARD} --xlayouts='${KS_KEYBOARD}'
lang ${KS_LANG}
network --bootproto=${KS_NET_BOOTPROTO} --device=${KS_ETH} --onboot=yes --ipv6=auto --activate
network --hostname=${KS_HOSTNAME}
${ROOTPW_LINE}
services --enabled="chronyd"
timezone ${KS_TIMEZONE} --isUtc
${REBOOT_LINE}
bootloader --append="${KS_BOOTLOADER_APPEND}" --location=${KS_BOOTLOADER_LOCATION} --boot-drive=${KS_DISK}
clearpart --all --initlabel --drives=${KS_DISK}
part /boot --fstype="xfs" --ondisk=${KS_DISK} --size=${KS_PART_BOOT}
part pv.01 --fstype="lvmpv" --ondisk=${KS_DISK} --size=1 --grow
volgroup ${KS_VG_NAME} --pesize=4096 pv.01
logvol / --fstype="xfs" --size=${KS_PART_ROOT} --name=root --vgname=${KS_VG_NAME}
logvol swap --fstype="swap" --size=${KS_PART_SWAP} --name=swap --vgname=${KS_VG_NAME}
logvol /home --fstype="xfs" --size=${KS_PART_HOME} --name=home --vgname=${KS_VG_NAME}
firewall --${KS_FIREWALL}
selinux --${KS_SELINUX}
${X11_LINE}
KSEOF
# 追加软件包(数组在 heredoc 里处理不了,单独写)
cat >> "$OUTPUT_FILE" << PKGEOF
%packages
$(for pkg in "${KS_PACKAGES[@]}"; do echo "$pkg"; done)
%end
%addon com_redhat_kdump --enable --reserve-mb=auto
%end
PKGEOF
[ -n "$KS_PRE_SCRIPT" ] && { echo "%pre"; echo "$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 "$KS_POST_SCRIPT" ] && { echo "%post"; echo "$KS_POST_SCRIPT"; } >> "$OUTPUT_FILE"
echo "=========================================="
echo " ks.cfg 生成完毕: $OUTPUT_FILE"
echo " 大小: $(wc -c < "$OUTPUT_FILE") bytes"
echo " 访问: http://${KS_HTTP_SERVER}/ks/ks.cfg"
echo "=========================================="
echo " 磁盘: ${KS_DISK} 分区: /boot=${KS_PART_BOOT}M /=${KS_PART_ROOT}M"
echo " 网络: ${KS_ETH} dhcp 主机: ${KS_HOSTNAME}"
echo " 软件包: ${KS_PACKAGES[*]}"
部署并执行:
bash
# 将上述脚本保存为 /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= 缺一不可。
bash
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 |
bash
# 验证服务
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、加监控、初始化脚本等 |
生产部署前自检清单
bash
# 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-initqueue[730]: Warning: dracut-initqueue timeout
dracut-initqueue[730]: Warning: Could not boot.
dracut-initqueue[730]: 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 中执行:
bash
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 变量:
bash
# 方式一:改脚本变量后重新生成(推荐)
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 缓存被连不上外网的基础源污染
解决:
bash
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 命令不会持久化,重启后失效
解决:
bash
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 不一致
检查:
bash
# 在 PXE 服务器上查看 DHCP 日志
journalctl -u dhcpd -f
# 确认 DHCP 配置中的 subnet 和你 VMware NAT 网段一致
grep subnet /etc/dhcp/dhcpd.conf
# 应输出: subnet 192.168.145.0 netmask 255.255.255.0 {
客户端和 PXE 服务器必须在同一 VMware NAT 网络中。
附:关键文件速查
| 文件 | 作用 |
|---|---|
/etc/dhcp/dhcpd.conf |
DHCP 配置:IP池 + TFTP地址 + 引导文件名 |
/etc/xinetd.d/tftp |
TFTP 启用开关 (disable = no) |
/var/lib/tftpboot/pxelinux.0 |
PXE 引导程序 |
/var/lib/tftpboot/vmlinuz |
安装内核 |
/var/lib/tftpboot/initrd.img |
初始化内存盘 |
/var/lib/tftpboot/pxelinux.cfg/default |
PXE 启动菜单(含 inst.repo=) |
/var/www/html/pub/ |
HTTP 安装源(ISO bind mount) |
/var/www/html/ks/ks.cfg |
Kickstart 应答文件(由脚本生成) |
/root/gen_kscfg.sh |
ks.cfg 生成脚本(手动修改的唯一入口) |