为虚拟机实例启用嵌套虚拟化

本文档介绍如何在 Compute Engine 虚拟机实例上启用对嵌套虚拟化的支持。它还介绍了启动和配置嵌套虚拟机的基本步骤。

嵌套虚拟化向 Compute Engine 虚拟机添加了 Intel VT-x 处理器虚拟化指令支持。使用嵌套虚拟化,您可以在 Compute Engine 上正常启动虚拟机实例,然后在虚拟机实例上安装与 KVM 兼容的管理程序,以便您可以在该管理程序之上再运行另一个虚拟机实例。目前不支持其他管理程序,例如 Hyper-V、ESX 和 Xen。您可以在运行于 Haswell 或更新平台上的任何 Linux 虚拟机实例上使用嵌套虚拟化。

嵌套虚拟化非常适合无法在其中将虚拟机映像转换或导入至 Compute Engine 的基于虚拟机的应用和工作负载。例如,您可以使用嵌套虚拟化为在基于 KVM 的虚拟机上运行的本地工作负载构建灾难恢复解决方案,该解决方案可以无缝地故障转移到在 Compute Engine 上运行的虚拟机,而无需耗费额外的时间或编排来将基于 KVM 的虚拟机转换为原生 Compute Engine 映像。另一个比较适合嵌套虚拟化的工作负载是软件验证框架,它需要在不同的兼容 KVM 的操作系统的众多版本上测试和验证软件包的新版本。运行嵌套虚拟机无需转换和管理大型 Compute Engine 映像库。

准备工作

嵌套虚拟化的工作原理

Compute Engine 虚拟机运行在被称为 L0 环境的物理硬件(主机服务器)之上。在主机服务器中,预安装的管理程序允许单个服务器托管多个 Compute Engine 虚拟机,这些虚拟机在 Compute Engine 上被称为 L1 或原生虚拟机。当您使用嵌套虚拟化时,可以在 L1 来宾操作系统之上安装另一个管理程序,并使用 L1 管理程序创建嵌套虚拟机(称为 L2 虚拟机)。运行来宾管理程序和嵌套虚拟机的 L1 或原生 Compute Engine 虚拟机也可被称为主机虚拟机。

嵌套虚拟化图解

限制

  • 只能针对在 Haswell 处理器或更高版本上运行的 L1 虚拟机启用嵌套虚拟化。如果地区的默认处理器是 Sandy Bridge 或 Ivy Bridge,您可以使用最低要求 CPU 选择为特定实例选择 Haswell 或更高版本。查看区域和地区页面,以确定哪些地区支持 Haswell 或更高版本的处理器。
  • 只有 Linux 实例上运行的基于 KVM 的管理程序才支持嵌套虚拟化。ESX 和 Xen 管理程序不受支持。
  • 嵌套虚拟化目前不支持 Windows 实例。

经过测试的 KVM 版本

Google 使用以下主机虚拟机和嵌套虚拟机组合运行基本的嵌套虚拟化启动和集成测试:

  • 托管以下嵌套虚拟机的 Debian 9(内核版本 4.9):
    • CentOS 6.5(内核版本 2.6)
    • Debian 9(内核版本 4.9)
    • RHEL 5.11(内核版本 2.6)
    • SLES 12 SP3 (内核版本 4.4)
    • Ubuntu 16.04 LTS(内核版本 4.15)
    • Windows Server 2008 R2
    • Windows Server 2016 Datacenter Edition
  • 托管以下嵌套虚拟机的 SLES 12 SP3(内核版本 4.4):
    • SLES 12 SP3 (内核版本 4.4)
  • 托管以下嵌套虚拟机的 Ubuntu 16.04 LTS(内核版本 4.15):
    • Ubuntu 16.04 LTS(内核版本 4.15)

如果您在此处未列出的发行版本和内核/KVM 版本上运行嵌套虚拟机时遇到问题,请在报告问题之前,使用上述环境之一作为主机 Compute Engine 实例上的来宾操作系统重现您的问题。

性能

