пятница, 6 января 2017 г.

Создаем свой виртуальный Appliance для автоматического развертывания и настройки ВМ

Уже давно многие вендоры используют механизм распространения своих приложений в виде виртуальных апплайнсов (Virtual Appliance). VMware vSphere поддерживает импорт ВМ из шаблонов, созданных в формате Open Virtualization Format 1.0.

В качестве примеров таких приложений можно привести VMware vCenter Server Appliance, vRealize Operations Manager Appliance, vRealize Automation и другие.

Среди плюсов OVF - возможность автоматизировать процесс настройки ВМ при развертывании, например, задавать имя, IP адрес и другие сетевые настройки, пароль администратора и т.д.

Все настройки описываются в формате параметр/значение (OVF Properties) в свойствах виртуальной машины.

В качестве примера рассмотрим процедуру создания своего собственного шаблона OVF.

Создаем свой OVF шаблон на базе ОС Windows

Процедуру создания OVF шаблона можно разделить на несколько этапов:
  1. Создание эталонного образа ВМ, установка ОС и приложений внутри ВМ.
  2. Настройка параметров OVF в свойствах ВМ.
  3. Выбор механизма передачи параметров OVF в гостевую ОС.
  4. Подготовка скрипта, который использует переданные в гостевую ОС параметры и выполняет необходимые настройки.
  5. Экспорт ВМ в формат OVF/OVA.
В качестве эталонного образа может выступать ВМ с любой ОС, поддерживаемой средой виртуализации, будь то Linux или Windows.

Для включения возможности использования параметров OVF требуется сначала выключить ВМ и в ее свойствах включить поддержку vApp options (для vSphere Web Client это делается на вкладке vApp Options включением опции Enable vApp options, для vSphere Client на вкладке Options -> vApp Options -> vApp options: Enabled.

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

Перед тем, как задавать значения параметров для передачи в гостевую ОС, потребуется их определить (для vSphere Web Client это делается на той же вкладке vApp Options -> Properties, для vSphere Client - в Options -> vApp Options -> Advanced -> Properties).

Каждый параметр имеет следующий набор свойств:
  • Категория (Category).
  • Отображаемое имя (Label).
  • Идентификатор класса (class ID).
  • Идентификатор ключа (Key ID).
  • Порядковый номер ключа (Key instance ID).
  • Описание (Description).
  • Тип (Type).

Отображаемое имя и описание - это то, что видят пользователи при развертывании данного апплайнса.

Вводимые пользователем данные можно ограничить определенным форматом, выбрав тот или иной тип, например, строка, пароль, целое число, IP адрес, выпадающий список из нескольких пунктов и т.д. Для ряда параметров, типа String или Boolean вы можете указать значение по умолчанию (Default Value), которое будет автоматически установлено при развертывании данного апплайнса.

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

Категория позволяет объединять параметры в определенные логические группы, например, можно создать категорию Network Settings, в которую будут включаться все параметры, относящиеся к сетевым настройкам ВМ. Такие параметры будут отображаться на форме развертывания новой ВМ вместе. Идентификатор ключа позволяет устанавливать порядок отображения параметров на форме в момент развертывания ВМ. Например, если вы хотите, чтобы сначала пользователь вводил сетевой адрес, а потом маску сети, то для первого параметра установите значение Key instance ID равное 0, для второго - 1.

Более детально настройки параметров описаны в документе: https://www.vmware.com/support/developer/converter-sdk/conv43_apireference/vim.vApp.PropertyInfo.html

После создания параметров вам потребуется задать для них какие-либо значения (для vSphere Web Client на той же вкладке vApp Options -> Application properties, для vSphere Client в Options -> vApp Options -> Properties). Если вы разворачиваете ВМ из OVF шаблона, то, соответственно, все параметры нужно будет задать в момент развертывания.

Следующий шаг - выбор способа передачи параметров в гостевую ОС. VMware поддерживает два варианта передачи параметров:
  • Через ISO образ.
  • Через VMware Tools.
Настройка механизма передачи для vSphere Web Client выполняется на вкладке vApp Options -> OVF settings, для vSphere Client в Options -> vApp Options -> OVF Settings).

При передаче параметров через ISO, гипервизор запишет параметры в XML файл, сохранит его в ISO образе и подмонтирует к ВМ через виртуальный CD-привод.

