Стадия жизненного цикла модуля: General Availability

У модуля есть требования для установки

Работа с виртуальными машинами

Установка и настройка ОС

Как установить ОС в виртуальной машине из ISO-образа?

Ниже приведён типовой сценарий установки гостевой ОС Windows из ISO-образа. Перед началом разместите ISO-образ на HTTP-ресурсе, доступном из кластера.

  1. Создайте пустой VirtualDisk для установки ОС:

    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: VirtualDisk
    metadata:
      name: win-disk
      namespace: default
    spec:
      persistentVolumeClaim:
        size: 100Gi
        storageClassName: local-path
  2. Создайте ресурсы ClusterVirtualImage для ISO-образа ОС Windows и дистрибутива драйверов VirtIO:

    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: ClusterVirtualImage
    metadata:
      name: win-11-iso
    spec:
      dataSource:
        type: HTTP
        http:
          url: "http://example.com/win11.iso"
    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: ClusterVirtualImage
    metadata:
      name: win-virtio-iso
    spec:
      dataSource:
        type: HTTP
        http:
          url: "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso"
  3. Создайте виртуальную машину (ВМ):

    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: VirtualMachine
    metadata:
      name: win-vm
      namespace: default
      labels:
        vm: win
    spec:
      virtualMachineClassName: generic
      runPolicy: Manual
      osType: Windows
      bootloader: EFI
      cpu:
        cores: 6
        coreFraction: 50%
      memory:
        size: 8Gi
      enableParavirtualization: true
      blockDeviceRefs:
        - kind: VirtualDisk
          name: win-disk
        - kind: ClusterVirtualImage
          name: win-11-iso
        - kind: ClusterVirtualImage
          name: win-virtio-iso
  4. Запустите виртуальную машину:

    d8 v start win-vm
  5. Подключитесь к консоли ВМ и завершите установку ОС и драйверов VirtIO при помощи графического установщика.

    Подключение по VNC:

    d8 v vnc -n default win-vm
  6. После завершения установки перезагрузите виртуальную машину.

  7. Для дальнейшей работы снова подключитесь по VNC:

    d8 v vnc -n default win-vm

Как предоставить файл ответов Windows (Sysprep)?

Автоматическая установка Windows выполняется с файлом ответов (unattend.xml или autounattend.xml).

В примере ниже файл ответов:

  • задаёт русский язык интерфейса и раскладку;
  • подключает драйверы VirtIO для этапа установки (порядок устройств в blockDeviceRefs у ресурса VirtualMachine должен совпадать с путями в файле);
  • создаёт разметку диска для установки с EFI;
  • создаёт администратора cloud и обычного пользователя user.
