Bare-Metal Provisioning своими руками, или Автоматическая подготовка серверов с нуля +7


Привет, я Денис и одно из моих направлений деятельности – разработка инфраструктурных решений в X5. Сегодня хотел бы поделиться с вами о том, как можно на базе общедоступных инструментов развернуть автоматическую систему подготовки серверов. На мой взгляд, это интересное, простое и гибкое решение.



Под подготовкой подразумевается: сделать из нового сервера из коробки, полностью настроенный сервер с о.с. Linux или c гипервизором ESXi (разливка серверов Windows в данной статье не обсуждается).

Термины:

  • серверы – серверы, которые необходимо настроить.
  • инсталл-сервер – основной сервер, который обеспечивает весь процесс подготовки по сети.

Зачем нужна автоматизация?


Допустим, есть задача: массово подготавливать сервера с нуля, в пике – 30 в день. Сервера разных производителей и моделей, на них могут устанавливаться различные ОС, может иметься или отсутствовать гипервизор.

Какие операции входят в процесс настройки (без автоматизации):

  • подключить клавиатуру, мышь, монитор к серверу;
  • настроить BIOS, RAID, IPMI;
  • обновить прошивки компонентов;
  • развернуть образ файловой системы (либо установить гипервизор и скопировать виртуальные машины);

Примечание. Как вариант, деплой ОС возможен через установку с файлом автоответов. Но это не будет обсуждаться в статье. Хотя ниже вы увидите, что добавить этот функционал несложно.

  • настроить параметры ОС (hostname, IP, прочее).

При данном подходе выполняются одинаковые настройки последовательно на каждом сервере. Эффективность такой работы очень низкая.

Суть автоматизации – исключить участие человека из процесса подготовки сервера. Максимально, насколько это возможно.

Благодаря автоматизации сокращается время простоя между операциями и появляется возможность подготовить несколько серверов одновременно. Также сильно снижается вероятность возникновения ошибок из-за человеческого фактора.



Как происходит автоматическая настройка серверов?


Разберем все этапы детально.

У вас есть linux-сервер, который вы используете в качестве PXE инсталл-сервера. На нем установлены и настроены службы: DHCP, TFTP.

Итак, загружаем сервер (который необходимо настроить) по PXE. Вспомним, как это работает:

  • На сервере выбрана загрузка по сети.
  • Сервер загружает PXE-ROM сетевой карты и обращается к инсталл-серверу по DHCP для получения сетевого адреса.
  • DHCP инсталл-сервера выдает адрес, а также инструкцию о дальнейшей загрузке через PXE.
  • Сервер загружает сетевой загрузчик с инсталл-сервера по PXE, дальнейшая загрузка происходит согласно конфигурационному файлу PXE.
  • Происходит загрузка на основе полученных параметров (ядро, initramfs, точки монтирования, образ squashfs и прочее).

Примечание. В статье приводится описание загрузки по PXE через BIOS mode. В настоящее время производителями активно внедряется UEFI bootmode. Для PXE отличие будет в конфигурации DHCP-сервера и наличия дополнительного загрузчика.

Рассмотрим пример конфигурации PXE-сервера (меню pxelinux).

Файл pxelinux.cfg/default:

default menu.c32
prompt 0
timeout 100
menu title X5 PXE Boot Menu
LABEL InstallServer Menu
	MENU LABEL InstallServer
	KERNEL menu.c32
	APPEND pxelinux.cfg/installserver
LABEL VMware Menu
	MENU LABEL VMware ESXi Install
	KERNEL menu.c32
	APPEND pxelinux.cfg/vmware
LABEL toolkit // меню по умолчанию
	MENU LABEL Linux Scripting Toolkits
	MENU default
	KERNEL menu.c32
	APPEND pxelinux.cfg/toolkit // переход на следующее меню

Файл pxelinux.cfg/toolkit:

prompt 0
timeout 100
menu title X5 PXE Boot Menu
label mainmenu
    menu label ^Return to Main Menu
    kernel menu.c32
    append pxelinux.cfg/default
label x5toolkit-auto // по умолчанию — автоматический режим
        menu label x5 toolkit autoinstall
        menu default
        kernel toolkit/tkcustom-kernel
        append initrd=toolkit/tk-initramfs.gz quiet net.ifnames=0 biosdevname=0 nfs_toolkit_ip=192.168.200.1 nfs_toolkit_path=tftpboot/toolkit nfs_toolkit_script=scripts/mount.sh script_cmd=master-install.sh CMDIS2=”…”
