第 27/30 天(重访进阶版)
引言
在上篇中,我们搭建了 PXE 服务器、编写了 Preseed 应答文件、配置了 Cloud-init 初始化脚本,完成了从零到一的自动化部署入门。但生产环境的要求远不止于此——UEFI 安全启动如何处理?如何将 Preseed 嵌入 Packer 镜像构建流水线?Cloud-init 的 disk_setup 和 growpart 如何自动扩展磁盘?面对数百台服务器的集群,人力逐台配置已不再可能。
本重访篇将深入生产级部署的三大核心场景:
- PXE 进阶:UEFI 双模式引导、MAAS 裸金属管理、大规模并发部署优化
- Preseed 高级技巧:自定义分区方案、late_command 脚本链、Packer 镜像构建集成
- Cloud-init 深度实战:磁盘自动扩容、网络配置模板、Ansible 联动编排
一、PXE 生产级进阶:UEFI 双模式与大规模部署
1.1 UEFI + BIOS 双模式 PXE 服务器
现代服务器大多使用 UEFI 固件,传统的 BIOS PXE 方案(pxelinux.0)不再适用。一个健壮的 PXE 服务器必须同时支持两种模式。
目录结构设计:
/srv/tftp/
├── pxelinux.cfg/ # BIOS 引导配置
│ └── default
├── grub/ # UEFI 引导配置
│ ├── grub.cfg
│ └── fonts/
├── ubuntu/ # 内核和 initrd
│ ├── linux
│ └── initrd.gz
├── pxelinux.0 # BIOS 引导文件
├── ldlinux.c32
├── grubx64.efi # UEFI 引导文件
└── shimx64.efi # 安全启动 shim
UEFI 的 DHCP 配置差异:
# /etc/dhcp/dhcpd.conf — 支持 UEFI + BIOS 双模式
option arch code 93 = unsigned integer 16; # DHCP option 93 标识客户端架构
subnet 192.168.100.0 netmask 255.255.255.0 {
range 192.168.100.100 192.168.100.200;
option routers 192.168.100.1;
option domain-name-servers 8.8.8.8, 1.1.1.1;
next-server 192.168.100.10; # TFTP 服务器
# 根据客户端架构返回不同引导文件
if option arch = 00:00 {
# BIOS (x86 legacy)
filename "pxelinux.0";
} elsif option arch = 00:07 {
# UEFI x64
filename "grubx64.efi";
} elsif option arch = 00:09 {
# UEFI x64 (安全启动)
filename "shimx64.efi";
} else {
# 默认 UEFI
filename "grubx64.efi";
}
}
UEFI GRUB 引导菜单:
# /srv/tftp/grub/grub.cfg
set default="0"
set timeout=30
if loadfont $prefix/fonts/unicode.pf2; then
set gfxmode=auto
insmod efi_gop
insmod efi_uga
insmod gfxterm
terminal_output gfxterm
fi
menuentry "Ubuntu 24.04 LTS 自动安装 (UEFI)" {
linux /ubuntu/linux auto=true priority=critical url=http://192.168.100.10/preseed/ubuntu-auto.seed
initrd /ubuntu/initrd.gz
}
menuentry "Ubuntu 24.04 LTS 手动安装" {
linux /ubuntu/linux
initrd /ubuntu/initrd.gz
}
menuentry "从本地硬盘启动" {
exit
}
获取 UEFI 引导文件:
# 安装 UEFI 引导文件 (Ubuntu 24.04)
apt install -y grub-efi-amd64-signed shim-signed
# 复制到 TFTP 目录
cp /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed /srv/tftp/grubx64.efi
cp /usr/lib/shim/shimx64.efi.signed /srv/tftp/shimx64.efi
# 复制 GRUB 字体(用于中文菜单显示)
mkdir -p /srv/tftp/grub/fonts
cp /usr/share/grub/unicode.pf2 /srv/tftp/grub/fonts/
⚠️ 生产贴士:安全启动(Secure Boot)启用时,必须使用
shimx64.efi+grubx64.efi组合。如果遇到Security Violation错误,检查 BIOS 中是否关闭了 Secure Boot,或使用签名的引导文件。
1.2 大规模并发部署优化
当同时部署 50+ 台服务器时,单一 TFTP 和 HTTP 服务器会成为瓶颈。
优化策略:
# 1. TFTP 并发优化 — 增加 tftp-hpa 线程数
# /etc/default/tftpd-hpa
TFTP_OPTIONS="--secure --create --blocksize 1468 --timeout 30 --retries 5 --port-range 40000-40050"
# 2. HTTP 源使用轻量级替代方案(带宽控制)
# 使用 Nginx 限制并发连接数
cat > /etc/nginx/sites-available/ubuntu-install << 'EOF'
server {
listen 80;
root /srv/ubuntu-install;
autoindex on;
# 限制并发连接,防止带宽打满
limit_conn_zone $binary_remote_addr zone=install:10m;
limit_conn install 10;
# 限制传输速度(每连接 10MB/s)
limit_rate 10m;
# 启用 sendfile 和 gzip 静态压缩
sendfile on;
tcp_nopush on;
gzip_static on;
}
EOF
# 3. 使用 BitTorrent 或 Zsync 分发 ISO(超大规模 200+ 台)
apt install -y zsync
# 从 Ubuntu 镜像站获取 .zsync 文件
zsync http://releases.ubuntu.com/24.04/ubuntu-24.04.1-live-server-amd64.iso.zsync
1.3 MAAS:Ubuntu 裸金属管理平台
对于超大规模(100+ 台)的物理机集群,手动配置 PXE 已经不够用了。Canonical 官方的 MAAS(Metal as a Service) 提供了完整的裸金属生命周期管理:
# 安装 MAAS
sudo snap install maas --channel=3.5/stable
# 初始化 MAAS(需要 PostgreSQL 数据库)
sudo maas init region+rack
--database-uri "postgres://maas:password@localhost/maas"
--maas-url "http://192.168.100.10:5240/MAAS"
# 创建管理员账户
sudo maas createadmin --username admin --password 'YourPassword' --email admin@example.com
# 导入 Ubuntu 镜像
sudo maas admin boot-resources import
# 通过 CLI 注册节点并开始部署
maas admin machines create
architecture="amd64/generic"
mac_addresses="aa:bb:cc:dd:ee:ff"
power_type="ipmi"
power_parameters_power_address="192.168.100.200"
power_parameters_power_user="admin"
power_parameters_power_pass="password"
MAAS 与 PXE 的关系:MAAS 底层仍然使用 PXE 进行网络引导,但它提供了 Web UI + API + DHCP 自动管理,让裸金属部署像云服务器一样简单。
二、Preseed 高级技巧:从入门到生产
2.1 自定义分区方案(LVM + 加密)
手动编辑 Preseed 分区方案是最容易出错的地方,但也最值得投入时间:
# /srv/ubuntu-install/preseed/ubuntu-lvm-encrypted.seed
# ---- 分区部分 ----
# 使用 LVM 分区方案
d-i partman-auto/method string lvm
# 自定义分区配方(recipe)
d-i partman-auto/choose_recipe select custom-recipe
# 定义分区方案
d-i partman-auto/expert_recipe string
custom-recipe ::
1024 1024 1024 ext4
$primary{ } $bootable{ }
method{ format } format{ }
use_filesystem{ } filesystem{ ext4 }
mountpoint{ /boot }
label{ boot }
.
4096 4096 4096 linux-swap
method{ swap } format{ }
label{ swap }
.
10240 100000 100000000 ext4
method{ lvm } lv_name{ root }
format{ } use_filesystem{ } filesystem{ ext4 }
mountpoint{ / }
label{ root }
.
10240 50000 500000 ext4
method{ lvm } lv_name{ data }
format{ } use_filesystem{ } filesystem{ ext4 }
mountpoint{ /data }
label{ data }
.
# 加密 LVM(企业安全要求)
d-i partman-crypto/passphrase string YourEncryptionPassphrase
d-i partman-crypto/passphrase-again string YourEncryptionPassphrase
d-i partman-crypto/confirm_nooverwrite boolean true
# 确认分区写入
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
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
2.2 late_command 脚本链:安装完成后的自动化
late_command 是 Preseed 中最强大的功能之一——它在安装程序完成、系统首次重启之前执行,此时磁盘已分区、用户已创建,但系统尚未进入用户态:
# 在 Preseed 末尾添加
d-i preseed/late_command string
# 1. 下载并执行自定义脚本
in-target wget -O /tmp/post-install.sh http://192.168.100.10/scripts/post-install.sh &&
in-target bash /tmp/post-install.sh;
# 2. 添加 SSH 公钥
in-target mkdir -p /home/ubuntu/.ssh &&
in-target chmod 700 /home/ubuntu/.ssh &&
in-target wget -O /home/ubuntu/.ssh/authorized_keys http://192.168.100.10/keys/admin.pub &&
in-target chmod 600 /home/ubuntu/.ssh/authorized_keys &&
in-target chown -R ubuntu:ubuntu /home/ubuntu/.ssh;
# 3. 配置 APT 源为国内镜像
in-target sed -i 's|archive.ubuntu.com|mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list;
# 4. 安装监控 Agent
in-target apt-get update &&
in-target apt-get install -y prometheus-node-exporter telegraf;
# 5. 写入主机标识
in-target sh -c 'echo "Deployed by PXE at $(date)" > /etc/deployment-info'
注意: late_command 中的每个命令用 ; 或 && 分隔。如果命令包含空格,需要整体用 in-target 包裹。对于复杂的多行脚本,建议将脚本放在 HTTP 服务器上:
# 更简洁的写法:只调用一个远程脚本
d-i preseed/late_command string
in-target wget -q -O /tmp/deploy.sh http://192.168.100.10/scripts/deploy.sh &&
in-target bash /tmp/deploy.sh
2.3 Packer + Preseed:构建黄金镜像
使用 Packer 可以构建预配置的 Ubuntu 镜像,后续部署时无需再执行 Preseed 的安装流程,直接使用预构建镜像即可:
# ubuntu-golden.pkr.hcl
variable "ubuntu_version" {
default = "24.04"
}
source "qemu" "ubuntu" {
iso_url = "https://releases.ubuntu.com/${var.ubuntu_version}/ubuntu-${var.ubuntu_version}-live-server-amd64.iso"
iso_checksum = "sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
memory = 4096
disk_size = "40G"
qemu_binary = "qemu-system-x86_64"
ssh_username = "ubuntu"
ssh_password = "ubuntu"
ssh_timeout = "30m"
shutdown_command = "echo 'ubuntu' | sudo -S shutdown -P now"
# 使用 Preseed 自动安装
boot_command = [
"<tab><wait>",
"auto=true ",
"priority=critical ",
"url=http://192.168.100.10/preseed/packer-ubuntu.seed",
"<enter>"
]
boot_wait = "10s"
}
build {
sources = ["source.qemu.ubuntu"]
# 安装基础软件
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y docker.io prometheus-node-exporter nginx",
"sudo systemctl enable docker",
"sudo apt-get clean",
"sudo dd if=/dev/zero of=/zero bs=1M || true",
"sudo rm -f /zero"
]
}
# 写入版本信息
provisioner "file" {
source = "version.txt"
destination = "/tmp/version.txt"
}
}
构建完成后,可以将镜像上传到 MAAS 或网络存储,后续部署直接从预构建镜像启动,省去安装过程:
# 构建镜像
packer build ubuntu-golden.pkr.hcl
# 转换为 MAAS 可用格式
qemu-img convert -f qcow2 -O raw output-qemu/packer-ubuntu /srv/images/ubuntu-golden.raw
三、Cloud-init 深度实战:企业级场景
3.1 磁盘自动扩容(growpart + disk_setup)
云环境中,根磁盘经常需要自动扩容。Cloud-init 的 growpart 和 disk_setup 模块可以自动处理:
#cloud-config
# 自动扩展主分区
growpart:
mode: auto
devices:
- "/"
ignore_growroot_disabled: false
# 配置额外的数据盘
disk_setup:
/dev/vdb:
table_type: gpt
layout:
- [100, 83] # 100% 空间,Linux 分区类型
overwrite: false
fs_setup:
- device: /dev/vdb1
filesystem: ext4
label: data
overwrite: false
mounts:
- [/dev/vdb1, /data, ext4, "defaults,noatime,nodiratime", "0", "2"]
# 创建目录并设置权限
runcmd:
- [mkdir, -p, /data]
- [chown, ubuntu:ubuntu, /data]
- [systemctl, enable, --now, docker]
💡 实战要点:
growpart依赖于cloud-guest-utils包,如果镜像中未安装,需要在package_update: true和packages中提前安装。disk_setup和fs_setup仅在首次启动时生效,已格式化的磁盘不会重复执行。
3.2 网络配置模板(Netplan 集成)
通过 Cloud-init 的 network-config 实现复杂的网络拓扑:
# 网络配置文件(单独的 network-config,不是 user-data 的一部分)
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 192.168.100.50/24
gateway4: 192.168.100.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
routes:
- to: 10.0.0.0/8
via: 192.168.100.254
eth1:
dhcp4: false
addresses:
- 10.0.1.50/24
# 内部网络,不设默认网关
bonds:
bond0:
interfaces: [eth0, eth1]
parameters:
mode: 802.3ad
mii-monitor-interval: 100
lacp-rate: fast
addresses:
- 192.168.200.50/24
在 user-data 中通过 bootcmd 写入 network-config:
#cloud-config
bootcmd:
- |
cat > /etc/netplan/99-custom.yaml << 'NETPLAN'
network:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 192.168.100.50/24
gateway4: 192.168.100.1
nameservers:
addresses: [8.8.8.8]
NETPLAN
- netplan apply
3.3 Cloud-init + Ansible 联动编排
将 Cloud-init 作为”引导程序”,完成系统初始化后自动触发 Ansible Playbook 执行:
#cloud-config
package_update: true
packages:
- ansible
- git
- python3-pip
write_files:
- path: /etc/ansible/ansible.cfg
content: |
[defaults]
host_key_checking = False
remote_user = ubuntu
stdout_callback = yaml
permissions: '0644'
- path: /etc/ansible/inventory.yml
content: |
all:
hosts:
localhost:
ansible_connection: local
vars:
role: webserver
environment: production
permissions: '0644'
runcmd:
# 从 Git 仓库拉取 Ansible Playbook
- git clone https://git.internal.example.com/ops/ansible-roles.git /opt/ansible-roles
# 安装 Ansible Galaxy 依赖
- ansible-galaxy collection install community.general -p /opt/ansible-roles/collections
# 执行 Playbook
- ansible-playbook -i /etc/ansible/inventory.yml /opt/ansible-roles/playbooks/site.yml
# 标记部署完成
- echo "Ansible provisioning complete" > /var/log/provisioning-done.log
final_message: "Ubuntu 24.04 部署完成 — Ansible 配置已应用,主机 $(hostname) 就绪"
3.4 Cloud-init 模块执行顺序揭秘
理解 Cloud-init 的模块执行顺序是调试的关键:
| 阶段 | 模块 | 说明 |
|---|---|---|
| init-local | 无 | 最早阶段,读取数据源 |
| init | 网络、数据源发现 | 配置网络,识别数据源 |
| modules-config | bootcmd |
启动时执行的命令 |
| modules-config | disk_setup, growpart, mounts |
磁盘和分区配置 |
| modules-config | apt_sources, packages |
包管理 |
| modules-final | write_files |
写入文件 |
| modules-final | runcmd |
运行命令 |
| modules-final | ssh_authorized_keys |
设置 SSH 密钥 |
| modules-final | final_message |
最终消息 |
调试命令:
# 查看 Cloud-init 执行状态
cloud-init status --long
# 查看各阶段执行详情
cloud-init analyze show
# 查看事件时间线
cloud-init analyze events
# 重新运行 Cloud-init(仅开发测试用)
cloud-init clean --logs
cloud-init init
四、全自动部署流水线:从提交到交付
将以上所有技术整合到一条 CI/CD 流水线中,实现”代码提交 → 镜像构建 → 批量部署 → 配置验证”的自动化闭环:
# .gitlab-ci.yml 示例
stages:
- build-image
- test-image
- deploy
variables:
UBUNTU_VERSION: "24.04"
IMAGE_NAME: "ubuntu-golden-${UBUNTU_VERSION}"
build-image:
stage: build-image
script:
- packer init ubuntu-golden.pkr.hcl
- packer build -var "ubuntu_version=${UBUNTU_VERSION}" ubuntu-golden.pkr.hcl
- qemu-img convert -O qcow2 output-qemu/packer-ubuntu /images/${IMAGE_NAME}-${CI_COMMIT_SHORT_SHA}.qcow2
artifacts:
paths:
- /images/${IMAGE_NAME}-${CI_COMMIT_SHORT_SHA}.qcow2
test-image:
stage: test-image
script:
# 使用 Cloud-init 启动测试实例
- cloud-localds /tmp/test-seed.iso tests/cloud-init-test.yaml
- virt-install --name test-${CI_COMMIT_SHORT_SHA} --ram 2048 --vcpus 2
--disk /images/${IMAGE_NAME}-${CI_COMMIT_SHORT_SHA}.qcow2
--disk /tmp/test-seed.iso,device=cdrom
--os-variant ubuntu24.04 --nographics --console pty --wait 60
- ssh -o StrictHostKeyChecking=no ubuntu@192.168.100.100 'uname -a && docker --version'
- virsh destroy test-${CI_COMMIT_SHORT_SHA}
deploy-prod:
stage: deploy
script:
- maas admin machines deploy --distro-series focal --hwe-kernel ga-22.04
--install-kvm-host true
$(maas admin machines read | jq -r '.[] | select(.status_name=="Ready") | .system_id')
only:
- main
五、生产环境部署清单与最佳实践
部署前检查清单
| 检查项 | 命令 | 预期结果 |
|---|---|---|
| DHCP 服务运行 | systemctl is-active isc-dhcp-server |
active |
| TFTP 文件可访问 | tftp 127.0.0.1 -c get pxelinux.0 |
文件下载成功 |
| HTTP 安装源 | curl -I http://127.0.0.1/ubuntu/dists/ |
HTTP 200 |
| Preseed 语法 | debconf-get-selections --installer |
无错误 |
| 防火墙放行 | ufw status verbose |
DHCP/TFTP/HTTP 已放行 |
安全加固建议
- PXE 服务器独立 VLAN:部署服务器配置在独立的 PXE VLAN 中,不对外暴露
- Preseed 密码加密:永远使用
openssl passwd -6生成密码哈希,从不明文存储 - Cloud-init 敏感数据:使用
cloud-init的write_files写入敏感文件时,设置permissions: 0600 - 镜像签名验证:使用
packer构建后,生成镜像 SHA256 校验和并签名 - 审计日志:在
late_command中添加部署审计日志,记录每台服务器的部署时间、镜像版本和部署者
总结
本重访篇从三个维度深入了 Ubuntu 自动化部署的生产实践:
- PXE 生产级进阶:从 UEFI 双模式到 MAAS 裸金属管理,再到大规模并发优化,让 PXE 真正服务于企业级场景
- Preseed 高级技巧:自定义分区方案、late_command 脚本链、Packer 镜像构建,从”能够安装”到”安装即配置就绪”
- Cloud-init 深度实战:磁盘自动扩容、网络配置模板、Ansible 联动编排,让云实例初始化真正”一键就绪”
自动化部署的终极目标是:让运维人员从”装机工”转变为”架构师”。当你的基础设施能够通过代码定义、自动部署、自动验证时,运维就真正变成了软件工程。
下期预告
第 28 天(重访):故障排查实战——系统救援、启动修复、日志分析、崩溃排查
当系统突然崩溃、启动失败或服务异常时,如何快速定位并修复问题?下期将带你深入 Ubuntu 系统救援模式,掌握 GRUB 修复、单用户模式、日志分析、崩溃转储等实战技能,成为真正的系统排障专家。
📖 系列目录
- 第 1 天:Ubuntu 简介与版本选择
- 第 2 天:手把手安装 Ubuntu
- 第 3 天:Ubuntu 桌面环境初探
- 第 4 天:Ubuntu 终端基础
- 第 5 天:用户与权限管理
- 第 6 天:软件包管理
- 第 7 天:文件与文本操作
- 第 8 天:系统服务管理
- 第 9 天:磁盘与文件系统管理
- 第 10 天:网络配置与管理
- 第 11 天:进程管理与监控
- 第 12 天:计划任务与自动化
- 第 13 天:备份与恢复策略
- 第 14 天:系统更新与升级管理
- 第 15 天:SSH 远程管理与安全加固
- 第 16 天:Web 服务器搭建
- 第 17 天:数据库服务器部署
- 第 18 天:Docker 在 Ubuntu 上的安装与容器化管理
- 第 19 天:文件共享服务
- 第 20 天:监控与告警系统
- 第 21 天:邮件服务器基础
- 第 22 天:系统安全加固
- 第 23 天:性能调优与内核参数(重访)
- 第 24 天:虚拟化技术(重访)
- 第 25 天:容器编排入门(重访)
- 第 26 天:高可用与负载均衡(重访)
- 第 27 天(重访):自动部署生产实践——PXE + Preseed + Cloud-init 深度进阶 ← 本篇
- 第 28 天:故障排查实战 🔜
- 第 29 天:Ubuntu 社区与文档 🔜
- 第 30 天:30 天回顾 🔜















暂无评论内容