如何使用Docker Desktop / Hyper-V / MobyLinuxVM从Windows主机LAN透明地访问Linux容器

时间:2019-01-20 11:26:11

标签: linux windows docker hyper-v

如何使用Docker Desktop / Hyper-V / MobyLinuxVM从Windows主机LAN透明地访问Linux容器?

我的愿望:

  • 一台Windows 10主机,其局域网在子网169.254.0.0上
  • Linux容器(Docker桌面18.09.1 / Hyper-V / MobyLinuxVM)
  • 与Windows主机位于同一局域网中的Linux容器
  • Windows主机和Linux容器(透明网络)之间没有NAT
  • Linux容器和Windows主机之间的共享目录

1 个答案:

答案 0 :(得分:0)

在经过一周的调查后,如何使用MobyLinuxVM Hyper-V从Windows主机专用LAN透明地访问Linux容器,并且在阅读了许多论坛之后,这里是我的操作方法:

  • 在Windows主机上,安装新的以太网设备Microsoft Loopback KM-TEST
  • 在此设备上配置静态IP 169.254.0.1
  • 在Docker Desktop配置页面上,选择Network Subnet 169.254.0.0
  • 通过以下代码替换C:\ Program Files \ Docker \ Docker \ resources \ MobyLinux.ps1中的代码: (所有修改均以“#BLT”为前缀) (用您的语言用Microsoft KM-TEST以太网设备的描述替换“ XXXXXXXXXXX”

Param(
    [string] $VmName = "MobyLinuxVM",
    [string] $IsoFile = ".\docker-for-win.iso",
    # BLT
    # [string] $SwitchName = "DockerNAT",
    [string] $SwitchName = "Custom",
    [string] $VhdPathOverride = $null,
    [long] $VhdSize = 64*1000*1000*1000,
    [string] $confIsoFile = $null,
    [string] $DockerIsoFile = $null,
    [Parameter(ParameterSetName='Create',Mandatory=$false)][switch] $Create,
    [Parameter(ParameterSetName='Create',Mandatory=$false)][int] $CPUs = 2,
    [Parameter(ParameterSetName='Create',Mandatory=$false)][long] $Memory = 2048,
    [Parameter(ParameterSetName='Create',Mandatory=$false)][string] $SwitchSubnetAddress = "10.0.75.0",
    [Parameter(ParameterSetName='Create',Mandatory=$false)][int] $SwitchSubnetMaskSize = 24,
    [Parameter(ParameterSetName='Destroy',Mandatory=$false)][switch] $Destroy,
    [Parameter(ParameterSetName='Destroy',Mandatory=$false)][switch] $KeepVolume,
    [Parameter(ParameterSetName='Start',Mandatory=$false)][switch] $Start,
    [Parameter(ParameterSetName='Stop',Mandatory=$false)][switch] $Stop
)

Write-Output "Script started at $(Get-Date -Format "HH:mm:ss.fff")"

# This makes sure the system modules can be imported
$env:PSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath','Machine')

# Make sure we stop at Errors unless otherwise explicitly specified
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"

# Explicitly disable Module autoloading and explicitly import the
# Modules this script relies on. This is not strictly necessary but
# good practise as it prevents arbitrary errors
$PSModuleAutoloadingPreference = 'None'

Import-Module Microsoft.PowerShell.Utility
Import-Module Microsoft.PowerShell.Management
Import-Module Hyper-V
Import-Module NetAdapter
Import-Module NetTCPIP

Write-Output "Modules loaded at $(Get-Date -Format "HH:mm:ss.fff")"

function Get-Vhd-Root {
    if($VhdPathOverride){
        return $VhdPathOverride
    }
    # Default location for VHDs
    $VhdRoot = "$((Hyper-V\Get-VMHost -ComputerName localhost).VirtualHardDiskPath)".TrimEnd("\")

    # Where we put Moby
    return "$VhdRoot\$VmName.vhdx"
}

function New-Switch {
    $ipParts = $SwitchSubnetAddress.Split('.')
    [int]$switchIp3 = $null
    [int32]::TryParse($ipParts[3] , [ref]$switchIp3 ) | Out-Null
    $Ip0 = $ipParts[0]
    $Ip1 = $ipParts[1]
    $Ip2 = $ipParts[2]
    $Ip3 = $switchIp3 + 1
    $switchAddress = "$Ip0.$Ip1.$Ip2.$Ip3"

    # BLT
    # $vmSwitch = Hyper-V\Get-VMSwitch $SwitchName -SwitchType Internal -ea SilentlyContinue
    $vmSwitch = Hyper-V\Get-VMSwitch $SwitchName -ea SilentlyContinue
    $vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $SwitchName -ea SilentlyContinue
    if ($vmSwitch -and $vmNetAdapter) {
        Write-Output "Using existing Switch: $SwitchName"
    } else {
        Write-Output "Creating Switch: $SwitchName..."

        Hyper-V\Remove-VMSwitch $SwitchName -Force -ea SilentlyContinue
        # BLT
        # Hyper-V\New-VMSwitch $SwitchName -SwitchType Internal -ea SilentlyContinue | Out-Null
        Hyper-V\New-VMSwitch $SwitchName -NetAdapterInterfaceDescription "XXXXXXXXXXX Microsoft KM-TEST" -ea SilentlyContinue | Out-Null
        $vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $SwitchName

        Write-Output "Switch created."
    }

    # Make sure there are no lingering net adapter
    $netAdapters = Get-NetAdapter | ? { $_.Name.StartsWith("vEthernet ($SwitchName)") }
    if (($netAdapters).Length -gt 1) {
        Write-Output "Disable and rename invalid NetAdapters"

        $now = (Get-Date -Format FileDateTimeUniversal)
        $index = 1
        $invalidNetAdapters =  $netAdapters | ? { $_.DeviceID -ne $vmNetAdapter.DeviceId }

        foreach ($netAdapter in $invalidNetAdapters) {
            $netAdapter `
                | Disable-NetAdapter -Confirm:$false -PassThru `
                | Rename-NetAdapter -NewName "Broken Docker Adapter ($now) ($index)" `
                | Out-Null

            $index++
        }
    }

    # Make sure the Switch has the right IP address
    $networkAdapter = Get-NetAdapter | ? { $_.DeviceID -eq $vmNetAdapter.DeviceId }
    if ($networkAdapter | Get-NetIPAddress -IPAddress $switchAddress -ea SilentlyContinue) {
        $networkAdapter | Disable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue
        $networkAdapter | Enable-NetAdapterBinding  -ComponentID ms_server -ea SilentlyContinue
        Write-Output "Using existing Switch IP address"
        return
    }

    # BLT
    #$networkAdapter | Remove-NetIPAddress -Confirm:$false -ea SilentlyContinue
    #$networkAdapter | Set-NetIPInterface -Dhcp Disabled -ea SilentlyContinue
    #$networkAdapter | New-NetIPAddress -AddressFamily IPv4 -IPAddress $switchAddress -PrefixLength ($SwitchSubnetMaskSize) -ea Stop | Out-Null

    $networkAdapter | Disable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue
    $networkAdapter | Enable-NetAdapterBinding  -ComponentID ms_server -ea SilentlyContinue
    Write-Output "Set IP address on switch"
}

function Remove-Switch {
    Write-Output "Destroying Switch $SwitchName..."

    # Let's remove the IP otherwise a nasty bug makes it impossible
    # to recreate the vswitch
    $vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $SwitchName -ea SilentlyContinue
    if ($vmNetAdapter) {
        $networkAdapter = Get-NetAdapter | ? { $_.DeviceID -eq $vmNetAdapter.DeviceId }
        $networkAdapter | Remove-NetIPAddress -Confirm:$false -ea SilentlyContinue
    }

    Hyper-V\Remove-VMSwitch $SwitchName -Force -ea SilentlyContinue
}

function New-MobyLinuxVM {
    if (!(Test-Path $IsoFile)) {
        Fatal "ISO file at $IsoFile does not exist"
    }

    $CPUs = [Math]::min((Hyper-V\Get-VMHost -ComputerName localhost).LogicalProcessorCount, $CPUs)

    $vm = Hyper-V\Get-VM $VmName -ea SilentlyContinue
    if ($vm) {
        if ($vm.Length -ne 1) {
            Fatal "Multiple VMs exist with the name $VmName. Delete invalid ones or reset Docker to factory defaults."
        }
    } else {
        Write-Output "Creating VM $VmName..."
        $vm = Hyper-V\New-VM -Name $VmName -Generation 2 -NoVHD
        $vm | Hyper-V\Set-VM -AutomaticStartAction Nothing -AutomaticStopAction ShutDown -CheckpointType Disabled
    }

    if ($vm.Generation -ne 2) {
            Fatal "VM $VmName is a Generation $($vm.Generation) VM. It should be a Generation 2."
    }

    if ($vm.State -ne "Off") {
        Write-Output "VM $VmName is $($vm.State). Cannot change its settings."
        return
    }

    Write-Output "Setting CPUs to $CPUs and Memory to $Memory MB"
    $Memory = ([Math]::min($Memory, ($vm | Hyper-V\Get-VMMemory).MaximumPerNumaNode))
    $vm | Hyper-V\Set-VM -MemoryStartupBytes ($Memory*1024*1024) -ProcessorCount $CPUs -StaticMemory

    Ensure-VHD-Path($vm)

    $vmNetAdapter = $vm | Hyper-V\Get-VMNetworkAdapter
    if (!$vmNetAdapter) {
        Write-Output "Attach Net Adapter"
        $vmNetAdapter = $vm | Hyper-V\Add-VMNetworkAdapter -Passthru
    }

    Write-Output "Connect Internal Switch $SwitchName"
    # BLT
    # $vmNetAdapter | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $(Hyper-V\Get-VMSwitch -ComputerName localhost $SwitchName -SwitchType Internal)
    $vmNetAdapter | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $(Hyper-V\Get-VMSwitch -ComputerName localhost $SwitchName)

    if ($vm.DVDDrives) {
        Write-Output "Remove existing DVDs"
        Hyper-V\Remove-VMDvdDrive $vm.DVDDrives -ea SilentlyContinue
    }

    Write-Output "Attach DVD $IsoFile"
    $vm | Hyper-V\Add-VMDvdDrive -Path $IsoFile
    $iso = $vm | Hyper-V\Get-VMFirmware | select -ExpandProperty BootOrder | ? { $_.FirmwarePath.EndsWith("Scsi(0,1)") }
    $vm | Hyper-V\Set-VMFirmware -EnableSecureBoot Off -FirstBootDevice $iso

    $vm | Hyper-V\Set-VMComPort -number 1 -Path "\\.\pipe\docker$VmName-com1"

    # Enable only required VM integration services
    $intSvc = @()
    $intSvc += "Microsoft:$($vm.Id)\84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47" # Heartbeat
    $intSvc += "Microsoft:$($vm.Id)\9F8233AC-BE49-4C79-8EE3-E7E1985B2077" # Shutdown
    $intSvc += "Microsoft:$($vm.Id)\2497F4DE-E9FA-4204-80E4-4B75C46419C0" # TimeSynch
    $vm | Hyper-V\Get-VMIntegrationService | ForEach-Object {
        if ($intSvc -contains $_.Id) {
            Hyper-V\Enable-VMIntegrationService $_
            Write-Output "Enabled $($_.Name)"
        } else {
            Hyper-V\Disable-VMIntegrationService $_
            Write-Output "Disabled $($_.Name)"
        }
    }
    $vm | Hyper-V\Disable-VMConsoleSupport

    Write-Output "VM created."
}

function Remove-MobyLinuxVM {
    Write-Output "Removing VM $VmName..."

    Hyper-V\Remove-VM $VmName -Force -ea SilentlyContinue

    if (!$KeepVolume) {
        $VmVhdFile = Get-Vhd-Root
        Write-Output "Delete VHD $VmVhdFile"
        Remove-Item $VmVhdFile -ea SilentlyContinue
    }
}

function Start-MobyLinuxVM {
    Write-Output "Starting VM $VmName..."

    $vm = Hyper-V\Get-VM $VmName -ea SilentlyContinue

    if ($vm.DVDDrives) {
        Write-Output "Remove existing DVDs"
        Hyper-V\Remove-VMDvdDrive $vm.DVDDrives -ea SilentlyContinue
    }

    Write-Output "Attach DVD $IsoFile"
    $vm | Hyper-V\Add-VMDvdDrive -ControllerNumber 0 -ControllerLocation 1 -Path $IsoFile

    if ((Get-Item $confIsoFile).length -gt 0) {
        Write-Output "Attach Config ISO $confIsoFile"
        if (($vm | Get-VMScsiController).length -le 1) {
            $vm | Add-VMScsiController
        }
        $vm | Hyper-V\Add-VMDvdDrive -ControllerNumber 1 -ControllerLocation 1 -Path $confIsoFile
    }
    if ((Get-Item $DockerIsoFile).length -gt 0) {
        Write-Output "Attach Docker ISO $DockerIsoFile"
        if (($vm | Get-VMScsiController).length -le 2) {
            $vm | Add-VMScsiController
        }
        $vm | Hyper-V\Add-VMDvdDrive -ControllerNumber 2 -ControllerLocation 1 -Path $DockerIsoFile
    }

    Ensure-VHD-Path($vm)

    $iso = $vm | Hyper-V\Get-VMFirmware | select -ExpandProperty BootOrder | ? { $_.FirmwarePath.EndsWith("Scsi(0,1)") }

    $vm | Hyper-V\Set-VMFirmware -EnableSecureBoot Off -BootOrder $iso

    Hyper-V\Start-VM -VMName $VmName
}

function Ensure-VHD-Path {
    Param($vm)

    $VmVhdFile = Get-Vhd-Root
    $vhd = Get-VHD -Path $VmVhdFile -ea SilentlyContinue
    if (!$vhd) {
        Write-Output "Creating dynamic VHD: $VmVhdFile"
        $vhd = New-VHD -ComputerName localhost -Path $VmVhdFile -Dynamic -SizeBytes $VhdSize -BlockSizeBytes 1MB
    }

    if ($vm.HardDrives.Path -ne $VmVhdFile) {
        if ($vm.HardDrives) {
            Write-Output "Remove existing VHDs"
            Hyper-V\Remove-VMHardDiskDrive $vm.HardDrives -ea SilentlyContinue
        }

        Write-Output "Attach VHD $VmVhdFile"
        $vm | Hyper-V\Add-VMHardDiskDrive -Path $VmVhdFile
    }
}

function Stop-MobyLinuxVM {
    $vms = Hyper-V\Get-VM $VmName -ea SilentlyContinue
    if (!$vms) {
        Write-Output "VM $VmName does not exist"
        return
    }

    foreach ($vm in $vms) {
        Stop-VM-Force($vm)
    }
}

function Stop-VM-Force {
    Param($vm)

    if ($vm.State -eq 'Off') {
        Write-Output "VM $VmName is stopped"
        return
    }

    $code = {
        Param($vmId) # Passing the $vm ref is not possible because it will be disposed already

        $vm = Hyper-V\Get-VM -Id $vmId -ea SilentlyContinue
        if (!$vm) {
            Write-Output "VM with Id $vmId does not exist"
            return
        }

        $shutdownService = $vm | Hyper-V\Get-VMIntegrationService -Name Shutdown -ea SilentlyContinue
        if ($shutdownService -and $shutdownService.PrimaryOperationalStatus -eq 'Ok') {
            Write-Output "Shutdown VM $VmName..."
            $vm | Hyper-V\Stop-VM -Confirm:$false -Force -ea SilentlyContinue
            if ($vm.State -eq 'Off') {
                return
            }
        }

        Write-Output "Turn Off VM $VmName..."
        $vm | Hyper-V\Stop-VM -Confirm:$false -TurnOff -Force -ea SilentlyContinue
    }

    Write-Output "Stopping VM $VmName..."
    $job = Start-Job -ScriptBlock $code -ArgumentList $vm.VMId.Guid
    if (Wait-Job $job -Timeout 20) { Receive-Job $job }
    Remove-Job -Force $job -ea SilentlyContinue

    if ($vm.State -eq 'Off') {
        Write-Output "VM $VmName is stopped"
        return
    }

    # If the VM cannot be stopped properly after the timeout
    # then we have to kill the process and wait till the state changes to "Off"
    for ($count = 1; $count -le 10; $count++) {
        $ProcessID = (Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "Name = '$($vm.Id.Guid)'").ProcessID
        if (!$ProcessID) {
            Write-Output "VM $VmName killed. Waiting for state to change"
            for ($count = 1; $count -le 20; $count++) {
                if ($vm.State -eq 'Off') {
                    Write-Output "Killed VM $VmName is off"
                    Remove-Switch
                    $oldKeepVolumeValue = $KeepVolume
                    $KeepVolume = $true
                    Remove-MobyLinuxVM
                    $KeepVolume = $oldKeepVolumeValue
                    return
                }
                Start-Sleep -Seconds 1
            }
            Fatal "Killed VM $VmName did not stop"
        }

        Write-Output "Kill VM $VmName process..."
        Stop-Process $ProcessID -Force -Confirm:$false -ea SilentlyContinue
        Start-Sleep -Seconds 1
    }

    Fatal "Couldn't stop VM $VmName"
}

function Fatal {
    throw "$args"
    Exit 1
}

# Main entry point
Try {
    Switch ($PSBoundParameters.GetEnumerator().Where({$_.Value -eq $true}).Key) {
        'Stop'     { Stop-MobyLinuxVM }
        'Destroy'  { Stop-MobyLinuxVM; Remove-Switch; Remove-MobyLinuxVM }
        'Create'   { New-Switch; New-MobyLinuxVM }
        'Create'   { New-MobyLinuxVM }
        'Start'    { Start-MobyLinuxVM }
    }
} Catch {
    throw
    Exit 1
}

Param( [string] $VmName = "MobyLinuxVM", [string] $IsoFile = ".\docker-for-win.iso", # BLT # [string] $SwitchName = "DockerNAT", [string] $SwitchName = "Custom", [string] $VhdPathOverride = $null, [long] $VhdSize = 64*1000*1000*1000, [string] $confIsoFile = $null, [string] $DockerIsoFile = $null, [Parameter(ParameterSetName='Create',Mandatory=$false)][switch] $Create, [Parameter(ParameterSetName='Create',Mandatory=$false)][int] $CPUs = 2, [Parameter(ParameterSetName='Create',Mandatory=$false)][long] $Memory = 2048, [Parameter(ParameterSetName='Create',Mandatory=$false)][string] $SwitchSubnetAddress = "10.0.75.0", [Parameter(ParameterSetName='Create',Mandatory=$false)][int] $SwitchSubnetMaskSize = 24, [Parameter(ParameterSetName='Destroy',Mandatory=$false)][switch] $Destroy, [Parameter(ParameterSetName='Destroy',Mandatory=$false)][switch] $KeepVolume, [Parameter(ParameterSetName='Start',Mandatory=$false)][switch] $Start, [Parameter(ParameterSetName='Stop',Mandatory=$false)][switch] $Stop ) Write-Output "Script started at $(Get-Date -Format "HH:mm:ss.fff")" # This makes sure the system modules can be imported $env:PSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath','Machine') # Make sure we stop at Errors unless otherwise explicitly specified $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" # Explicitly disable Module autoloading and explicitly import the # Modules this script relies on. This is not strictly necessary but # good practise as it prevents arbitrary errors $PSModuleAutoloadingPreference = 'None' Import-Module Microsoft.PowerShell.Utility Import-Module Microsoft.PowerShell.Management Import-Module Hyper-V Import-Module NetAdapter Import-Module NetTCPIP Write-Output "Modules loaded at $(Get-Date -Format "HH:mm:ss.fff")" function Get-Vhd-Root { if($VhdPathOverride){ return $VhdPathOverride } # Default location for VHDs $VhdRoot = "$((Hyper-V\Get-VMHost -ComputerName localhost).VirtualHardDiskPath)".TrimEnd("\") # Where we put Moby return "$VhdRoot\$VmName.vhdx" } function New-Switch { $ipParts = $SwitchSubnetAddress.Split('.') [int]$switchIp3 = $null [int32]::TryParse($ipParts[3] , [ref]$switchIp3 ) | Out-Null $Ip0 = $ipParts[0] $Ip1 = $ipParts[1] $Ip2 = $ipParts[2] $Ip3 = $switchIp3 + 1 $switchAddress = "$Ip0.$Ip1.$Ip2.$Ip3" # BLT # $vmSwitch = Hyper-V\Get-VMSwitch $SwitchName -SwitchType Internal -ea SilentlyContinue $vmSwitch = Hyper-V\Get-VMSwitch $SwitchName -ea SilentlyContinue $vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $SwitchName -ea SilentlyContinue if ($vmSwitch -and $vmNetAdapter) { Write-Output "Using existing Switch: $SwitchName" } else { Write-Output "Creating Switch: $SwitchName..." Hyper-V\Remove-VMSwitch $SwitchName -Force -ea SilentlyContinue # BLT # Hyper-V\New-VMSwitch $SwitchName -SwitchType Internal -ea SilentlyContinue | Out-Null Hyper-V\New-VMSwitch $SwitchName -NetAdapterInterfaceDescription "XXXXXXXXXXX Microsoft KM-TEST" -ea SilentlyContinue | Out-Null $vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $SwitchName Write-Output "Switch created." } # Make sure there are no lingering net adapter $netAdapters = Get-NetAdapter | ? { $_.Name.StartsWith("vEthernet ($SwitchName)") } if (($netAdapters).Length -gt 1) { Write-Output "Disable and rename invalid NetAdapters" $now = (Get-Date -Format FileDateTimeUniversal) $index = 1 $invalidNetAdapters = $netAdapters | ? { $_.DeviceID -ne $vmNetAdapter.DeviceId } foreach ($netAdapter in $invalidNetAdapters) { $netAdapter ` | Disable-NetAdapter -Confirm:$false -PassThru ` | Rename-NetAdapter -NewName "Broken Docker Adapter ($now) ($index)" ` | Out-Null $index++ } } # Make sure the Switch has the right IP address $networkAdapter = Get-NetAdapter | ? { $_.DeviceID -eq $vmNetAdapter.DeviceId } if ($networkAdapter | Get-NetIPAddress -IPAddress $switchAddress -ea SilentlyContinue) { $networkAdapter | Disable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue $networkAdapter | Enable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue Write-Output "Using existing Switch IP address" return } # BLT #$networkAdapter | Remove-NetIPAddress -Confirm:$false -ea SilentlyContinue #$networkAdapter | Set-NetIPInterface -Dhcp Disabled -ea SilentlyContinue #$networkAdapter | New-NetIPAddress -AddressFamily IPv4 -IPAddress $switchAddress -PrefixLength ($SwitchSubnetMaskSize) -ea Stop | Out-Null $networkAdapter | Disable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue $networkAdapter | Enable-NetAdapterBinding -ComponentID ms_server -ea SilentlyContinue Write-Output "Set IP address on switch" } function Remove-Switch { Write-Output "Destroying Switch $SwitchName..." # Let's remove the IP otherwise a nasty bug makes it impossible # to recreate the vswitch $vmNetAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $SwitchName -ea SilentlyContinue if ($vmNetAdapter) { $networkAdapter = Get-NetAdapter | ? { $_.DeviceID -eq $vmNetAdapter.DeviceId } $networkAdapter | Remove-NetIPAddress -Confirm:$false -ea SilentlyContinue } Hyper-V\Remove-VMSwitch $SwitchName -Force -ea SilentlyContinue } function New-MobyLinuxVM { if (!(Test-Path $IsoFile)) { Fatal "ISO file at $IsoFile does not exist" } $CPUs = [Math]::min((Hyper-V\Get-VMHost -ComputerName localhost).LogicalProcessorCount, $CPUs) $vm = Hyper-V\Get-VM $VmName -ea SilentlyContinue if ($vm) { if ($vm.Length -ne 1) { Fatal "Multiple VMs exist with the name $VmName. Delete invalid ones or reset Docker to factory defaults." } } else { Write-Output "Creating VM $VmName..." $vm = Hyper-V\New-VM -Name $VmName -Generation 2 -NoVHD $vm | Hyper-V\Set-VM -AutomaticStartAction Nothing -AutomaticStopAction ShutDown -CheckpointType Disabled } if ($vm.Generation -ne 2) { Fatal "VM $VmName is a Generation $($vm.Generation) VM. It should be a Generation 2." } if ($vm.State -ne "Off") { Write-Output "VM $VmName is $($vm.State). Cannot change its settings." return } Write-Output "Setting CPUs to $CPUs and Memory to $Memory MB" $Memory = ([Math]::min($Memory, ($vm | Hyper-V\Get-VMMemory).MaximumPerNumaNode)) $vm | Hyper-V\Set-VM -MemoryStartupBytes ($Memory*1024*1024) -ProcessorCount $CPUs -StaticMemory Ensure-VHD-Path($vm) $vmNetAdapter = $vm | Hyper-V\Get-VMNetworkAdapter if (!$vmNetAdapter) { Write-Output "Attach Net Adapter" $vmNetAdapter = $vm | Hyper-V\Add-VMNetworkAdapter -Passthru } Write-Output "Connect Internal Switch $SwitchName" # BLT # $vmNetAdapter | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $(Hyper-V\Get-VMSwitch -ComputerName localhost $SwitchName -SwitchType Internal) $vmNetAdapter | Hyper-V\Connect-VMNetworkAdapter -VMSwitch $(Hyper-V\Get-VMSwitch -ComputerName localhost $SwitchName) if ($vm.DVDDrives) { Write-Output "Remove existing DVDs" Hyper-V\Remove-VMDvdDrive $vm.DVDDrives -ea SilentlyContinue } Write-Output "Attach DVD $IsoFile" $vm | Hyper-V\Add-VMDvdDrive -Path $IsoFile $iso = $vm | Hyper-V\Get-VMFirmware | select -ExpandProperty BootOrder | ? { $_.FirmwarePath.EndsWith("Scsi(0,1)") } $vm | Hyper-V\Set-VMFirmware -EnableSecureBoot Off -FirstBootDevice $iso $vm | Hyper-V\Set-VMComPort -number 1 -Path "\\.\pipe\docker$VmName-com1" # Enable only required VM integration services $intSvc = @() $intSvc += "Microsoft:$($vm.Id)\84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47" # Heartbeat $intSvc += "Microsoft:$($vm.Id)\9F8233AC-BE49-4C79-8EE3-E7E1985B2077" # Shutdown $intSvc += "Microsoft:$($vm.Id)\2497F4DE-E9FA-4204-80E4-4B75C46419C0" # TimeSynch $vm | Hyper-V\Get-VMIntegrationService | ForEach-Object { if ($intSvc -contains $_.Id) { Hyper-V\Enable-VMIntegrationService $_ Write-Output "Enabled $($_.Name)" } else { Hyper-V\Disable-VMIntegrationService $_ Write-Output "Disabled $($_.Name)" } } $vm | Hyper-V\Disable-VMConsoleSupport Write-Output "VM created." } function Remove-MobyLinuxVM { Write-Output "Removing VM $VmName..." Hyper-V\Remove-VM $VmName -Force -ea SilentlyContinue if (!$KeepVolume) { $VmVhdFile = Get-Vhd-Root Write-Output "Delete VHD $VmVhdFile" Remove-Item $VmVhdFile -ea SilentlyContinue } } function Start-MobyLinuxVM { Write-Output "Starting VM $VmName..." $vm = Hyper-V\Get-VM $VmName -ea SilentlyContinue if ($vm.DVDDrives) { Write-Output "Remove existing DVDs" Hyper-V\Remove-VMDvdDrive $vm.DVDDrives -ea SilentlyContinue } Write-Output "Attach DVD $IsoFile" $vm | Hyper-V\Add-VMDvdDrive -ControllerNumber 0 -ControllerLocation 1 -Path $IsoFile if ((Get-Item $confIsoFile).length -gt 0) { Write-Output "Attach Config ISO $confIsoFile" if (($vm | Get-VMScsiController).length -le 1) { $vm | Add-VMScsiController } $vm | Hyper-V\Add-VMDvdDrive -ControllerNumber 1 -ControllerLocation 1 -Path $confIsoFile } if ((Get-Item $DockerIsoFile).length -gt 0) { Write-Output "Attach Docker ISO $DockerIsoFile" if (($vm | Get-VMScsiController).length -le 2) { $vm | Add-VMScsiController } $vm | Hyper-V\Add-VMDvdDrive -ControllerNumber 2 -ControllerLocation 1 -Path $DockerIsoFile } Ensure-VHD-Path($vm) $iso = $vm | Hyper-V\Get-VMFirmware | select -ExpandProperty BootOrder | ? { $_.FirmwarePath.EndsWith("Scsi(0,1)") } $vm | Hyper-V\Set-VMFirmware -EnableSecureBoot Off -BootOrder $iso Hyper-V\Start-VM -VMName $VmName } function Ensure-VHD-Path { Param($vm) $VmVhdFile = Get-Vhd-Root $vhd = Get-VHD -Path $VmVhdFile -ea SilentlyContinue if (!$vhd) { Write-Output "Creating dynamic VHD: $VmVhdFile" $vhd = New-VHD -ComputerName localhost -Path $VmVhdFile -Dynamic -SizeBytes $VhdSize -BlockSizeBytes 1MB } if ($vm.HardDrives.Path -ne $VmVhdFile) { if ($vm.HardDrives) { Write-Output "Remove existing VHDs" Hyper-V\Remove-VMHardDiskDrive $vm.HardDrives -ea SilentlyContinue } Write-Output "Attach VHD $VmVhdFile" $vm | Hyper-V\Add-VMHardDiskDrive -Path $VmVhdFile } } function Stop-MobyLinuxVM { $vms = Hyper-V\Get-VM $VmName -ea SilentlyContinue if (!$vms) { Write-Output "VM $VmName does not exist" return } foreach ($vm in $vms) { Stop-VM-Force($vm) } } function Stop-VM-Force { Param($vm) if ($vm.State -eq 'Off') { Write-Output "VM $VmName is stopped" return } $code = { Param($vmId) # Passing the $vm ref is not possible because it will be disposed already $vm = Hyper-V\Get-VM -Id $vmId -ea SilentlyContinue if (!$vm) { Write-Output "VM with Id $vmId does not exist" return } $shutdownService = $vm | Hyper-V\Get-VMIntegrationService -Name Shutdown -ea SilentlyContinue if ($shutdownService -and $shutdownService.PrimaryOperationalStatus -eq 'Ok') { Write-Output "Shutdown VM $VmName..." $vm | Hyper-V\Stop-VM -Confirm:$false -Force -ea SilentlyContinue if ($vm.State -eq 'Off') { return } } Write-Output "Turn Off VM $VmName..." $vm | Hyper-V\Stop-VM -Confirm:$false -TurnOff -Force -ea SilentlyContinue } Write-Output "Stopping VM $VmName..." $job = Start-Job -ScriptBlock $code -ArgumentList $vm.VMId.Guid if (Wait-Job $job -Timeout 20) { Receive-Job $job } Remove-Job -Force $job -ea SilentlyContinue if ($vm.State -eq 'Off') { Write-Output "VM $VmName is stopped" return } # If the VM cannot be stopped properly after the timeout # then we have to kill the process and wait till the state changes to "Off" for ($count = 1; $count -le 10; $count++) { $ProcessID = (Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "Name = '$($vm.Id.Guid)'").ProcessID if (!$ProcessID) { Write-Output "VM $VmName killed. Waiting for state to change" for ($count = 1; $count -le 20; $count++) { if ($vm.State -eq 'Off') { Write-Output "Killed VM $VmName is off" Remove-Switch $oldKeepVolumeValue = $KeepVolume $KeepVolume = $true Remove-MobyLinuxVM $KeepVolume = $oldKeepVolumeValue return } Start-Sleep -Seconds 1 } Fatal "Killed VM $VmName did not stop" } Write-Output "Kill VM $VmName process..." Stop-Process $ProcessID -Force -Confirm:$false -ea SilentlyContinue Start-Sleep -Seconds 1 } Fatal "Couldn't stop VM $VmName" } function Fatal { throw "$args" Exit 1 } # Main entry point Try { Switch ($PSBoundParameters.GetEnumerator().Where({$_.Value -eq $true}).Key) { 'Stop' { Stop-MobyLinuxVM } 'Destroy' { Stop-MobyLinuxVM; Remove-Switch; Remove-MobyLinuxVM } 'Create' { New-Switch; New-MobyLinuxVM } 'Create' { New-MobyLinuxVM } 'Start' { Start-MobyLinuxVM } } } Catch { throw Exit 1 }

  • 从Docker桌面设置中,重新启动docker桌面
  • 如果出现问题,请退出Docker Desktop并重新启动
  • 使用选项 -v (供您共享)和-network = host
  • 启动容器Linux
  • 您应该能够从Linux ping到Windows(ping 169.254.0.1)以及从Windows到Linux(ping 169.254.0.2)

希望它能起作用!