Автор: DevOps Team Leader компании Hostkey Егор Гараджа
В прошлой статье мы рассказали, как нам удалось решить проблему установки Windows и Linux через Foreman в Legacy-режиме (BIOS/MBR). Другой важной задачей стала поддержка инсталляций в режиме EFI. В случае Foreman этот вопрос решается через использование отдельного загрузчика. Для Linux наш выбор пал на Grub2, и реализация загрузки оказалась достаточно тривиальной — нами была создана конфигурация Grub для загрузки initrd и ядра.
В случае с Windows ситуация сложнее. Изначально загрузка Windows осуществлялась через расширение для iPXE. Проблема, однако, в том, что это расширение, как и многие другие (memdisk и т. п.), не поддерживалось официально для EFI-версии загрузчика. Кнечно, можно попытаться решить этот вопрос самостоятельными правками и сборкой нужных модулей, но мы в целом хотели уйти от iPXE, поскольку это не совсем честный pxe-загрузчик, а скорее мини-операционная система со своими драйверами для сетевых карт, что изредка создавало проблемы при инсталляциях.
Мы сделали попытку использовать модуль Grub2, позволяющий смонтировать по http образ диска и загрузиться с него. Этот вариант не сработал, так как производительность модуля при работе по сети оказалась очень низкой, не говоря уже о стабильности.
В результате изучения вопроса мы пришли к следующим вариантам решения проблемы:
Мы попробовали разные варианты, но в этой статье мы хотим рассказать о первом, поскольку сейчас он является основным в нашей инфраструктуре и хорошо испытан. Главный плюс этого подхода — это расширение к существующей процедуре установки ,а потому не требует поддерживать различную конфигурацию для режимов EFI и Legacy. Главной проблемой стала архитектурная избыточность — не вся ОС ставится через Linux LiveCD, а лишь WindowsPE, что приводит к лишним шагам и потенциальным точкам отказа.
Установка будет проходить через следующие этапы:
- Штатная загрузка Linux LiveCD через PXE Grub2.
-
Скрипт установки Linux должен:
- произвести разбивку диска для EFI и WindowsPE;
- загрузить WindowsPE и организовать его EFI-загрузку с диска;
- загрузить все необходимые скрипты и шаблоны с Foreman для установки Windows;
- сообщить Foreman о завершении установки, в результате чего следующая загрузка произойдет с диска, то есть загрузится WindowsPE.
- Далее установка должна пройти штатно — аналогично legacy-режиму, где сразу грузится WindowsPE.
Штатная загрузка Linux LiveCD через PXE Grub2
<%#
kind: PXEGrub2
name: HK_Windows_PXEGrub2
model: ProvisioningTemplate
oses:
- Windows
%>
set default=0
set timeout=<%= host_param('loader_timeout') || 10 %>
menuentry '<%= template_name %>' {
linuxefi (http,<%= host_param('medium_fqdn') %>)/live-master/vmlinuz rootfstype=auto ro rd.neednet=1 rd.live.image rd.luks=0 rd.md=0 rd.dm=0 root=live:<%= host_param('livesystem_url') %>/<%= host_param('live_squash') %> systemd.setenv=BRANCH=<%= host_param('medium_branch') %> systemd.setenv=SPEEDISO=peinstall systemd.setenv=PEMIRROR="<%= medium_uri %>" systemd.setenv=FOREMAN_TOKEN="<%= @host.token %>" systemd.setenv=FOREMAN_FQDN=<%= foreman_server_fqdn %> ipv6.disable=1 noeject
initrdefi (http,<%= host_param('medium_fqdn') %>)/live-master/initrd
}
Темплейт Grub2 загружает штатный LiveCD, сборку которого мы описывали в предыдущей статье. Параметры systemd.setenv позволяют пробросить в загруженную систему ряд переменных окружений, благодаря чему можно запустить конкретный скрипт для последующей инсталляции и задать ему параметры для получения с Foreman шаблонов.
Скрипт установки Linux
В systemd LiveCD добавляется сервис, который стартует только в том случае, если SPEEDISO=peinstall. Подробнее мы рассказывали об этом в одной из прошлых статей.
Стартовый скрипт выполняет основную магию: выбирает INST_DRIVE среди имеющихся в системе по указанной администратором логике, а затем производит следующие операции:
# Создает, с помощью parted, раздел ESP размером 1 гигабайт в начале диска:
parted -s /dev/${INST_DRIVE} mklabel gpt mkpart fat32 1MiB 1GiB set 1 esp on
EFI_PART=$(lsblk -l | grep "${INST_DRIVE}" | grep part | awk '{print $1}')
mkfs.fat -F32 /dev/$EFI_PART
# Подготавливает директории для монтирования файловых систем:
mkdir -p /mnt/{iso,efipart,wim}
# Загружает WindowsPE ISO с зеркала с помощью wget и монтирует его (вместо загрузки можно воспользоваться функционалом Fuse и сразу приступить к монтированию):
wget ${medium_uri}/${branch}/windowspe.iso
mount -o loop windowspe.iso /mnt/iso
# Подключает EFI-партицию и отправляет туда содержимое WindowsPE ISO, а также EFI-загрузчик Microsoft (мы держим его прямо на LiveCD, но можно закидывать через сеть. Он лежит в директории EFI любого инсталляционного диска Windows).
mount /dev/$EFI_PART /mnt/efipart
rsync -r /mnt/iso/ /mnt/efipart/
rsync -r /root/EFI /mnt/efipart/
# Немного исправляет пути для стандартного шаблона Grub2 с Foreman, обеспечивающего chain-load:
cp /mnt/efipart/EFI/Boot/bootx64.efi /mnt/efipart/EFI/Microsoft/Boot/bootmgfw.efi
umount /mnt/iso
# Забирает скрипты инсталляции Windows с Foreman:
curl -s "http://${foreman_fqdn}/unattended/script?token=${foreman_token}" >> /foreman.ps1
curl -s "http://${foreman_fqdn}/unattended/provision?token=${foreman_token}" >> /unattend.xml
# Добавляет скрипты в bootwim с помощью утилиты из комплекта wim-tools:
echo "add /foreman.ps1 /scripts/foreman.ps1" >> /wimupdate.txt
echo "add /unattend.xml /unattend.xml" >> /wimupdate.txt
wimupdate /mnt/efipart/sources/boot.wim < /wimupdate.txt
umount /mnt/efipart
# Сообщает Foreman, что установка завершена, это позволит осуществить следующую загрузку с диска:
wget "http://${foreman_fqdn}/unattended/built?token=${foreman_token}"
sync && reboot
Дальнейшая установка
После выполнения этих процедур произойдет загрузка с WindowsPE с помощью штатного BCD Microsoft, расположенного на EFI-партиции. Дальнейшая процедура идентична установке с использованием прямой загрузки в WindowsPE — с той лишь разницей, что все необходимые скрипты установки загружены с Foreman, поэтому часть процедуры уже не нужна. Можно включить в startpe.cmd скрипт проверку на наличие этих файлов и перейти к исполнению основного скрипта инсталляции foreman.ps1 сразу, если он есть в системе.
Разбивка диска также чуть усложняется для поддержки EFI. Вот вариант partition_table для Foreman (часть erb-шаблона, с конструкцией выбора efi/legacy):
<% if host_param_true?('uefi') -%>
@"
clean
convert gpt
create partition msr size=16
create partition efi size=500
create partition primary
shrink minimum=500
format quick fs=ntfs label="Windows"
assign letter="C"
create partition primary
format quick fs=ntfs label="Recovery"
assign letter="R"
set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"
gpt attributes=0x8000000000000001
list volume
exit
"@ | Tee-Object -FilePath "$SysDrive\dp.txt" -Append
<% else -%>
@"
clean
convert mbr
create partition primary size=500
select partition 1
active
format quick fs=ntfs
create partition primary
select partition 2
format quick fs=ntfs label='vg<%= @host.shortname %>'
assign letter='C'
exit
"@ | Tee-Object -FilePath "$SysDrive\dp.txt" -Append
<% end -%>
echo "Execute Diskpart..."
& diskpart /s $SysDrive\dp.txt
Поскольку WindowsPE уже загружен в память (речь конкретно о Boot.wim, в который мы добавляли установочные скрипты на этапе Linux), то диск можно полностью зачистить и создать чистую установку в соответствии с рекомендациями Microsoft по разбивке дисков для EFI-инсталляций.
Дальнейшие шаги необходимо описать в foreman.ps1 — это PowerShell-скрипт, который произведет загрузку необходимых драйверов, образа wim (вместе с утилитой setup.exe) со штатного инсталляционного DVD Microsoft и запустит setup.exe с использованием файла ответов unattend.xml.
Результат: установленная ОС Microsoft в режиме EFI с помощью сервиса Foreman и без использования инфраструктуры Microsoft.