通过 PXE 批量重装 Ubuntu

# 概述

虽然 Ubuntu 安装程序也支持 Kickstart 脚本,但这种方式简单却不够灵活。完整版的 Ubuntu Server 18.04 使用 debian-installer,有自己的应答脚本 Preseed,它不仅能应答所有安装选项,还能完成一些手动安装做不到的操作。

CentOS 篇中,我们给 PXE Server 安装了 DHCP/TFTP/Nginx 等很多软件,麻烦不说还容易出错。这次我们改用 Dnsmasq,只需一个配置文件,可同时提供 DHCP 和 TFTP。同时省去了 HTTP 服务,直接从 APT 代理安装系统。

# 准备 PXE Server 环境

安装相关软件。

sudo apt update
sudo apt install dnsmasq

配置 dnsmasq,开启 DHCP 和 TFTP 功能。

mkdir -p /srv/tftp/

$ cat /etc/dnsmasq.conf

interface=eth0
bind-interfaces
dhcp-range=172.16.199.1,172.16.199.254
dhcp-boot=grubnetx64.efi.signed
dhcp-host=172.16.199.1

enable-tftp
tftp-root=/srv/tftp/

其中,interface 是当前活动网卡名,dhcp-boot 为 grub 引导文件的位置,dhcp-host 作为 DHCP 客户端的网关 IP(用来可访问当前机器),tftp-root 则是 TFTP Server 的根目录。

至于 Nginx,可以用默认配置。然后启动服务。

sudo systemctl start dnsmasq

# 相关文件

# Boot loader

UEFI 启动需要一个 boot loader(指 GRUB),可以从软件源下载,然后丢进 TFTP 根目录。

sudo apt-get install grub-efi-amd64-signed

sudo cp /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed /srv/tftp/grubnetx64.efi.signed

grubnetx64.efi.signed 这个名字就是在 /etc/dnsmasq.conf 的 dhcp-boot 字段中定义的那个。

# GRUB

接下来配置 GRUB,其实就是 Ubuntu 启动时选择系统的菜单,只不过我们要把系统从本地换到远程。

$ mkdir -p /srv/tftp/grub

$ cat /srv/tftp/grub/grub.cfg

set default="0"
set timeout=6
menuentry "Install Ubuntu" {
  set gfxpayload=keep
  linux /ubuntu/linux gfxpayload=800x600x16,800x600 --- auto=true priority=critical interface=auto hostname=fox GRUB_DISABLE_OS_PROBER=true url=tftp://172.16.1.1/preseed.cfg quiet
  initrd /ubuntu/initrd.gz
}

由于咱需要操作 LVM 分区,而 Ubuntu 18.04 的 os-prober 操作 LVM 分区时会触发 BUG (opens new window),所以加了个环境变量 GRUB_DISABLE_OS_PROBER=true 禁用这个。其他配置项说明可以参考上篇。

# 网络引导

这里 (opens new window)下载 Ubuntu 的网络引导镜像,解压到 TFTP 目录下。

mkdir -p /srv/tftp/ubuntu

wget http://archive.ubuntu.com/ubuntu/dists/bionic-updates/main/installer-amd64/current/images/netboot/netboot.tar.gz
tar -xvzf netboot.tar.gz -C /srv/tftp/ubuntu

# 软件包缓存

Ubuntu 的安装程序可以从网络下载软件包,但如果批量安装,咱的外网就要炸了。需要在内网搭建软件包缓存服务,这里复用了 PXE Server。

sudo apt-get install apt-cacher-ng

安装后,在 Preseed 文件中设置源代理 d-i mirror/http/proxy string http://172.16.1.1:3142/ 指向该机器,即可启用 APT 缓存服务。

# Preseed

Preseed 文件像是剧本,描述安装步骤。咱先简单举几个🌰,大家可根据自身需求修改相关配置。

系统盘使用 LVM 方式分区,除了 /boot,/boot/efi 分区外,其余空间全给根目录,禁用 SWAP 分区。启用 ROOT 用户,允许 ROOT 用户远程登录,自动安装 openssh-server openssh-client vim curl。然后细分需求

  • 系统盘名字固定,比如单块 SATA 系统盘默认设备名是 /dev/sda,单块 NVMe SSD 是 /dev/nvme0n1,这样系统盘可以写死在配置中。
### Unattended Installation
d-i auto-install/enable boolean true
d-i debconf/priority select critical

### Localization
d-i debian-installer/locale string en_US.UTF-8
d-i localechooser/languagelist select en
d-i localechooser/shortlist/en select US
d-i localechooser/continentlist select Asia
d-i localechooser/countrylist/Asia select China
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/xkb-keymap select us

### Account setup
d-i passwd/root-login boolean true
d-i passwd/make-user boolean false
d-i passwd/root-password password ABC123def
d-i passwd/root-password-again password ABC123def
d-i user-setup/allow-password-weak boolean true

### Network configuration
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string cat
d-i hw-detect/load_firmware boolean true

# If you want the preconfiguration file to work on systems both with and
# without a dhcp server, uncomment these lines and the static network
# configuration below.
d-i netcfg/dhcp_failed note
d-i netcfg/dhcp_options select Configure network manually

# Static network configuration.
d-i netcfg/get_ipaddress string 172.16.1.207
d-i netcfg/get_netmask string 255.255.0.0
d-i netcfg/get_gateway string 172.16.255.254
d-i netcfg/get_nameservers string 223.5.5.5
d-i netcfg/confirm_static boolean true  

