你需要知道的关于Neutron的一切事情

  • 创建一个网络 (GREEN). 在这个网络上创建一个子网,然后在这个网络上生成两个虚拟主机(VMs).
  • 创建另一个网络(RED),在这个网络上创建不同的子网,然后在这个网络上产生另两个不同的VMs。
  • 验证上面操作流程创建的端口,理解OPenStack网络端口为什么是有用的。

 

 上面操作完成后,我们将有如上的网络拓扑图,包含

  • 两个虚拟网络 (GREEN & RED)
  • 两个子网, 每一个虚拟网络10.10.10.0/24和20.10.10.0/24
  • 四个虚拟机 (每个虚拟网络有两个) ,每个虚拟机由各个通信网络中的DHCP动态分配的IP。
  • 两个DHCP主机(在网络创建时会自动创建)
  • 使用中的六个网络端口。
root@controller (admin) $> neutron net-list
root@controller (admin) $> #...................................................................

现在我们创建第一个网络GREEN。

root@controller (admin) >gt; neutron net-create GREEN
Created a new network:
+---------------------------+----------------------------------------------------------+
| Field                     | Value                                                    |
+---------------------------+----------------------------------------------------------+
| admin_state_up            | True                                                     |
| id                        | 6df1fa59-25a3-4f8c-8d14-ae7f7828c1a2                     |
| name                      | GREEN                                                    |
| provider:network_type     | gre                                                      |
| provider:physical_network |                                                          |
| provider:segmentation_id  | 101                                                      |
| router:external           | False                                                    |
| shared                    | False                                                    |
| status                    | ACTIVE                                                   |
| subnets                   |                                                          |
| tenant_id                 | 34609d0ea9ce48f98145ecc5bbac9f77                         |
+---------------------------------------------+----------------------------------------+
root@controller (admin) $> #...................................................................


正如上面看到的,GREEN网络创建成功。这个网络是段ID101的GRE型网络。你也能注意到这不是一个外部网络(看到上面命令的输出属性router:external 值为false)。

现在让我们在这个网络下创建一个子网。我们选择10.10.10.0./24端口范围给这个子网。并命名为10_10_10.。

root@controller (admin) $> neutron subnet-create --name 10_10_10 GREEN 10.10.10.0/24

Created a new subnet:
+-------------------------+----------------------------------------------------------+
| Field                   | Value                                                    |
+-------------------------+----------------------------------------------------------+
| allocation_pools        | {"start": "10.10.10.2", "end": "10.10.10.254"}           |
| cidr                    | 10.10.10.0/24                                            |
| dns_nameservers         |                                                          |
| enable_dhcp             | True                                                     |
| gateway_ip              | 10.10.10.1                                               |
| host_routes             |                                                          |
| id                      | 1667e8b9-e2fe-41a6-bfd2-7de41f777d6e                     |
| ip_version              | 4                                                        |
| ipv6_address_mode       |                                                          |
| ipv6_ra_mode            |                                                          |
| name                    | 10_10_10                                                 |
| network_id              | 6df1fa59-25a3-4f8c-8d14-ae7f7828c1a2                     |
| tenant_id               | 34609d0ea9ce48f98145ecc5bbac9f77                         |
+-------------------------+----------------------------------------------------------+
root@controller (admin) $>; #...................................................................

我们看到这个子网创建成功,分配给它的默认IP池为10.10.10.2-10.10.10.254。这个子网中的任何新设备被分配的IP地址都在该范围中。第一个IP和最后一个IP是特殊的IP,是保留IP。 10.10.10.1这个IP是默认的网关地址,在上面的neutron subnet-create命令的输出中我们可以看到。 这个IP范围中的最后一个IP10.10.10.255是个广播地址,它是为了广播用的。

再次执行neutron net-list命令,我们看到我们的GREEN网络有了新的子网10.10.10.0/24。

root@controller (admin) $> neutron net-list
+-------------+-------+----------------------------------------------------+
| id          | name  | subnets                                            |
+-------------+-------+----------------------------------------------------+
| 6df --- 1a2 | GREEN | 1667e8b9-e2fe-41a6-bfd2-7de41f777d6e 10.10.10.0/24 |
+-------------+-------+----------------------------------------------------+
root@controller (admin) $> #...................................................................

