创建嵌套虚拟机


系统默认允许嵌套虚拟化,因此除非有人修改了嵌套虚拟化的限制条件,否则在组织、文件夹或项目中创建嵌套虚拟机之前,您无需进行任何更改。如果您的项目不属于某个组织,那么系统在默认情况下允许使用嵌套虚拟化,并且您无法更改限制条件。如需了解如何修改确定是否可以创建嵌套虚拟机的限制条件,请参阅管理嵌套虚拟化限制条件

本文档介绍如何创建各种类型的 2 级 (L2) 虚拟机 (VM) 实例。在创建嵌套虚拟机之前,您必须创建已启用嵌套虚拟化的 L1 虚拟机。如需 L1 和 L2 虚拟机的说明,请参阅嵌套虚拟化概览

创建启用了嵌套虚拟化的 L1 虚拟机后,您可以执行以下任一操作:

  • 创建具有外部网络访问权限的 L2 虚拟机
  • 创建使用专用网络网桥连接到 L1 虚拟机的 L2 虚拟机
  • 从 L1 虚拟机外部创建具有网络访问权限的 L2 虚拟机

准备工作

  • 设置身份验证(如果尚未设置)。身份验证是通过其进行身份验证以访问 Google Cloud 服务和 API 的过程。如需从本地开发环境运行代码或示例,您可以按如下方式向 Compute Engine 进行身份验证。

    选择标签页以了解您打算如何使用本页面上的示例:

    gcloud

    1. 安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

      gcloud init
    2. 设置默认区域和可用区

    REST

    如需在本地开发环境中使用本页面上的 REST API 示例,请使用您提供给 gcloud CLI 的凭据。

      安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

      gcloud init

创建具有外部网络访问权限的 L2 虚拟机

按照以下流程创建一个具有外部网络访问权限的 L2 虚拟机。此流程使用 qemu-system-x86_64 启动 L2 虚拟机。如果您按照其他流程创建 L2 虚拟机时遇到问题,请在联系支持团队之前先按照此流程重现该问题。

  1. 创建启用了嵌套虚拟化的 L1 虚拟机

  2. 使用 gcloud compute ssh 命令连接到该虚拟机:

    gcloud compute ssh VM_NAME
    

    VM_NAME 替换为要连接的虚拟机的名称。

  3. 安装最新的 qemu-kvm 软件包:

    sudo apt update && sudo apt install qemu-kvm -y
    
  4. 下载与 QEMU 兼容的操作系统映像以用于 L2 虚拟机。

  5. 使用以下命令启动 L2 虚拟机。出现提示时,使用 user: rootpassword: root 登录。

    sudo qemu-system-x86_64 -enable-kvm -hda IMAGE_NAME -m 512 -curses
    

    IMAGE_NAME 替换为要用于 L2 虚拟机的 QEMU 兼容操作系统映像的名称。

  6. 测试 L2 虚拟机是否具有外部访问权限:

    user@nested-vm:~$ host google.com
    

创建使用专用网络网桥连接到 L1 虚拟机的 L2 虚拟机

