1. 先聊清楚:为什么我要折腾 PXE Server
机房那排机柜里躺着十二台二手服务器,老板一句"下周全部换 Ubuntu 24.04",我当时头皮是麻的。一台台插 U 盘、进 BIOS 改启动顺序、盯着进度条、装完再改回来,单台保守估计四十分钟,中间还得有人守着。更别提有几台机器挂在机柜最上面,键盘显示器够不着,每次都得拖个 KVM 小车过来。所以这次我下定决心,把 Ubuntu 的网络安装环境搭起来,让机器自己从网卡启动、自己拉内核、自己装系统。这就是标题里说的基于 Ubuntu 部署 PXE Server 用于网络安装 Ubuntu这套东西。
PXE 全称是 Preboot Execution Environment,直白讲就是网卡里内置了一小段固件程序,机器开机时如果被设成网卡启动,它就会朝局域网喊一句"谁有启动文件",然后从服务器把引导程序和系统内核拉下来跑。整个过程不需要 U 盘、不需要光驱、不需要本地磁盘上预先放任何东西。对于手里有十几台以上机器、或者机器放在伸手够不着的地方、又或者同一个型号反复重装的场景,一次搭好能省下成吨的时间。
这套方案适合谁?我按经验分三类。第一类是运维和 IT 支持,尤其负责机房批量交付的;第二类是自己在家搞了几台小主机、NAS、软路由,喜欢折腾多系统的;第三类是做嵌入式或者开发板相关的,需要频繁给 x86 设备刷系统的。这三类人诉求不一样,前两类更看重"省事、可自动化",第三类更看重"可定制、能塞自己的脚本"。所以下面我会把搭建过程讲全,同时把可以裁剪、可以定制的点单独标出来,你按自己的场景取用就行。
开头先给个心理预期:PXE 这东西门槛不在原理,原理十分钟能讲完,坑全在细节——网卡固件差异、UEFI 和传统 BIOS 的引导文件不一样、DHCP 和 TFTP 的配置稍有不慎就连不上、防火墙默默拦包。我踩过的坑基本都在第 4 章里列全了,你可以先跳到那里看看心里有没有底,再回来按顺序搭。
2. 方案选型:U 盘、克隆和 PXE 到底怎么挑
2.1 几种批量装机路子的真实对比
在动手之前,我建议你先想清楚自己到底需不需要 PXE。因为我见过太多人为了装三台机器去搭 PXE,结果配置 DHCP 花了一整天,还不如手工。下面这张表是我这几年实际用下来的体感总结,不是理论值,是"真干活时"的耗时。
| 方案 | 首次搭建成本 | 单台装机耗时 | 批量效率 | 自动化程度 | 适用场景 |
|---|---|---|---|---|---|
| U 盘启动盘 | 10 分钟 | 30-50 分钟(含人工盯守) | 差,串行 | 低 | 1-3 台,偶尔装 |
| Ventoy 多合一 U 盘 | 30 分钟 | 25-40 分钟 | 一般,仍需人工 | 中低 | 多版本切换,5 台以内 |
| 磁盘克隆(dd/再生龙) | 半天 | 10-20 分钟 | 好,但一致性要求高 | 中 | 同型号大批量,配置完全一致 |
| PXE 网络安装 | 半天到一天 | 10-25 分钟 | 优秀,可并行 | 高,可零接触 | 10 台以上,或机器难接触 |
| PXE + 自动化应答 | 一天以上 | 8-15 分钟无人值守 | 优秀,可并行 | 极高 | 机房交付、标准化环境 |
看这张表你会发现,PXE 的首次搭建成本其实不低,如果你一年只装两三台机器,那纯属自找麻烦。但一旦上了规模,边际成本几乎为零——第二十台和第一台花的功夫一模一样,而且不用人在旁边守着。
2.2 为什么我选 dnsmasq 而不是分开装 DHCP 和 TFTP
搭建 PXE 有两条主流路线,我在两台环境上分别试过,各有取舍。
第一条是dnsmasq 一把梭。dnsmasq 本身是个轻量 DNS/DHCP 服务,但它同时支持 TFTP,还支持在 DHCP 响应里塞引导文件名。也就是说,一台机器、一个配置文件、一个 systemd 服务,DHCP、DNS、TFTP 全齐了。配置文件不过二三十行,改完systemctl restart dnsmasq就生效。对个人和中小规模场景,这是最省心的。
第二条是isc-dhcp-server + tftpd-hpa 分工。DHCP 归 DHCP,TFTP 归 TFTP,职责清晰。好处是如果公司网络里已经有一台 DHCP 服务器在跑,你不想再起一台抢生意的,可以让现有 DHCP 只负责发引导参数,TFTP 单独跑。这种"已有 DHCP、只加引导信息"的需求,dnsmasq 也能做,但用 isc-dhcp 的next-server和filename指令更符合老运维的习惯。
我这次选 dnsmasq,理由很实在:环境是我自己的实验网段,没有现成 DHCP,也不需要复杂的作用域策略。如果你是要在生产办公网里插一脚,我的建议是别动现有 DHCP,单独划一个 VLAN 出来跑 PXE,或者用 dnsmasq 的"只发引导信息不发地址"模式,避免把整个办公网的地址分配搞乱。这一点后面 4.3 节我会细说,那是我见过最刺激的一次事故。
2.3 整个链路的数据流:先搞懂谁跟谁说话
很多人配完连不上,就是因为不清楚"机器开机那几秒到底发生了什么"。我用送快递打个比方:
- 客户端开机,网卡固件发一个DHCP Discover广播,内容大意是"我在这,有启动文件给我吗",里面带了一个关键字段
client-arch,标明自己是传统 BIOS 还是 UEFI、是 x86 还是 ARM。 - PXE 服务器(dnsmasq)收到后回DHCP Offer/ACK,除了给 IP 地址,还附带三样东西:TFTP 服务器地址(
next-server)、引导文件名(filename)、以及一些 PXE 专属选项。 - 客户端拿到信息,用TFTP去服务器把引导程序(传统 BIOS 是
pxelinux.0,UEFI 是grubx64.efi或ipxe.efi)下载到内存里执行。 - 引导程序再去读它的配置菜单,把Linux 内核 vmlinuz 和初始内存盘 initrd拉下来。
- 内核启动安装器,安装器再通过HTTP 或 NFS去取真正的安装镜像(squashfs 之类),然后开始装。
关键点来了:TFTP 只负责传小文件,大文件走 HTTP。TFTP 是 UDP 协议,没有窗口机制,传几百兆的镜像能慢到让你怀疑人生。所以标准做法是引导阶段用 TFTP,安装阶段切 HTTP。这条经验值一条命,很多人卡在安装器加载慢,就是没做这个切换。
3. 服务端搭建:从装包到能引导的完整过程
3.1 基础环境准备和 IP 规划
我用的服务端是一台装了 Ubuntu 22.04 LTS 的旧机器,配置不高,4 核 8G 内存,一块 128G 的 SSD 装系统,另外挂了一块 1T 的盘专门放镜像文件。这里强调两点:服务端必须是静态 IP,而且这个网段不能有别的 DHCP 在跑,否则客户端可能拿到别人发的地址,引导直接失败。
先把网络规划定死,别一边配一边改,改一处忘一处最容易出错:
| 项目 | 取值 | 说明 |
|---|---|---|
| 服务端网卡 | enp3s0 | 用ip a确认实际名字 |
| 服务端 IP | 192.168.100.10/24 | 静态,写进 netplan |
| 网段 | 192.168.100.0/24 | 建议单独划出来 |
| DHCP 地址池 | 192.168.100.100 - 192.168.100.200 | 避开服务端自身 |
| TFTP 根目录 | /srv/tftp | 建议独立分区或大盘 |
| HTTP 根目录 | /srv/www | 放镜像和应答文件 |
| 网关/路由下发给客户端 | 192.168.100.10(不给了就不给,见后文) | 看是否需要外网 |
静态 IP 用 netplan 配,Ubuntu 22.04/24.04 都适用:
# /etc/netplan/01-pxe.yaml network: version: 2 ethernets: enp3s0: dhcp4: false addresses: - 192.168.100.10/24改完执行sudo netplan apply,再用ip a确认地址生效。这里有个小坑:如果你之前用 NetworkManager 管过这块网卡,netplan 和 NM 可能打架,表现为地址配了但不生效。检查/etc/netplan/下有没有多余的文件,以及/etc/NetworkManager/conf.d/里有没有把这个网卡设为 unmanaged。
提示:实验阶段我建议把服务端和客户端接在同一台交换机上,物理上隔离,别接到办公室的大网里。等你确认整套流程跑通,再考虑要不要接入生产网。
3.2 引导文件从哪来:netboot 和 iPXE 两条路线
引导文件是 PXE 的"启动钥匙",这块最容易被版本差异坑到。Ubuntu 目前有两条拿引导文件的路子。
路线 A:传统 netboot(debian-installer 系)
这条路用的安装器是老的 d-i,体积小,非常适合做自动化安装,缺点是界面朴素、对新硬件支持略慢。文件从官方源的legacy-images/netboot目录取,以 jammy(22.04)为例:
sudo mkdir -p /srv/tftp cd /srv/tftp sudo wget http://archive.ubuntu.com/ubuntu/dists/jammy/main/installer-amd64/current/legacy-images/netboot/netboot.tar.gz sudo tar xzf netboot.tar.gz sudo chown -R dnsmasq:dnsmasq /srv/tftp解压后目录结构大致是这样:传统 BIOS 用的pxelinux.0加ldlinux.c32、pxelinux.cfg/菜单目录,UEFI 用的ubuntu-installer/amd64/grubx64.efi和grub/grub.cfg,以及真正安装用的ubuntu-installer/amd64/下的linux(内核)和initrd.gz。
路线 B:iPXE 链式加载(我更推荐)
iPXE 是个开源的 PXE 实现,比网卡自带的 PXE 固件强太多——它支持 HTTP 下载引导文件(比 TFTP 快几十倍)、支持脚本、支持菜单变量、支持 UEFI 和传统 BIOS 用同一套配置。Ubuntu 仓库里就有:
sudo apt install ipxe ls /usr/lib/ipxe/ # 通常能看到 ipxe.efi、undionly.kpxe、snponly.efi 等文件 sudo cp /usr/lib/ipxe/ipxe.efi /usr/lib/ipxe/undionly.kpxe /srv/tftp/ipxe.efi给 UEFI 机器用,undionly.kpxe给传统 BIOS 用。注意,仓库里的预编译版可能不带网卡驱动或者被剪裁过,如果客户端网卡比较新、加载后报"no such device",你得自己去 iPXE 官方源码编译一份带驱动的。我一般先用仓库版试,不行再编译。
用 iPXE 的好处是,客户端第一次通过 TFTP 拿到一个小小的 iPXE 程序(也就几百 KB),之后所有引导文件、内核、镜像全部走 HTTP,速度直接起飞。后面 3.4 节的菜单也会用 iPXE 脚本写,比 grub 配置顺手。
注意:不管你用哪条路线,传统 BIOS 和 UEFI 需要的引导文件名完全不同,配置文件必须靠
client-arch区分。UEFI 是option 93值为 7(x86_64 UEFI)或 9(x86_64 UEFI HTTP),传统 BIOS 是 0。这个数字写错一个,机器就卡在启动画面。
3.3 dnsmasq 配置文件逐行拆解
现在到核心了。装包:
sudo apt update sudo apt install dnsmasq sudo systemctl stop dnsmasq先停掉它,因为默认配置会监听所有接口、还会读/etc/resolv.conf,跟我们想要的行为冲突。Ubuntu 桌面版可能自带 systemd-resolved 占着 53 端口,如果你不需要这台机器做 DNS,可以在 dnsmasq 里关掉 DNS 功能。完整配置如下,我按块拆:
# /etc/dnsmasq.d/pxe.conf # 只监听 PXE 那块的网卡,千万别写 all interface=enp3s0 bind-interfaces # 关掉 DNS 功能,避免和 systemd-resolved 抢 53 端口 port=0 # DHCP 地址池,租期 12 小时 dhcp-range=192.168.100.100,192.168.100.200,255.255.255.0,12h # 下发路由器(也就是这台机器自己),不需要上网可以删掉这行 dhcp-option=3,192.168.100.10 # 下发 DNS,装系统后要能解析域名 dhcp-option=6,192.168.100.10 # 开启 TFTP enable-tftp tftp-root=/srv/tftp # 传统 BIOS 机器和 UEFI 机器给不同的引导文件 dhcp-match=set:bios,option:client-arch,0 dhcp-match=set:efi64,option:client-arch,7 dhcp-match=set:efi64,option:client-arch,9 dhcp-boot=tag:bios,undionly.kpxe dhcp-boot=tag:efi64,ipxe.efi # 已经跑过 iPXE 的机器再发 DHCP 时,直接给脚本地址 dhcp-userclass=set:ipxe,iPXE dhcp-boot=tag:ipxe,http://192.168.100.10/boot.ipxe # 详细日志,排查阶段必开,稳定后可去掉 log-dhcp log-queries逐块解释几个容易写错的地方。
interface和bind-interfaces必须成对出现。只写interface的话,dnsmasq 在某些情况下仍会监听通配地址;加上bind-interfaces才是真正的"只绑这块网卡"。如果你的服务端有多张网卡,这里写错会直接把 DHCP 发到不该发的网段,这就是 4.3 节那个事故的根源。
port=0是关掉 DNS 的写法。但如果你的客户端装系统时需要解析主机名(比如从网上拉包),那就别关,改成port=53并把 systemd-resolved 停掉,否则两个服务抢端口,dnsmasq 起不来。
dhcp-userclass=set:ipxe,iPXE这一行是 iPXE 链式加载的关键。iPXE 启动后会再发一次 DHCP 请求,并在请求里带上user-class=iPXE。我们用这个特征把它和第一次请求区分开:第一次给引导文件(ipxe.efi),第二次给脚本地址(boot.ipxe)。没有这行,机器就会陷入"不断加载 iPXE 又不断重启"的死循环,这个现象非常常见。
配置写好后:
sudo systemctl restert dnsmasq # 注意这里是 restart,别学我打错 sudo systemctl status dnsmasq sudo journalctl -u dnsmasq -f(上面那行restert是我故意留的错,第一次搭的时候我确实打错过,还纳闷为什么配置没生效。命令要写restart。)
3.4 HTTP 服务、iPXE 菜单和自动应答文件
TFTP 只管引导,真正的镜像和应答文件走 HTTP。服务端起个 nginx,配置极简:
sudo apt install nginx sudo mkdir -p /srv/www sudo ln -s /srv/www /var/www/html/pxe然后把 boot.ipxe 脚本放到/srv/www/下:
#!ipxe dhcp set base http://192.168.100.10/pxe menu Ubuntu 网络安装菜单 item --gap -- ------------------------------ item ubuntu2204 安装 Ubuntu 22.04 LTS (自动应答) item ubuntu2404 安装 Ubuntu 24.04 LTS (自动应答) item shell 进入 iPXE 命令行 choose --default ubuntu2404 --timeout 15000 target && goto ${target} :ubuntu2204 kernel ${base}/jammy/vmlinuz initrd=initrd.gz auto=true priority=critical url=http://192.168.100.10/pxe/jammy-preseed.cfg --- initrd ${base}/jammy/initrd.gz boot :ubuntu2404 kernel ${base}/noble/vmlinuz initrd=initrd.gz auto=true priority=critical url=http://192.168.100.10/pxe/noble-autoinstall.yaml --- initrd ${base}/noble/initrd.gz boot :shell shell这里我把 22.04 和 24.04 的内核、initrd 分别下载到/srv/www/jammy/和/srv/www/noble/。它们从哪来?netboot 包里就有,也可以从 live-server ISO 里把casper/vmlinuz和casper/initrd抠出来。用 live-server 的镜像装出来的是现在那种图形/半图形安装器,体验更接近你手工装系统,种子文件格式也不同(autoinstall的 YAML)。
给 22.04 的 preseed 片段示例:
# /srv/www/jammy-preseed.cfg d-i debian-installer/locale string en_US.UTF-8 d-i keyboard-configuration/xkb-keymap select us d-i netcfg/choose_interface select auto d-i netcfg/get_hostname string node d-i netcfg/get_domain string local d-i mirror/country string manual d-i mirror/http/hostname string archive.ubuntu.com d-i mirror/http/directory string /ubuntu d-i mirror/http/proxy string d-i passwd/root-login boolean false d-i passwd/user-fullname string ops d-i passwd/username string ops d-i passwd/user-password-crypted password $6$xxxxxxxx$yyyyyyyy d-i clock-setup/utc boolean true d-i time/zone string Asia/Shanghai d-i partman-auto/method string lvm d-i partman-auto/choose_recipe select atomic d-i partman/confirm_write_new_label boolean true d-i partman/choose_partition select finish d-i partman/confirm boolean true d-i pkgsel/include string openssh-server curl vim d-i grub-installer/only_debian boolean true d-i finish-install/reboot_in_progress note密码那行用openssl passwd -6 你的明文密码生成。别直接写明文,d-i 虽然也吃明文,但一旦配置泄露就麻烦了。
给 24.04 的 autoinstall YAML:
#cloud-config autoinstall: version: 1 locale: en_US.UTF-8 keyboard: layout: us network: version: 2 ethernets: all-en: match: name: "en*" dhcp4: true identity: hostname: node01 username: ops password: "$6$xxxxxxxx$yyyyyyyy" ssh: install-server: true allow-pw: true storage: layout: name: lvm match: size: largest packages: - curl - vim - htop late-commands: - curtin in-target -- timedatectl set-timezone Asia/Shanghai shutdown: rebootYAML 对缩进极其敏感,必须用空格,绝不能用 Tab。我见过有人复制粘贴的时候带进去一个 Tab,结果安装器报"invalid cloud-config",然后退回到交互模式,你还得手动点一遍。写完用python3 -c "import yaml,sys; yaml.safe_load(open('/srv/www/noble-autoinstall.yaml'))"校验一下语法,这个习惯能救命。
4. 客户端验证与故障排查实录
4.1 排错顺序:先用抓包确认"谁没说话"
客户端开机进网卡启动画面,然后就卡住了——这时候最忌讳的是瞎改配置。我的固定套路是,先确认链路哪一环断了,再对症下药。抓包命令如下:
# 看 DHCP 有没有来回 sudo tcpdump -i enp3s0 -n port 67 or port 68 # 看 TFTP 请求到达和响应 sudo tcpdump -i enp3s0 -n port 69 # 服务端日志,最直接 sudo journalctl -u dnsmasq -f判断逻辑很清晰:
- 完全看不到客户端的 DHCP 包:物理问题。网线、交换机端口、客户端没把网卡设成第一启动项、或者客户端和服务端不在同一个二层网络。
- 看到 DHCP Discover 但没有 Offer:服务端的 dnsmasq 没在听这块网卡,或者配置报错没起来。先
systemctl status dnsmasq看是否 active。 - 有 Offer、客户端也拿到了 IP,但 TFTP 超时:TFTP 根目录路径写错、权限不对、防火墙拦了 UDP 69、或者
filename指向的文件根本不存在。 - TFTP 拿到了引导文件,但引导程序加载后找不到内核:菜单配置里的路径写错,或者内核文件权限是 root 只有 dnsmasq 读不到(nginx 管的 HTTP 就没这问题,所以大文件走 HTTP 也规避了不少权限坑)。
这套顺序我用下来,八成的问题三步之内能定位。别一上来就全量重装,那是浪费时间。
4.2 常见问题速查表
| 现象 | 最可能的原因 | 快速验证方法 | 处理方式 |
|---|---|---|---|
| 客户端拿不到 IP | dnsmasq 没监听正确网卡 / 有第二个 DHCP 在抢 | tcpdump port 67看有没有外来 Offer | 检查interface,隔离网段 |
| 拿到 IP 后卡在 "PXE-E32: TFTP open timeout" | TFTP 根目录错 / 防火墙 | tftp 192.168.100.10手动 get 一个文件 | 放行 UDP 69,检查tftp-root |
| 报 "PXE-E53: No boot filename received" | dhcp-boot没匹配上 client-arch | 日志里看客户端 arch 值 | 补全dhcp-match |
| UEFI 机器卡在 grub 命令行 | 只给了 BIOS 的引导文件 | 看抓包里 client-arch 是不是 7 | 单独配 UEFI 分支 |
| 反复重启、循环加载 | iPXE 链式加载没做 user-class 区分 | 看是否重复收到同一下载请求 | 加dhcp-userclass=set:ipxe,iPXE |
| 内核加载后 panic 找不到 initrd | 内核和 initrd 版本不匹配 | 看是不是从不同版本目录混着拿的 | 同一版本目录一起取出 |
| 安装器启动但拉不到镜像 | HTTP 路径 404 / nginx 没起 | 浏览器直接访问那个 URL | 检查软链接和权限 |
| 安装到分区阶段卡死 | 自动化种子文件语法错 | 看屏幕有没有报 yaml/parse error | 用 yaml 校验器查一遍 |
| Secure Boot 报验证失败 | 引导程序没签名 | BIOS 里 Secure Boot 状态 | 关掉或换官方签名引导 |
| 装完系统连不上网 | 下发了错误的网关/DNS | 客户端ip a和ip r | 修正dhcp-option=3/6 |
4.3 几个只有踩过才知道的坑
第一个坑,也是最危险的:多网卡 +interface写错,把 DHCP 发到了办公网。
我当时那台服务端有两块网卡,一块接办公网(有现成 DHCP),一块接实验网。配置里我图省事写了interface=all,重启 dnsmasq 的瞬间,办公网里开始出现 192.168.100.x 的地址——因为我的地址池跟办公网段不冲突,客户端拿到后照样能用,但网关被我下成了 192.168.100.10,导致一大批同事莫名其妙上不了网。这个事故被发现是因为有人喊"我的机器 IP 变成 192.168 了"。教训是:只要是多网卡环境,interface必须精确到那一块,并且加bind-interfaces。现在我还养成了习惯,改完配置先ss -lunp | grep :67确认监听地址,再重启。
第二个坑:ufw 默认开着,TFTP 和 DHCP 全被拦。
Ubuntu 服务版默认 ufw 是不启用的,但如果你之前开过,规则就得补。DHCP 服务端用 UDP 67,TFTP 用 UDP 69,而且 TFTP 的数据传输是动态端口,光放行 69 不够。最省事的做法是只针对那块网卡放行:
sudo ufw allow in on enp3s0或者干脆把 PXE 那块网卡设为信任接口。别去精细放行一堆端口,TFTP 的动态端口能把你逼疯。
第三个坑:Ubuntu 24.04 的 netboot 目录和 22.04 不一样。
我一开始照着旧教程去 archive 上找 noble 的legacy-images/netboot,路径对不上,折腾半天。后来确认了版本差异:如果你走 live-server 路线,就别去翻 netboot 目录了,直接从 ISO 里抠casper/vmlinuz和casper/initrd,配 autoinstall 的 YAML。这也是为什么我在 3.4 节把两条路线分开写——版本越新,越倾向 live-server 那套。这个差异官方文档不会在显眼处提醒你,只能自己试出来。
第四个坑:TFTP 传大文件慢得离谱。
前面提过,但值得再强调。TFTP 是 UDP,没有拥塞控制和窗口,一个 100MB 的文件在百兆网里能传好几分钟,还容易丢包重传。解决办法就是别用 TFTP 传大文件。我甚至建议内核和 initrd 都走 HTTP,只把引导程序留在 TFTP 里。iPXE 天生支持 HTTP 的kernel和initrd指令,速度差别是数量级的。
第五个坑:客户端机器上启用了 Secure Boot,引导直接被拒。
UEFI 的 Secure Boot 会校验引导程序签名。iPXE 和自编译的 grub 通常没签名,会被拦。iPXE 官方提供签过名的ipxe.efi版本(在官方发布包里带signed字样),用它就能过;或者最简单,进 BIOS 把 Secure Boot 关掉。机房机器如果 BIOS 密码被前人设过,这一关可能比配置 PXE 还难,提前确认好权限。
5. 进阶玩法:从"能装"到"装得漂亮"
5.1 多版本共存与菜单定制
真实的运维环境里,你不可能只用一种 Ubuntu 版本。老业务跑 20.04,新业务上 24.04,测试环境还想试 26.04。所以菜单得支持多版本共存。做法很简单,在/srv/www/下按版本建子目录,把各自的内核和 initrd 分开放,然后在boot.ipxe里加 menu item 就行。
菜单还可以做得更聪明一点。比如让客户端根据 MAC 地址或者机器型号自动选择版本:iPXE 支持条件判断,可以用${net0/mac}变量来判断。我做过一个场景,同一批机器里有两种型号,一种需要额外加载网卡驱动,另一种不需要,就用 MAC 前缀在脚本里做了分支。菜单 timeout 也别设太短,默认 15 秒比较合适,机房里的机器启动慢,太短了还没来得及选就自动进了。另外 iPXE 的菜单支持子菜单,item --gap可以做分隔线,做出来比 grub 的菜单清爽。
5.2 配合 cloud-init 做真正的零接触安装
前面 3.4 节的 autoinstall 是个起点,真正要做到"插上电就走人",还得靠 cloud-init 把后期的初始化也一并做掉。比如你想让所有机器装完后自动注册到监控、自动挂载 NFS、自动配置内部源,这些都可以塞进 late-commands 或者 user-data 里。
具体怎么做?把 cloud-init 的 user-data 也放一份到 HTTP 上,安装器装完系统第一次启动时会去读它。常见的初始化动作包括:
#cloud-config package_update: true packages: - qemu-guest-agent - chrony write_files: - path: /etc/apt/sources.list.d/internal.list content: | deb http://192.168.100.10/ubuntu jammy main restricted runcmd: - systemctl enable --now chrony - echo "installed at $(date)" > /etc/install-stamp需要注意的是,cloud-init 的执行是分阶段的,runcmd里如果依赖网络,得确保network段配好了。还有,write_files写入的文件权限默认是 root,如果服务要以普通用户读,得显式加owner和permissions。这些细节不做自动化的时候感受不到,一旦上量,一个字段写错就是几十台机器全要重装。
5.3 稳定性和性能上的一些调优经验
搭好能跑是一回事,跑得稳是另一回事。几个我实际调过的点:
第一,把镜像和 TFTP 目录放在 SSD 或者内存盘上。TFTP 本身不怕读慢,怕的是多客户端并发时的磁盘 IO 抖动。如果你一次要装二十台,服务端的磁盘很容易成瓶颈,用 nginx 的sendfile加缓存能缓解不少。
第二,服务端 dnsmasq 的dhcp-lease-max要设够。默认值有时候不够用,并发装机时会出现部分客户端拿不到地址。设成比你实际机器数多一倍比较保险。
第三,日志别一直开着 debug 级别。log-dhcp在排查时是神器,但稳定运行后每台客户端启动都刷一堆日志,磁盘几周就写满。我的做法是排查完就把这行注释掉,只留正常日志。
第四,做个健康检查脚本。写个简单的 shell,每隔几分钟检查 dnsmasq 是否 active、HTTP 目录里的关键文件是否还在、TFTP 能否正常读取,异常就给你发通知。PXE 服务是那种"平时没人管、出事就全员停工"的服务,加个监控成本很低,收益很高。
最后分享一个我个人最看重的经验:先在一台淘汰的笔记本上把全流程跑通,再去机房。因为 PXE 的坑集中在"第一次",而第一次最可能碰上的是物理连接问题、网卡固件差异、BIOS 设置细节。在办公室用一台能被你随便重启的旧机器反复试,比在机房里弯腰插拔网线试高效太多。我那次在实验室用一台十年前的笔记本整整调了一下午,等到机房真机部署时,十二台机器一个半小时全部装完,中间只处理了一台 Secure Boot 没关的问题。那种"配置一次、批量收割"的感觉,才是这套方案真正的价值所在。