既然我们的基本网络组件已经准备好了,那么让我们在这个网络上启动两个小的虚拟机,这两个虚拟机使用的cirros镜像。

root@controller (admin) $> nova boot --flavor m1.tiny --image cirros --min-count 2 --nic net-id=6df1fa59-25a3-4f8c-8d14-ae7f7828c1a2 greenboxes

+--------------------------------------+-------------------------------------------------+
| Property                             | Value                                           |
+--------------------------------------+-------------------------------------------------+
| OS-DCF:diskConfig                    | MANUAL                                          |
| OS-EXT-AZ:availability_zone          | nova                                            |
| OS-EXT-SRV-ATTR:host                 | -                                               |
| OS-EXT-SRV-ATTR:hypervisor_hostname  | -                                               |
| OS-EXT-SRV-ATTR:instance_name        | instance-00000016                               |
| OS-EXT-STS:power_state               | 0                                               |
| OS-EXT-STS:task_state                | scheduling                                      |
| OS-EXT-STS:vm_state                  | building                                        |
| OS-SRV-USG:launched_at               | -                                               |
| OS-SRV-USG:terminated_at             | -                                               |
| accessIPv4                           |                                                 |
| accessIPv6                           |                                                 |
| adminPass                            | BurRPPiCm6TK                                    |
| config_drive                         |                                                 |
| created                              | 2016-03-09T02:04:56Z                            |
| flavor                               | m1.tiny (1)                                     |
| hostId                               |                                                 |
| id                                   | b2647e00-81e3-4290-b9c3-ce5e584c7265            |
| image                                | cirros (5c912cd4-729e-4f29-8761-eb04e630741d)   |
| key_name                             | default                                         |
| metadata                             | {}                                              |
| name                                 | greenboxes-b2647e00-81e3-4290-b9c3-ce5e584c7265 |
| os-extended-volumes:volumes_attached | []                                              |
| progress                             | 0                                               |
| security_groups                      | default                                         |
| status                               | BUILD                                           |
| tenant_id                            | 34609d0ea9ce48f98145ecc5bbac9f77                |
| updated                              | 2016-03-09T02:04:56Z                            |
| user_id                              | e9b23b6cb7ef4e7c99d8934e34163726                |
+--------------------------------------+-------------------------------------------------+
root@controller (admin) $> #...................................................................

上面信息说明两个使用flavor为m1.tiny的虚拟机正在构建中。

root@controller (admin) $> nova list --fields Name,Networks
+--------------------------------------+----------------------------------+------------------+
| ID                                   | Name                             | Networks         |
+--------------------------------------+----------------------------------+------------------+
| b2647e00-81e3-4290-b9c3-ce5e584c7265 | greenboxes-b2647e00-ce5e584c7265 | GREEN=10.10.10.5 |
| f0d6e68c-5e18-432c-b0a7-4ef6da054053 | greenboxes-f0d6e68c-4ef6da054053 | GREEN=10.10.10.6 |
+--------------------------------------+----------------------------------+------------------+
root@controller (admin) $> #...................................................................

上面的nova list命令说明虚拟机已经启动,在子网10_10_10中使用了两个IP。作为启动过程的一部分,在网络虚拟交换机上的端口被绑定到虚拟机的虚拟接口上。看一下这个网络的所有端口,可以使我们对添加到这个网络的设备有一个清晰的了解。

root@controller (admin) $> neutron port-list -c id -c fixed_ips
+-----+-----------------------------------------------------------------------------------+
| id  | fixed_ips                                                                         |
+--------------------------------------+--------------------------------------------------+
| XXX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e",  "ip_address": "10.10.10.6"} |
| XXX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e",  "ip_address": "10.10.10.5"} |
| XXX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e",  "ip_address": "10.10.10.4"} |
+-----+-----------------------------------------------------------------------------------+
root@controller (admin) $> #...................................................................