即使利用硬件辅助的嵌套虚拟化,嵌套虚拟机本身以及在其中运行的任何应用或工作负载也会有性能损失。虽然无法预测给定应用或工作负载的确切性能下降情况,但对于受 CPU 限制的工作负载而言,预计至少会下降 10%,而对于受 I/O 限制的工作负载则可能会下降更多。

在实例上启用嵌套虚拟化

您可以使用 API 或 gcloud 组件启用嵌套虚拟化。要启用嵌套虚拟化,必须使用在 L1 或主机虚拟机实例中启用 VMX 的特殊许可密钥创建自定义映像,然后在满足嵌套虚拟化限制的实例上使用该映像。许可密钥不会产生额外费用。

  1. 从公开映像或带操作系统的自定义映像创建启动磁盘。或者,您可以跳过此步骤,将许可应用于某个虚拟机实例的现有磁盘。

    gcloud

    使用 gcloud 命令行工具从您选择的启动磁盘映像创建磁盘。在本例中,从 debian-9 映像系列创建一个名为 disk1 的磁盘:

    gcloud compute disks create disk1 --image-project debian-cloud --image-family debian-9

    API

    debian-9 映像系列创建一个名为 disk1 的磁盘:

    POST https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/us-central1-b/disks
    
    {
     "name": "disk1",
     "sourceImage": "/projects/debian-cloud/global/images/family/debian-9"
    }

    其中:[PROJECT_ID] 是您的项目 ID。

  2. 使用您创建的启动磁盘或现有实例的启动磁盘,通过虚拟化所需的特殊许可密钥创建自定义映像。

    gcloud

    如果使用“gcloud”命令行工具创建映像,请使用“--licenses”标志提供以下许可网址:

    https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx

    例如,以下命令从名为“disk1”的示例磁盘创建名为“nested-vm-image”的映像:

    gcloud compute images create nested-vm-image \
      --source-disk disk1 --source-disk-zone us-central1-b \
      --licenses "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx"

    API

    在 API 中,请在 API 请求中包含许可属性:

    POST https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/global/images
    
    {
       "licenses": ["projects/vm-options/global/licenses/enable-vmx"],
       "name": "nested-vm-image",
       "sourceDisk": "zones/us-central1-b/disks/disk1"
    }

    其中,[PROJECT_ID] 是您的项目 ID。

  3. 使用必要许可创建映像后,如果不再需要来源磁盘,可将其删除。
  4. 使用带许可的新自定义映像创建虚拟机实例。必须在支持 Haswell CPU 平台或更新版本的地区中创建该实例。例如:
    gcloud compute instances create example-nested-vm --zone us-central1-b \
                  --min-cpu-platform "Intel Haswell" \
                  --image nested-vm-image
  5. 确认嵌套虚拟化已在虚拟机中启用。
    1. 连接到虚拟机实例。例如:
      gcloud compute ssh example-nested-vm
    2. 通过运行以下命令检查是否已启用嵌套虚拟化。非零响应可确认嵌套虚拟化已得到启用。
      grep -cw vmx /proc/cpuinfo

启动嵌套虚拟机

您可以通过多种不同方式启动嵌套虚拟机。本部分介绍了在运行 Debian 的 L1 虚拟机上使用 qemu-system-x86_64 启动嵌套虚拟机的示例。如果您在使用此处记录的过程之外的方法运行嵌套虚拟机时遇到问题,请在将该问题报告为错误之前,使用此过程再现您的问题。

  1. 连接到虚拟机实例。例如:

    gcloud compute ssh example-nested-vm
    
  2. 更新虚拟机实例并安装一些必要的包:

    sudo apt-get update && sudo apt-get install qemu-kvm -y
    
  3. 下载操作系统映像:

    wget https://people.debian.org/~aurel32/qemu/amd64/debian_squeeze_amd64_standard.qcow2
    
  4. 运行 screen

    screen
    
  5. 在出现 screen 欢迎提示时按 Enter 键。

  6. 启动嵌套虚拟机。出现提示时,使用 user: rootpassword: root 登录。

    sudo qemu-system-x86_64 -enable-kvm -hda debian_squeeze_amd64_standard.qcow2 -m 512 -curses
    
  7. 测试您的虚拟机是否具有外部访问权限:

    user@nested-vm:~$ wget google.com && cat index.html
  8. 完成后,通过按 Ctrl + aCtrl + d 按钮退出嵌套虚拟机。

