
在香港IDC做裸金属大规模上线部署时,PXE自动化装机是常规操作,但我多次遇到 PXE 网络引导过程中出现 TFTP 超时、initrd 卡死、kickstart 渲染失败等问题,尤其在服务器厂商异构、交换网络多跳复杂的环境下更是频繁。为了构建一个更稳定、可恢复且适用于异构服务器环境的启动流程,我最终选择将传统的 PXE + Kickstart 方案升级为 PXE 引导 + Cloud-Init 自动初始化 组合方案,并结合本地 HTTP 镜像仓库、元数据服务及分段 YAML 模板实现高可用部署体系。
这篇文章将从我在香港部署数十台物理机的实践出发,详解如何搭建 Cloud-Init + PXE 的自动化启动系统,并解决 PXE 引导过程中的各种稳定性问题。
一、问题复现:PXE 引导失败的常见场景
在部署过程中,我遇到以下典型的失败模式:
TFTP timeout / Retransmission failed
交换机端口没有开启 fast-forwarding,或中间设备不支持 UDP 大包,导致 TFTP 文件传输失败。
initramfs 获取不到 ks.cfg/kickstart 文件
DNS 不通、HTTP 源未准备好或网络切换后未自动更新路由,initramfs 卡在安装前步骤。
Kickstart 模板参数不适配硬件
相同模板无法同时支持 HPE、Dell 和 Supermicro,不同 RAID 卡、接口名等需要区分处理。
这类问题在传统 kickstart 里难以做动态判断和适配,最终我决定全面切换为 Cloud-Init 驱动自动化配置,并通过元数据服务灵活下发参数。
二、部署架构:PXE + iPXE + Cloud-Init 多层自动化
为了构建一个高可用的自动引导部署体系,我将整个流程拆解为:
- PXE/iPXE 网络引导阶段:统一内核与initrd加载
- 预安装阶段:装系统基础镜像(Cloud-Init Ready)
- 初始化阶段:Cloud-Init 拉取配置并执行初始化流程
- 后续交付阶段:加入配置管理平台(如 Ansible、SaltStack)
PXE 引导只是完成了系统基础安装,真正的灵活配置和业务适配都由 Cloud-Init 完成。
三、PXE 引导配置优化
我使用 dnsmasq 同时提供 DHCP + TFTP 功能,并配置如下:
# /etc/dnsmasq.d/pxe.conf
port=0
dhcp-range=192.168.100.10,192.168.100.200,12h
dhcp-boot=undionly.kpxe
enable-tftp
tftp-root=/var/lib/tftpboot
log-queries
iPXE 脚本加载方式:
#!ipxe
dhcp
set base-url http://192.168.100.1/images/ubuntu
kernel ${base-url}/vmlinuz boot=casper netboot=nfs ip=dhcp cloud-config-url=http://192.168.100.1/user-data/${mac}
initrd ${base-url}/initrd
boot
注意事项:
- cloud-config-url 指定每台机器的 Cloud-Init 配置源(支持按 MAC 或主板 SN 分发)
- 使用 HTTP 提供 kernel/initrd,比 TFTP 更快更可靠
- 所有引导文件统一挂载在本地 nginx,避免公网依赖
四、制作 Cloud-Init 镜像模板
我使用 cloud-image 作为基础,并内置 Cloud-Init:
# 下载 Ubuntu 镜像
wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
# 转为裸金属支持的 raw 镜像
qemu-img convert -f qcow2 -O raw focal-server-cloudimg-amd64.img /var/www/images/ubuntu.img
制作镜像后,通过自动化预设 seed.img:
cloud-localds user-data.img user-data.yaml meta-data.yaml
也可以通过网络元数据服务器(如 cloud-init-meta-server)动态生成。
五、构建 Cloud-Init 配置服务端(动态 user-data 分发)
我搭建了一个简单的 Flask 服务,通过请求中携带的 MAC 地址返回对应配置:
from flask import Flask, request, send_file
app = Flask(__name__)
@app.route('/user-data/<mac>')
def user_data(mac):
filename = f"/var/cloud-init-profiles/{mac}.yaml"
return send_file(filename, mimetype='text/yaml')
示例 user-data.yaml:
#cloud-config
hostname: node-${MAC}
users:
- name: devops
ssh-authorized-keys:
- ssh-rsa AAAAB3...
sudo: ['ALL=(ALL) NOPASSWD:ALL']
shell: /bin/bash
runcmd:
- apt update && apt install -y docker.io
- echo "Node ready." >> /var/log/init-success.log
部署好该服务后,iPXE 引导中的 URL 会自动拉取对应配置文件并执行。
六、处理常见 Cloud-Init 问题
配置拉取失败,Cloud-Init 停滞:
- 保证 network-online.target 正确配置
- 确认 DHCP 后可解析并连通配置服务器
Cloud-Init 被禁用(常见于非官方 ISO):
- 检查 /etc/cloud/cloud.cfg.d/ 目录下是否存在 disable-cloudinit.cfg
- 必要时重打包 initrd 添加 Cloud-Init 支持
MAC 与主机匹配混乱:
- 优化 BIOS 引导顺序,避免 PXE+非PXE多路径混淆
- 使用 iDRAC / IPMI 接口绑定 MAC 地址管理
七、部署总结与延伸
通过将传统 PXE 安装流程从单一 kickstart 模板转变为 PXE + Cloud-Init 解耦架构,我在香港裸金属服务器大规模部署中显著提高了稳定性和适配性:
- PXE 引导只负责系统安装,避免复杂逻辑堆叠
- Cloud-Init 完成全部配置初始化,实现跨平台统一初始化逻辑
- 通过 MAC 地址路由配置,实现“同构差异化”管理
- 结合 Ansible 做最终收敛,如加入 K8s 集群、注册监控平台
后续还可以对接如 MAAS、Foreman、Metal3 等更完善的 BareMetal 自动化管理平台,在香港机房内部署私有化裸金属控制面。
| 组件 | 版本或类型 |
|---|---|
| Cloud-Init | 23.1 |
| PXE 引导工具 | iPXE + dnsmasq |
| 操作系统 | Ubuntu 20.04 / 22.04 |
| HTTP 文件服务 | Nginx |
| 元数据服务 | 自研 Flask 服务 / cloud-localds |
| 目标服务器型号 | Supermicro X12 / Dell R640 等 |
这种方案在我管理的香港三层网络架构中已稳定运行,成功将部署失败率从最初的 20% 降至低于 1%。对于部署频繁的 SaaS 平台、云边混合架构尤其适用。