Пример содержимого файла autounattend.xml…
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
  <settings pass="offlineServicing"></settings>
  <settings pass="windowsPE">
    <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <SetupUILanguage>
        <UILanguage>ru-RU</UILanguage>
      </SetupUILanguage>
      <InputLocale>0409:00000409;0419:00000419</InputLocale>
      <SystemLocale>en-US</SystemLocale>
      <UILanguage>ru-RU</UILanguage>
      <UserLocale>en-US</UserLocale>
    </component>
    <component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <DriverPaths>
        <PathAndCredentials wcm:keyValue="4b29ba63" wcm:action="add">
          <Path>E:\amd64\w11</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:keyValue="25fe51ea" wcm:action="add">
          <Path>E:\NetKVM\w11\amd64</Path>
        </PathAndCredentials>
      </DriverPaths>
    </component>
    <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <DiskConfiguration>
        <Disk wcm:action="add">
          <DiskID>0</DiskID>
          <WillWipeDisk>true</WillWipeDisk>
          <CreatePartitions>
            <!-- Recovery partition -->
            <CreatePartition wcm:action="add">
              <Order>1</Order>
              <Type>Primary</Type>
              <Size>250</Size>
            </CreatePartition>
            <!-- EFI system partition (ESP) -->
            <CreatePartition wcm:action="add">
              <Order>2</Order>
              <Type>EFI</Type>
              <Size>100</Size>
            </CreatePartition>
            <!-- Microsoft reserved partition (MSR) -->
            <CreatePartition wcm:action="add">
              <Order>3</Order>
              <Type>MSR</Type>
              <Size>128</Size>
            </CreatePartition>
            <!-- Windows partition -->
            <CreatePartition wcm:action="add">
              <Order>4</Order>
              <Type>Primary</Type>
              <Extend>true</Extend>
            </CreatePartition>
          </CreatePartitions>
          <ModifyPartitions>
            <!-- Recovery partition -->
            <ModifyPartition wcm:action="add">
              <Order>1</Order>
              <PartitionID>1</PartitionID>
              <Label>Recovery</Label>
              <Format>NTFS</Format>
              <TypeID>de94bba4-06d1-4d40-a16a-bfd50179d6ac</TypeID>
            </ModifyPartition>
            <!-- EFI system partition (ESP) -->
            <ModifyPartition wcm:action="add">
              <Order>2</Order>
              <PartitionID>2</PartitionID>
              <Label>System</Label>
              <Format>FAT32</Format>
            </ModifyPartition>
            <!-- MSR partition does not need to be modified -->
            <!-- Windows partition -->
            <ModifyPartition wcm:action="add">
              <Order>3</Order>
              <PartitionID>4</PartitionID>
              <Label>Windows</Label>
              <Letter>C</Letter>
              <Format>NTFS</Format>
            </ModifyPartition>
          </ModifyPartitions>
        </Disk>
        <WillShowUI>OnError</WillShowUI>
      </DiskConfiguration>
      <ImageInstall>
        <OSImage>
          <InstallTo>
            <DiskID>0</DiskID>
            <PartitionID>4</PartitionID>
          </InstallTo>
        </OSImage>
      </ImageInstall>
      <UserData>
        <ProductKey>
          <Key><PRODUCT_KEY></Key>
          <WillShowUI>OnError</WillShowUI>
        </ProductKey>
        <AcceptEula>true</AcceptEula>
      </UserData>
      <UseConfigurationSet>false</UseConfigurationSet>
    </component>
  </settings>
  <settings pass="generalize"></settings>
  <settings pass="specialize">
    <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <RunSynchronous>
        <RunSynchronousCommand wcm:action="add">
          <Order>1</Order>
          <Path>powershell.exe -NoProfile -Command "$xml = [xml]::new(); $xml.Load('C:\Windows\Panther\unattend.xml'); $sb = [scriptblock]::Create( $xml.unattend.Extensions.ExtractScript ); Invoke-Command -ScriptBlock $sb -ArgumentList $xml;"</Path>
        </RunSynchronousCommand>
        <RunSynchronousCommand wcm:action="add">
          <Order>2</Order>
          <Path>powershell.exe -NoProfile -Command "Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\Specialize.ps1' -Raw | Invoke-Expression;"</Path>
        </RunSynchronousCommand>
        <RunSynchronousCommand wcm:action="add">
          <Order>3</Order>
          <Path>reg.exe load "HKU\DefaultUser" "C:\Users\Default\NTUSER.DAT"</Path>
        </RunSynchronousCommand>
        <RunSynchronousCommand wcm:action="add">
          <Order>4</Order>
          <Path>powershell.exe -NoProfile -Command "Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\DefaultUser.ps1' -Raw | Invoke-Expression;"</Path>
        </RunSynchronousCommand>
        <RunSynchronousCommand wcm:action="add">
          <Order>5</Order>
          <Path>reg.exe unload "HKU\DefaultUser"</Path>
        </RunSynchronousCommand>
      </RunSynchronous>
    </component>
  </settings>
  <settings pass="auditSystem"></settings>
  <settings pass="auditUser"></settings>
  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <InputLocale>0409:00000409;0419:00000419</InputLocale>
      <SystemLocale>en-US</SystemLocale>
      <UILanguage>ru-RU</UILanguage>
      <UserLocale>en-US</UserLocale>
    </component>
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <UserAccounts>
        <LocalAccounts>
          <LocalAccount wcm:action="add">
            <Name>cloud</Name>
            <DisplayName>cloud</DisplayName>
            <Group>Administrators</Group>
            <Password>
              <Value><ADMIN_PASSWORD></Value>
              <PlainText>true</PlainText>
            </Password>
          </LocalAccount>
          <LocalAccount wcm:action="add">
            <Name>User</Name>
            <DisplayName>user</DisplayName>
            <Group>Users</Group>
            <Password>
              <Value><USER_PASSWORD></Value>
              <PlainText>true</PlainText>
            </Password>
          </LocalAccount>
        </LocalAccounts>
      </UserAccounts>
      <AutoLogon>
        <Username>cloud</Username>
        <Enabled>true</Enabled>
        <LogonCount>1</LogonCount>
        <Password>
          <Value><ADMIN_PASSWORD></Value>
          <PlainText>true</PlainText>
        </Password>
      </AutoLogon>
      <OOBE>
        <ProtectYourPC>3</ProtectYourPC>
        <HideEULAPage>true</HideEULAPage>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
        <HideOnlineAccountScreens>false</HideOnlineAccountScreens>
      </OOBE>
      <FirstLogonCommands>
        <SynchronousCommand wcm:action="add">
          <Order>1</Order>
          <CommandLine>powershell.exe -NoProfile -Command "Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\FirstLogon.ps1' -Raw | Invoke-Expression;"</CommandLine>
        </SynchronousCommand>
      </FirstLogonCommands>
    </component>
  </settings>