При передаче параметров через VMware Tools, можно будет считать их, воспользовавшись утилитой vmtoolsd из гостевой ОС. Соответственно, для работы данного варианта требуется наличие установленных компонентов VMware Tools.

Для Windows используется команда:
"C:\Program Files\VMware\VMware Tools\vmtoolsd.exe" --cmd "info-get guestinfo.ovfEnv"

Для Linux используется команда:
/usr/sbin/vmtoolsd --cmd "info-get guestinfo.ovfEnv"

Вывод команды:
C:\Users\Administrator>"C:\Program Files\VMware\VMware Tools\vmtoolsd.exe" --cmd "info-get guestinfo.ovfEnv"

<?xml version="1.0" encoding="UTF-8"?>
<Environment
     xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:ve="http://www.vmware.com/schema/ovfenv"
     oe:id=""
     ve:vCenterId="vm-1930">
   <PlatformSection>
      <Kind>VMware ESXi</Kind>
      <Version>6.0.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en</Locale>
   </PlatformSection>
   <PropertySection>
         <Property oe:key="Administrator_Password" oe:value="P@ssw0rd1"/>
         <Property oe:key="DNS_Servers.3" oe:value="192.168.2.2,192.168.2.1"/>
         <Property oe:key="Default_Gateway.2" oe:value="192.168.3.1"/>
         <Property oe:key="Domain_Suffix.5" oe:value="company.local"/>
         <Property oe:key="Hostname.4" oe:value="server03"/>
         <Property oe:key="IP_Address.0" oe:value="192.168.3.100"/>
         <Property oe:key="Netmask.1" oe:value="255.255.255.0"/>
   </PropertySection>
   <ve:EthernetAdapterSection>
      <ve:Adapter ve:mac="00:50:56:a3:33:53" ve:network="vs-VLAN3" ve:unitNumber="7"/>
   </ve:EthernetAdapterSection>
</Environment>

Обратите внимание на цифры, присутствующие в названии параметров - это Key instance ID, который был задан при определении параметра. Если вы не задавали Key instance ID и Class ID, то имя параметра будет соответствовать значению Key ID.

Для считывания параметров можно воспользоваться простым скриптом на Powershell.
$sdpath = "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe" 

[xml]$xml = & $sdpath --cmd "info-get guestinfo.ovfEnv" | Out-String

$properties = $xml.Environment.PropertySection.Property
$AdministratorPassword = ($properties | where-object {$_.Key -eq "Administrator_Password"}).Value
$IPAddress = ($properties | where-object {$_.Key -eq "IP_Address.0"}).Value
$Netmask = ($properties | where-object {$_.Key -eq "Netmask.1"}).Value
$DefaultGateway = ($properties | where-object {$_.Key -eq "Default_Gateway.2"}).Value
$DNSServers = ($properties | where-object {$_.Key -eq "DNS_Servers.3"}).Value
$Hostname = ($properties | where-object {$_.Key -eq "Hostname.4"}).Value
$DomainSuffix = ($properties | where-object {$_.Key -eq "Domain_Suffix.5"}).Value

После получения параметров вы можете дописать скрипт, который выполнит все необходимые настройки внутри гостевой ОС, например, задаст пароль локального администратора и переименует компьютер:
net user Administrator $AdministratorPassword
rename-computer -NewName $Hostname

Или настроит статический IP адрес, маску и маршрутизатор по умолчанию на сетевом адаптере:
#https://d-fens.ch/2013/11/01/nobrainer-using-powershell-to-convert-an-ipv4-subnet-mask-length-into-a-subnet-mask-address/
function Convert-IpAddressToMaskLength([string] $dottedIpAddressString)
{
  $result = 0; 
  # ensure we have a valid IP address
  [IPAddress] $ip = $dottedIpAddressString;
  $octets = $ip.IPAddressToString.Split('.');
  foreach($octet in $octets)
  {
    while(0 -ne $octet) 
    {
      $octet = ($octet -shl 1) -band [byte]::MaxValue
      $result++; 
    }
  }
  return $result;
}

$PrefixLength = Convert-IpAddressToMaskLength($Netmask)

New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress $IPAddress -PrefixLength $PrefixLength -DefaultGateway $DefaultGateway
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses $DNSServers

Возможности скрипта ограничены только вашей фантазией, при желании, можно автоматически создавать новый домен, разворачивать Exchange Server, используя параметры, заданные пользователем при развертывании ВМ из шаблона.

