笔记

笔记

我在 Windows 10 主机上的 Hyper-V 中安装了 Ubuntu 18.04,并公开了一些端口上的功能,这些端口可供主机上运行的其他程序在访问时使用http://vm-ip-number:port(即http://192.168.0.1:12345)。效果很好。

我想使用名称而不是虚拟机的 IP 号来连接它(出于所有常见原因,因为这将进入配置文件)。对于完整的 Linux 或 MacOS 服务器,我会让主机使用 zeroconf 宣布自己,但一些搜索表明,这对于跨虚拟机边界的 Windows 10 来说效果不佳。

另一种方法可能是让 Hyper-V 为虚拟机分配一个我提供的名称,然后在虚拟机启动时使用该名称,但我对 Hyper-V 不够熟悉,不知道这是否可行。在客户机中安装额外的工具是可以接受的,仅在必要时在主机中安装。

我所希望的是在运行时能够http://my-own-name访问虚拟机,而无需在任何地方对 IP 号码进行硬编码。

有什么建议吗?

答案1

以下 PowerShell 脚本将生成一个hosts包含所有正在运行的虚拟机的 IP 地址和虚拟机名称的文件:

get-vm | Get-VMNetworkAdapter | ? IPAddresses -ne $null | % {Write-Output "$($_.ipaddresses[0]) $($_.VMName)"} | Set-Content -Path ".\hosts"

需要注意的几点:

  • 该脚本需要以管理员身份运行。

  • 设置内容 命令需要更好的 Path 参数,因为目前它将覆盖hosts当前文件夹中的文件。

  • 如果你不想hosts覆盖文件,而是附加文件,请使用 添加内容 命令。这样,您可以从初始hosts文件开始,然后将其附加到该文件。

  • 写入输出 命令输出字段中的第一个条目ipaddresses。如果同时启用了 IPv4 和 IPv6,列表将包含两个条目,其中第一个条目通常是 IPv4 的条目。

hosts该脚本为我正在运行的虚拟机生成的示例文件:

172.17.223.121 Windows10Test

(最好避免在虚拟机名称中使用空格,否则需要在脚本上进行更多工作。)

答案2

在 Windows HOSTS 文件中添加 IP 地址。完整路径为C:\Windows\System32\drivers\etc\hosts。以管理员身份运行记事本(或任何文本编辑器),然后使用Ctrl+打开该文件O。然后添加 IP 地址。格式为 IP 地址,然后是来宾名称。以下是示例:

# This is a comment
# 127.0.0.1 localhost loopback
# ::1 localhost

192.168.0.1 myguest

如需了解更多信息,请阅读维基百科:主机(文件)

答案3

我找到了一种通过名称从其他来宾虚拟机或主机连接到来宾虚拟机的方法,使用 Hyper-V 自动管理的名称,如下所示:

  1. 将客户机连接到默认交换机(这是新虚拟机的默认设置)。
  2. 在客户机上将 IP 地址设置为 DHCP(这是新虚拟机的默认设置)。
  3. 将客户操作系统中的计算机名称/主机名设置为唯一的名称,例如myfirstvm。DNS 后缀无关紧要。
  4. 从另一台虚拟机或主机尝试联系<host_name>.mshome.net,例如,如果将主机名设置为myfirstvm,则尝试myfirstvm.mshome.net从主机 ping 操作。

解释:

hosts在我的系统上,我在与该文件相同的目录中找到了一个名为的文件hosts.ics。它似乎包含所有附加到Default Switch 以及基础系统的条目,但都带有后缀mshome.net。我相信连接到默认交换机的网络适配器使用 Internet 连接共享功能,该功能会自动创建和维护此hosts.ics文件。当您的客户机启动时,它会通过 DHCP 请求 IP 地址并尝试向 Hyper-V 内置的 DNS 服务器注册自己。我相信 Hyper-V 会采用客户机尝试注册的名称并将其存储在此hosts.ics文件中,并使用新的后缀mshome.net。如果您的两个客户虚拟机都尝试注册相同的名称,则此过程将中断。

不确定这是新功能还是一直都有。在 Windows 11 Pro、Hyper-V 10.0.22000.1 上发现

答案4

