ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PXE环境下Ubuntu 20.04无人值守批量安装全流程详解

PXE环境下Ubuntu 20.04无人值守批量安装全流程详解 1. PXE 启动全流程拆解从网卡通电到安装器加载1.1 PXE 不是黑魔法而是四段接力机房管理员或者运维兄弟们应该都有过这种经历几十台机器拿着 U 盘一台台装系统光插拔 U 盘和等待重启就折腾一整天中间还要防止哪台机器提前开机导致镜像写错。自从我搭好 PXE 批量安装环境之后从拆箱到进系统桌面几十台 Ubuntu 20.04 的机器半小时就能全部搞定。PXE 的全称是 Preboot eXecution Environment本质上就是让网卡在还没有操作系统的时候就能从网络拉取引导程序。整个链路依赖 DHCP 和 TFTP 两个核心协议还有一个 HTTP 或 NFS 服务来提供系统安装源。一次完整的 PXE 启动过程你可以理解为四段接力赛客户端网卡通电后广播发送 DHCP Discover 请求目的是弄到一个 IP 地址。DHCP 服务器回应 Offer除了分配 IP还附带两个关键参数next-serverTFTP 服务器的地址和 filename要下载的引导文件名。客户端拿到这些信息后通过 TFTP 协议去下载引导文件。引导文件被加载到内存后再由它去加载内核与 initrd最后拉起安装器或直接启动 Live 系统。这四步环环相扣任何一段出了问题表现都是网卡报错或者直接卡在某个黑屏界面。后面我会详细讲每一步的配置和排障尤其是那个“PXE boot failed”的提示几乎是所有新手第一个遇到的拦路虎。1.2 为什么选 Ubuntu 20.04 做批量安装Ubuntu 20.04 是 LTS 版本内核是 5.4硬件兼容性覆盖了绝大多数近五年的 x86 服务器和台式机。相比 22.04 和 24.0420.04 的安装器和内核参数相对保守对老旧网卡和磁盘控制器的驱动支持更稳而且它在 UEFI 和 Legacy BIOS 双模式下都有比较成熟的 PXE 引导方案不会出现某些新版本引导文件缺失的问题。另外20.04 的 apt 软件源无论是官方还是国内镜像都很稳定系统装完之后紧接着跑 apt update 和 apt upgrade 不会有兼容性灾难。对于电脑教室、实验室集群、工控机批量部署这类场景LTS 版本意味着三年以上的维护周期不会被频繁的版本升级打乱节奏。后面所有配置我都基于 Ubuntu 20.04 这个版本来写但原理同样适用于 18.04、22.04只要把镜像源路径和引导参数稍微改一下就能复用。2. 服务端搭建DHCP、TFTP、HTTP 三层缺一不可2.1 用 dnsmasq 一个服务搞定 DHCP 和 TFTPPXE 环境最少需要三个服务DHCP 分配地址、TFTP 传引导文件、HTTP 或 NFS 提供安装源。很多人习惯分开装 isc-dhcp-server、tftp-hpa、nginx 三个组件但我在实际部署中更推荐用 dnsmasq 一个进程同时承担 DHCP 和 TFTP再用 nginx 或 Python 的 http.server 提供安装源。好处显而易见服务少了一个排障链路短了一截dnsmasq 的配置也远比 isc-dhcp-server 好写。先看我这套环境的基础规划。服务端我用一台 Ubuntu 20.04 的物理机或虚拟机网卡 IP 固定为 192.168.1.10客户端网段是 192.168.1.0/24地址池为 192.168.1.100 到 192.168.1.200。dnsmasq 安装很简单一条命令搞定apt install dnsmasq然后编辑 /etc/dnsmasq.conf关键配置如下port0 interfaceeth0 bind-interfaces dhcp-range192.168.1.100,192.168.1.200,12h dhcp-option3,192.168.1.1 dhcp-option6,192.168.1.1 dhcp-matchset:efi-x86_64,option:client-arch,7 dhcp-boottag:efi-x86_64,grubx64.efi dhcp-bootpxelinux.0 enable-tftp tftp-root/var/lib/tftpboot逐行解释一下。port0 是关闭 dnsmasq 自己的 DNS 功能避免和现有 DNS 冲突。dhcp-range 指定了分配范围。dhcp-option3 是网关dhcp-option6 是 DNS如果客户端需要上网更新软件包这两个必须正确。dhcp-match 那一行是关键它根据 DHCP 请求里的 client-arch 字段来识别客户端固件类型值 7 代表 x86_64 UEFI如果客户端没有匹配到任何 tag就会走最后的 dhcp-bootpxelinux.0也就是 Legacy BIOS 的引导文件。这种双模式分流写法可以让你一套 TFTP 目录同时服务 UEFI 和 BIOS 机器省去很多麻烦。配置完记得重启服务并验证端口systemctl restart dnsmasq ss -ulnp | grep :67如果 67 端口正常监听就说明 DHCP 服务起来了。TFTP 用的是 69 端口dnsmasq 启用了 enable-tftp 之后会自动监听。2.2 HTTP 源与 NFS 源怎么选安装源的选择要分场景。Ubuntu 20.04 的 Desktop 版用的是 squashfs 文件系统你无法直接把它解压到磁盘上引导安装器必须通过 NFS 把整个 ISO 挂载出来安装器才能读取 casper 目录。Ubuntu Server 版则是基于 debian-installer它支持 http、ftp、nfs 等多种源其中最稳的是 HTTP。所以我通常备两套方案如果装的是 Ubuntu 20.04 Server用 HTTP 源配置 url 指向 nginx 目录即可。如果装的是 Ubuntu 20.04 Desktop用 NFS 源在引导参数里加 rootflags 和 nfsroot。NFS 服务端安装和导出目录的配置如下apt install nfs-kernel-server mkdir -p /srv/ubuntu2004 mount -o loop ubuntu-20.04.6-desktop-amd64.iso /srv/ubuntu2004 echo /srv/ubuntu2004 192.168.1.0/24(ro,sync,no_subtree_check,no_root_squash) /etc/exports exportfs -ra如果你用的是 HTTP 源那就更简单。挂载 ISO 到 nginx 的网站根目录下面比如 /var/www/html/ubuntu2004然后确保客户端能通过 http://192.168.1.10/ubuntu2004/ 访问到里面的 dists 和 pool 目录。这里有个容易踩的坑HTTP 源在 Desktop 版安装时需要在引导参数里加上 urlhttp://192.168.1.10/ubuntu2004/ 以及 toram 或 不指定 toram 的选项。不加 url 的话安装器会默认找本地 CD-ROM找不到就报错退出。3. 引导文件与自动应答让安装全程无人值守3.1 Legacy BIOS 的 pxelinux 配置先讲 Legacy BIOS 的引导链路。执行 PXE 客户端下载的第一个文件是 pxelinux.0它来自 syslinux 包。安装和准备目录apt install pxelinux mkdir -p /var/lib/tftpboot/pxelinux.cfg cp /usr/lib/PXELINUX/pxelinux.0 /var/lib/tftpboot/ cp /usr/lib/syslinux/modules/bios/ldlinux.c32 /var/lib/tftpboot/pxelinux.0 启动后会去找 pxelinux.cfg 目录下的配置文件。匹配顺序是按客户端的 MAC 地址用下划线替代冒号、IP 十六进制形式最后回落到 default 文件。所以你可以用 default 文件做通用配置也可以为特定机器建单独的配置。default 文件内容大概长这样DEFAULT install PROMPT 0 TIMEOUT 30 LABEL install MENU LABEL Install Ubuntu 20.04 Server KERNEL ubuntu-install/vmlinuz INITRD ubuntu-install/initrd APPEND ipdhcp urlhttp://192.168.1.10/ubuntu2004/ preseed/urlhttp://192.168.1.10/preseed/ks.cfg这里 KERNEL 和 INITRD 指向的文件是从 Ubuntu 20.04 Server ISO 的 install/ 目录里复制出来的 vmlinuz 和 initrd。注意Desktop ISO 里没有这两个文件它只有 casper/vmlinuz 和 casper/initrd。如果你要装 Desktop 版路径和 APPEND 参数会不一样需要额外配置 NFS。3.2 UEFI 模式下的 grub 引导配置UEFI 的引导链路和 BIOS 完全不同PXE 客户端下载的不是 pxelinux.0而是 grubx64.efi。这个文件来自 grub-efi-amd64-signed 或 shim-signed 包。准备 UEFI 引导文件apt install grub-efi-amd64-signed shim-signed mkdir -p /var/lib/tftpboot/efi cp /usr/lib/shim/shimx64.efi.signed /var/lib/tftpboot/efi/shimx64.efi cp /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed /var/lib/tftpboot/efi/grubx64.efi不过在实际部署中我更喜欢直接用 grubx64.efi 而不是 shim因为 shim 会额外验证签名配置不当反而导致启动失败。dnsmasq 里 dhcp-boottag:efi-x86_64,grubx64.efi就会让 UEFI 客户端去 TFTP 根目录找 grubx64.efi。UEFI 的 grub 会加载 /var/lib/tftpboot/grub/grub.cfg注意这个路径和 BIOS 的 pxelinux.cfg 不一样。grub.cfg 的写法如下set default0 set timeout5 menuentry Install Ubuntu 20.04 Server { linux /ubuntu-install/vmlinuz ipdhcp urlhttp://192.168.1.10/ubuntu2004/ preseed/urlhttp://192.168.1.10/preseed/ks.cfg initrd /ubuntu-install/initrd }UEFI 的 grub 和 BIOS 的 pxelinux 在参考文献写法上有一个容易搞混的地方pxelinux 用 KERNEL、INITRD、APPEND 三个关键字grub 用 linux、initrd 两个命令。一旦写反或者混用客户端会在加载内核时报错。3.3 ks.cfg 自动应答文件详解无人值守的核心是 preseed 文件Server 版或 user-data 文件Desktop 版用 cloud-init。Server 版用 preseed 最简单因为 debian-installer 原生支持。下面这份是我生产环境用的一份精简配置包含分区、源、软件包选择、post 脚本# 系统语言和键盘 d-i debian-installer/locale string en_US.UTF-8 d-i keyboard-configuration/xkb-keymap select us d-i time/zone string Asia/Shanghai # 网络交给 DHCP 或者用静态 IP d-i netcfg/choose_interface select auto d-i netcfg/dhcp_timeout string 30 # 镜像源 d-i mirror/country string manual d-i mirror/http/hostname string 192.168.1.10 d-i mirror/http/directory string /ubuntu2004 d-i mirror/http/proxy string # 磁盘分区清空所有分区并自动分区 d-i partman-auto/disk string /dev/sda d-i partman-auto/method string regular d-i partman-auto/choose_recipe select atomic d-i partman-partitioning/confirm_write_new_label boolean true d-i partman/choose_partition select finish d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true # 用户名和密码 d-i passwd/root-login boolean false d-i passwd/user-fullname string admin d-i passwd/username string admin d-i passwd/user-password password admin123 d-i passwd/user-password-again password admin123 # 软件包选择 tasksel tasksel/first multiselect ubuntu-server d-i pkgsel/include string openssh-server vim curl # 禁止安装推荐软件包减少体积 d-i pkgsel/install-recommends boolean false # 最后执行脚本 d-i preseed/late_command string \ in-target apt-get update \ in-target apt-get install -y nvidia-driver-520如果装的是 UEFI 机器分区表需要额外处理。atomic 自动分区方案默认可能不会创建 ESP 分区导致安装完成后无法启动。这种情况下我建议改成自定义分区脚本d-i partman-auto/expert_recipe string \ boot-root :: \ 100 50 100 ext4 \ $primary{ } $bootable{ } \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ 300% 1000 300% ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ . d-i partman-auto/choose_recipe select boot-root d-i partman-basicfilesystems/choose_label string gpt d-i partman-basicfilesystems/default_label string gpt d-i partman-partitioning/confirm_write_new_label boolean true或者更省事的办法是加一条d-i partman-efi/confirm_nooverwrite boolean true让安装器自动创建 EFI System Partition。我在实践中更倾向于不加任何人为分区限制直接 atomic 加 ESP 自动创建这样最不容易出错。4. Legacy 与 UEFI 双模式实践解决 boot failed 的首选思路4.1 为什么会出现“PXE boot failed”提示很多人在搭好环境后客户端开机却卡在类似这样的提示Setup Notice: PXE over IPv4 (88-a4-c2-22-b5-97) boot failed. Would you like to ...先解释一下这个提示的含义。88-a4-c2-22-b5-97 是客户端的 MAC 地址。出现 “boot failed” 意味着 UEFI 固件已经发送了 DHCP 请求但没能成功下载引导文件或者下载了引导文件但无法执行。换句话说PXE 的前两步可能通了第三步或第四步断了。常见原因有三个DHCP 服务器没有下发 filename 参数或者下发的文件名在 TFTP 目录里找不到。比如 dnsmasq 配置里写了 grubx64.efi但 TFTP 根目录下根本没有这个文件。TFTP 网络不通。很多机房会把客户端和服务端放在不同 VLAN中间交换机没有放通 TFTP 的 69 端口或者服务端防火墙没放行 UDP 69。下载的引导文件类型不匹配。UEFI 客户端下发了 pxelinux.0或者 BIOS 客户端下发了 grubx64.efi这两种文件无法交叉执行就会报 boot failed。排查的时候我会在服务端用 tcpdump 抓包看 DHCP Offer 和 TFTP RRQ 请求是否到达tcpdump -i eth0 port 67 or port 69 -n -e如果抓包能抓到 DHCP Discover 但没有后续的 TFTP RRQ说明客户端拿到了 IP 但没拿到正确的 filename回去查 dnsmasq 的 dhcp-boot 配置。如果抓到了 TFTP RRQ但客户端还是报 boot failed那大概率是文件名不对或者文件本身损坏直接在 TFTP 根目录里 ls 一下确认即可。4.2 一套 TFTP 目录同时服务两种固件最好的双模式兼容方案就是在 dnsmasq 里做 tag 分流。前面的配置已经体现了这一点dhcp-matchset:efi-x86_64,option:client-arch,7 dhcp-boottag:efi-x86_64,grubx64.efi dhcp-bootpxelinux.0注意一点不同的 UEFI 固件厂商client-arch 值可能不一样。标准值是 7 代表 x86_64 UEFI但有些旧版固件会返回 9x86_64 UEFI with HTTP有些会返回 16 或其他值。如果你发现某些机器走了 BIOS 引导但明明是 UEFI可以在 dnsmasq 里同时匹配多个值dhcp-matchset:efi-x86_64,option:client-arch,7 dhcp-matchset:efi-x86_64,option:client-arch,9 dhcp-boottag:efi-x86_64,grubx64.efi4.3 按 MAC 地址绑定特定启动项在电脑教室或测试环境里经常会遇到不同型号的机器混用。有些机器是 UEFI有些是 Legacy BIOS甚至有些是 32 位的 UEFI。如果你不想让 dnsmasq 的全局配置管全部机器可以在 pxelinux.cfg 里按 MAC 建配置文件文件名是/var/lib/tftpboot/pxelinux.cfg/01-88-a4-c2-22-b5-97注意文件名是大写的 MAC 地址。同时在 dnsmasq 里给特定机器分配固定 IPdhcp-host88:a4:c2:22:b5:97,192.168.1.150这样那台机器在 PXE 启动时会优先加载以它 MAC 命名的配置文件加载特定内核参数或安装源。这个技巧在混合架构机房非常实用。5. 装机效率再进阶驱动、离线包与镜像源加速5.1 从 U 盘到 PXE 再到网刻很多人会问市面上那么多PXE网刻工具比如奇东锐腾这类一键网刻软件为什么还要自己搭环境网刻工具适合纯 Windows 环境的整机房克隆操作门槛低但缺点也很明显它依赖特定的 DHCP 和 TFTP 实现不支持灵活的自动应答难以做跨硬件平台的镜像适配而且对 Linux 的支持非常有限。自己搭 PXE 的优势在于完全可控、跨平台、可以和配置管理系统结合。尤其是 Ubuntu 20.04 这种需要特定分区方案、特定驱动注入的场景PXE 加自动应答是唯一能保证批量一致性的方案。我见过很多用网刻工具装 Linux 翻车的案例最常见的就是镜像里打包了第一台机器的网卡驱动和 hostname结果克隆出来的每一台机器网络都不通。5.2 NVIDIA 驱动和本地 whl 包的批量部署装机完成只是第一步装完之后的驱动和软件部署才是大头。我之前在机房批量装过带 NVIDIA 显卡的机器Ubuntu 20.04 装完后默认用的是 nouveau 驱动屏幕分辨率混乱GPU 计算性能也跑不起来。处理方案有两层第一层在 ks.cfg 里写 late_command装完系统直接在目标环境里装驱动d-i preseed/late_command string \ in-target apt-get install -y nvidia-driver-520但这种方法依赖网络源而机房环境经常是内网没法访问外网。所以更稳妥的做法是下载离线 deb 包放到 HTTP 源目录里post 脚本里用 dpkg -i 安装d-i preseed/late_command string \ in-target dpkg -i /media/cdrom/pool/nvidia/*.debPython 环境的批量部署也是同理。如果你想给所有机器装同样的 Python 依赖提前用 pip download 拉取 whl 包到本地pip download -d /srv/pip-packages -r requirements.txt然后客户端机器上执行pip install --no-index --find-links/srv/pip-packages -r requirements.txt这两个思路本质上是同一件事把对外网的依赖在服务端提前解决客户端只访问内网源既快又稳。5.3 镜像源加速与离线 rootfs 准备Ubuntu 20.04 的安装源和 apt 源如果都走官方地址速度会非常难受尤其是在几十台机器同时安装的时候。我的做法是在服务端提前缓存一份 Ubuntu 20.04 的完整仓库或者直接通过 rsync 从清华镜像同步关键目录然后让客户端全部指向内网地址。清华镜像的 Ubuntu 仓库地址形如https://mirrors.tuna.tsinghua.edu.cn/ubuntu/如果你需要离线 rootfs直接从清华镜像下载 ubuntu-base 也是可以的。20.04 的 rootfs 包名称是ubuntu-base-20.04.6-base-amd64.tar.gz下载后用 chroot 装完基础软件再打包分发在无外网环境里批量制作容器或系统盘时效率极高。这里有个小技巧下载时注意 URL 路径深度Ubuntu 的 rootfs 并不在 releases 目录下而是在 ubuntu-base/releases/ 下面找不到文件的通常都是路径搞错了。6. 排障实录PXE 环境最常见的 10 个坑6.1 抓包定位“三不通”问题PXE 排障最忌讳瞎猜。遇到问题我通常是先看三层通不通、IP 有没有分配到再抓包看 TFTP 请求有没有到达服务端。下面表格是几类最常见问题和解决思路现象可能原因排查方向DHCP Discover 无响应dnsmasq 未启动、端口被占用ss -ulnp 检查 67 端口获取到 IP 但 TFTP 超时防火墙拦 69 端口、服务端 TFTP 未启用tcpdump 看 69 端口流量下载引导文件 404TFTP 根目录路径不对、文件名大小写错误对比 dnsmasq 配置和实际文件grub 报 file not foundgrub.cfg 里路径写错确认 TFTP 根目录下文件路径内核加载后黑屏initrd 路径错误或内核参数写错检查 vmlinuz 和 initrd 的一致性preseed 文件不生效url 参数被忽略或文件名错误确认 preseed url 可访问且内容合法我特别想强调 TFTP 根目录的权限问题。dnsmasq 默认以 nobody 用户运行 TFTP如果 /var/lib/tftpboot 目录下的文件权限不是所有人可读客户端就会下载失败。最简单的处理chmod -R 644 /var/lib/tftpboot/ find /var/lib/tftpboot -type d -exec chmod 755 {} \;6.2 安装器启动后找不到安装源内核成功加载了安装器也起来了但卡在 “No common CD-ROM drive was found” 或者提示无法访问镜像源。这种情况十有八九是 HTTP 或 NFS 源配置有问题。HTTP 源要确认 nginx 服务是否在跑、路径是否正确。很多人会在这时候用 ps aux 看进程还在但实际网页打不开我用 curl 验证curl -I http://192.168.1.10/ubuntu2004/dists/如果返回 403 或 404就去检查目录权限和 nginx 的 location 配置。NFS 源则要看 exportfs -v 输出的导出选项里有没有 ro,no_root_squash并且确认客户端内核支持 NFS v3 还是 v4。20.04 默认的内核支持 NFS v4在 ks.cfg 里指定 nfs://192.168.1.10/srv/ubuntu2004 时注意不要写错版本参数。6.3 DHCP 地址频繁变化导致装机中断我的第一套 PXE 环境遇到过一种诡异问题安装过程进行到 30% 左右突然断掉重试之后还是同样位置失败。后来抓包发现dnsmasq 默认的 lease 时间只有 2 小时客户端在安装过程中 DHCP 租约到期后重新续约IP 没变但网关、DNS 这些 option 重新下发后某些安装器组件会重新读网络配置导致链路中断。解决方法是把 dhcp-range 后面的租期改长或者直接在地址池里排除掉安装服务器自己的 IPdhcp-range192.168.1.100,192.168.1.200,12h12 小时对于一次完整装机完全够用。如果你用的是 isc-dhcp-server对应参数是 default-lease-time 43200。6.4 装完系统后网卡起不来这是 ubuntu 20.04 网络相关的经典坑安装时用的是 DHCP装完重启后却发现网卡没有获取到 IP。原因在于 Netplan 在安装时生成的配置文件可能写了错误的 interface name或者 cloud-init 没有正确清理安装时的网络配置。我的做法是在 ks.cfg 的 late_command 里强制重写 netplan 配置d-i preseed/late_command string \ in-target bash -c echo network: {config: disabled} /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg \ in-target bash -c cat /etc/netplan/01-netcfg.yaml EOF network: version: 2 ethernets: all-eth: match: name: e* dhcp4: true EOF这样不管网卡名是 ens33、enp2s0 还是 eth0都能用通配符匹配上并且关闭 cloud-init 对网络配置的干扰。6.5 新机器网卡无法 PXE 启动有些新机器的网卡默认关闭了 PXE 启动选项或者 UEFI 固件里没有开启 Network Boot。这个问题不属于服务端配置范畴但却是批量装机时最常见的物理层故障。建议在采购初期就约定 BIOS 设置标准开启 UEFI 网络栈IPv4 PXE、关闭 Secure Boot或者添加自己的引导签名、启动顺序里把 Network Boot 放到第一位。如果机器太多不可能一台台手动设置可以考虑用 BIOS 的远程管理工具如 Dell iDRAC、HP iLO批量修改启动项。我试过用 racadm 脚本对整批机器设置一次性启动设备配合 PXE 环境效率能再上一个台阶。7. 实操心得与后续扩展7.1 从固定机房走向动态装机把 PXE 环境稳定跑起来之后你会发现它的用途远不止装机。Ubuntu 20.04 的 vmlinuz 和 initrd 配合不同引导参数可以进入 Live 模式做系统救援也可以在内存里跑一个临时环境去修复磁盘分区。我有一次帮同事恢复一台忘记密码的服务器就是通过 PXE 引导进 Live 系统chroot 挂载磁盘后重置密码全程不到十分钟。后续可以做自动化扩展。比如在 DHCP 里给不同 MAC 段分配不同的 preseed 文件实现 Windows 机器走网刻、Linux 机器走 PXE 的分流或者在 TFTP 根目录里同时维护多版本内核和 initrdgrub 菜单里选择不同系统入口。这些都是在现有环境上做加法不必重构架构。7.2 我踩过最深的坑路径大小写最后分享一个导致我搞了整整一个下午的问题。我把 grub.cfg 里的 kernel 路径写成了 /Ubuntu-install/vmlinuz而实际目录是 /ubuntu-installLinux 路径大小写敏感TFTP 服务同样严格判定大小写结果就是客户端下载文件时报 404grub 一直报 file not found。这类问题用眼睛检查很难发现尤其是当你习惯 Windows 路径不区分大小写的时候。后来我养成一个习惯所有 TFTP 路径统一用小写文件复制完之后用 curl 或 tftp 命令实际拉一遍验证避免上线时翻车。tftp 192.168.1.10 -c get ubuntu-install/vmlinuz能正常下载再进下一步不能就赶紧查权限和路径。这套 PV环境跑了两年现在机房里新增机器从拆箱到系统就绪控制在半小时内对比以前一台台插 U 盘安装的日子节省的时间难以估量。
返回列表