Предположим, что скрипт, выполняющий нужные настройки написан, и возникает закономерный вопрос - как его запускать?

Для ОС Windows есть несколько вариантов:
  • Использовать планировщик задач (Task Scheduler), который будет запускать скрипт при старте компьютера.
  • Использовать локальную групповую политику (gpedit.msc Computer Configuration -> Windows Settings -> Scripts (Startup/Shutdown) -> Startup), которая также запустит скрипт при старте компьютера.
  • Использовать сценарий setupcomplete.cmd, запускающийся после выполнения Sysprep.
Первые два варианта имеют свои недостатки, т.к. они будут запускать скрипт при каждой загрузке ОС. Поэтому нам потребуются дополнительные механизмы внутри самого скрипта, которые бы проверяли, что он уже отрабатывал, или которые бы отключали задание в планировщике или групповую политику после первого успешного запуска.

Более простым вариантом является использование связки Sysprep и файла C:\Windows\Setup\Scripts\setupcomplete.cmd внутри которого можно настроить запуск нашего скрипта на PowerShell, например:
"C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe" "C:\Customization\script.ps1"

Также нам потребуется файл ответов unattend.xml для автоматической настройки через Sysprep. Вот пример файла для Windows Server 2012 R2 Standard:
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
   <settings pass="generalize">
      <component name="Microsoft-Windows-PnpSysprep" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
      </component>
   </settings>
   <settings pass="oobeSystem">
      <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <OOBE>
            <SkipMachineOOBE>true</SkipMachineOOBE>
            <HideEULAPage>true</HideEULAPage>
            <SkipUserOOBE>true</SkipUserOOBE>
            <ProtectYourPC>1</ProtectYourPC>
         </OOBE>
         <TimeZone>Russian Standard Time</TimeZone>
         <UserAccounts>
            <AdministratorPassword>
               <Value>P@ssw0rd</Value>
               <PlainText>true</PlainText>
            </AdministratorPassword>
         </UserAccounts>
      </component>
   </settings>
   <settings pass="specialize">
      <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <RegisteredOwner>User</RegisteredOwner>
         <RegisteredOrganization>Company</RegisteredOrganization>
         <ProductKey>D2N9P-3P6X9-2R39C-7RTCD-MDVJX</ProductKey>
         <ComputerName>*</ComputerName>
      </component>
      <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <Identification>
            <JoinWorkgroup>WORKGROUP</JoinWorkgroup>
         </Identification>
      </component>
   </settings>
</unattend>

Последнее, что остается - запустить Sysprep, указав файл ответов:
"C:\Windows\System32\Sysprep\sysprep.exe" /generalize /oobe /shutdown /unattend:C:\Sysprep\unattend.xml

При следующем включении ВМ, Sysprep автоматически сгенерирует новый SID и имя компьютера, а в конце запустить скрипт setupcomplete.cmd, который, в свою очередь, запустит наш скрипт.

После выключения ВМ вы можете экспортировать ее в OVF/OVA формат (Template -> Export OVF Template).

Наш шаблон для развертывания апплайнса готов.

Изменение конфигурации ВМ при развертывании

Помимо настройки гостевой ОС, OVF позволяет пользователю изменять конфигурацию ВМ в момент развертывания. Допустим, вы хотите сделать один универсальный шаблон, подходящий и для тестового развертывания, и для типового развертывания, и для развертывания в высоконагруженных средах. Отредактировав файл OVF, вы можете добавить меню с выбором предопределенной аппаратной конфигурации ВМ, например:
  • Small - 1 процессор, 512 МБ ОЗУ.
  • Normal - 2 процессора, 1024 МБ ОЗУ.
  • Large - 4 процессора, 4096 МБ ОЗУ.
В зависимости от выбранного пользователем варианта, гипервизор создаст ВМ с нужным количеством ядер и оперативной памяти.

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

В файле после секции <References></References> вставьте текст, описывающий варианты конфигурации ВМ:
 <DeploymentOptionSection>
<Info>Deployment options for VMware Virtual SAN Witness Appliance</Info>
<Configuration ovf:id="Small">
   <Label>Small</Label>
<Description>VM with 1 CPU and 512MB RAM.</Description>
</Configuration>
<Configuration ovf:id="Normal">
   <Label>Normal</Label>