上面的neutron port-list命令输出暗示有三个端口被创建。两个绑定到两个虚拟机接口上,另一个绑定到DHCP服务器上。因为当创建网络时我们不能禁止DHCP,所以 这个DHCP服务器被创建出来了。这个DHCP服务是负责给虚拟机分配动态IP的。

另一方面,验证端口创建的更容易的方式是从Horizon Dashboard中创建。点击一个特殊的网络,跳转到该网络的详情页面。在详情页面我们能看到该网络使用的所有端口情况。例如打开GREEN网络的详情页面,我们会看到:

我们能看到两个 Compute:None 端口。这个属性告诉我们这些端口已经连接到虚拟机上了。一个端口(10.10.10.4) 连接到DHCP服务上了。



OpenStack中的一个网络是一种VLAN但它更灵活。

一个子网是一个IP地址块,并且它和配置状态有关。网络上新的端口被创建时子网被用来给该端口分配IP地址。

一个Neutron端口是给虚拟网络绑定一个独立的设备的一个连接点,例如虚拟服务器的NIC。端口也描述了相关的网络配置,例如该端口上的MAC和IP地址。

现在我们第一组VMs和网络组件已经创建完了,同样的我们将创建另一个新的网络(命名RED),创建该网络另一个新的子网,最后在该网络和子网下启动两个VM。假设我们重复以上的步骤,我们能在horizon dashboard中验证上面的四个实例。这四个实例运行在两个节点组中。两个连接到GREEN网络中(10.10.10.X)另两个连接到RED网络(20.10.10.X)中。

四个实例正在运行中,两个在GREEN(10.10.10.X) 网络中,另两个在RED网络 (20.10.10.X)中.

存在两个网络。

root@controller (admin) $> neutron net-list
+--------------------------------------+-------+-----------------------------------------+
| id                                   | name  | subnets                                 |
+--------------------------------------+-------+-----------------------------------------+
| 6df1fa59-25a3-4f8c-8d14-ae7f7828c1a2 | GREEN | 1667e8b9--7de41f777d6e 10.10.10.0/24    |
| 1048605e-41f6-4c10-be89-935f836a1b40 | RED   | db05f7e4--d9e164f86ca8 20.10.10.0/24    |
+--------------------------------------+-------+-----------------------------------------+
root@controller (admin) $> #................................................................

一共有六个端口被创建。四个端口分别连接到四个虚机上,两个端口连接到每个网络DHCP服务上。

root@controller (admin) $> neutron port-list -c id -c fixed_ips
+-----+----------------------------------------------------------------------------------+
| id  | fixed_ips                                                                        |
+-----+----------------------------------------------------------------------------------+
| XXX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e", "ip_address": "10.10.10.6"}|
| XXX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e", "ip_address": "10.10.10.5"}|
| XXX | {"subnet_id": "db05f7e4-2d48-4d86-8089-d9e164f86ca8", "ip_address": "20.10.10.6"}|
| XXX | {"subnet_id": "db05f7e4-2d48-4d86-8089-d9e164f86ca8", "ip_address": "20.10.10.4"}|
| XXX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e", "ip_address": "10.10.10.4"}|
| XXX | {"subnet_id": "db05f7e4-2d48-4d86-8089-d9e164f86ca8", "ip_address": "20.10.10.5"}|
+-----+----------------------------------------------------------------------------------+
root@controller (admin) $> #..................................................................

正如文件开始保证的在这个章节中我们创建了我们的网络基础设施。这表明在Neutron 网络,子网和端口的帮助下我们能创建更复杂且更灵活的网络设置来满足我们的需求。

安全组(Security Groups)

在前面一节中,即使VMs在同样的网络下被创建且能互相连接,因为安全组的原因我们也许不能ping或者ssh这些虚拟机。这是你的计算实例都有控制输入和输出通道的虚拟防火墙,这就是安全组。Openstack的安全组在每个虚拟机上都有实现。您能创建一 群安全组规则并分配它们给这些虚拟机。

让我们列出所有的安全组。

root@controller (admin) $> nova secgroup-list
+--------------------------------------+----------+----------------+
| Id                                   | Name     | Description    |
+--------------------------------------+----------+----------------+
| 4be520ef-58ad-4fa2-a471-2721290b88d7 | default  | default        |
+--------------------------------------+----------+----------------+
root@controller (admin) $> #...................................................................