按照以下步骤使用专用网络网桥创建 L2 虚拟机与先前创建的 L1 虚拟机。如需了解如何更改 VPC 网络的默认最大传输单元 (MTU),请参阅最大传输单元概览

  1. 创建启用了嵌套虚拟化的 L1 虚拟机

  2. 使用 gcloud compute ssh 命令连接到该虚拟机:

    gcloud compute ssh VM_NAME
    

    VM_NAME 替换为要连接的虚拟机的名称。

  3. 安装创建专用网桥所需的软件包:

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

    sudo virsh net-start default
    
  5. 运行以下命令来检查您是否拥有 virbr0 网桥:

    ip addr
    
  6. 输出内容类似如下:

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host
           valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 42:01:0a:80:00:15 brd ff:ff:ff:ff:ff:ff
        inet 10.128.0.21/32 brd 10.128.0.21 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::4001:aff:fe80:15/64 scope link
           valid_lft forever preferred_lft forever
    3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
        link/ether 52:54:00:8c:a6:a1 brd ff:ff:ff:ff:ff:ff
        inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
           valid_lft forever preferred_lft forever
    4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000
        link/ether 52:54:00:8c:a6:a1 brd ff:ff:ff:ff:ff:ff
    
  7. 创建一个从 L1 虚拟机到 L2 虚拟机的 tap 接口:

    sudo tunctl -t tap0
    sudo ifconfig tap0 up
    
  8. tap 接口绑定到专用网桥:

    sudo brctl addif virbr0 tap0
    
  9. 运行以下命令来验证网桥网络的设置:

    sudo brctl show
    
  10. 输出内容类似如下:

    bridge name     bridge id               STP enabled     interfaces
    virbr0          8000.5254008ca6a1       yes             tap0
                                                            virbr0-nic
    
  11. 下载与 QEMU 兼容的操作系统映像以用于 L2 虚拟机。

  12. 运行 screen,然后在出现欢迎提示时按 Enter 键:

    screen
    
  13. 使用以下命令启动 L2 虚拟机。出现提示时,使用 user: rootpassword: root 登录。

    sudo qemu-system-x86_64 -enable-kvm -hda IMAGE_NAME -m 512 -net nic -net tap,ifname=tap0,script=no -curses
    

    IMAGE_NAME 替换为要用于 L2 虚拟机的 QEMU 兼容操作系统映像的名称。

  14. 在 L2 虚拟机上,运行 ip addr show 确认该虚拟机的 virbr0 空间中已有地址,例如 192.168.122.89

    user@nested-vm:~$ ip addr
    
  15. 在端口 8000 上启动占位符 Web 服务器:

    user@nested-vm:~$ python -m http.server
    
  16. 使用 Ctrl+ACtrl+D 退出 screen 会话。

  17. 测试 L1 虚拟机是否可以对 L2 虚拟机执行 ping 操作,并将以下 IP 地址替换为 L2 虚拟机的 IP 地址:

    curl 192.168.122.89:8000
    
  18. 输出内容类似如下:

    <!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>
    

从 L1 虚拟机外部创建具有网络访问权限的 L2 虚拟机

您可以使用别名 IP 设置 L2 虚拟机,使 L1 虚拟机外部的虚拟机可以访问 L2 虚拟机。按照以下过程,通过之前创建的 L1 虚拟机外部的别名 IP 创建具有网络访问权限的 L2 虚拟机。如需了解如何创建别名 IP 地址,请参阅配置别名 IP 地址范围

