Ubuntu 系统系列 | 第 27 天(重访):自动部署生产实践——PXE + Preseed + Cloud-init 深度进阶与企业级流水线

第 27/30 天(重访进阶版)

引言

在上篇中,我们搭建了 PXE 服务器、编写了 Preseed 应答文件、配置了 Cloud-init 初始化脚本,完成了从零到一的自动化部署入门。但生产环境的要求远不止于此——UEFI 安全启动如何处理?如何将 Preseed 嵌入 Packer 镜像构建流水线?Cloud-init 的 disk_setupgrowpart 如何自动扩展磁盘?面对数百台服务器的集群,人力逐台配置已不再可能。

本重访篇将深入生产级部署的三大核心场景:

  1. PXE 进阶:UEFI 双模式引导、MAAS 裸金属管理、大规模并发部署优化
  2. Preseed 高级技巧:自定义分区方案、late_command 脚本链、Packer 镜像构建集成
  3. 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 的 growpartdisk_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: truepackages 中提前安装。disk_setupfs_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 已放行

安全加固建议

  1. PXE 服务器独立 VLAN:部署服务器配置在独立的 PXE VLAN 中,不对外暴露
  2. Preseed 密码加密:永远使用 openssl passwd -6 生成密码哈希,从不明文存储
  3. Cloud-init 敏感数据:使用 cloud-initwrite_files 写入敏感文件时,设置 permissions: 0600
  4. 镜像签名验证:使用 packer 构建后,生成镜像 SHA256 校验和并签名
  5. 审计日志:在 late_command 中添加部署审计日志,记录每台服务器的部署时间、镜像版本和部署者

总结

本重访篇从三个维度深入了 Ubuntu 自动化部署的生产实践:

  1. PXE 生产级进阶:从 UEFI 双模式到 MAAS 裸金属管理,再到大规模并发优化,让 PXE 真正服务于企业级场景
  2. Preseed 高级技巧:自定义分区方案、late_command 脚本链、Packer 镜像构建,从”能够安装”到”安装即配置就绪”
  3. Cloud-init 深度实战:磁盘自动扩容、网络配置模板、Ansible 联动编排,让云实例初始化真正”一键就绪”

自动化部署的终极目标是:让运维人员从”装机工”转变为”架构师”。当你的基础设施能够通过代码定义、自动部署、自动验证时,运维就真正变成了软件工程。


下期预告

第 28 天(重访):故障排查实战——系统救援、启动修复、日志分析、崩溃排查

当系统突然崩溃、启动失败或服务异常时,如何快速定位并修复问题?下期将带你深入 Ubuntu 系统救援模式,掌握 GRUB 修复、单用户模式、日志分析、崩溃转储等实战技能,成为真正的系统排障专家。


📖 系列目录

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容