label x5toolkit-shell // для отладки - консоль
        menu label x5 toolkit shell
        kernel toolkit/tkcustom-kernel
        append initrd=toolkit/tkcustom-initramfs.gz quiet net.ifnames=0 biosdevname=0 nfs_toolkit_ip=192.168.200.1 nfs_toolkit_path=tftpboot/toolkit nfs_toolkit_script=scripts/mount.sh script_cmd=/bin/bash CMDIS2=”…”

Ядро и initramfs на данном этапе – это промежуточный linux-образ, с помощью которого и будет происходить основная подготовка и настройка сервера.

Как вы видите, загрузчик передает много параметров ядру. Часть этих параметров используется самим ядром. А некоторые мы можем использовать для своих целей. Об этом будет рассказано далее, а пока можно просто запомнить, что все переданные параметры будут доступны в промежуточном linux-образе через /proc/cmdline.

Где их взять, ядро и initramfs?
За основу, можно выбрать любой linux-дистрибутив. На что обращаем внимание при выборе:

  • загрузочный образ должен быть универсальным (наличие драйверов, возможности установки дополнительных утилит);
  • скорее всего, потребуется кастомизировать initramfs.

Как это сделано в нашем решении для X5? В качестве основы выбран CentOS 7. Провернем следующий трюк: подготовим будущую структуру образа, упакуем ее в архив и создадим initramfs, внутри которого будет наш архив файловой системы. При загрузке образа архив будет разворачиваться в создаваемый раздел tmpfs. Таким образом мы получим минимальный, при этом полноценный live-образ linux со всеми необходимыми утилитами, состоящий всего из двух файлов: vmkernel и initramfs.

#создаем директории: 

mkdir -p /tftpboot/toolkit/CustomTK/rootfs /tftpboot/toolkit/CustomTK/initramfs/bin

#подготавливаем структуру:

yum groups -y install "Minimal Install" --installroot=/tftpboot/toolkit/CustomTK/rootfs/
yum -y install nfs-utils mariadb ntpdate mtools syslinux mdadm tbb libgomp efibootmgr dosfstools net-tools pciutils openssl make ipmitool OpenIPMI-modalias rng-tools --installroot=/tftpboot/toolkit/CustomTK/rootfs/
yum -y remove biosdevname --installroot=/tftpboot/toolkit/CustomTK/rootfs/

# подготавливаем initramfs:

wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64 -O /tftpboot/toolkit/CustomTK/initramfs/bin/busybox
chmod a+x /tftpboot/toolkit/CustomTK/initramfs/bin/busybox
cp /tftpboot/toolkit/CustomTK/rootfs/boot/vmlinuz-3.10.0-957.el7.x86_64 /tftpboot/toolkit/tkcustom-kernel

# создаем /tftpboot/toolkit/CustomTK/initramfs/init (ниже содержание скрипта):

#!/bin/busybox sh
/bin/busybox --install /bin
mkdir -p /dev /proc /sys /var/run /newroot
mount -t proc proc /proc
mount -o mode=0755 -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm /dev/mapper /dev/vc
mount -t devpts -o gid=5,mode=620 devpts /dev/pts
mount -t sysfs sysfs /sys
mount -t tmpfs -o size=4000m tmpfs /newroot
echo -n "Extracting rootfs... "
xz -d -c -f rootfs.tar.xz | tar -x -f - -C /newroot
echo "done"
mkdir -p /newroot/dev /newroot/proc /newroot/sys
mount --move /sys  /newroot/sys
mount --move /proc /newroot/proc
mount --move /dev  /newroot/dev
exec switch_root /newroot /sbin/init

# упаковываем rootfs и initramfs:

cd /tftpboot/toolkit/CustomTK/rootfs
tar cJf /tftpboot/toolkit/CustomTK/initramfs/rootfs.tar.xz --exclude ./proc --exclude ./sys --exclude ./dev .
cd /tftpboot/toolkit/CustomTK/initramfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > /tftpboot/toolkit/tkcustom-initramfs-new.gz

Итак, мы указали ядро и initramfs, которые должны быть загружены. В результате на данном этапе, загрузив промежуточный образ linux по PXE, мы получим консоль ОС.