以下过程假定之前创建了一个名为 subnet1 的子网。如果您已有同名的子网,请将 subnet1 替换为您的子网名称,或创建名为 subnet1 的新子网。

  1. 创建启用了嵌套虚拟化的 L1 虚拟机,并添加别名 IP 范围以及对 HTTP/HTTPS 流量的支持:

    gcloud

    gcloud compute instances create VM_NAME --enable-nested-virtualization \
        --tags http-server,https-server --can-ip-forward \
        --min-cpu-platform "Intel Haswell" \
        --network-interface subnet=subnet1,aliases=/30
    

    VM_NAME 替换为 L1 虚拟机的名称。

    REST

    POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances
    
    {
      ...
      "name": VM_NAME,
      "tags": {
        "items": [
          http-server,https-server
        ],
      },
      "canIpForward": true,
      "networkInterfaces": [
        {
          "subnetwork": "subnet1",
          "aliasIpRanges": [
            {
              "ipCidrRange": "/30"
            }
          ],
        }
      ],
      "minCpuPlatform": "Intel Haswell",
      "advancedMachineFeatures": {
        "enableNestedVirtualization": true
      },
      ...
    }
    

    替换以下内容:

    • PROJECT_ID:项目 ID

    • ZONE:要在其中创建虚拟机的可用区

    • VM_NAME:虚拟机的名称

  2. 使用 gcloud compute ssh 命令连接到该虚拟机。如果您在连接到虚拟机时遇到问题,请尝试重置虚拟机或修改防火墙规则。

    gcloud compute ssh VM_NAME
    

    VM_NAME 替换为要连接的虚拟机的名称。

  3. 更新虚拟机并安装必要的软件包:

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

    sudo virsh net-start default
    
  5. 运行以下命令来检查您是否拥有 virbr0 网桥:

    user@nested-vm:~$ ip addr
    
  6. 验证输出是否类似于以下内容:

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host
           valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 42:01:0a:80:00:15 brd ff:ff:ff:ff:ff:ff
        inet 10.128.0.21/32 brd 10.128.0.21 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::4001:aff:fe80:15/64 scope link
           valid_lft forever preferred_lft forever
    3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
        link/ether 52:54:00:8c:a6:a1 brd ff:ff:ff:ff:ff:ff
        inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
           valid_lft forever preferred_lft forever
    4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000
        link/ether 52:54:00:8c:a6:a1 brd ff:ff:ff:ff:ff:ff
    
  7. 创建一个从 L1 虚拟机到 L2 虚拟机的 tap 接口:

    sudo tunctl -t tap0
    sudo ifconfig tap0 up
    
  8. tap 接口绑定到专用网桥:

    sudo brctl addif virbr0 tap0
    
  9. 运行以下命令来验证网桥网络的设置:

    sudo brctl show
    
  10. 验证输出是否类似于以下内容:

    bridge name     bridge id               STP enabled     interfaces
    virbr0          8000.5254008ca6a1       yes             tap0
                                                            virbr0-nic
    
  11. 下载与 QEMU 兼容的操作系统映像以用于 L2 虚拟机。

  12. 运行 screen,然后在出现欢迎提示时按 Enter 键:

    screen
    
  13. 使用以下命令启动嵌套虚拟机。出现提示时,使用 user: rootpassword: root 登录。

    sudo qemu-system-x86_64 -enable-kvm -hda IMAGE_NAME -m 512 -net nic -net tap,ifname=tap0,script=no -curses
    

    IMAGE_NAME 替换为要用于 L2 虚拟机的 QEMU 兼容操作系统映像的名称。

  14. 在 L2 虚拟机上,运行 ip addr 以确认 L2 虚拟机在 virbr0 空间中具有地址,例如 192.168.122.89

    user@nested-vm:~$ ip addr
    
  15. 在端口 8000 上启动占位符 Web 服务器:

    user@nested-vm:~$ python -m http.server
    
  16. 使用 Ctrl+ACtrl+D 退出 screen 会话。

  17. 测试 L1 虚拟机是否可以对 L2 虚拟机执行 ping 操作,并将以下 IP 地址替换为 L2 虚拟机的 IP 地址:

    curl 192.168.122.89:8000
    
  18. 验证来自 L2 虚拟机的响应是否类似于以下内容:

    <!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>
    
  19. 在 L1 虚拟机上,设置 iptables 以允许从 L1 虚拟机转发到 L2 虚拟机。对于以下说明中使用的 L2 操作系统映像,您必须清空 IP 地址表:

    sudo iptables -F
    
  20. 确定 L1 虚拟机的别名 IP 地址:

    ip route show table local
    
  21. 验证输出是否类似如下所示。在此示例中,有两个 IP 地址与 L2 虚拟机的 eth0 以太网设备关联。第一个 10.128.0.2 是 L2 虚拟机的主要 IP 地址,由 sudo ifconfig -a 返回。第二个是 10.128.0.13,即 L2 虚拟机的别名 IP 地址。

    local 10.128.0.2 dev eth0 proto kernel scope host src 10.128.0.2
    broadcast 10.128.0.2 dev eth0 proto kernel scope link src 10.128.0.2
    local 10.128.0.13/30 dev eth0 proto 66 scope host
    broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
    local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
    local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
    broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
    broadcast 192.168.122.0 dev virbr0 proto kernel scope link src
    192.168.122.1 linkdown
    local 192.168.122.1 dev virbr0 proto kernel scope host src 192.168.122.1
    broadcast 192.168.122.255 dev virbr0 proto kernel scope link src
    192.168.122.1 linkdown
    
  22. 运行以下命令,将流量从 10.128.0.13 示例别名 IP 转发到 L2 虚拟机的 192.168.122.89 示例 IP:

    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 -s 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
    

    如需了解如何排查 iptables 问题,请参阅 iptables 不转发流量

  23. 通过登录与 L1 虚拟机位于同一网络的另一个虚拟机,对别名 IP 发出 curl 请求并将下面的 IP 地址替换为 L2 虚拟机的别名 IP,验证是否可以从 L1 虚拟机外部访问 L2 虚拟机:

    user@another-vm:~$ curl 10.128.0.13:8000
    
  24. 验证 curl 响应是否类似于以下内容:

    <!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>
    

后续步骤