Вложенная виртуализация (Nested Virtualization) на Microsoft Hyper-V: различия между версиями

Материал из Home wiki
Перейти к навигации Перейти к поиску
 
Строка 30: Строка 30:
 
[[Файл:windows-10-nested-hyper-v-5.png|400px]]
 
[[Файл:windows-10-nested-hyper-v-5.png|400px]]
  
адо отметить, что для вложенных виртуальных машин не поддерживаются следующие операции (как минимум):
+
Надо отметить, что для вложенных виртуальных машин не поддерживаются следующие операции (как минимум):
 
*Dynamic Memory
 
*Dynamic Memory
 
*Горячее изменение памяти работающей ВМ
 
*Горячее изменение памяти работающей ВМ

Текущая версия на 14:20, 15 августа 2018

Вложенная виртуализация (Nested Virtualization) на Microsoft Hyper-V впервые появилась в Windows 10.

Мы часто пишем о проблемах вложенной виртуализации (nested virtualization), которая представляет собой способ запустить виртуальные машины на гипервизоре, который сам установлен в виртуальной машине. На платформе VMware ESXi эта проблема давно решена, и там nested virtualization работает уже давно, мало того - для виртуального ESXi есть даже свои VMware Tools.

Как мы недавно писали, решения для вложенной виртуализации на платформе Microsoft Hyper-V не было. При попытке запустить виртуальную машину на виртуальном хосте Hyper-V администраторы получали вот такую ошибку:

VM failed to start. Failed to start the virtual machine because one of the Hyper-V components is not running.

Hyper-v-in-hyper-v-4.png

Все это происходило из-за того, что Microsoft не хотела предоставлять виртуальным машинам средства эмуляции техник аппаратной виртуализации Intel VT и AMD-V. Стандартная схема виртуализации Hyper-V выглядела так:

Windows-10-nested-hyper-v-1.png

Virtualization Extensions передавались только гипервизору хостовой ОС, но не гостевой ОС виртуальной машины. А без этих расширений виртуальные машины в гостевой ОС запущены быть не могли.

В Windows 10 Build 10565 (этот превью-билд можно скачать по этой ссылке для x64 и по этой для x86) компания Microsoft, наконец-то, решила поменять ситуацию: теперь Virtualization Extensions передаются в гостевую ОС:

Windows-10-nested-hyper-v-2.png

Соответственно, это позволит запустить вложенные виртуальные машины на хосте, который сам является виртуальной машиной с гостевой ОС Windows и включенной ролью Hyper-V.

На данный момент работает это только с Intel VT, но по идее к релизу Windows Server 2016 должно заработать и для AMD-V.

Сфера применения вложенных виртуальных машин не такая уж и узкая. Сейчас видится 2 основных направления:

  • Виртуальные тестовые лаборатории, где развертываются виртуальные серверы Hyper-V и строятся модели работающих виртуальных инфраструктур в рамках одного физического компьютера.
  • Использование контейнеризованных приложений (например, на движке Docker) в виртуальных машинах на виртуальных хостах Hyper-V. Более подробно о Windows Server Containers можно почитать вот тут. Немногим ранее мы писали об аналогичной архитектуре на базе VMware vSphere, анонсированной на VMware VMworld 2015.

Windows-10-nested-hyper-v-5.png

Надо отметить, что для вложенных виртуальных машин не поддерживаются следующие операции (как минимум):

  • Dynamic Memory
  • Горячее изменение памяти работающей ВМ
  • Снапшоты
  • Live Migration
  • Операции save/restore

Ну и вообще, вряд ли вложенная виртуализация вообще когда-нибудь будет полностью поддерживаться в производственной среде. Кстати, вложенная виртуализация на сторонних гипервизорах (например, VMware vSphere) также не поддерживается.

Для того, чтобы вложенная виртуализация заработала, нужно:

1. Использовать Windows 10 Build 10565. Windows Server 2016 Technical Preview 3 (TPv3) и Windows 10 GA - не подойдут, так как в них возможности Nested Virtualization нет.

2. Включить Mac Spoofing на сетевом адаптере виртуального хоста Hyper-V, так как виртуальный коммутатор хостового Hyper-V будет видеть несколько MAC-адресов с этого виртуального адаптера.

3. В Windows 10 нужно отключить фичу Virtualization Based Security (VBS), которая предотвращает трансляцию Virtualization Extensions в виртуальные машины.

4. Предусмотреть память под сам гостевой гипервизор и под все запущенные виртуальные машины. Память для гипервизора можно рассчитать так:

Windows-10-nested-hyper-v-6.png

Теперь для того чтобы включить Nested Virtualization, выполните следующий сценарий PowerShell (в нем и VBS отключается, и Mac Spoofing включается):

Invoke-WebRequest https://raw.githubusercontent.com/Microsoft/Virtualization-Documentation/master/hyperv-tools/Nested/Enable-NestedVm.ps1 -OutFile ~/Enable-NestedVm.ps1
~/Enable-NestedVm.ps1 -VmName <VmName>

Windows-10-nested-hyper-v-3.png

Далее создайте виртуальную машину на базе Windows 10 Build 10565, и включите в ней Mac Spoofing (если вы не сделали это в скрипте). Сделать это можно через настройки ВМ или следующей командой PowerShell:

Set-VMNetworkAdapter -VMName <VMName> -MacAddressSpoofing on

Ну а дальше просто запускайте виртуальную машину на виртуальном хосте Hyper-V:

Windows-10-nested-hyper-v-4.png

Скрипт Enable-NestedVm.ps1