列出安全组default的每个规则

root@controller (admin) $> nova secgroup-list-rules default
+----------------+-----------+---------+------------+--------------+
| IP Protocol    | From Port | To Port | IP Range   | Source Group |
+----------------+-----------+---------+------------+--------------+
|                |           |         | default    |              |
| icmp           | -1        | -1      | 0.0.0.0/0  |              |
| tcp            | 1         | 65535   | 0.0.0.0/0  |              |
|                |           |         |            | default      |
| udp            | 1         | 65535   | 0.0.0.0/0  |              |
+----------------+-----------+---------+-------------+-------------+
root@controller (admin) $> #...................................................................

让我们发现任何一个vm的安全组.

root@controller (admin) $> nova show greenboxes-b2647e00-81e3-4290-b9c3-ce5e584c7265 | grep security_groups
| security_groups                      | default                   |
root@controller (admin) $> #...................................................................

这表明我们的VM1 (greenboxes-b2647e00-81e3-4290-b9c3-ce5e584c7265)默认以安全组default启动。现在让我们给default增加一个规则以便允许ssh(端口22)连接。

root@controller (admin) $> nova secgroup-add-rule default tcp 22 22 0.0.0.0/0
+----------------+-----------+---------+------------+------------+
| IP Protocol    | From Port | To Port | IP Range | Source Group |
+----------------+-----------+---------+------------+------------+
| tcp            | 22        | 22      | 0.0.0.0/0  |            |
+----------------+-----------+---------+------------+------------+
root@controller (admin) $> nova secgroup-list-rules default | grep 22
| tcp            | 22        | 22      | 0.0.0.0/0  |            |
root@controller (admin) $> #...................................................................

列出所有安全组,我们能验证默认的secgroup有一个端口22的规则。这意味着在以default安全组启动的虚拟机上,端口22被开放从而允许ssh的连接。这些安全组规则是项目的标准规范,项目组成员能编辑这些安全组规则也能增加新的安全组规则。

路由器(Routers)

在上面的拓扑图里,在GREEN网络里的实例可以与其他设备进行交互,只要它们在同一个网络里。这意味着VM1能发送和接收来自VM2的包,但是不能发送和接收来自VM3或者VM4的包数据。为了提供两个不同网络之间的互连,路由器使这个变成了可能。

路由器是逻辑上的网络组件,主要表现在:

  • 在网络间转发数据包;
  • 提供L3和NAT为了提供VMs对外部的租户网络访问的权限。

让我们来证明路由器的这两个特征。在本章节中,我们将介绍一个新的路由器。绑定每个网络的末端到一个路由上,这样它们就连接在一起了。然后我们将创建一个 虚拟的外部网络,并使这个路由器成为一个网关。这种方式使我们所有的VMs能在外部世界进行交互。在这个章节末尾,我们将有一个如下的配置图。

为了连接两个网络我们需要一个路由器。让我们创建一个外部虚拟路由器并命名为EXT_VR。

root@controller (admin) $> neutron router-create EXT_VR
Created a new router:
+-----------------------+--------------------------------------+
| Field                 | Value                                |
+-----------------------+--------------------------------------+
| admin_state_up        | True                                 |
| distributed           | False                                |
| external_gateway_info |                                      |
| ha                    | False                                |
| id                    | c326b2b4-9076-4e12-9850-77d5f1c66c86 |
| name                  | EXT_VR                               |
| routes                |                                      |
| status                | ACTIVE                               |
| tenant_id             | 34609d0ea9ce48f98145ecc5bbac9f77     |
+-----------------------+--------------------------------------+
root@controller (admin) $> #...................................................................

现在我们在这个刚创建的路由器EXT_VR上需要在每个网络上添加虚拟接口。这种方式能使我们有一个多个子网的网络,并决定哪个子网连接路由器。