启动主机和嵌套虚拟机之间的专用桥

要启用主机和嵌套虚拟机之间的连接,您可以创建一个专用桥。此示例过程适用于运行 Debian 的 L1 虚拟机。

  1. 连接到虚拟机实例。例如:

    gcloud compute ssh example-nested-vm
    
  2. 更新虚拟机实例并安装一些必要的包:

    sudo apt-get update && sudo apt-get install uml-utilities qemu-kvm bridge-utils virtinst libvirt-daemon-system libvirt-clients -y
    
  3. 启动随附于 libvirt 包的默认网络:

    sudo virsh net-start default
    
  4. 检查您现在是否具有 virbr0 桥:

    sudo ifconfig -a
    
     eth0      Link encap:Ethernet  HWaddr 42:01:0a:80:00:02
               inet addr:10.128.0.2  Bcast:10.128.0.2  Mask:255.255.255.255
               UP BROADCAST RUNNING MULTICAST  MTU:1460  Metric:1
               RX packets:14799 errors:0 dropped:0 overruns:0 frame:0
               TX packets:9294 errors:0 dropped:0 overruns:0 carrier:0
               collisions:0 txqueuelen:1000
               RX bytes:97352041 (92.8 MiB)  TX bytes:1483175 (1.4 MiB)
    
     lo        Link encap:Local Loopback
               inet addr:127.0.0.1  Mask:255.0.0.0
               inet6 addr: ::1/128 Scope:Host
               UP LOOPBACK RUNNING  MTU:65536  Metric:1
               RX packets:0 errors:0 dropped:0 overruns:0 frame:0
               TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
               collisions:0 txqueuelen:0
               RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    
     virbr0    Link encap:Ethernet  HWaddr 5a:fa:7e:d2:8b:0d
               inet addr:192.168.122.1  Bcast:192.168.122.255  Mask:255.255.255.0
               UP BROADCAST MULTICAST  MTU:1500  Metric:1
               RX packets:0 errors:0 dropped:0 overruns:0 frame:0
               TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
               collisions:0 txqueuelen:0
               RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
  5. 创建虚拟接口:

    sudo modprobe dummy
    
  6. 将该虚拟接口添加到桥:

    sudo brctl addif virbr0 dummy0
    
  7. 检查是否已添加该接口:

    sudo brctl show
    
    bridge name    bridge id          STP enabled    interfaces
    virbr0         8000.6a20fa136bb6  yes            dummy0
  8. 创建一个从主机虚拟机到嵌套虚拟机的 tun 接口:

    sudo tunctl -t tap0
    sudo ifconfig tap0 up
    
  9. 将 tun 接口绑定到桥虚拟机:

    sudo brctl addif virbr0 tap0
    
  10. 仔细检查是否已正确设置桥网络:

    sudo brctl show
    
    bridge name     bridge id               STP enabled     interfaces
    virbr0          8000.5254005085fe       yes              dummy0
                                                             tap0
  11. 下载操作系统映像:

    wget https://people.debian.org/~aurel32/qemu/amd64/debian_squeeze_amd64_standard.qcow2
    
  12. 运行 screen

    screen
    
  13. 在出现 screen 欢迎提示时按 Enter 键。

  14. 启动嵌套虚拟机。出现提示时,使用 user: rootpassword: root 登录。

    sudo qemu-system-x86_64 -enable-kvm -hda debian_squeeze_amd64_standard.qcow2 -m 512 -net nic -net tap,ifname=tap0,script=no -curses
    
  15. 在嵌套虚拟机上,运行 ifconfig 以确认虚拟机在 virbr0 空间中具有地址,例如 192.168.122.89:

    user@nested-vm:~$ ifconfig
  16. 在端口 8000 上启动虚拟网络服务器:

    user@nested-vm:~$ python -m SimpleHTTPServer
  17. 通过按 Ctrl + aCtrl + a 按钮退出嵌套虚拟机。

  18. 测试主机虚拟机是否可以对嵌套虚拟机执行 ping 操作:

    curl 192.168.122.89:8000
    

    嵌套虚拟机应该返回类似于以下的内容:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
    <title>Directory listing for /</title>
    <body>
    <h2>Directory listing for /</h2>
    <hr>
    <ol>
    <li><a href=".aptitude/">.aptitude/</a>
    <li><a href=".bashrc">.bashrc</a>
    <li><a href=".profile">.profile</a>
    </ol>
    <hr>
    </body>
    </html>
    