<Description>VM with 2 CPU and 1024MB RAM.</Description>
</Configuration>
<Configuration ovf:id="Large">
   <Label>Large</Label>
<Description>VM with 4 CPU and 4096MB RAM.</Description>
</Configuration>
  </DeploymentOptionSection>

Найдите в файле секцию <VirtualHardwareSection></VirtualHardwareSection> внутри которой описывается аппаратная конфигурация ВМ. Найдите области с тегами <Item></Item>, описывающие конфигурацию процессоров и ОЗУ, например:
      <Item>
        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
        <rasd:Description>Number of Virtual CPUs</rasd:Description>
        <rasd:ElementName>2 virtual CPU(s)</rasd:ElementName>
        <rasd:InstanceID>1</rasd:InstanceID>
        <rasd:ResourceType>3</rasd:ResourceType>
        <rasd:VirtualQuantity>2</rasd:VirtualQuantity>
      </Item>
      <Item>
        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
        <rasd:Description>Memory Size</rasd:Description>
        <rasd:ElementName>4096MB of memory</rasd:ElementName>
        <rasd:InstanceID>2</rasd:InstanceID>
        <rasd:ResourceType>4</rasd:ResourceType>
        <rasd:VirtualQuantity>4096</rasd:VirtualQuantity>
      </Item>

Замените их на теги с аттрибутами, указывающие на ранее добавленные варианты конфигураций, например:
      <Item ovf:configuration="Small">
        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
        <rasd:Description>Number of Virtual CPUs</rasd:Description>
        <rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
        <rasd:InstanceID>1</rasd:InstanceID>
        <rasd:ResourceType>3</rasd:ResourceType>
        <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
      </Item>
      <Item ovf:configuration="Normal">
        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
        <rasd:Description>Number of Virtual CPUs</rasd:Description>
        <rasd:ElementName>2 virtual CPU(s)</rasd:ElementName>
        <rasd:InstanceID>1</rasd:InstanceID>
        <rasd:ResourceType>3</rasd:ResourceType>
        <rasd:VirtualQuantity>2</rasd:VirtualQuantity>
      </Item>
      <Item ovf:configuration="Large">
        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
        <rasd:Description>Number of Virtual CPUs</rasd:Description>
        <rasd:ElementName>4 virtual CPU(s)</rasd:ElementName>
        <rasd:InstanceID>1</rasd:InstanceID>
        <rasd:ResourceType>3</rasd:ResourceType>
        <rasd:VirtualQuantity>4</rasd:VirtualQuantity>
      </Item>
      <Item ovf:configuration="Small">
        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
        <rasd:Description>Memory Size</rasd:Description>
        <rasd:ElementName>512MB of memory</rasd:ElementName>
        <rasd:InstanceID>2</rasd:InstanceID>
        <rasd:ResourceType>4</rasd:ResourceType>
        <rasd:VirtualQuantity>512</rasd:VirtualQuantity>
      </Item>
      <Item ovf:configuration="Normal">
        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
        <rasd:Description>Memory Size</rasd:Description>
        <rasd:ElementName>1024MB of memory</rasd:ElementName>
        <rasd:InstanceID>2</rasd:InstanceID>
        <rasd:ResourceType>4</rasd:ResourceType>
        <rasd:VirtualQuantity>1024</rasd:VirtualQuantity>
      </Item>
      <Item ovf:configuration="Large">
        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
        <rasd:Description>Memory Size</rasd:Description>
        <rasd:ElementName>4096MB of memory</rasd:ElementName>
        <rasd:InstanceID>2</rasd:InstanceID>
        <rasd:ResourceType>4</rasd:ResourceType>
        <rasd:VirtualQuantity>4096</rasd:VirtualQuantity>
      </Item>

Аналогичным образом вы можете добавлять в ВМ сетевые адаптеры, задавать количество и объем жестких дисков и т.д.

Сохраните и закройте OVF файл.

Откройте MF файл, который размещается в том же каталоге, что и OVF. С помощью команды
Get-FileHash -Path .\appliance.ovf -Algorithm sha1 

посчитайте новую хэш-сумму и запишите в MF файл.

Теперь, при развертывании ВМ из OVF шаблона пользователь сможет выбирать одну из доступных конфигураций.

Автоматизация процедуры развертывания апплайнса из шаблона

