Штампуем окна: автоматизированное развёртывание виртуальных машин Windows на Hyper-V при помощи Vagrant (часть 3)

в 13:01, , рубрики: devops, hyper-v, vagrant, виртуализация, ит-инфраструктура, системное администрирование, метки:

В предыдущих (раз, два) публикациях я рассказал, как подготовить гипервизор и бокс для последующего автоматизированного разворачивания. В последней части этой трилогии я хотел бы раскрыть тему, собственно, деплоя и провижионинга подготовленных боксов. Также здесь я подведу итоги проделанного и в конце вы можете найти все ссылки по теме.

После того, как бокс добавлен в Vagrant — мы можем быстро “оживить” его, подняв из него машину (или несколько). Все метаданные о ней будут храниться в одной директории, рядом с образами жёстких дисков. Давайте пройдёмся по этой простой процедуре.

  • На нужном диске создаётся папка с произвольным названием. Я предпочитаю называть их по имени машины для пущего порядка.
  • Через Powershell или командную строку заходим внутрь этого каталога.
  • Для инициализации папки нужно выполнить команду vagrant init:

    vagrant init Win2012R2x64

    Параметр указывает имя бокса, который мы хотим здесь развернуть. В принципе, его всегда можно подправить позже. В результате выполнения команды в каталоге создаётся файл с назамысловатым именем Vagrantfile, в котором на языке Ruby указаны минимальные параметры поднимаемой машины: провайдер (гипервизор), тип ОС и имя бокса. В принципе, я предпочитаю просто копировать готовый файл, нежели править шаблон. Описание всех параметров можно найти в документации, я лишь приведу свой пример с рабочим провижионингом (листинг доступен на Gist):

    Vagrantfile

    # Несколько первых строк - стандартные заголовки, необходимые для функционирования файла. Они оставлены неизменными.
    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
    VAGRANTFILE_API_VERSION = "2"
    Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    
    # Далее следует изменение параметров серии config.vm, которые затрагивают непосредственно поднимаемую машину.
    # Какой мы будем использовать бокс
      config.vm.box = "Win2012R2x64"
    
    # Семейство операционных систем, в нашем случае просто windows
      config.vm.guest = :windows
    
    # Указываем, под каким именем Vagrant будет у себя учитывать эту машину. Это не влияет ни на имя хоста, ни на то, как он будет называться в Hyper-V. Если опустить - будет назначено имя “default”.
      config.vm.define :godnota
    
    # Как мы будем взаимодействовать с ОС. Традиционно используется SSH, но сейчас стало возможно использовать нативные средства - WinRM
      config.vm.communicator = "winrm"
    
    # Здесь указывается тип провижионинга. В будущем здесь засияет Chef, но для первоначальной настройки и примера вполне достаточно обойтись простым скриптом. Предполагается, что скрипт лежит в той же папке, где и сам Vagrantfile. Возможно указать и удалённый путь, как на веб-сервере, так и на расшаренном ресурсе - не забывайте только дублировать слеши (например, \\server\DevOps\Vagrant\provision.ps1), иначе Руби поймёт их неправильно.
      config.vm.provision "shell", path: "provision.ps1"
    
    # Параметр, определяющий, сколько секунд Vagrant будет дожидаться полной загрузки виртуалки. Поскольку у нас не линукс, то советую поставить значение минут в 5, тем более мы предварительно прошлись sysprep, так что первая загрузка длится сильно дольше последующих
      config.vm.boot_timeout = 300
    
    # Сколько секунд ждать, прежде чем машина выключиться, получив соответствующий сигнал
      config.vm.graceful_halt_timeout = 180
    
    # Vagrant умеет синхронизовать (на самом деле монтировать по SMB) каталог на гипервизоре внутрь виртуальной машины. Фича полезная и по умолчанию включённая, однако в текущей версии для гостевых ОС Windows без специальных шаманств неработающая (для Ubuntu hashicorp/precise64 всё проходит гладко), поэтому во избежание ошибки приходится её отключать
      config.vm.synced_folder ".", "/vagrant", disabled: true
    
    # Далее ковыряем настройки Hyper-V. Советую увеличить это значение, иначе вагрант может не успеть определить адрес и, соответственно, не сможет сделать провижионинг. 
      config.vm.provider "hyperv" do |hv|
        hv.ip_address_timeout = 300
      end
    
    # Последние штрихи - указываем учётные данные, которые будут использоваться для доступа. Обычно это локальный администратор, пароль указывается в открытом виде (всё равно потом лучше его поменять).
      config.winrm.username = "Administrator"
      config.winrm.password = "P@$$word"
    end
    

