如何使用自动安装编写和执行 Ubuntu 无人值守安装
能够配置和创建操作系统的可复制安装至关重要,尤其是在企业环境中。为了执行无人值守安装,Ubuntu 过去支持 Debian preseed 和 Kickstart 文件。从 Ubuntu 服务器 20.04 和 Ubuntu 桌面版本 23.04 开始,这些安装方法已被弃用,并且发行版采用了新的自动安装格式,该格式利用了 cloud-init。
在本教程中,我们学习如何编写 Ubuntu 自动安装文件,以及如何使用它来执行无人值守、可复制的安装。
在本教程中您将学习:
- 如何编写自动安装文件并从各个方面定义 Ubuntu 安装
- 如何将自动安装文件传递给 Ubuntu 桌面和服务器安装程序
介绍自动安装文件
作为 Debian 衍生发行版,Ubuntu 最初支持预置文件作为执行无人值守和可复制安装的方法。后来添加了对 Kickstart 安装语法的支持作为兼容性层,但并未实现所有 Kickstart 命令。从Ubuntu服务器20.04开始,从Ubuntu桌面23.04开始,Ubuntu采用了自己的自动安装方法,基于YAML语法:autoinstall。
我们通常在自动安装文件中看到的第一件事是“#cloud-config”注释:如果我们希望文件由 cloud-init 处理,则这是必需的(稍后会详细介绍)。配置文件中使用的主键是autoinstall
;相反,文件语法版本是使用 version
键指定的。在撰写本文时,仅版本 1 可用:
#cloud-config
autoinstall:
version: 1
选择来源
要指定我们要安装的“环境”,我们可以使用 source
键,它采用映射作为值。在此映射中,我们将要安装的环境名称作为 id 键的值传递。例如,在安装 Ubuntu 桌面时,我们可以在 ubuntu-minimal-desktop 和 full-fledged ubuntu-desktop 之间进行选择。如果省略该键,则使用第一个可用的源:
#cloud-config
autoinstall:
source:
id: ubuntu-desktop-minimal
设置系统区域设置、时区和键盘布局
要设置系统区域设置,我们使用 locale
键。相反,键盘设置是通过keyboard
键指定的。该键将映射作为值;在下面的示例中,我们仅指定我们想要使用的布局(us);还可以指定其他参数,例如布局变体。最后,要设置系统时区,我们使用 timezone
键:
#cloud-config
autoinstall:
locale: en_US.UTF-8
keyboard:
layout: us
timezone: Europe/Rome
配置网络接口
为了配置系统网络,我们使用 network
键,使用 netplan 语法。在下面的示例中,我指定希望通过 dhcp4 自动配置 enp1s0 以太网接口:
#cloud-config
autoinstall:
network:
version: 2
ethernets:
enp1s0:
dhcp4: true
配置主机名和系统初始用户
为了安全地使用我们的系统,我们需要创建至少一个非特权用户。我们还需要设置系统主机名。这两个方面都是通过identity
密钥进行管理的。键以映射作为值;我们使用相应的键定义用户名
、密码
和主机名
:
#cloud-config
autoinstall:
identity:
username: egidio
hostname: linuxconfig
password: '$6$mzOdSSVUfhWRNhcX$mkm.drDfOLfpZRX/58ouJqMvitV2O0147VgKzKGYE6Qx4adJ8TXqMHs2dh2L7kT4TW5e97FCRDkGK1J.LouyV.'
正如您在示例中看到的,我们提供的密码必须已经使用 sha512 算法进行了哈希处理。
设置 SSH 服务器
在自动安装文件中,我们有机会指定是否要安装和配置 SSH 服务器。我们可以启用或禁用密码身份验证,并最终传递授权公钥列表,该列表将添加到初始用户的 authorized_keys 文件中:
#cloud-config
autoinstall:
ssh:
install-server: true
allow-pw: true
authorized-keys: []
安装附加包
要指定由 apt-get 包管理器拉入的附加包列表(作为系统安装的一部分),我们可以使用 packages
键。这是一个例子:
#cloud-config
autoinstall:
packages:
- git
- python3-pip
要安装 snap 包,我们使用 snaps
关键字。该关键字接受映射列表作为值。在每个映射中,我们使用name
、channel
和classic
键分别指定要安装的snap包的名称、应该安装snap的渠道(默认为“stable”),以及是否应该以经典模式安装snap包(默认为“false”):
#cloud-config
autoinstall:
snaps:
- name: pycharm-community
channel: stable
classic: true
定义安装后操作
为了定义安装后操作,我们使用shutdown
键。该键接受两个可能的值:“poweroff”和“reboot”:
#cloud-config
autoinstall:
shutdown: reboot
定义存储设置
系统安装最重要的方面之一是分区设置。交互式安装 Ubuntu 时,我们可以在一些预定义的布局中进行选择,或者创建自定义设置。在执行自动安装时我们可以做同样的事情。可用的预定义布局有:lvm、direct 和 zfs。
lvm 布局是默认布局:它创建 EFI(在现代 UEFI 系统的情况下)和引导标准分区,并将系统的其余部分安装在 LVM 逻辑卷上。根逻辑卷的大小根据策略确定:当设置为“缩放”(默认)时,分配给逻辑卷的空间量取决于卷组大小。如果可能,一些空间会保留未使用,以允许创建快照:
- Less than 10 GiB
100% 的可用空间
- Between 10 GiB and 20 GiB
10GiB
- Between 20GiB and 200 GiB
50% 的可用空间
- Greater than 200 GiB
100GiB
相反,当策略设置为“全部”时,所有可用空间都会分配给根逻辑卷。在此上下文中,password
密钥可用于启用 LUKS 加密,并提供加密密码:
#cloud-config
autoinstall:
storage:
layout:
name: lvm
policy: all
password: mysecretpassword
直接布局将 Ubuntu 安装在标准分区上。在 UEFI 系统上,它创建一个大小为 1GiB 的 EFI 分区,并将其余空间分配给根分区。
最后,zfs 布局将系统安装在 ZFS 池上,创建多个卷。下面您可以看到 Ubuntu 全新安装后 zfs 设置的屏幕截图:
选择安装磁盘
除了分区布局之外,我们还可以明确设置安装时使用的磁盘。我们通过 match
键来完成此操作。我们可以按路径、供应商、型号、序列等选择磁盘。如果省略关键字,则安装发生在最大的可用磁盘上。在下面的示例中,我们按路径选择磁盘 (/dev/vda):
#cloud-config
autoinstall:
storage:
layout:
name: lvm
match:
path: /dev/vda
创建自定义布局
要定义自定义分区布局,我们使用 config 关键字。该关键字接受映射列表作为值,每个映射都描述 curtin 安装程序的一个命令。让我们看一个例子。假设我们要在 LUKS 设置的 LVM 上安装 Ubuntu,创建以下分区:
- 600 MiB 的小 EFI 分区,格式化为 fat32,挂载到 /boot/efi
- 1 GiB 的启动分区,格式化为 ext4,挂载到 /boot
- 第三个分区跨越其余空间,使用 LUKS 加密,并格式化为 LVM 物理卷
- 1 个 45 GiB 的 LVM 逻辑卷安装在 /
- 1 个 50 GiB 的 LVM 逻辑卷挂载到 /home
以下是我们将如何进行。我们需要做的第一件事是使用disk命令。它用于设置磁盘并创建分区表:
#cloud-config
autoinstall:
storage:
config:
- id: disk-vda
type: disk
ptable: gpt
path: /dev/vda
wipe: superblock-recursive
使用 id 关键字,我们为该部分和命令提供标识符:这允许我们在使用其他命令时引用磁盘。相反,使用 type
关键字,我们指定要运行的命令,在本例中为“disk”。
当 ptable 键存在时,将在磁盘上创建指定类型的分区表。在本例中,我们使用“gpt”作为值。 path
关键字用于通过路径 (/dev/vda) 来识别磁盘。
wipe
关键字非常重要:使用时,它会破坏磁盘或分区的内容。我们可以使用多种类型的“擦除”:超级块、超级块递归、零、随机、和pvremove。当我们只想擦除磁盘的超级块时,我们使用超级块;当我们想要擦除最终嵌入的超级块时,我们使用超级块递归(使用 LVM 时它们可能存在) , 例如)。使用零或随机m擦除,我们可以分别用零或随机数据覆盖磁盘。最后,pvremove 专门擦除 LVM 元数据,以确保磁盘不是 LVM 设置的一部分。在上面的示例中,我们使用了超级块递归,这在大多数情况下应该足够了。
使用“partition”命令创建标准分区
我们现在需要在磁盘上创建三个“标准”分区。第一个是EFI分区,第二个是要挂载到/boot的分区,第三个是用LUKS加密的分区,并用作LVM物理卷。要创建分区,我们使用partition命令。在下面的示例中,为了清楚起见,我们重点关注当前命令,省略之前使用的命令:
#cloud-config
autoinstall:
storage:
config:
# [...]
- id: efipart
type: partition
device: disk-vda
offset: 1048576
size: 600M
flag: boot
grub_device: true
- id: bootpart
type: partition
device: disk-vda
size: 1G
- id: pvpart
type: partition
device: disk-vda
size: -1
由于“efipart”分区是第一个分区,因此我们使用 offset
关键字来确保它是在距离磁盘开头 1MiB 处创建的(该值以字节为单位提供:1048576)。我们使用 flag
关键字来指定要在分区上设置的标志,在本例中为“boot”。最后,我们使用 grub_device
关键字来确保 GRUB 安装在分区上。
在所有定义中,我们使用 device
键来引用必须在其上创建分区的磁盘,并使用 size
键来指定分区大小。对于“pvpart”分区,我们使用“-1”作为值:这确保该分区占用磁盘上的所有剩余空间。
使用 dm_crypt 命令创建加密的 LUKS 卷
下一步我们要使用 LUKS 加密“pvpart”分区。我们可以使用dm_crypt命令来做到这一点:
#cloud-config
autoinstall:
storage:
config:
# [...]
- id: pvpart-crypt
type: dm_crypt
volume: pvpart
key: mysecretpassword
使用dm_crypt命令时,我们将要加密的分区的标识符作为volume
密钥的值传递,然后使用key
来提供加密密码。
创建LVM卷组和逻辑卷
要创建 LVM 卷组,我们使用 lvm_volgroup 命令,并使用 devices
关键字列出要添加到组中的物理卷:
#cloud-config
autoinstall:
storage:
config:
# [...]
- id: vg0
type: lvm_volgroup
name: vg0
devices:
- pvpart-crypt
要创建逻辑卷,我们使用 lvm_partition 命令。我们使用 name
键指定 LV 名称,应在其上创建 LV 的卷组作为 volgroup
的值,并通过 size 指定其大小
键:
#cloud-config
autoinstall:
storage:
config:
# [...]
- id: root_lv
type: lvm_partition
name: root_lv
volgroup: vg0
size: 45G
- id: home_lv
type: lvm_partition
name: home_lv
volgroup: vg0
size: 50G
格式化分区
要在我们定义的分区上创建文件系统并设置其安装点,我们使用format命令。我们使用volume
关键字通过id引用分区,并使用fstype
关键字指定应创建什么文件系统:
#cloud-config
autoinstall:
storage:
config:
# [...]
- id: efipart_fs
type: format
volume: efipart
fstype: fat32
- id: bootpart_fs
type: format
volume: bootpart
fstype: ext4
- id: root_lv_fs
type: format
volume: root_lv
fstype: ext4
- id: home_lv_fs
type: format
volume: home_lv
fstype: ext4
最后,为了指定文件系统应该安装在哪里,我们使用 mount 命令。使用 device
键我们指定文件系统 ID;使用path
,而是它的挂载点:
#cloud-config
autoinstall:
storage:
config:
# [...]
- id: efipart-mount
type: mount
device: efipart_fs
path: /boot/efi
- id: bootpart-mount
type: mount
device: bootpart_fs
path: /boot
- id: root_lv_mount
type: mount
device: root_lv_fs
path: /
- id: home_lv_mount
type: mount
device: home_lv_fs
path: /home
将配置传递给 Ubuntu 安装程序
这是我们的自动安装配置文件的完整内容:
#cloud-config
autoinstall:
version: 1
source:
id: ubuntu-server-minimal
locale: en_US.UTF-8
keyboard:
layout: us
timezone: Europe/Rome
network:
version: 2
ethernets:
enp1s0:
dhcp4: true
identity:
username: egidio
hostname: linuxconfig
password: '$6$mzOdSSVUfhWRNhcX$mkm.drDfOLfpZRX/58ouJqMvitV2O0147VgKzKGYE6Qx4adJ8TXqMHs2dh2L7kT4TW5e97FCRDkGK1J.LouyV.'
ssh:
install-server: true
allow-pw: true
authorized-keys: []
packages:
- git
- python3-pip
snaps:
- name: pycharm-community
channel: stable
classic: true
shutdown: reboot
storage:
config:
- id: disk-vda
type: disk
ptable: gpt
path: /dev/vda
wipe: superblock-recursive
- id: efipart
type: partition
device: disk-vda
offset: 1048576
size: 600M
flag: boot
grub_device: true
- id: bootpart
type: partition
device: disk-vda
size: 1G
- id: pvpart
type: partition
device: disk-vda
size: -1
- id: pvpart-crypt
type: dm_crypt
volume: pvpart
key: mysecretpassword
- id: vg0
type: lvm_volgroup
name: vg0
devices:
- pvpart-crypt
- id: root_lv
type: lvm_partition
name: root_lv
volgroup: vg0
size: 45G
- id: home_lv
type: lvm_partition
name: home_lv
volgroup: vg0
size: 50G
- id: efipart_fs
type: format
volume: efipart
fstype: fat32
- id: bootpart_fs
type: format
volume: bootpart
fstype: ext4
- id: root_lv_fs
type: format
volume: root_lv
fstype: ext4
- id: home_lv_fs
type: format
volume: home_lv
fstype: ext4
- id: efipart-mount
type: mount
device: efipart_fs
path: /boot/efi
- id: bootpart-mount
type: mount
device: bootpart_fs
path: /boot
- id: root_lv_mount
type: mount
device: root_lv_fs
path: /
- id: home_lv_mount
type: mount
device: home_lv_fs
path: /home
如何将配置传递给 Ubuntu 安装程序?安装桌面版 Ubuntu 时,最简单、最方便的方法是通过 HTTP 或 FTP 提供我们的配置文件,并在图形安装程序的相应部分传递其 URL:
每次 Ubuntu 安装后都会自动创建自动安装配置文件,即使是交互执行的安装也是如此。您可以找到保存为 /var/log/installer/autoinstall-user-data
的文件。该文件可用于复制安装。
如果我们想要进行完全自动化的安装,或者在安装Ubuntu服务器时使用自动安装配置,我们必须使用cloud-init,并将该配置作为cloud-config数据文件:这就是“#cloud-config”自动安装文件开头的注释是:
为了让 cloud-init 在启动时读取该文件,我们需要将其重命名为 user-data
并在同一目录中创建一个名为 meta-data
的空文件。我们按“e”修改实时 ISO GRUB 菜单的第一个条目:
然后我们修改以“linux”开头的行,在三个连字符之前添加以下字符串(转义分号很重要!):
autoinstall ds=nocloud-net\;s=<directory-URL>
其中
此时要开始安装,我们只需按Ctrl-x即可。
结束语
从 Ubuntu 服务器 20.04 和 Ubuntu 桌面 23.04 开始,可以使用本机自动安装格式执行无人值守且可重复的系统安装。在本教程中,我们了解了如何编写自动安装文件,以及其中可以使用的一些密钥。最后我们看到了如何将文件传递到 Ubuntu 桌面和服务器安装程序。有关完整的 Ubuntu 自动安装参考,请查看官方文档。