将嵌套虚拟机配置为可从主机虚拟机外部进行访问

您可以使用多个网络接口设置实例,或使用别名 IP 设置实例,以便主机虚拟机外部的虚拟机可以对嵌套虚拟机执行 ping 操作。

以下示例过程设置主机和嵌套虚拟机,以便可以使用别名 IP 从同一网络上的其他虚拟机访问嵌套虚拟机。此过程适用于运行 Debian 的 L1 虚拟机。

  1. 创建已启用嵌套虚拟化的虚拟机,但请确保添加别名 IP 范围以及对 HTTP/HTTPS 流量的支持。例如:

    gcloud compute instances create example-nested-vm --image nested-vm-image \
        --tags http-server,https-server --can-ip-forward \
        --min-cpu-platform "Intel Haswell" \
        --network-interface subnet=subnet1,aliases=/30
    
  2. 连接到虚拟机实例。例如:

    gcloud compute ssh example-nested-vm
    
  3. 更新虚拟机实例并安装一些必要的包:

    sudo apt-get update && sudo apt-get install uml-utilities qemu-kvm bridge-utils virtinst libvirt-daemon-system libvirt-clients -y
    
  4. 启动随附于 libvirt 包的默认网络:

    sudo virsh net-start default
    
  5. 检查您现在是否具有 virbr0 桥:

    sudo ifconfig -a
     
     eth0      Link encap:Ethernet  HWaddr 42:01:0a:80:00:02
               inet addr:10.128.0.2  Bcast:10.128.0.2  Mask:255.255.255.255
               UP BROADCAST RUNNING MULTICAST  MTU:1460  Metric:1
               RX packets:14799 errors:0 dropped:0 overruns:0 frame:0
               TX packets:9294 errors:0 dropped:0 overruns:0 carrier:0
               collisions:0 txqueuelen:1000
               RX bytes:97352041 (92.8 MiB)  TX bytes:1483175 (1.4 MiB)

    lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

    virbr0 Link encap:Ethernet HWaddr 5a:fa:7e:d2:8b:0d inet addr:192.168.122.1 Bcast:192.168.122.255 Mask:255.255.255.0 UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

  6. 创建虚拟接口:

    sudo modprobe dummy
    
  7. 将该虚拟接口添加到桥:

    sudo brctl addif virbr0 dummy0
    
  8. 检查是否已添加该接口:

    sudo brctl show
    bridge name    bridge id          STP enabled    interfaces
    virbr0         8000.6a20fa136bb6  yes            dummy0
  9. 创建一个从主机虚拟机到嵌套虚拟机的 tun 接口:

    sudo tunctl -t tap0
    sudo ifconfig tap0 up
    
  10. 将 tun 接口绑定到桥虚拟机:

    sudo brctl addif virbr0 tap0
    
  11. 仔细检查是否已正确设置桥网络:

    sudo brctl show
    
    bridge name     bridge id               STP enabled     interfaces
    virbr0          8000.5254005085fe       yes              dummy0
                                                             tap0
  12. 下载操作系统映像:

    wget https://people.debian.org/~aurel32/qemu/amd64/debian_squeeze_amd64_standard.qcow2
    
  13. 运行 screen

    screen
    
  14. 在出现 screen 欢迎提示时按 Enter 键。

  15. 启动嵌套虚拟机。出现提示时,使用 user: rootpassword: root 登录。

    sudo qemu-system-x86_64 -enable-kvm -hda debian_squeeze_amd64_standard.qcow2 -m 512 -net nic -net tap,ifname=tap0,script=no -curses
    
  16. 在嵌套虚拟机上,运行 ifconfig 以确认虚拟机在 virbr0 空间中具有地址,例如 192.168.122.89:

    user@nested-vm:~$ ifconfig
  17. 在端口 8000 上启动虚拟网络服务器:

    user@nested-vm:~$ python -m SimpleHTTPServer
  18. 通过按 Ctrl + aCtrl + a 按钮退出嵌套虚拟机。

  19. 测试主机虚拟机是否可以对嵌套虚拟机执行 ping 操作:

    curl 192.168.122.89:8000
    

    嵌套虚拟机应该返回类似于以下的内容:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
    <title>Directory listing for /</title>
    <body>
    <h2>Directory listing for /</h2>
    <hr>
    <ol>
    <li><a href=".aptitude/">.aptitude/</a>
    <li><a href=".bashrc">.bashrc</a>
    <li><a href=".profile">.profile</a>
    </ol>
    <hr>
    </body>
    </html>
    
  20. 在主机虚拟机上,设置 iptables 以允许从主机虚拟机转发到嵌套虚拟机。例如,如果要使用别名 IP 10.128.0.13 将流量转发到 192.168.122.89 处的托管虚拟机:

    echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
    sudo iptables -t nat -A PREROUTING -d 10.128.0.13 -j DNAT --to-destination 192.168.122.89
    sudo iptables -t nat -A POSTROUTING -d 192.168.122.89 -j MASQUERADE
    sudo iptables -A INPUT -p udp -j ACCEPT
    sudo iptables -A FORWARD -p tcp -j ACCEPT
    sudo iptables -A OUTPUT -p tcp -j ACCEPT
    sudo iptables -A OUTPUT -p udp -j ACCEPT
    
  21. 接下来,登录到托管虚拟机所在网络的其他虚拟机,并向别名 IP 发出 curl 请求。例如:

    user@another-vm:~$ curl 10.128.0.13:8000

    嵌套虚拟机应该返回类似于以下的内容:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
    <title>Directory listing for /</title>
    <body>
    <h2>Directory listing for /</h2>
    <hr>
    <ol>
    <li><a href=".aptitude/">.aptitude/</a>
    <li><a href=".bashrc">.bashrc</a>
    <li><a href=".profile">.profile</a>
    </ol>
    <hr>
    </body>
    </html>
    