Что же происходит на этапе провижионинга shell? В случае, если после загрузки система полноценно поднялась (а не залипла на каком-то вопросе, на который забыли дать ответ в файле unattend.xml) Вагрант подключается к ВМ, создаёт временную папку на системном диске (обычно C:Tmp), закидывает туда скрипт и локально запускает под указанным нами пользователем. В файле скрипта можно сделать множество полезного, я лишь хочу показать, чего туда понаписал я (листинг доступен на Gist):

provision.ps1

# Устанавливаем Chocolatey одной командой. Про этот чудесный продукт уже немало написано, в том числе и на хабре, поэтому я не буду про него распространяться здесь. Замечу лишь, что именно он позволит нам автоматизировать большую часть рутины, в том числе и с разворачиванием наших приложений
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
# Ставим Boxstarter и прописываем пути
cinst boxstarter
$env:Path+=";"+$env:AppData+"Boxstarter"
$env:PSModulePath+=";"+$env:APPDATA+"Boxstarter"
Import-Module Boxstarter.Chocolatey
$Boxstarter.AutoLogin=$true
# Настраиваем внешний вид Проводника
Set-WindowsExplorerOptions -EnableShowHiddenFilesFoldersDrives -EnableShowFileExtensions
# Включаем доступ по RDP
Enable-RemoteDesktop
# Устанавливаем последние Far и 7-zip
choco install far,7zip -y
# Не забываем про Classic Shell, но только модуль для кнопки Start
choco install classic-shell -installArgs ADDLOCAL=ClassicStartMenu -y
# Не люблю лишние ярлыки 
del C:UsersAdministratorDesktop*.lnk
# Ставим обновления и перезагружаемся
Install-WindowsUpdate -AcceptEula -SuppressReboots
Restart-Computer

В принципе, ничего особенного, за исключением того, что некоторые команды, включая Install-WindowsUpdate, являются модулями Boxstarter (за что мы его и любим).

В идеале после этого можно засунуть наш скрипт в папку выполнять заветную команду vagrant up. Однако, после этого могут возникнуть некоторые сложности. Хотел бы сразу предупредить о них:

  • Бывает, что при попытке поднятия виртуалки вы получите ошибку от Powershell с указанием, что диска IDE у неё быть не может. Это известный баг, который связан с тем, что если бокс подготовлен во второй версии виртуальных машин Hyper-V, то там действительно как класс отсутствуют виртуальные контроллеры IDE, о чём совершенно не догадывается наш Vagrant. Лечится недоразумение очень просто, редактированием скрипта импорта ВМ: C:HashiCorpVagrantembeddedgemsgemsvagrant-1.7.2pluginsprovidershypervscriptsimport_vm.ps1 — в соответствующих строчках нужно
    заменить “IDE” на “VHD”
     # Determine boot device
    Switch ((Select-Xml -xml $vmconfig -XPath "//boot").node.device0."#text") {
      "Floppy" { $bootdevice = "floppy" }
      "HardDrive" { $bootdevice = "VHD" }
      "Optical" { $bootdevice = "CD" }
      "Network" { $bootdevice = "LegacyNetworkAdapter" }
      "Default" { $bootdevice = "VHD" }
    } #switch

    Запомните этот путь, он ещё может пригодится: например, если вы наткнётесь на неизведанные доселе ошибки или захотите что-то туда прикрутить.

  • Ещё один нюанс связан с очерёдностью загрузки. Дело в том, что даже если на оригинальной машине стоял приоритет у загрузки с жёсткого диска, при запуске вышеупомянутого файла он почему-то понижается и в итоге при первом запуске система долго пытается загрузиться с сети по PXE и в результате Vagrant может не дождаться загрузки и не может определить IP адрес. Наблюдается в машинах второго поколения. Обычно это не то, что требуется, поэтому нужно либо отследить, когда виртуалка включилась и быстренько её перезагрузить, предварительно выставив правильный приоритет в настройках, либо соорудить костыль над вышеупомянутым файлом импорта: перед строкой “$vm_id = (Get-VM $vm_name).id.guid” необходимо добавить пару строк:
    if ($generation -eq 2)
    		{
    		$hddd = Get-VMHardDiskDrive $vm_name
    		Set-VMFirmware $vm_name -BootOrder $hddd
    		}
    

    Условие нужно, т.к. в машинах первого поколения не поддерживается VMFirmware. В результате у ВМ останется всего один вариант для загрузки — VHD файл.

  • Скрипт import_vm.ps1 в версии 1.7.2 не умеет правильно добавлять несколько дисков к поднимаемой машине. Связано это с тем, что он не копирует имя VHD файла (да и сам файл не копируется), а ставит передаваемое ему от import.rb дефолтное “disk.vhd”, и при подключении второго диска возникает ошибка:

    Cannot add 'disk.vhdx'. The disk is already connected to the virtual machine ‘Windows2012R2x64’'. (Virtual machine ID 35449990-E3D2-4BE1-99E5-2CAACC537097).

    Это известная проблема. Пока я не смог её победить наскоком, так что буду рад, если кто-то подскажет, как её можно одолеть. Иначе остаётся ждать, пока то поправят разработчики.