</unattend>

Вместо <PRODUCT_KEY> подставьте ключ продукта Windows, а вместо <ADMIN_PASSWORD> и <USER_PASSWORD> подставьте пароли создаваемых учётных записей. Windows читает эти пароли из файла в открытом виде, поэтому не оставляйте готовый файл ответов на общедоступном ресурсе.

  1. Сохраните файл ответов в autounattend.xml (воспользуйтесь примером из блока выше или измените его под свои требования).

  2. Создайте секрет с типом provisioning.virtualization.deckhouse.io/sysprep:

    d8 k create secret generic sysprep-config --type="provisioning.virtualization.deckhouse.io/sysprep" --from-file=./autounattend.xml
  3. Создайте виртуальную машину, которая в процессе установки будет использовать файл ответов. Укажите в спецификации provisioning с типом SysprepRef. При необходимости добавьте в спецификацию другие файлы в формате Base64, необходимые для успешного выполнения скриптов внутри файла ответов:

    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: VirtualMachine
    metadata:
      name: win-vm
      namespace: default
      labels:
        vm: win
    spec:
      virtualMachineClassName: generic
      provisioning:
        type: SysprepRef
        sysprepRef:
          kind: Secret
          name: sysprep-config
      runPolicy: AlwaysOn
      osType: Windows
      bootloader: EFI
      cpu:
        cores: 6
        coreFraction: 50%
      memory:
        size: 8Gi
      enableParavirtualization: true
      blockDeviceRefs:
        - kind: VirtualDisk
          name: win-disk
        - kind: ClusterVirtualImage
          name: win-11-iso
        - kind: ClusterVirtualImage
          name: win-virtio-iso

Как создать golden image для Linux?

