Меня зовут Владислав, я системный инженер в «Инферит Облако» — российском провайдере облачной инфраструктуры для бизнеса. В работе с большими парками серверов я часто сталкивался с проблемами классической автоустановки Ubuntu Server, и в статье расскажу, как удалось упростить этот процесс.
Проблема и мотивация
Работая с большим парком серверов, я не раз сталкивался с проблемами классической автоустановки операционных систем. PXE, DHCP, TFTP/HTTP-серверы, таблицы MAC-адресов и ручная правка preseed-файлов превращали процесс в настоящий кошмар. В этой статье расскажу, как мы упростили установку Ubuntu Server, используя самодостаточный ISO-образ с autoinstall, который автоматически определяет оборудование, настраивает сеть и запрашивает конфигурацию через API, минимизируя ручную работу.
Проблема: почему PXE — это боль
Классическая установка через PXE требует сложной инфраструктуры: DHCP-сервер с кастомными настройками, TFTP или HTTP-сервер для загрузки образов, а также таблицы MAC-адресов, которые нужно заранее собирать. Процесс выглядит так:
Загружаешься с live-образа, чтобы собрать MAC-адреса и информацию о дисках.
Вносишь данные в таблицу или базу.
Генерируешь preseed-файл и надеешься, что он корректен.
Запускаешь установку — если повезет.
Каждый новый сервер — это ручная сверка, правка конфигов и потерянное время. Хотелось решения, где инженер вносит данные один раз в одном месте, а установка проходит автоматически.
Решение: самодостаточный ISO с autoinstall
Был разработан подход, который устраняет необходимость в PXE и сложной инфраструктуре. Вместо этого используется ISO-образ Ubuntu Server, который:
Содержит минимальный autoinstall.yaml для поднятия сети.
Автоматически определяет сетевые интерфейсы и диски.
Запрашивает полный конфигурационный файл через API.
Выполняет установку без участия человека.
Преимущества подхода
Простота: нет необходимости в DHCP, TFTP или таблицах MAC-адресов.
Гибкость: ISO сам определяет оборудование, что снижает риск ошибок.
Автоматизация: инженер вносит данные только в API, остальное происходит автоматически.
Как это работает: общая схема
Загрузка ISO: сервер загружается с модифицированного ISO-образа Ubuntu Live Server.
Поднятие сети: autoinstall.yaml настраивает сетевые интерфейсы (bonding, VLAN) и получает IP через DHCP.
Сбор данных: скрипты собирают информацию о сетевых интерфейсах, дисках и IPMI.
Запрос к API: данные отправляются на сервер API, который возвращает полный autoinstall.yaml.
Установка: Cloud-init применяет полученный YAML для завершения установки.