问题排查

Google 使用 Compute Engine 实例上的特定 Linux 发行版本和内核/KVM 版本运行基本的嵌套虚拟化启动和集成测试。并且,这些测试使用特定过程。在将问题报告为错误之前,请使用以下项重现这些问题:

运行 grep -c vmx /proc/cpuinfo 会返回 0,并说明我的虚拟机未启用嵌套。

  1. 确保使用 Haswell 或更高版本的 CPU 平台来启动虚拟机。
  2. 确保对虚拟机映像使用正确的许可

我无法退出嵌套虚拟机。

如果在每个嵌套虚拟机会话之前未运行 screen,则您可以关闭嵌套虚拟机,或从另一个终端终止该进程。要关闭嵌套虚拟机,请在您的嵌套虚拟机中运行 poweroff 命令。或者,登录到另一个终端中的主机虚拟机并终止该进程,然后在启动新的嵌套虚拟机之前在主机虚拟机上运行 screen

我的 iptables 规则未将流量转发到我的嵌套虚拟机。

  • iptables 自上而下解析规则,请确保您的规则相较于其他规则具有更高优先级。
  • 检查是否存在拦截包的冲突规则。
  • 考虑清空 iptables:

    1. 首先,设置默认政策:

      sudo iptables -P INPUT ACCEPT
      sudo iptables -P FORWARD ACCEPT
      sudo iptables -P OUTPUT ACCEPT
      
    2. 接下来,清空所有表和链,并删除非默认链:

      sudo iptables -t nat -F
      sudo iptables -t mangle -F
      sudo iptables -F
      sudo iptables -X
      
此页内容是否有用?请给出您的反馈和评价:

发送以下问题的反馈:

此网页
Compute Engine 文档