Вот и всё! Теперь можно штамповать виртуальные машины в любом количестве (ну, насколько хватит ресурсов гипервизора, конечно). Если не переименовать название инстанса (а оно берётся из бокса), то в имени просто дописывается “_1”, соответственно ещё одна будет иметь в конце “_1_1” (а не “_2”): так уж написан файл import_vm.ps1.

Если что-то не получилось и не всё пошло гладко — всегда можно попробовать поработать с чужим боксом. Свой я, к сожалению, дать не могу (туда уже забиты лицензионные ключи и сертификаты), однако можно воспользоваться боксом Мэта Рока, о котором уже упоминалось в статье; благо этот бокс доступен публично из интернета, его можно добавить одной командой (по умолчанию Vagrant ищет боксы у себя в облаке):

vagrant up mwrock/Windows2012R2 --provider hyperv

Поднятие о нескольких машин

Что ж, в итоге мы получили поднятие ВМ по нажатию нескольких кнопок. Что же делать, когда хочется установить сразу целое окружение? Конечно, для этого понадобиться немного поскриптовать, но не обязательно сразу браться за Powershell. К счастью, конфигурационные файлы написаны на Ruby, что позволяет поднимать несколько машин за раз из одного Vagrantfile, одной командой, причём можно даже указывать несколько различных боксов в одном файле. Процедура подробно описана в документации. При должном владении языком можно творить настоящие чудеса: например, автоматически, беря из JSON файла, назначать настройки сети и имена хостов всем машинам, а также накатывать на них рецепты Chef в зависимости от роли в том же файле (например, так). Однако, это далеко выходит за рамки этой, и так уже расползшейся статьи.

Итоги

Что ж, что мы получили в результате? Систему быстрого развёртывания Windows машин в родном для них окружении, поднимающую готовую виртуалку и настраивающую её согласно нашим требованиям.
Чем это помогло конкретно нашей компании? Это значительно экономит время на поднятие новой инфраструктуры: раньше установка системы из ISO файла, настройка и обновление всего софта могла занять полдня, теперь это считанные минуты. Даже клонирование машин из шаблонов System Center обычно происходит дольше, не говоря уже о перечисленных недостатках типа наследования файлов виртуальных жёстких дисков. Также это отличный инструмент для наших разработчиков и тестировщиков, которые получили возможность быстро поднять на локальной системе (даже на рабочих станциях с Windows 8.1) какое-то тестовое окружение. Для этого я написал простую короткую инструкцию, так что им не надо вникать в детали и отвлекаться от рабочего процесса.
Что дальше? Сейчас мы ведём активную работу по анализу и документированию имеющихся окружений, чтобы потом описать их в виде рецептов Chef. Наши службы планируется устанавливать и выкатывать в виде пакетов. Это позволит автоматизировать множество процессов, избавиться от множества рутины и ошибок, связанных с человеческим фактором. В общем, то, что доктор DevOps прописал. Также в планах осваивание Packer, если понадобиться быстро готовить много разных образов. Поддержка Hyper-V и Azure в нём появилась совсем недавно, но выглядит очень многообещающей. Также в планах работа по быстрой синхронизации папок между ВМ и хостом — к сожалению, обычный шаринг работает довольно медленно для большого количества файлов и активных операциях (например, билде).
Если эта статья была полезной и тема в целом интересна — я с радостью сделаю отдельную публикацию о наших последующих победах и неудачах на этом поприще.

Ссылки

Автор: Uburwator

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js