此 powershell 脚本基于卢克·施莱瑟的回答(https://superuser.com/a/1485949/280673)来回答这个问题。

最重要的区别是它不会用虚拟机的 IP 地址覆盖整个 hosts 文件,而是在有新虚拟机时附加新条目并覆盖现有条目的地址。这样就无需进行备份,并保留了 hosts 文件中您可能为本地开发或其他目的而创建的其他条目。

#Requires -RunAsAdministrator

# Based on: https://superuser.com/a/1485949

Write-Host "Fix entries for local HyperV VMs in hosts file."

Set-StrictMode -Version 3.0
$ErrorActionPreference = "Stop"
$hostsFile = "${env:SystemRoot}/System32/drivers/etc/hosts"

$hostsFileContentOriginal = (Get-Content $hostsFile)
$hostsFileContent = $hostsFileContentOriginal

Write-Debug "Original hosts file content:"
Write-Debug ($hostsFileContent | Out-String)
Write-Debug "`n`n"

foreach ($vm in (Get-VM | Get-VMNetworkAdapter))
{
   $vmName = $($vm.VMName)
   $vmNameSanitized = $vm.VMName.ToLower() -replace '[. ]',''
   $hostName = "${vmNameSanitized}.local"
   Write-Verbose "Porcessing $vm."
   if (0 -lt $vm.IPAddresses.Length)
   {
      Write-Verbose "VM ${vmname} has IP addresses, updating hosts file."
      $address = $vm.IPAddresses[0]
      Write-Verbose "Selected IP Address for ${vmname}: $address."
      Write-Debug "All IP Addresses for ${vmname}: $($vm.IPAddresses)."

      $vmPreviouslyAddedToHostsFile = $hostsFileContent -match ".*$hostName.*"
      if($vmPreviouslyAddedToHostsFile)
      {
         Write-Verbose "VM $vmName already present in hosts, updating entry."
         # # Regex explanation
         # - `([^\d]*)` is a grouping `()` of 0 or more of the character group `[]*` that contains no digits `^\d`.
         # - `(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})` is a grouping `()` of 4 collections of 1 to 3 digits `\d{1,3}`, separated by a dot `\.`, which should match an ipv4 address.
         # - `(\s+$hostName.*)` is a grouping `()` that matches 1 or more spaces `\s+`, followed by the content of the hostname variable `$hostName`, followed by 0 or more of any characters `.*`.
         # # Substitution expression explanation
         # `${n}` is used to access the n-th (1 indexed) grouping from the regex.
         # Because we want to access the `$address` variable we can't use literal strings `''` but have to use normal strings instead `""`.
         # This results in having to escape the `$` character when doing the group substitution access.
         # # Caveat emptor
         # This only works for ipv4 addresses as written.
         # If you want to support ipv6 addresses, consider using `([a-f0-9:]+:+)+[a-f0-9]+` from https://stackoverflow.com/a/37355379 .
         # It will fail in interesting ways if the entry is commented out and there are digits in the comment in a position preceding the address.
         $hostsFileContent = $hostsFileContent -replace "([^\d]*)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\s+$hostName.*)","`${1}$address`${3}" -join "`r`n"
      }
      else
      {
         Write-Verbose "VM $vmName not previously in hosts, appending entry."
         $hostsFileContent += "$address $hostName" -join "`r`n"
      }
   }
   else
   {
      Write-Verbose "VM ${vmname} doesn't have IP addresses, skipping."
   }
}

$originalAsString = ($hostsFileContentOriginal | Out-String)
$updatedAsString = ($hostsFileContent | Out-String)

Write-Debug "`n`nUpdated hosts file content:"
Write-Debug "$updatedAsString"

if($originalAsString -ne $updatedAsString)
{
   Write-Host "There are new addresses for VMs, updtating hosts file."

   # $datestring = (Get-Date -Format "o") -replace '[:\-]','-'
   # $backupHostsFile = "$hostsFile.${datestring}.bak"
   # Write-Verbose "Backing up hosts file to $backupHostsFile."
   # Copy-Item $hostsFile $backupHostsFile

   # Write-Verbose "Setting new hosts file content."

   Set-Content -Path $hostsfile -Value $hostsFileContent -Encoding UTF8
}
else
{
   Write-Host "No new VMs or addresses, exiting."
}

笔记

根据您的虚拟机,您可能必须在虚拟机内安装其他软件包,例如hyperv在 arch 上,然后您才能从 HyperV 和 powershell 中找出分配给 VM 的 IP。这可能会因不同的操作系统而异,因此如果您无法从主机端获取地址,您可能需要查找系统指南(或发布新问题)。

我保留了备份部分,只是将其注释掉,以便更容易试验和迭代脚本。此外,如果需要,还可以更轻松地在更合理的位置进行备份(如果没有进行任何更改,则无需备份,因此不要在写入更改之前进行备份)。

我还将有关替换地址的正则表达式如何工作的注释放在代码中,而不是放在帖子正文外面。这是为了让在复制粘贴后丢失了对此帖子的引用时更容易跟踪代码,并且希望如果有人想对其进行更改,这会有所帮助。

相关内容