Шаблон апплайнса для развертывания и настройки ВМ готов. Но создавать новые ВМ через vSphere Client или vSphere Web Client довольно скучное занятие. Хотелось бы и его автоматизировать. Помочь в этом нам может утилита OVF Tool (https://www.vmware.com/support/developer/ovf/).

После установки запустите команду, указав путь к OVF файлу, чтобы получить информацию о шаблоне:
C:\Users\Administrator>"C:\Program Files\VMware\VMware OVF Tool\ovftool.exe" "D:\Software\appliance01\appliance01.ovf"
OVF version:   1.0
VirtualApp:    false
Name:          Test Appliance
Version:       1.0
Full Version:  1.0.0
Vendor:        Company inc.
Product URL:   http://blog.vmpress.org
Vendor URL:    http://blog.vmpress.org

Download Size:  68096 bytes

Deployment Sizes:
  Flat disks:   1024.00 MB
  Sparse disks: Unknown

Networks:
  Name:        VM Network
  Description: The VM Network network

Virtual Machines:
  Name:               appliance01
  Operating System:   windows8server64guest
  Virtual Hardware:
    Families:         vmx-10
    Number of CPUs:   1
    Cores per socket: 1
    Memory:           512.00 MB

    Disks:
      Index:          0
      Instance ID:    11
      Capacity:       1024.00 MB
      Disk Types:     SCSI-lsilogicsas

    NICs:
      Adapter Type:   E1000e
      Connection:     VM Network

Properties:
  Key:         Netmask
  Label:       Netmask
  Type:        ip

  Key:         Default_Gateway
  Label:       Default Gateway
  Type:        ip

  Key:         IP_Address
  InstanceId   0
  Category:    Network Settings
  Label:       IP Address
  Type:        ip
  Description: Укажите IP адрес для виртуальной
               машины.

Deployment Options:
  Id:          Small
  Label:       Small
  Description: VM with 1 CPU and 512MB RAM.

  Id:          Normal
  Label:       Normal
  Description: VM with 2 CPU and 1024MB RAM.

  Id:          Large
  Label:       Large
  Description: VM with 4 CPU and 4096MB RAM.

  Id:          Large
  Label:       Large
  Description: VM with 4 CPU and 4096MB RAM.

Чтобы создать новую ВМ из шаблона выполните команду:
C:\Users\Administrator>"C:\Program Files\VMware\VMware OVF Tool\ovftool.exe" --noSSLVerify^
 --acceptAllEulas^
 --name="TestVM01"^
 -ds="stor01"^
 --net:"VM Network"="VM Network 2"^
 --deploymentOption="Small"^
 --prop:"Default_Gateway"="192.168.1.1"^
 --prop:"IP_Address.0"="192.168.1.100"^
 --prop:"Netmask"="255.255.255.0"^
 --powerOn^
 "X:\Software\appliance01\appliance01.ovf"^
 vi://Administrator:P@ssw0rd@vcenter.company.local/VDC/host/CLU01

Символ "^" переноса строки и пробелы в начале каждой строки используются для лучшей читаемости (при желании можно написать команду в одну строчку).

--noSSLVerify - пропускает проверку SSL сертификата на vCenter Server.
--acceptAllEulas - автоматически принимает лицензионное соглашение в шаблоне апплайнса.
--name="TestVM01" - задает имя создаваемой ВМ.
-ds="stor01" - указывает имя хранилища, где требуется разместить ВМ.
--net:"VM Network"="VM Network2" - указывает к какой сети следует подключить адаптер, где название "VM Network" должно совпадать с именем сети (можно посмотреть в выводе первой команды, заданной в апплайнсе, а "VM Network 2" - группе портов в виртуальной инфраструктуре, куда требуется подключить виртуальный адаптер.
--deploymentOption - указывает тип создаваемой ВМ.
--prop:<Параметр>=<Значение> - перечень параметров можно посмотреть в выводе первой команды OVF Tool.
--powerOn - (опционально) - включить ВМ после создания.
"X:\Software\appliance01\appliance01.ovf" - путь к OVF файлу.
vi://Administrator:P@ssw0rd@vcenter.company.local/VDC/host/CLU01 - указывает адрес сервера vCenter для подключения (vcenter.company.local), логин (Administrator), пароль (P@ssw0rd), имя виртуального ЦОД (VDC) и кластера (CLU01), где будет создана ВМ.

Дополнительные параметры к OVF Tools можно посмотреть в руководстве пользователя.