### Partition
# Specify a disk to partition. The device name
# can be given in either devfs or traditional non-devfs format.
d-i partman-auto/disk string /dev/nvme0n1
d-i grub-installer/bootdev string /dev/nvme0n1
# In addition, you'll need to specify the method to use.
# The presently available methods are: "regular", "lvm" and "crypto"
d-i partman-auto/method string lvm

# If one of the disks that are going to be automatically partitioned
# contains an old LVM configuration, the user will normally receive a
# warning. This can be preseeded away...
d-i partman-auto/purge_lvm_from_device boolean true

# http://cptyesterday.wordpress.com/2012/06/17/notes-on-using-expert_recipe-in-debianubuntu-preseed-files/
d-i partman-auto/choose_recipe select boot-root
d-i partman-auto-lvm/new_vg_name string main
d-i partman-auto-lvm/guided_size string max

d-i partman-auto/expert_recipe string                \
    boot-root ::                                     \
        1 1 1 free                                   \
            $bios_boot{ }                            \
            method{ biosgrub }                       \
        .                                            \
        256 256 256 fat32                            \
            $primary{ }                              \
            $iflabel{ gpt }                          \
            $reusemethod{ }                          \
            method{ efi } format{ }                  \
            mountpoint{ /boot/efi }                  \
        .                                            \
        512 512 512 ext4                             \
            $primary{ }                              \
            $bootable{ }                             \
            method{ format } format{ }               \
            use_filesystem{ } filesystem{ ext4 }     \
            mountpoint{ /boot }                      \
        .                                            \
        4096 4096 -1 ext4                            \
            $lvmok{ }                                \
            method{ format } format{ }               \
            use_filesystem{ } filesystem{ ext4 }     \
            mountpoint{ / }                          \
            lv_name{ root }                          \
        .

# Write the changes to disks and configure LVM
d-i partman/confirm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true

# http://ubuntuforums.org/showthread.php?p=9626883
d-i partman-lvm/device_remove_lvm boolean true

# This makes partman automatically partition without confirmation.
d-i partman/choose_partition \
      select Finish partitioning and write changes to disk
d-i partman/confirm_nooverwrite boolean true

d-i pkgsel/include string openssh-server openssh-client vim curl

### Mirror settings
d-i mirror/country string manual
d-i mirror/http/hostname string mirrors.ustc.edu.cn
d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string http://172.16.1.1:3142/

# Custom commands
d-i preseed/late_command string \
    in-target sh -c 'sed -i "/#PermitRootLogin/a PermitRootLogin yes" /etc/ssh/sshd_config';

finish-install finish-install/keep-consoles boolean false
d-i finish-install/reboot_in_progress note
  • 机器安装多块硬盘,比如有 5 块 NVMe SSD,256GBx1 & 2TBx4,系统装在 256GB 的硬盘上。

删除指定硬盘的配置,在 early_command 中使用 Bash 脚本,通过 debconf-set 来动态指定。

# 删除下面两个配置
d-i partman-auto/disk string /dev/nvme0n1
d-i grub-installer/bootdev string /dev/nvme0n1

# 用这个脚本找出小于 300GB 的硬盘,指定为系统盘
d-i partman/early_command string \
  for BOOTDEV in nvme0n1 nvme1n1 nvme2n1 nvme3n1 nvme4n1; do \
    if [ -d /sys/block/$BOOTDEV ]; then \
      SIZE=`cat /sys/block/$BOOTDEV/size`; \
      GB=$(($SIZE/2**21)); \
      if [ $GB -lt 300 ]; then \
        debconf-set partman-auto/disk "/dev/$BOOTDEV"; \
        debconf-set grub-installer/bootdev "/dev/$BOOTDEV"; \
      fi; \
    fi; \
  done
  • 机器本来有系统,需要批量重装,原系统也是 LVM 方式分区。

使用脚本移除已存在的 LVM 分区,再尝试安装(有相关配置项,但可能存在 BUG (opens new window))。

d-i partman-lvm/device_remove_lvm boolean true
d-i partman-auto/purge_lvm_from_device boolean true

# 上述配置项可能存在 BUG,那就再亲自删一遍
d-i partman/early_command string \
    set -- $(vgs --rows --noheadings | head -n 1); \
    for vg in "$@"; do \
        swapoff "/dev/$vg/swap"; \
        vgremove -f "$vg"; \
    done; \
    set -- $(pvs --rows --noheadings | head -n 1); \
    for pv in "$@"; do \
        pvremove -f "$pv"; \
    done

Preseed 文件放到 TFTP Server 目录 /srv/tftp/ 就好,下载路径就是 GRUB 菜单配置里 url 指定的那个。

# PXE 引导

确保内网中没有其他 DHCP 服务,重启服务器,启动时选择从 PXE 引导,观察是否获得 IP,能否下载启动文件。正常的话稍后就能看到 Ubuntu 安装界面。

# 参考

  • https://wiki.ubuntu.com/UEFI/PXE-netboot-install
  • https://www.debian.org/releases/etch/ia64/apbs04.html.en
概述
准备 PXE Server 环境
相关文件
Boot loader
GRUB
网络引导
软件包缓存
Preseed
PXE 引导
参考