param([string]$vmName)
#
# Enable-NestedVm.ps1
#
# Checks VM for nesting comatability and configures if not properly setup.
#
# Author: Drew Cross

if([string]::IsNullOrEmpty($vmName)) {
    Write-Host "No VM name passed"
    Exit;
}

# Constants
$4GB = 4294967296

#
# Need to run elevated.  Do that here.
#

# Get the ID and security principal of the current user account
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent();
$myWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($myWindowsID);

# Get the security principal for the administrator role
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;

# Check to see if we are currently running as an administrator
if ($myWindowsPrincipal.IsInRole($adminRole)) {
    # We are running as an administrator, so change the title and background colour to indicate this
    $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)";

    } else {
    # We are not running as an administrator, so relaunch as administrator

    # Create a new process object that starts PowerShell
    $newProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";

    # Specify the current script path and name as a parameter with added scope and support for scripts with spaces in it's path
    $newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"

    # Indicate that the process should be elevated
    $newProcess.Verb = "runas";

    # Start the new process
    [System.Diagnostics.Process]::Start($newProcess) | Out-Null;

    # Exit from the current, unelevated, process
    Exit;
    }

#
# Get Vm Information
#

$vm = Get-VM -Name $vmName

$vmInfo = New-Object PSObject
    
# VM info
Add-Member -InputObject $vmInfo NoteProperty -Name "ExposeVirtualizationExtensions" -Value $false
Add-Member -InputObject $vmInfo NoteProperty -Name "DynamicMemoryEnabled" -Value $vm.DynamicMemoryEnabled
Add-Member -InputObject $vmInfo NoteProperty -Name "SnapshotEnabled" -Value $false
Add-Member -InputObject $vmInfo NoteProperty -Name "State" -Value $vm.State
Add-Member -InputObject $vmInfo NoteProperty -Name "MacAddressSpoofing" -Value ((Get-VmNetworkAdapter -VmName $vmName).MacAddressSpoofing)
Add-Member -InputObject $vmInfo NoteProperty -Name "MemorySize" -Value (Get-VMMemory -VmName $vmName).Startup


# is nested enabled on this VM?
$vmInfo.ExposeVirtualizationExtensions = (Get-VMProcessor -VM $vm).ExposeVirtualizationExtensions

Write-Host "This script will set the following for $vmName in order to enable nesting:"
    
$prompt = $false;

# Output text for proposed actions
if ($vmInfo.State -eq 'Saved') {
    Write-Host "\tSaved state will be removed"
    $prompt = $true
}
if ($vmInfo.State -ne 'Off' -or $vmInfo.State -eq 'Saved') {
    Write-Host "Vm State:" $vmInfo.State
    Write-Host "    $vmName will be turned off"
    $prompt = $true         
}
if ($vmInfo.ExposeVirtualizationExtensions -eq $false) {
    Write-Host "    Virtualization extensions will be enabled"
    $prompt = $true
}
if ($vmInfo.DynamicMemoryEnabled -eq $true) {
    Write-Host "    Dynamic memory will be disabled"
    $prompt = $true
}
if($vmInfo.MacAddressSpoofing -eq 'Off'){
    Write-Host "    Optionally enable mac address spoofing"
    $prompt = $true
}
if($vmInfo.MemorySize -lt $4GB) {
    Write-Host "    Optionally set vm memory to 4GB"
    $prompt = $true
}

if(-not $prompt) {
    Write-Host "    None, vm is already setup for nesting"
    Exit;
}

Write-Host "Input Y to accept or N to cancel:" -NoNewline

$char = Read-Host

while(-not ($char.StartsWith('Y') -or $char.StartsWith('N'))) {
    Write-Host "Invalid Input, Y or N" 
    $char = Read-Host
}


if($char.StartsWith('Y')) {
    if ($vmInfo.State -eq 'Saved') {
        Remove-VMSavedState -VMName $vmName
    }
    if ($vmInfo.State -ne 'Off' -or $vmInfo.State -eq 'Saved') {
        Stop-VM -VMName $vmName
    }
    if ($vmInfo.ExposeVirtualizationExtensions -eq $false) {
        Set-VMProcessor -VMName $vmName -ExposeVirtualizationExtensions $true
    }
    if ($vmInfo.DynamicMemoryEnabled -eq $true) {
        Set-VMMemory -VMName $vmName -DynamicMemoryEnabled $false
    }

    # Optionally turn on mac spoofing
    if($vmInfo.MacAddressSpoofing -eq 'Off') {
        Write-Host "Mac Address Spoofing isn't enabled (nested guests won't have network)." -ForegroundColor Yellow 
        Write-Host "Would you like to enable MAC address spoofing? (Y/N)" -NoNewline
        $input = Read-Host

        if($input -eq 'Y') {
            Set-VMNetworkAdapter -VMName $vmName -MacAddressSpoofing on
        }
        else {
            Write-Host "Not enabling Mac address spoofing."
        }

    }

    if($vmInfo.MemorySize -lt $4GB) {
        Write-Host "VM memory is set less than 4GB, without 4GB or more, you may not be able to start VMs." -ForegroundColor Yellow
        Write-Host "Would you like to set Vm memory to 4GB? (Y/N)" -NoNewline
        $input = Read-Host 

        if($input -eq 'Y') {
            Set-VMMemory -VMName $vmName -StartupBytes $4GB
        }
        else {
            Write-Host "Not setting Vm Memory to 4GB."
        }
    }
    Exit;
}

if($char.StartsWith('N')) {
    Write-Host "Exiting..."
    Exit;
}

Write-Host 'Invalid input'