root@controller (admin) $> neutron subnet-list -c id -c name -c cidr
+--------------------------------------+----------+---------------+
| id                                   | name     | cidr          |
+------------ -------------------------+----------+---------------+
| 1667e8b9-e2fe-41a6-bfd2-7de41f777d6e | 10_10_10 | 10.10.10.0/24 |
| db05f7e4-2d48-4d86-8089-d9e164f86ca8 | 20_10_10 | 20.10.10.0/24 |
+--------------------------------------+----------+---------------+
root@controller (admin) $> neutron router-interface-add c326b2b4-9076-4e12-9850-77d5f1c66c86 10_10_10
Added interface 64b09b83-1500-4caf-b9ea-ea4c8ec127d7 to router c326b2b4-9076-4e12-9850-77d5f1c66c86.
root@controller (admin) $> neutron router-interface-add c326b2b4-9076-4e12-9850-77d5f1c66c86 20_10_10
Added interface 0b1e3767-363e-49ba-8ee9-33c02db354bc to router c326b2b4-9076-4e12-9850-77d5f1c66c86.
root@controller (admin) $> #...................................................................

现在这两个网络通过路由器EXT_VR就能进行互连,我们通过下面的命令来验证这个互连性。

cirros@vm1 $  ping -c 2 10.10.10.6>
PING 10.10.10.6 (10.10.10.6) 56(84) bytes of data.
64 bytes from 10.10.10.6: icmp_seq=1 ttl=64 time=0.102 ms
64 bytes from 10.10.10.6: icmp_seq=2 ttl=64 time=0.120 ms
--- 10.10.10.6 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.102/0.111/0.120/0.009 ms
cirros@vm1 $
cirros@vm1 $  ping -c 2 20.10.10.5
PING 20.10.10.5 (20.10.10.5) 56(84) bytes of data.
64 bytes from 20.10.10.5: icmp_seq=1 ttl=64 time=0.132 ms
64 bytes from 20.10.10.5: icmp_seq=2 ttl=64 time=0.180 ms
--- 20.10.10.5 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.132/0.156/0.180/0.024 ms
root@controller (admin) $> #...................................................................

即使我们通过不同的网络能看到VMs之间的互连性,但是仍不能连接互联网(internet)。来源于VMs的包仍然不能到达外部网络。我们目前的网络基础设施的状态如下图:

上面图中所示:两个网络(RED和GREEN)通过路由器互连,但是路由器和互联网之间没有连接。为了实现这种互连,我们需要设定该路由器为网关,这意味着连接路由器的一个接口到一个外部网络。在这种情况下,我们有一个存在的外部网络,我们将增加这个网络作为这个路由器的网关。

root@controller (admin) $> neutron router-gateway-set EXT_VR ext
root@controller (admin) $> #...................................................................

这样将创建一个新的端口,这个端口将绑定这个外部网络到路由器上。让我们通过以下命名验证一下:

root@controller (admin) $> neutron port-list -c id -c fixed_ips
+----+----------------------------------------------------------------------------------------+
| id | fixed_ips                                                                              |
+----+----------------------------------------------------------------------------------------+
| XX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e", "ip_address": "10.10.10.6"}      |
| XX |  {"subnet_id": "acd4141a-149d-4a11-80bb-17eabaf0149f", "ip_address": "192.168.100.104"}|
| XX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e", "ip_address": "10.10.10.5"}      |
| XX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e", "ip_address": "10.10.10.1"}      |
| XX | {"subnet_id": "db05f7e4-2d48-4d86-8089-d9e164f86ca8", "ip_address": "20.10.10.6"}      |
| XX | {"subnet_id": "db05f7e4-2d48-4d86-8089-d9e164f86ca8", "ip_address": "20.10.10.4"}      |
| XX | {"subnet_id": "1667e8b9-e2fe-41a6-bfd2-7de41f777d6e", "ip_address": "10.10.10.4"}      |
| XX | {"subnet_id": "db05f7e4-2d48-4d86-8089-d9e164f86ca8", "ip_address": "20.10.10.5"}      |
| XX | {"subnet_id": "acd4141a-149d-4a11-80bb-17eabaf0149f", "ip_address": "192.168.100.105"}|
| XX | {"subnet_id": "db05f7e4-2d48-4d86-8089-d9e164f86ca8", "ip_address": "20.10.10.1"}      |
+----+----------------------------------------------------------------------------------------+
root@controller (admin) $> #...................................................................