Шаг 1. Подготовка ISO-образа:
Для создания самодостаточного ISO-образа мы используем Ubuntu Live Server как основу.
Процесс включает следующие шаги:
-
Распаковка ISO:
7z -y x jammy-live-server-amd64.iso -osource-files
-
Редактирование GRUB.
Открываем boot/grub/grub.cfg и добавляем запись для автоматической установки:
menuentry "Autoinstall Ubuntu Server" {
set gfxpayload=keep
linux /casper/vmlinuz autoinstall ---
initrd /casper/initrd
} -
Добавление autoinstall.yaml.
Копируем файл autoinstall.yaml в папку source-files. Этот файл содержит начальную конфигурацию для поднятия сети и запроса финального YAML через API.
-
Сборка ISO: используем xorriso для создания нового ISO:
xorriso -as mkisofs -r \
-V 'Ubuntu 22.04 LTS AUTO (EFIBIOS)' \
-o ../ubuntu-22.04-autoinstall.iso \
--grub2-mbr ../BOOT/1-Boot-NoEmul.img \
-partition_offset 16 \
--mbr-force-bootable \
-append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b ../BOOT/2-Boot-NoEmul.img \
-appended_part_as_gpt \
-iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \
-c '/boot.catalog' \
-b '/boot/grub/i386-pc/eltorito.img' \
-no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \
-eltorito-alt-boot \
-e '--interval:appended_partition_2:::' \
-no-emul-boot \
.
Этот процесс создает ISO, который автоматически запускает установку без необходимости PXE-инфраструктуры.
Шаг 2: Настройка autoinstall.yaml
Файл autoinstall.yaml — сердце решения. Он выполняет начальную настройку и
подготавливает сервер к полноценной установке. Основные задачи:
Настройка сети (bonding, VLAN, DHCP).
Сбор информации о дисках и IPMI.
Запрос финального YAML через API.
Пример начального autoinstall.yaml:
#cloud-config
autoinstall:
version: 1
debug: true
early-commands:
# Загрузка модуля VLAN
- modprobe 8021q || true
# Поиск сетевых интерфейсов Mellanox
- lshw -c network -json 2>/dev/null | awk -v RS='{' -F'"' '/vendor/ && /Mellanox Technologies/ {for(i=1;i<=NF;i++){if($i=="logicalname"){print $(i+2)}}}' > /tmp/mellanox_interfaces
# Настройка bonding
- ip link add bond0 type bond
- ip link set bond0 type bond miimon 100 mode 802.3ad
- |
first_card=$(sed -n '1p' /tmp/mellanox_interfaces)
second_card=$(sed -n '2p' /tmp/mellanox_interfaces)
ip link set $first_card down
ip link set $second_card down
ip link set $first_card master bond0
ip link set $second_card master bond0
- ip link set bond0 up
- sleep 10
# Настройка VLAN
- ip link add link bond0 name bond0.160 type vlan id 160
- ip link set bond0.160 up
- sleep 10
- dhclient bond0.160 || true
- ip a > /tmp/network-status.log
# Обнаружение дисков
- |
lsblk -dno NAME,TRAN,MODEL | awk '$2 == "sata" || $2 == "nvme" {print $1 ":" $3}' > /tmp/disks
if [ -s /tmp/disks ]; then
awk -F: '
{
model = $2
gsub(/^[[:space:]]+|[[:space:]]+$/, "", model);
if (model != "") {
count[model]++
disks[model] = disks[model] $1 "\n"
}
}
END {
for (m in count) {
if (count[m] >= 1) {
printf "%s", disks[m]
}
}
}' /tmp/disks > /tmp/disk
fi
# Получение IPMI IP
- apt update
- apt install ipmitool -y
- ipmitool lan print 1 | grep 'IP Address' | grep -v Source | awk -F: '{print $2}' | awk '{print $1}' > /tmp/lan
# Запрос финального YAML через API
- |
first_disk=$(sed -n '1p' /tmp/disk 2>/dev/null | tr -d '\n' || echo "sda")
second_disk=$(sed -n '2p' /tmp/disk 2>/dev/null | tr -d '\n' || echo "$first_disk")
ipmi_ip=$(sed -n '1p' /tmp/lan 2>/dev/null | tr -d '\n')
bond0_iface0=$(sed -n '1p' /tmp/mellanox_interfaces)
bond0_iface1=$(sed -n '2p' /tmp/mellanox_interfaces)
curl --retry 3 --retry-delay 5 -X POST https://service_ip:5111/api/update_host \
-H "Content-Type: application/json" \
-H "Accept: application/x-yaml" \
-d "{\"ipmi_ip\": \"$ipmi_ip\", \"bond0_iface0\": \"$bond0_iface0\", \"bond0_iface1\": \"$bond0_iface1\", \"first_disk\": \"$first_disk\", \"second_disk\": \"$second_disk\"}" \
--output /autoinstall.yaml
# Проверка успешности запроса
- |
if [ ! -s /autoinstall.yaml ]; then
echo "Failed to fetch autoinstall.yaml from API" > /tmp/autoinstall-error.log
exit 1
fi
locale: en_US.UTF-8
keyboard:
layout: us
storage:
config:
- type: disk
id: disk-sda
ptable: gpt
path: /dev/$first_disk
wipe: superblock
grub_device: true
- type: partition
id: efi-part-sda
device: disk-sda
size: 512M
flag: boot
- type: format
id: efi-format-sda
volume: efi-part-sda
fstype: fat32
label: ESP
- type: mount
id: efi-mount-sda
device: efi-format-sda
path: /boot/efi
identity:
realname: testuser
hostname: testserver01
username: testuser
password: “.…”
timezone: Europe/Moscow
late-commands:
- curtin in-target -- apt-get update
- curtin in-target -- apt-get install -y curl vim ssh openssh-server
Объяснение ключевых моментов:
Сеть: создается bonding-интерфейс bond0 в режиме LACP (802.3ad) для повышения отказоустойчивости. На него настраивается VLAN 160, и IP-адрес запрашивается через DHCP.
Диски: скрипт определяет SATA и NVMe-диски через lsblk, сохраняя их имена для использования в разметке.
API: данные об оборудовании отправляются на сервер API, который возвращает финальный autoinstall.yaml с полной конфигурацией (диски, пользователи, пакеты).
Шаг 3: Работа с API
Для обработки запросов мы используем самописный сервис на Flask. Он принимает JSON с данными о сервере (IPMI IP, сетевые интерфейсы, диски) и возвращает YAML с полной конфигурацией. Пример запроса:
curl -X POST http://service_ip:5111/api/update_host \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"ipmi_ip": “ipmi_ip",
"bond0_iface0": "enp94s0f0np0",
"bond0_iface1": "enp94s0f1np1",
"bond1_iface0": "enp95s0f0np0",
"bond1_iface1": "enp95s0f1np1",
"first_disk": "sda",
"second_disk": "sdb"
}'
Ответ:
#cloud-config
autoinstall:
version: 1
debug: true
bootcmd:
- ["systemctl", "enable", "debug-shell.service"]
error-handling:
continue-on-error: true
network:
version: 2
ethernets:
enp94s0f0np0:
dhcp4: false
enp94s0f1np1:
dhcp4: false
enp95s0f0np0:
dhcp4: false
enp95s0f1np1:
dhcp4: false
bonds:
bond0:
interfaces: [enp94s0f0np0, enp94s0f1np1]
mtu: 9100
parameters:
mode: 802.3ad
transmit-hash-policy: layer2+3
lacp-rate: slow
dhcp4: false
bond1:
interfaces: [enp95s0f0np0, enp95s0f1np1]
mtu: 9100
parameters:
mode: 802.3ad
transmit-hash-policy: layer2+3
lacp-rate: slow
dhcp4: false
vlans:
bond0.60:
id: 60
link: bond0
mtu: 9100
addresses: [bond0.60_ip]
gateway4: gateway
nameservers:
addresses: [dns1_ip, dns2-ip]
search: [search.domain]
bond1.20:
id: 20
link: bond1
mtu: 9100
addresses: [bond1.20_ip]
storage:
config:
- type: disk
id: disk-sda
ptable: gpt
path: /dev/sda
wipe: superblock
grub_device: true
- type: disk
id: disk-sdb
ptable: gpt
path: /dev/sdb
wipe: superblock
grub_device: true
- type: partition
id: efi-part-sda
device: disk-sda
size: 512M
flag: boot
grub_device: true
- type: partition
id: efi-part-sdb
device: disk-sdb
size: 512M
flag: boot
- type: partition
id: raid-part-sda
device: disk-sda
size: -1
- type: partition
id: raid-part-sdb
device: disk-sdb
size: -1
- type: raid
id: md0
raidlevel: 1
devices: [raid-part-sda, raid-part-sdb]
name: ubuntu-raid
preserve: false
wipe: superblock
- type: format
id: efi-format-sda
volume: efi-part-sda
fstype: fat32
label: ESP
- type: format
id: efi-format-sdb
volume: efi-part-sdb
fstype: fat32
- type: format
id: root-format
volume: md0
fstype: ext4
- type: mount
id: efi-mount-sda
device: efi-format-sda
path: /boot/efi
- type: mount
id: root-mount
device: root-format
path: /
timezone: Europe/Moscow
identity:
hostname: testserver01
username: testuser
password: “…"
late-commands:
- curtin in-target -- apt-get update
- curtin in-target -- apt-get install -y curl vim ssh openssh-server
Сервис подставляет данные в шаблон autoinstall.yaml, который затем используется для завершения установки.
Для сервиса написана простая админка, где добавляем сервера. Страница добавления сервера выглядит так:

Основная страница:

Тут список серверов: после принятия запроса у нас заполняются данные в табличке и генерируется конфиг, при повторном запросе используется сгенерированный ранее, что дает возможность его ручной корректировки в случае ошибок. Тут же можно добавить следующий сервер, не заполняя таблицу, указанную выше, и примонтировать установочный диск к серверу.
В ipmi мы видим уже примонтированный диск — остается ребутнуть сервер и загрузиться с него:



Пошла установка, начали выполняться early-commands:

После их выполнения мы видим заполненную данными с сервера табличку:

И установка пошла с необходимыми параметрами:

ОС установилась, сервер перезагрузился и поднялся с необходимой нам разметкой, пользователем и сетью.

Отладка и устранение ошибок
Для упрощения диагностики мы сохраняем логи:
Сетевой статус: /tmp/network-status.log
Список дисков: /tmp/disks
Ошибки API: /tmp/autoinstall-error.log
Если установка не начинается, проверьте:
Доступность API-сервера (curl https://service_ip:5111).
Корректность VLAN и bonding через cat /tmp/network-status.log.
Наличие дисков в /tmp/disk.
Преимущества и сравнение с PXE
Аспект |
PXE |
Наш подход |
Инфраструктура |
DHCP, TFTP/HTTP, таблицы MAC |
Только ISO и API |
Ручная работа |
Сбор MAC, правка preseed |
Ввод данных в API один раз |
Гибкость |
Жесткая привязка к сети |
Автоматическое определение оборудования |
Отладка |
Сложная, много точек отказа |
Логи и debug-режим в autoinstall |
Заключение
Описанный выше подход к автоматической установке Ubuntu Server устраняет сложности PXE, значительно упрощая весь процесс. Самодостаточный ISO-образ настраивает сеть, собирает данные об оборудовании и запрашивает конфигурацию через API, минимизируя ручную работу. Это решение идеально для дата-центров, CI/CD-систем и любых сценариев, где нужна быстрая и шаблонная установка серверов.
vmcore
насколько я понимаю, подключать Virtual Media все равно приходится вручную?
KotPoliglot Автор
у нас автоматизировано через инструмент от производителя материнской платы, но загружаться с него нужно вручную, хотя некоторые производители серверов и это позволяют автоматизировать