Отлично, но теперь нужно передать управление нашей “автоматизации”.

Это можно сделать так.

Предположим, после загрузки образа мы планируем передавать управление в скрипт mount.sh.
Включим скрипт mount.sh в автозапуск. Для этого потребуется модифицировать initramfs:

  • распаковать initramfs (если используем вышеприведенный вариант initramfs, это не требуется)
  • включить в автозагрузку код, который будет анализировать передаваемые параметры через /proc/cmdline и передавать управление дальше;
  • запаковать initramfs.

Примечание. В случае с X5 toolkit управление загрузки передается в скрипт /opt/x5/toolkit/bin/hook.sh с помощью override.conf в getty tty1 (ExecStart=…)

Итак, загружается образ, в котором в автозапуске стартует скрипт mount.sh. Далее скрипт mount.sh в процессе выполнения анализирует переданные параметры (script_cmd=) и запускает необходимую программу/скрипт.

label toolkit-auto
kernel …
append … nfs_toolkit_script=scripts/mount.sh script_cmd=master-install.sh

label toolkit-shell
kernel …
append … nfs_toolkit_script=scripts/mount.sh script_cmd=/bin/bash



Здесь в левой части — меню PXE, в правой – схема передачи управления.

C передачей управления мы разобрались. В зависимости от выбора PXE-меню запускается либо скрипт автонастройки, либо консоль для отладки.

В случае автоматической настройки монтируются необходимые директории с инсталл-сервера, в которых присутствуют:

  • скрипты;
  • сохраненные шаблоны BIOS/UEFI различных серверов;
  • прошивки;
  • утилиты для серверов;
  • логи.

Далее скрипт mount.sh передает управление скрипту master-install.sh из директории со скриптами.

Дерево скриптов (порядок их запуска) выглядит примерно так:

  • master-install
  • sharefunctions (общие функции)
  • info (вывод информации)
  • models (установка параметров инсталляции на основе модели сервера)
  • prepare_utils (установка необходимых утилит)
  • fwupdate (обновление прошивок)
  • diag (элементарная диагностика)
  • biosconf (настройка биоса/уефи)
  • clockfix (настройка времени на мат. Плате)
  • srmconf (настройка интерфейса удаленного интерфейса)
  • raidconf (настройка логических томов)

один из:

  • preinstall (передача управления установщику ОС или гипервизора, например ESXi)
  • merged-install (непосредственный старт распаковки образа)

Теперь мы знаем:

  • как загружать сервер по PXE;
  • как передать управление в собственный скрипт.

Продолжим. Стали актуальными следующие вопросы:

  • Как идентифицировать сервер, который мы подготавливаем?
  • Какими утилитами и как конфигурировать сервер?
  • Как получить настройки для конкретного сервера?

Как идентифицировать сервер, который мы подготавливаем?


Это просто – DMI:

dmidecode –s system-product-name
dmidecode –s system-manufacturer
dmidecode –s system-serial-number

Здесь есть все, что нужно: вендор, модель, серийный номер. Если вы не уверены, что эта информация представлена во всех серверах, можете идентифицировать их по MAC-адресу. Либо обоими способами одновременно, если вендоры серверов разные и на некоторых моделях информация о серийном номере просто отсутствует.

На основе полученной информации монтируются сетевые папки с инсталл-сервера и подгружается все необходимое (утилиты, прошивки и прочее).

Какими утилитами и как конфигурировать сервер?


Приведу утилиты для linux для некоторых производителей. Все утилиты доступны на официальных сайтах вендоров.



С прошивками, я думаю, все понятно. Обычно они поставляются в виде упакованных исполняемых файлов. Исполняемый файл контролирует процесс обновления прошивки и сообщает код возврата.

BIOS и IPMI обычно настраиваются через шаблоны. При необходимости шаблон можно редактировать перед самой загрузкой.

Утилиты RAID у некоторых вендоров тоже могут настраивать по шаблону. Если это не так, то придется написать сценарий настройки.

Порядок настройки RAID чаще всего следующий:

  • Запрашиваем текущую конфигурацию.
  • Если уже есть логические массивы – стираем.
  • Смотрим, какие физические диски присутствуют и сколько их.
  • Создаем новый логический массив. Прерываем процесс в случае ошибки.

Как получить настройки для конкретного сервера?


