Установка Ubuntu Server по PXE (autoinstall)
Полностью автоматическая установка Ubuntu Server по сети: TFTP + веб-сервер, загрузка по UEFI, конфигурация autoinstall для cloud-init. Проверялось на Ubuntu 20.04; сервер развёртывания — Ubuntu 18.04 и новее. Если у целевой машины всего 2 ГБ ОЗУ, дополнительно см. Ubuntu autoinstall на машине с 2 ГБ ОЗУ.
Все команды выполняются от root.
Сервер TFTP и веб-сервер
Ставим TFTP-сервер (по нему грузится загрузчик) и Apache (по нему отдаются ISO и файлы автоустановки):
apt-get -y install tftpd-hpa apache2
Публикуем каталог TFTP через Apache — так один и тот же каталог доступен и по TFTP, и по HTTP:
cat > /etc/apache2/conf-available/tftp.conf <<EOF
<Directory /var/lib/tftpboot>
Options +FollowSymLinks +Indexes
Require all granted
</Directory>
Alias /tftp /var/lib/tftpboot
EOF
a2enconf tftp
systemctl restart apache2
Ядро и initrd из установочного образа
Скачиваем образ live-сервера:
wget http://old-releases.ubuntu.com/releases/20.04/ubuntu-20.04-live-server-amd64.iso -O /var/lib/tftpboot/ubuntu-20.04-live-server-amd64.iso
Извлекаем из него ядро и initramfs — грузиться по сети будут именно они, а сам ISO установщик подтянет по HTTP:
mount /var/lib/tftpboot/ubuntu-20.04-live-server-amd64.iso /mnt/
cp /mnt/casper/vmlinuz /var/lib/tftpboot/
cp /mnt/casper/initrd /var/lib/tftpboot/
umount /mnt
Загрузчик GRUB для сетевой загрузки
Скачиваем подписанный сетевой GRUB (нужен для UEFI Secure Boot):
wget http://archive.ubuntu.com/ubuntu/dists/focal/main/uefi/grub2-amd64/current/grubnetx64.efi.signed -O /var/lib/tftpboot/pxelinux.0
Конфигурация загрузчика даёт два пункта меню: полностью автоматическая установка и обычная ручная.
mkdir -p /var/lib/tftpboot/grub
cat > /var/lib/tftpboot/grub/grub.cfg <<'EOF'
default=autoinstall
timeout=30
timeout_style=menu
menuentry "Focal Live Installer - automated" --id=autoinstall {
echo "Loading Kernel..."
# символ ';' обязательно экранировать или взять аргумент в кавычки
linux /vmlinuz ip=dhcp url=http://${pxe_default_server}/tftp/ubuntu-20.04-live-server-amd64.iso autoinstall ds="nocloud-net;s=http://${pxe_default_server}/tftp/" root=/dev/ram0 cloud-config-url=/dev/null
echo "Loading Ram Disk..."
initrd /initrd
}
menuentry "Focal Live Installer" --id=install {
echo "Loading Kernel..."
linux /vmlinuz ip=dhcp url=http://${pxe_default_server}/tftp/ubuntu-20.04-live-server-amd64.iso root=/dev/ram0 cloud-config-url=/dev/null
echo "Loading Ram Disk..."
initrd /initrd
}
EOF
Разбор параметров ядра:
| Параметр | Зачем |
|---|---|
ip=dhcp |
получить адрес по DHCP до старта установщика |
url=…iso |
откуда установщику скачать образ |
autoinstall |
включить автоматическую установку без вопросов |
ds="nocloud-net;s=…" |
где лежат meta-data и user-data для cloud-init (каталог, слэш в конце обязателен) |
root=/dev/ram0 |
корень разворачивается в ОЗУ |
cloud-config-url=/dev/null |
не дать cloud-init второй раз скачать ISO в память — см. статью про 2 ГБ ОЗУ |
Конфигурация автоустановки
cloud-init ждёт в каталоге из ds= два файла. Первый — meta-data, он может быть почти пустым:
cat > /var/lib/tftpboot/meta-data <<EOF
instance-id: focal-autoinstall
EOF
Второй — user-data с секцией autoinstall. Удобный способ получить рабочую основу: один раз поставить систему руками и взять сгенерированный файл /var/log/installer/autoinstall-user-data, а затем поправить под себя.
cat > /var/lib/tftpboot/user-data <<'EOF'
#cloud-config
autoinstall:
version: 1
# interactive-sections не даёт автоматически перезагрузиться в конце
#interactive-sections:
# - locale
apt:
# даже при no/false запрос geoip всё равно выполняется
#geoip: no
preserve_sources_list: false
primary:
- arches: [amd64, i386]
uri: http://us.archive.ubuntu.com/ubuntu
- arches: [default]
uri: http://ports.ubuntu.com/ubuntu-ports
identity: {hostname: focal-autoinstall, password: <хеш_пароля>,
username: ubuntu}
keyboard: {layout: us, variant: ''}
locale: en_US.UTF-8
# имя интерфейса на вашем оборудовании будет другим
network:
network:
version: 2
ethernets:
ens192:
critical: true
dhcp-identifier: mac
dhcp4: true
ssh:
allow-pw: true
authorized-keys: []
install-server: true
# создаёт раздел EFI, раздел /boot и корень на LVM
storage:
grub:
reorder_uefi: False
swap:
size: 0
config:
- {ptable: gpt, path: /dev/sda, preserve: false, name: '', grub_device: false,
type: disk, id: disk-sda}
- {device: disk-sda, size: 536870912, wipe: superblock, flag: boot, number: 1,
preserve: false, grub_device: true, type: partition, id: partition-sda1}
- {fstype: fat32, volume: partition-sda1, preserve: false, type: format, id: format-2}
- {device: disk-sda, size: 1073741824, wipe: superblock, flag: linux, number: 2,
preserve: false, grub_device: false, type: partition, id: partition-sda2}
- {fstype: ext4, volume: partition-sda2, preserve: false, type: format, id: format-0}
- {device: disk-sda, size: -1, flag: linux, number: 3, preserve: false,
grub_device: false, type: partition, id: partition-sda3}
- name: vg-0
devices: [partition-sda3]
preserve: false
type: lvm_volgroup
id: lvm-volgroup-vg-0
- {name: lv-root, volgroup: lvm-volgroup-vg-0, size: 100%, preserve: false,
type: lvm_partition, id: lvm-partition-lv-root}
- {fstype: ext4, volume: lvm-partition-lv-root, preserve: false, type: format,
id: format-1}
- {device: format-1, path: /, type: mount, id: mount-2}
- {device: format-0, path: /boot, type: mount, id: mount-1}
- {device: format-2, path: /boot/efi, type: mount, id: mount-3}
write_files:
# какое ядро ставить
- path: /run/kernel-meta-package
content: |
linux-virtual
owner: root:root
permissions: "0644"
EOF
В поле
passwordподставляется хеш, а не пароль открытым текстом. Получить его:mkpasswd -m sha-512(пакетwhois) илиopenssl passwd -6. Файлuser-dataотдаётся по HTTP без авторизации — не кладите в него ничего, кроме одноразового установочного пароля, и меняйте его после развёртывания.
Настройка DHCP
В DHCP-сервере задаются опции 66 (адрес TFTP-сервера) и 67 (имя загрузчика — pxelinux.0). Конкретный синтаксис зависит от вашего DHCP-сервера.
Запуск
После этого UEFI-сервер загружается по сети и ставится полностью автоматически.
Грабли, на которые стоит рассчитывать
- Памяти нужно больше 2 ГБ. Для тестов достаточно ВМ с 3 ГБ. Параметр
cloud-config-url=/dev/nullне даёт cloud-init лишний раз скачать ISO — это снижает требования к памяти, уменьшает трафик и ускоряет загрузку (подробнее — Ubuntu autoinstall на машине с 2 ГБ ОЗУ). - Сгенерированный
/var/log/installer/autoinstall-user-dataне годится как есть:- нет свойства
version— конфигурация не проходит валидацию, добавьте его; - секции
networkнужен дополнительный уровень вложенности (network: network:); - у каждого элемента
storage→configсвойствоpreserveдолжно бытьfalse, иначе curtin не станет ставить систему на чистый диск; - свойство
toggleв секцииkeyboardприходит пустым и ломает валидацию — его проще удалить.
- нет свойства
- После установки машина снова уходит в PXE. curtin на UEFI-системах ставит текущий вариант загрузки первым в списке, то есть сетевую загрузку. Лечится недокументированным параметром curtin
reorder_uefi: False, который subiquity передаёт как есть (см. секциюstorage→grubвыше). geoip: noв секции apt не работает — запросы geoip всё равно уходят.- Размеры разделов задавайте числом в байтах. «Человекочитаемые» значения вида
size: 512Mсохраняются как float, и расчёт томов LVM в процентах ломается.
Чего в autoinstall не хватает
Часть задач штатно не решается и требует early-commands, late-commands или доработки через cloud-init:
- задание часового пояса;
- задание пароля root;
- отдельный прокси только для apt (установщик считает любой заданный прокси общесистемным);
- пауза в конце вместо автоматической перезагрузки — обходится через
interactive-sections, но тогда пауз становится три; - прямая передача конфигурации curtin: приходится писать YAML для cloud-init, который отдаёт YAML в subiquity, а тот генерирует YAML для curtin;
- выбор пакета ядра: имя берётся из
/run/kernel-meta-package, в initramfs оно жёстко задано какlinux-generic. Для виртуальных машин удобнееlinux-virtual— переопределяется черезwrite_files, как в примере выше.