正如上面我们看到的,存在两个新的端口(for 192.168.100.104 and for 192.168.100.105) . 这有两个新的连接点,一个绑定路由器到一个外部网络上,另一个外部网络绑定到internet上。这样现在来自虚拟机内部的包能到达internet了。

浮动IP(Floating IPs)

我们看到前面步骤创建的VMs能到达外部公共网络,但是相反是不可能的,我们不能从外部网络到达VMs。任何要到VM1(有内部IP10.10.0.5)的包到达外部路由时,该网关路由不能区分哪个时目的VM。让我们更详细的了解这种情况。

上面的图跟我们之前的网络基础设施图相似。刚开始我们尽力发送一个从VM1到外部机器(IP:56.57.58.59)的包。由于VM1被连接到虚拟路由器的外部连接上,包能到达有源IP10.10.10.5和目标IP56.57.58.59的虚拟路由器上。我们的虚拟路由器EXT_VR能改变这个源IP为自己的IP地址(192.168.0.19),从而发送包到正确的目标上。这是可行的。

但是对于从任何外部源到任何一个VM上的包,用路由器的公共IP地址能到达我们的外部虚拟路由器EXT_VR。但是基于在包里提供的信息,这个虚拟路由器无法识别目的VM.

一种允许外部网络连接一个VM的方式是为每个VM创建公共IP。但是长远来看这不是无法维护的。一般情况下我们希望我们的VMs能与外部网络断开除了特殊的情况外。每个VM用公共IP将导致我们维护每个VM的巨大的IP池。

浮动IP是有路由功能的公共IP,这样的IP能分配到一个VM上也能再次撤销这样的IP。这个在路由层次上进行维护。

作为本章节的一部分,让我们在公共网络EXT里产生一些浮动IP。然后我们将动态分配这些浮动IP到VM1,下面来验证一下:

root@controller (admin) $> neutron floatingip-list
root@controller (admin) $> root@controller (admin) $> neutron floatingip-create ext
Created a new floatingip:
+---------------------+----------------------------------------+
| Field               | Value                                  |
+---------------------+----------------------------------------+
| fixed_ip_address    |                                        |
| floating_ip_address | 192.168.100.103                        |
| floating_network_id | 4901039d-07b1-4ebf-91f1-559dd657e034   |
| id                  | 02ff1d61-6280-4f25-a8ff-9e5676ece01d   |
| port_id             |                                        |
| router_id           |                                        |
| status              | DOWN                                   |
| tenant_id           | 34609d0ea9ce48f98145ecc5bbac9f77       |
+---------------------+----------------------------------------+
root@controller (admin) $> neutron floatingip-associate 02ff1d61-6280-4f25-a8ff-9e5676ece01d 378956ec-3928-4e06-873c-423b7f017695
root@controller (admin) $>
root@controller (admin) $> neutron floatingip-list -c id -c fixed_ip_address -c floating_ip_address
+---------------------- ---------------+------------------+---------------------+
| id                                   | fixed_ip_address | floating_ip_address |
+--------------------------------------+------------------+---------------------+
| 02ff1d61-6280-4f25-a8ff-9e5676ece01d | 10.10.10.5       | 192.168.100.103     |
+--------------------------------------+------------------+---------------------+
root@controller (admin) $> #...................................................................

刚开始的 neutron floatingip-list 命令没有得到结果。这意味着不存在浮动IP。然后我们在外部网络上产生一个浮动IP。这个IP将从网络地址池中随意选择出来。我们发现了想要绑定的VM的端口。用neutron floatingip-associate命令我们能用一个虚拟的机器端口来连接这个浮动IP。这样就建立从一个外部网络到VM1的连接。

结论

来到了这个Neutron介绍的文末了。我们对在一个OpenStack Cloud如何创建网络和子网有了一个了解。我们也看到通过使用安全组来控制每个虚拟机的访问。我们也知道如何用L3交换和路由连接不同的网络。我们也知道如何用浮动IP去允许外部连接到虚拟机

原文地址:https://www.cnblogs.com/dream397/p/15035057.html