Предположим, настройки всех серверов будут храниться на инсталл-сервере. В таком случае, чтобы ответить на наш вопрос, нужно сначала решить: каким образом передавать настройки в инсталл-сервер.

На первых порах вполне можно обойтись текстовыми файлами. (В будущем можно использовать текстовый файл как резервный способ передачи настроек).

Можно «расшарить» текстовый файл на инсталл-сервере. И добавить его монтирование в скрипт mount.sh.

Строки будут, например, такого вида:

<серийный номер> <имя хоста> <подсеть>

Эти строки будут передаваться в файл инженером с его рабочей машины. И далее при настройке сервера параметры для конкретного сервера будут прочитаны из файла.

Но, в перспективе, лучше, задействовать БД для хранения настроек, состояний и журналов инсталляций серверов.

Конечно, одной БД не обойтись, и потребуется создать клиентскую часть, с помощью которой будут передаваться настройки в базу. Реализовать это сложнее, по сравнению с текстовым файлом, но, на самом деле, все не так трудно, как кажется. Минимальную версию клиента, который будет просто передавать данные в БД, вполне посильно написать самому. А улучшать клиентскую программу в дальнейшем можно будет и в свободном режиме (отчеты, печать этикеток, отправка уведомлений и прочее, что придет в голову).

Сделав определенный запрос в базу и указав серийный номер сервера, получим необходимые параметры для настройки сервера.

Плюс, нам не потребуется придумывать блокировки для одновременного доступа, как в случае с текстовым файлом.

Журнал настройки можем на всех этапах писать в БД и процесс установки контролировать через события и флаги этапов подготовки.

Теперь мы знаем, как:

  • загружать сервер по PXE;
  • передавать управление нашему скрипту;
  • идентифицировать сервер, который нужно подготовить, по серийному номеру;
  • конфигуририровать сервер соответствующими утилитами;
  • передавать настройки в БД инсталл-сервера с помощью клиентской части.

Выяснили, каким образом:

  • инсталлируемый сервер получает необходимые настройки из БД;
  • весь прогресс подготовки фиксируется в БД (логи, события, флаги этапов).

Что насчет разных типов устанавливаемого ПО? Как установить гипервизор, скопировать ВМ и настроить все это?


В случае развертывания образа файловой системы (linux) на железо все довольно просто:

  • После настройки всех компонентов сервера, развертываем образ.
  • Устанавливаем загрузчик grub.
  • Делаем chroot и настраиваем все что необходимо.

Как передать управление установщику ОС (на примере ESXi).

  • Организуем передачу управления из нашего скрипта установщику гипервизора по файлу автоответов (kickstart):
  • Удаляем текущие разделы на диске.
  • Создаем раздел размером 500MB.
  • Помечаем его как загрузочный.
  • Форматируем в FAT32.
  • Копируем на него в корень установочные файлы ESXi.
  • Устанавливаем syslinux.
  • Копируем syslinux.cfg в /syslinux/

default esxi
prompt 1
timeout 50
label esxi
kernel mboot.c32
append -c boot.cfg

  • Копируем mboot.c32 в /syslinux.
  • В boot.cfg должно быть kernelopt=ks=ftp://<IP инсталл-сервера>/ks_esxi.cfg
  • Перезагружаем сервер.

После перезагрузки сервера с его жесткого диска загрузится установщик ESXi. Все необходимые файлы установщика загрузятся в память и далее начнется установка ESXi, согласно указанному файлу автоответов.

Приведу здесь несколько строк из файла автоответов ks_esxi.cfg:

%firstboot --interpreter=busybox
…
# получаем серийный номер

SYSSN=$(esxcli hardware platform get | grep Serial | awk -F " " '{print $3}')

# получаем IP

IPADDRT=$(esxcli network ip interface ipv4 get | grep vmk0 | awk -F " " '{print $2}')
LAST_OCTET=$(echo $IPADDRT | awk -F'.' '{print $4}')

# подключаем NFS инсталл-сервера

esxcli storage nfs add -H is -s /srv/nfs_share -v nfsshare1

# копируем временные настройки ssh, для использования ssh-клиента

mv /etc/ssh /etc/ssh.tmp
cp -R /vmfs/volumes/nfsshare1/ssh /etc/
chmod go-r /etc/ssh/ssh_host_rsa_key

# копируем ovftool, для развертывания ВМ сейчас, плюс возможно пригодится позже