Golden image — это предварительно настроенный образ виртуальной машины (ВМ), который можно использовать для быстрого создания новых ВМ с уже установленным программным обеспечением и настройками.

  1. Создайте виртуальную машину, установите на неё необходимое программное обеспечение и выполните все требуемые настройки.

  2. Установите и настройте qemu-guest-agent (рекомендуется):

    • Для RHEL/CentOS:

      yum install -y qemu-guest-agent
    • Для Debian/Ubuntu:

      apt-get update
      apt-get install -y qemu-guest-agent
  3. Включите и запустите сервис:

    systemctl enable qemu-guest-agent
    systemctl start qemu-guest-agent
  4. Задайте машине политику запуска AlwaysOnUnlessStoppedManually, иначе выключить её не получится.

  5. Подготовьте образ. Очистите неиспользуемые блоки файловой системы:

    fstrim -v /
    fstrim -v /boot
  6. Очистите сетевые настройки:

    • Для RHEL:

      nmcli con delete $(nmcli -t -f NAME,DEVICE con show | grep -v ^lo: | cut -d: -f1)
      rm -f /etc/sysconfig/network-scripts/ifcfg-eth*
    • Для Debian/Ubuntu:

      rm -f /etc/network/interfaces.d/*
  7. Очистите системные идентификаторы:

    echo -n > /etc/machine-id
    rm -f /var/lib/dbus/machine-id
    ln -s /etc/machine-id /var/lib/dbus/machine-id
  8. Удалите ключи хоста SSH:

    rm -f /etc/ssh/ssh_host_*
  9. Очистите журнал systemd:

    journalctl --vacuum-size=100M --vacuum-time=7d
  10. Очистите кеш пакетных менеджеров:

    • Для RHEL:

      yum clean all
    • Для Debian/Ubuntu:

      apt-get clean
  11. Очистите временные файлы:

    rm -rf /tmp/*
    rm -rf /var/tmp/*
  12. Очистите логи:

    find /var/log -name "*.log" -type f -exec truncate -s 0 {} \;
  13. Очистите историю команд:

    history -c
  14. В RHEL сбросьте и восстановите контексты SELinux одним из двух способов.

    Восстановите контексты сразу:

    restorecon -R /

    Либо запланируйте пересчёт контекстов на следующую загрузку:

    touch /.autorelabel
  15. Проверьте, что в /etc/fstab указаны UUID или LABEL, а не имена вида /dev/sdX:

    blkid
    cat /etc/fstab
  16. Сбросьте состояние cloud-init (логи и seed):

    cloud-init clean --logs --seed
  17. Выполните финальную синхронизацию и очистку буферов:

    sync
    echo 3 > /proc/sys/vm/drop_caches
  18. Выключите виртуальную машину:

    poweroff
  19. Создайте ресурс VirtualImage, указав исходный ресурс VirtualDisk подготовленной ВМ:

    d8 k apply -f -<<EOF
    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: VirtualImage
    metadata:
      name: <IMAGE_NAME>
      namespace: <NAMESPACE>
    spec:
      dataSource:
        type: ObjectRef
        objectRef:
          kind: VirtualDisk
          name: <SOURCE_DISK_NAME>
    EOF

    Либо создайте ресурс ClusterVirtualImage, чтобы образ был доступен на уровне кластера для всех проектов:

    d8 k apply -f -<<EOF
    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: ClusterVirtualImage
    metadata:
      name: <IMAGE_NAME>
    spec:
      dataSource:
        type: ObjectRef
        objectRef:
          kind: VirtualDisk
          name: <SOURCE_DISK_NAME>
          namespace: <NAMESPACE>
    EOF

    Здесь <IMAGE_NAME> — имя создаваемого образа, <NAMESPACE> — неймспейс подготовленной машины, а <SOURCE_DISK_NAME> — имя её диска.

  20. Создайте новый ресурс VirtualDisk из полученного образа:

    d8 k apply -f -<<EOF
    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: VirtualDisk
    metadata:
      name: <VM_DISK_NAME>
      namespace: <NAMESPACE>
    spec:
      dataSource:
        type: ObjectRef
        objectRef:
          kind: VirtualImage
          name: <IMAGE_NAME>
    EOF

    Здесь <VM_DISK_NAME> — имя диска новой машины.

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

Подключение к виртуальной машине

К виртуальной машине (ВМ) можно подключиться через серийную консоль (d8 v console) или по VNC (d8 v vnc). Способы используют разные каналы связи с гостевой ОС и зависят от её настройки. Оба способа описаны в разделе «Подключение к виртуальной машине».

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

Почему не работает VNC, если серийная консоль доступна?

VNC выводит изображение экрана гостевой ОС и требует поддержки виртуального терминала в ядре. Серийная консоль при этом работает независимо от графической подсистемы.

Проверьте в гостевой ОС, включена ли поддержка виртуального терминала в конфигурации ядра:

cat /boot/config-$(uname -r) | grep CONFIG_VT

В выводе значение CONFIG_VT должно быть y:

CONFIG_VT=y

Если в выводе указано CONFIG_VT is not set, пересоберите ядро с включённым параметром либо используйте образ ОС с подходящей конфигурацией ядра.

Почему не работает серийная консоль, если VNC доступен?

Серийная консоль подключается к порту ttyS0 в гостевой ОС. Если служба getty для этого порта не запущена, при подключении через d8 v console не появится приглашение ко входу, хотя VNC продолжит работать.

В гостевой ОС включите и запустите службу serial-getty для ttyS0:

sudo systemctl enable --now serial-getty@ttyS0.service

После этого снова подключитесь к серийной консоли.

Конфигурирование виртуальных машин

Как использовать cloud-init для конфигурирования виртуальных машин?

Cloud-init применяется для первичной настройки гостевой ОС при первом запуске. Конфигурация задаётся в YAML и начинается с директивы #cloud-config.

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

Кроме того, в cloud-образах по умолчанию отключён вход в систему. Добавьте SSH-ключи пользователю по умолчанию либо создайте нового пользователя с SSH-доступом, иначе к машине не подключиться.

Обновление и установка пакетов

Пример cloud-config для обновления системы и установки пакетов из списка:

#cloud-config
# Обновить списки пакетов.
package_update: true
# Обновить установленные пакеты до последних версий.
package_upgrade: true
# Список пакетов для установки.
packages:
  - nginx
  - curl
  - htop
# Команды для выполнения после установки пакетов.
runcmd:
  - systemctl enable --now nginx.service

Создание пользователя

Пример cloud-config для создания локального пользователя с паролем и SSH-ключом:

#cloud-config
# Список пользователей для создания.
users:
    # Имя пользователя.
  - name: cloud
    # Хеш пароля.
    passwd: "<PASSWORD_HASH>"
    # Не блокировать учётную запись.
    lock_passwd: false
    # Права sudo без запроса пароля.
    sudo: ALL=(ALL) NOPASSWD:ALL
    # Оболочка по умолчанию.
    shell: /bin/bash
    # SSH-ключи для доступа.
    ssh-authorized-keys:
      - <SSH_PUBLIC_KEY>
# Разрешить аутентификацию по паролю через SSH.
ssh_pwauth: true

Чтобы получить хеш пароля для поля passwd, выполните команду:

mkpasswd --method=SHA-512 --rounds=4096

Создание файла с нужными правами

Пример cloud-config для создания файла с заданными правами доступа:

#cloud-config
# Список файлов для создания.
write_files:
    # Путь к файлу.
  - path: /opt/scripts/start.sh
    # Содержимое файла.
    content: |
      #!/bin/bash
      echo "Starting application"
    # Владелец файла, пользователь и группа.
    owner: cloud:cloud
    # Права доступа в восьмеричном формате.
    permissions: '0755'

Настройка диска и файловой системы

Пример cloud-config для разметки диска, создания файловой системы и монтирования:

#cloud-config
# Настройка разметки диска.
disk_setup:
  # Устройство диска.
  /dev/sdb:
    # Тип таблицы разделов, gpt или mbr.
    table_type: gpt
    # Автоматически создать разделы.
    layout: true
    # Не перезаписывать существующие разделы.
    overwrite: false

# Настройка файловых систем.
fs_setup:
    # Метка файловой системы.
  - label: data
    # Тип файловой системы.
    filesystem: ext4
    # Устройство раздела.
    device: /dev/sdb1
    # Автоматически определить раздел.
    partition: auto

# Монтирование файловых систем.
mounts:
  # [устройство, точка_монтирования, тип_ФС, опции, dump, pass]
  - ["/dev/sdb1", "/mnt/data", "ext4", "defaults", "0", "2"]

Настройка сетевых интерфейсов для дополнительных сетей

Настройки, описанные в этом разделе, применяются только для дополнительных сетей. Основная сеть (Main) настраивается автоматически через cloud-init и не требует ручной конфигурации.

Дополнительные сети настраиваются вручную через cloud-init. Конфигурационные файлы создаёт блок write_files, а применяет настройки блок runcmd.

Подключение дополнительных сетей к виртуальной машине описано в разделе «Дополнительные сетевые интерфейсы».

Ниже приведены примеры для распространённых способов настройки сети в гостевой ОС:

  • systemd-networkd
  • Netplan (Ubuntu)
  • ifcfg (RHEL/CentOS)
  • Alpine Linux

Пример cloud-config для дистрибутивов, использующих systemd-networkd (Debian, CoreOS и др.):

#cloud-config
write_files:
  - path: /etc/systemd/network/10-eth1.network
    content: |
      [Match]
      Name=eth1

      [Network]
      Address=192.168.1.10/24
      Gateway=192.168.1.1
      DNS=8.8.8.8

runcmd:
  - systemctl restart systemd-networkd

Пример cloud-config для Ubuntu и других систем, использующих Netplan:

#cloud-config
write_files:
  - path: /etc/netplan/99-custom.yaml
    content: |
      network:
        version: 2
        ethernets:
          eth1:
            addresses:
              - 10.0.0.5/24
            gateway4: 10.0.0.1
            nameservers:
              addresses: [8.8.8.8]
          eth2:
            dhcp4: true

runcmd:
  - netplan apply

Пример cloud-config для RHEL-совместимых дистрибутивов, использующих схему ifcfg и NetworkManager:

#cloud-config
write_files:
  - path: /etc/sysconfig/network-scripts/ifcfg-eth1
    content: |
      DEVICE=eth1
      BOOTPROTO=none
      ONBOOT=yes
      IPADDR=192.168.1.10
      PREFIX=24
      GATEWAY=192.168.1.1
      DNS1=8.8.8.8

runcmd:
  - nmcli connection reload
  - nmcli connection up eth1

Пример cloud-config для дистрибутивов, использующих традиционный формат /etc/network/interfaces (Alpine и аналоги):

#cloud-config
write_files:
  - path: /etc/network/interfaces
    append: true
    content: |
      auto eth1
      iface eth1 inet static
          address 192.168.1.10
          netmask 255.255.255.0
          gateway 192.168.1.1

runcmd:
  - /etc/init.d/networking restart

Как использовать Ansible для конфигурирования виртуальных машин?

Ansible — инструмент автоматизации, который выполняет задачи на удалённых серверах по протоколу SSH. Ниже показано, как управлять с его помощью виртуальными машинами (ВМ) проекта demo-app.

В рамках примера предполагается, что:

  • в неймспейсе demo-app есть ВМ frontend;
  • в ВМ есть пользователь cloud с доступом по SSH;
  • на машине, где запускается Ansible, приватный SSH-ключ хранится в файле /home/user/.ssh/id_rsa.
  1. Создайте файл inventory.yaml:

    ---
    all:
      vars:
        ansible_ssh_common_args: '-o ProxyCommand="d8 v port-forward --stdio=true %h %p"'
        # Пользователь по умолчанию, для доступа по SSH.
        ansible_user: cloud
        # Путь к приватному ключу.
        ansible_ssh_private_key_file: /home/user/.ssh/id_rsa
      hosts:
        # Имя хоста в формате <VM_NAME>.<NAMESPACE>.
        frontend.demo-app:
  2. Проверьте значение uptime виртуальной машины:

    ansible -m shell -a "uptime" -i inventory.yaml all
    
    # frontend.demo-app | CHANGED | rc=0 >>
    # 12:01:20 up 2 days,  4:59,  0 users,  load average: 0.00, 0.00, 0.00

Если вы не хотите использовать файл inventory, передайте все параметры прямо в командной строке:

ansible -m shell -a "uptime" \
  -i "frontend.demo-app," \
  -e "ansible_ssh_common_args='-o ProxyCommand=\"d8 v port-forward --stdio=true %h %p\"'" \
  -e "ansible_user=cloud" \
  -e "ansible_ssh_private_key_file=/home/user/.ssh/id_rsa" \
  all

Как автоматически сгенерировать inventory для Ansible?

Для использования команды d8 v ansible-inventory требуется версия d8 v0.27.0 или выше.

Команда работает только для виртуальных машин, у которых подключена основная сеть кластера (Main).

Вместо ручного создания inventory-файла можно использовать команду d8 v ansible-inventory, которая автоматически генерирует инвентарь Ansible из виртуальных машин в указанном неймспейсе. Команда совместима с интерфейсом ansible inventory script.

В инвентарь попадают только машины в фазе Running, которым назначен IP-адрес. Имена хостов формируются в формате <VM_NAME>.<NAMESPACE> (например, frontend.demo-app).

  1. При необходимости задайте переменные хоста через аннотации (например, пользователя для SSH):

    d8 k -n demo-app annotate vm frontend vars.ansible.deckhouse.io/ansible_user="cloud"
  2. Запустите Ansible с динамически сформированным инвентарём:

    ANSIBLE_INVENTORY_ENABLED=yaml ansible -m shell -a "uptime" all -i <(d8 v ansible-inventory -n demo-app -o yaml)

Конструкция <(...) необходима, потому что Ansible ожидает файл или скрипт в качестве источника списка хостов. Простое указание команды в кавычках не сработает, потому что Ansible попытается выполнить строку как скрипт. Конструкция <(...) передаёт вывод команды как файл, который Ansible может прочитать.

  1. Либо сохраните инвентарь в файл и выполните проверку:

    d8 v ansible-inventory --list -o yaml -n demo-app > inventory.yaml
    ansible -m shell -a "uptime" -i inventory.yaml all

Как перенаправить трафик на виртуальную машину?

Виртуальная машина работает в кластере Kubernetes, поэтому трафик к ней направляется так же, как к любой другой рабочей нагрузке. За маршрутизацию отвечает стандартный ресурс Kubernetes Service, который выбирает целевые объекты по лейблам.

  1. Создайте сервис с требуемыми настройками.

    В качестве примера приведена виртуальная машина с меткой vm: frontend-0, HTTP-сервисом, опубликованным на портах 80 и 443, и открытым SSH на порту 22:

    apiVersion: virtualization.deckhouse.io/v1alpha2
    kind: VirtualMachine
    metadata:
      name: frontend-0
      namespace: dev
      labels:
        vm: frontend-0
    spec: ...
  2. Чтобы направить сетевой трафик на порты виртуальной машины, создайте сервис:

    Следующий сервис обеспечивает доступ к виртуальной машине: он слушает порты 80 и 443 и перенаправляет трафик на соответствующие порты целевой виртуальной машины. SSH-доступ извне предоставляется по порту 2211:

    apiVersion: v1
    kind: Service
    metadata:
      name: frontend-0-svc
      namespace: dev
    spec:
      type: LoadBalancer
      ports:
      - name: ssh
        port: 2211
        protocol: TCP
        targetPort: 22
      - name: http
        port: 80
        protocol: TCP
        targetPort: 80
      - name: https
        port: 443
        protocol: TCP
        targetPort: 443
      selector:
        vm: frontend-0

Управление платформой

Как увеличить размер DVCR?

Размер тома DVCR задаётся в ModuleConfig модуля virtualization (spec.settings.dvcr.storage.persistentVolumeClaim.size). Новое значение должно быть больше текущего.

  1. Проверьте текущий размер DVCR:

    d8 k get mc virtualization -o jsonpath='{.spec.settings.dvcr.storage.persistentVolumeClaim}'

    Пример вывода:

    {"size":"58G","storageClass":"linstor-thick-data-r1"}
    
  2. Увеличьте size через patch (подставьте нужное значение):

    d8 k patch mc virtualization \
      --type merge -p '{"spec": {"settings": {"dvcr": {"storage": {"persistentVolumeClaim": {"size":"59G"}}}}}}'

    Пример вывода:

    moduleconfig.deckhouse.io/virtualization patched
    
  3. Убедитесь, что в ModuleConfig отображается новый размер:

    d8 k get mc virtualization -o jsonpath='{.spec.settings.dvcr.storage.persistentVolumeClaim}'

    Пример вывода:

    {"size":"59G","storageClass":"linstor-thick-data-r1"}
    
  4. Проверьте текущее состояние DVCR:

    d8 k get pvc dvcr -n d8-virtualization

    Пример вывода:

    NAME STATUS VOLUME                                    CAPACITY    ACCESS MODES   STORAGECLASS           AGE
    dvcr Bound  pvc-6a6cedb8-1292-4440-b789-5cc9d15bbc6b  57617188Ki  RWO            linstor-thick-data-r1  7d
    

Как сменить StorageClass у DVCR, если PVC уже создан?

StorageClass хранилища DVCR можно сменить только пересозданием PVC. При этом теряются все ранее загруженные в DVCR образы, то есть существующие ресурсы ClusterVirtualImage и VirtualImage фактически перестают соответствовать данным в хранилище.

Поле spec.settings.dvcr.storage.persistentVolumeClaim.storageClassName в ModuleConfig модуля virtualization задаёт класс хранения тома хранилища образов виртуальных машин (DVCR). Пока в пространстве имён d8-virtualization существует PVC этого тома, изменить поле через API нельзя.

У уже созданного PVC в Kubernetes нельзя сменить storageClassName, а штатного переноса данных DVCR между классами хранения нет.

Чтобы сменить StorageClass у DVCR, выполните следующие шаги:

  1. Остановите DVCR:

    d8 k -n d8-virtualization scale deployment dvcr --replicas=0
  2. Выведите список PVC в неймспейсе d8-virtualization и найдите PVC тома DVCR:

    d8 k get pvc -n d8-virtualization
  3. Удалите найденный PVC, подставив имя ресурса вместо <PVC_NAME>. Если команда завершается ошибкой из-за недостаточных прав, выполните её от имени system:sudouser:

    d8 k --as system:sudouser -n d8-virtualization delete pvc/<PVC_NAME>
  4. Задайте новый StorageClass в ModuleConfig, подставив нужный класс вместо <STORAGE_CLASS_NAME>:

    d8 k patch mc virtualization --type merge -p '{"spec":{"settings":{"dvcr":{"storage":{"persistentVolumeClaim":{"storageClassName":"<STORAGE_CLASS_NAME>"}}}}}}'

    Пример вывода:

    moduleconfig.deckhouse.io/virtualization patched
    
  5. Запустите DVCR:

    d8 k -n d8-virtualization scale deployment dvcr --replicas=1
  6. Проверьте PVC:

    d8 k get pvc -n d8-virtualization

    Пример вывода:

    NAME   STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          VOLUMEATTRIBUTESCLASS   AGE
    dvcr   Bound    pvc-b43f2e33-32cc-435a-aa1d-b53df35b030a   100Gi      RWO            linstor-thin-r1-hdd   <unset>                 34s
    

Хранилище выбранного StorageClass должно быть доступно на узлах, где запускается DVCR, то есть на system-узлах либо на worker-узлах, если system-узлов в кластере нет.

Как восстановить кластер, если после смены лицензии образы из registry.deckhouse.io не загружаются?

После смены лицензии на кластере с containerd v1 и удаления устаревшей лицензии образы из registry.deckhouse.io могут перестать загружаться. При этом на узлах остаётся устаревший файл конфигурации /etc/containerd/conf.d/dvcr.toml, который не удаляется автоматически. Из-за него не запускается модуль registry, без которого не работает DVCR.

Манифест NodeGroupConfiguration (NGC) после применения удалит файл на узлах. После запуска модуля registry манифест нужно удалить, так как это разовое исправление.

  1. Сохраните манифест в файл (например, containerd-dvcr-remove-old-config.yaml):

    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerd-dvcr-remove-old-config.sh
    spec:
      weight: 32 # Должен быть в диапазоне 32–90.
      nodeGroups: ["*"]
      bundles: ["*"]
      content: |
        # Copyright 2023 Flant JSC
        # Licensed under the Apache License, Version 2.0 (the "License");
        # you may not use this file except in compliance with the License.
        # You may obtain a copy of the License at
        #      http://www.apache.org/licenses/LICENSE-2.0
        # Unless required by applicable law or agreed to in writing, software
        # distributed under the License is distributed on an "AS IS" BASIS,
        # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        # See the License for the specific language governing permissions and
        # limitations under the License.
    
        rm -f /etc/containerd/conf.d/dvcr.toml
  2. Примените сохранённый манифест:

    d8 k apply -f containerd-dvcr-remove-old-config.yaml
  3. Проверьте, что модуль registry запущен:

    d8 k -n d8-system -o yaml get secret registry-state | yq -C -P '.data | del .state | map_values(@base64d) | .conditions = (.conditions | from_yaml)'

    Пример вывода при успешном запуске:

    conditions:
    # ...
      - lastTransitionTime: "..."
        message: ""
        reason: ""
        status: "True"
        type: Ready
  4. Удалите разовый манифест NodeGroupConfiguration:

    d8 k delete -f containerd-dvcr-remove-old-config.yaml

Порядок миграции описан в разделе «Миграция container runtime на containerd v2».