cp -R /vmfs/volumes/nfsshare1/ovftool /vmfs/volumes/datastore1/

# развертываем ВМ

/vmfs/volumes/datastore1/ovftool/tools/ovftool --acceptAllEulas --noSSLVerify --datastore=datastore1 --name=VM1 /vmfs/volumes/nfsshare1/VM_T/VM1.ova vi://root:esxi_password@127.0.0.1
/vmfs/volumes/datastore1/ovftool/tools/ovftool --acceptAllEulas --noSSLVerify --datastore=datastore1 --name=VM2 /vmfs/volumes/nfsshare1/VM_T/VM2.ova vi://root:esxi_password@127.0.0.1

# получаем строку с настройками нашего сервера

ssh root@is "mysql -h'192.168.0.1' -D'servers' -u'user' -p'secretpassword' -e \"SELECT ... WHERE servers.serial='$SYSSN'\"" | grep -v ^$ | sed 's/NULL//g' > /tmp/servers
...
# генерируем скрипт настройки сети

echo '#!/bin/sh' > /vmfs/volumes/datastore1/netconf.sh
echo "esxcli network ip interface ipv4 set -i=vmk0 -t=static --ipv4=$IPADDR --netmask=$S_SUB || exit 1" >> /vmfs/volumes/datastore1/netconf.sh
echo "esxcli network ip route ipv4 add -g=$S_GW -n=default || exit 1" >> /vmfs/volumes/datastore1/netconf.sh
chmod a+x /vmfs/volumes/datastore1/netconf.sh

# задаем параметр guestinfo.esxihost.id, указываем в нем серийный номер

echo "guestinfo.esxihost.id = \"$SYSSN\"" >> /vmfs/volumes/datastore1/VM1/VM1.vmx
echo "guestinfo.esxihost.id = \"$SYSSN\"" >> /vmfs/volumes/datastore1/VM2/VM2.vmx
...
# обновляем информацию в базе

SYSNAME=$(esxcli hardware platform get | grep Product | sed 's/Product Name://' | sed 's/^\ *//')
UUID=$(vim-cmd hostsvc/hostsummary | grep uuid | sed 's/\ //g;s/,$//' | sed 's/^uuid="//;s/"$//')
ssh root@is "mysql -D'servers' -u'user' -p'secretpassword' -e \"UPDATE servers ... SET ... WHERE servers.serial='$SYSSN'\""
ssh root@is "mysql -D'servers' -u'user' -p'secretpassword' -e \"INSERT INTO events ...\""

# возвращаем настройки SSH

rm -rf /etc/ssh
mv /etc/ssh.tmp /etc/ssh

# настраиваем сеть и перезагружаемся

esxcli system hostname set --fqdn=esx-${G_NICK}.x5.ru
/vmfs/volumes/datastore1/netconf.sh
reboot

На этом этапе установлен и настроен гипервизор, скопированы виртуальные машины.

Как теперь настроить виртуальные машины?

Мы немного схитрили: во время установки задали параметр guestinfo.esxihost.id = "$SYSSN" в файле VM1.vmx, указали в нем серийный номер физического сервера.

Теперь после старта виртуальная машина (с установленным пакетом vmware-tools) может получить доступ к этому параметру:

ESXI_SN=$(vmtoolsd --cmd "info-get guestinfo.esxihost.id")

То есть ВМ сумеет идентифицировать себя (она знает серийный номер физического хоста), сделать запрос к БД инсталл-сервера и получить параметры, которые нужно настроить. Это все оформляется в скрипт, который должен быть запущен автоматом при старте guestos vm (но однажды: RunOnce).

Теперь мы знаем, как:

  • загружать сервер по PXE;
  • передавать управление нашему скрипту;
  • идентифицировать сервер, который нужно подготовить, по серийному номеру;
  • конфигурировать сервер соответствующими утилитами;
  • передавать настройки в БД инсталл-сервера с помощью клиентской части;
  • настраивать различные типы П.О., в том числе развертывывать гипервизор esxi и настраивать виртуальные машины (и все автоматически).

Выяснили, каким образом:

  • инсталлируемый сервер получает необходимые настройки из БД;
  • весь прогресс подготовки фиксируется в БД (логи, события, флаги этапов).

Итог:

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

Напишите, пожалуйста, в комментариях, что вы думаете.




К сожалению, не доступен сервер mySQL