OpenStack之OVS L2代理

2023-12-19 10:18
文章标签 代理 openstack l2 ovs

本文主要是介绍OpenStack之OVS L2代理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

此代理使用“Open vSwitch”虚拟交换机为实例创建L2层连通性,以及与OpenStack Nova一起创建的网桥执行过滤。

ovs-neutron-agent可以配置为使用不同的网络技术,创建project隔离。这些技术被实现为ML2 type驱动程序,与Open vSwitch mechanism驱动程序一起使用。

VLAN 标签 Tags

在这里插入图片描述

Open vSwitch: http://openvswitch.org

GRE 隧道

GRE隧道的深度文档请参考RedHat的 Networking in too much detail

VXLAN 隧道

VXLAN是一种覆层计数,将二层的MAC帧封装在UDP中。更多详细信息请参见链接:The VXLAN wiki page

Geneve 隧道

Geneve使用UDP作为传输层协议,由于使用可扩展的选项头部,导致其长度是动态的。非常重要的是仅在较新的内核中支持(内核版本 >= 3.18,OVS版本 >= 2.4)。更多信息可参考文档:Geneve RFC document

网桥管理

为了使代理能够处理多个隧道技术,以及从project隔离中解耦分段技术需求,并为保持向后兼容性,OVS代理也需要在没有隧道的情况下正常工作,L2代理依赖于隧道网桥“br-tun”,以及众所周知的integration网桥“br-int”。

所有VM 的VIF都插入integration网桥。给定虚拟网络上的VM VIFs共享一个公共的“local”VLAN(即不传播到外部)。此local VLAN的VLAN ID映射到实现此虚拟网络的物理网络。

对于实现为VXLAN/GRE隧道的虚拟网络,逻辑交换机(Logical Switch - LS)标识符用于区分Hypervisor间隧道的project流量。在云中创建了到其它Hypervisors的隧道网格。这些隧道起止于每个Hypervisor的隧道网桥,与“br-int”集成网桥无关。patch端口完成将集成网桥(br-int)上的本地VLAN连接到隧道网桥(br-tun)的Hypervisor间隧道上。

对于实现为VLAN或flat网络的每个虚拟网络,一个veth或者一对patch端口用来将集成网桥(br-int)上的本地VLAN连接到物理网桥,伴随着根据需要进行的流规则的添加、修改或剥离VLAN标记,因此保留了与添加隧道能力之前的OVS代理使用方式的向后兼容性(有关更多详细信息,请查看https://review.openstack.org/#/c/4367)。

请记住,此设计决策可能在未来为了支持现有的VLAN标签流量(例如,来自NFV虚拟机)和/或 处理Open vSwitch原生支持的QinQ而被推翻。

Trunk接口处理用例

原理

在OVS代理的第一个设计出现时,trunking在OpenStack中只是一个白日梦。从那以后,OpenStack平台发生了很多事,许多部署从2012年初开始进入生产环境。

为了解决Open vSwitch之上的vlan-aware-vms 用例问题,以下方面必须考虑到:

  • 设计复杂性:重新开始总是一种选项,但只有在某些情况下才需要完全的架构重构。毕竟,客户马上就需要解决方案。值得注意的是,OVS代理设计已经相对复杂,因为它可要容纳许多的部署选项,尤其是在安全规则和/或加速的相关方面。

  • 升级复杂性:能够改造现有的设计意味着现有部署不需要通过forklift升级,就可获得新特性;或者,要避免迁移的愿望需要更复杂的解决方案,能够支持多模式的操作;

  • 设计可重用性:理想情况下,建议的设计很容易应用于Neutron L2代理支持的各种后端技术:Open vSwitch 和 Linux 网桥

  • 性能惩罚:从长远来看,没有哪种解决方案明显的不能满足高吞吐量的要求。

  • 功能兼容性:不管是好是坏,VLAN transparency 与VLAN awareness感知交织在一起。前者使平台不困扰于VM发送的数据包所关联的标签,由底层找出发送数据包的位置;后者是让平台使用报文相关联的VLAN标签,来确定报文需要去哪里。理想情况下,解决VLAN awreness感知用例的设计不会对解决VLAN transparency的用例产生负面影响。也就是说,这两个特性仍然在其使用中是相互排斥的,将子端口插入vlan-transparency标志设置为True的网络可能产生意外结果。事实上,从平台的视角识别哪些标签报文可被“transparently”对待,及哪些报文因VLAN感知而解复用(为了到达正确的目的地)是不可能的。只有两层VLAN标签堆叠在一起的情况下,结果才是可预测的,使客户的支持组合用例更加重要。

现在很明显,可接受的解决方案必须评估考虑到这些问题。值得列举的潜在解决方案有:

  • VLAN接口:按照外行的说法,这些接口允许在流量到达integration网桥之前解复用流量,在网桥上流量是被隔离的,然后被送到正确的目的地。此解决方案对于基于iptables和原生OVS安全规则都是证实可工作的:“https://etherpad.openstack.org/p/vlan@tap_experiment"。此解决方案具有以下设计启示:

  • 设计复杂性:这对现有的OVS设计的变更相对较小,它可以与iptables和原生OVS安全规则一同工作。

  • 升级复杂性:为了使用此解决方案无需进行重大升级,不会导致潜在的数据平面中断。

  • 设计可重用性:VLAN接口易于Open vSwitch和Linux网桥使用。

  • 性能惩罚:使用VLAN接口意味着必须涉及内核。对于Open vSwitch,要使用像DPDK这样的快速路径就是一个未解决的问题(Kernel NIC interfaces 还不在发行版和OVS的路线图上,而且很可能永远不会)。即使没有额外的网桥,例如
    使用原生的OVS防火墙,随着用户空间连接跟踪的出现,其允许 stateful firewall driver 与DPDK一同工作,在性能方面,纯用户空间使能DPDK的解决方案和基于内核的解决方案还是有很大差距的,至少在某些流量条件下。

  • 功能兼容性:为了在采用VLAN接口后让设计保持简单,同时使能VLAN transparency,Open vSwitch需要支持QinQ,目前的2.5版本缺少QinQ,并没有持续的整合计划。

  • 完全的openflow:用外行的术语来说,这意味着使用OpenFlow编程数据平面提供租户隔离,以及数据包处理。此解决方案具有以下设计含义:

  • 设计复杂性:这需要对当前Neutron L2代理的架构重构。

  • 升级复杂性:现有部署将无法正常工作,除非采取以下之一的操作:a)代理可以处理“旧”和“新”方式的数据路径访问;b)在发布版本升级期间,强制进行数据平面迁移,从而可能导致(可能无法恢复)数据平面中断。

  • 设计可重用性:Linux桥的解决方案仍将需要,以避免扩大与Open vSwitch之间的空隙(例如,OVS有DVR,而LB没有)。

  • 性能惩罚:使用OpenFlow将允许利用DPDK提供的用户空间程序和快速处理,但是尽管如此,工程造价还是相当高。安全规则必须由 learn-based firewall 提供充分利用DPDK的能力,至少直到 user space的连接跟踪功能在OVS中可用。

  • 功能兼容性:采用OpenFlow,租户隔离不再通过本地VLAN提供,从而Open vSwitch中满足QinQ支持的需求不再迫切。

  • 每Trunk端口OVS网桥:用外行术语来说,这类似于第一种选项,是在VM和integration网桥(br-int)之间引入一个额外的多路复用/解复用层,但不是使用VLAN接口,一个新的每端口OVS网桥和连接此新网桥到br-int网桥的patch端口组合将被使用。此解决方案具有以下设计含义:

  • 设计复杂性:此解决方案的复杂性介于上述两个选项之间,由于相同的工作已在 Mitaka 版本可用,并且数据路径逻辑可以部分重用。

  • 升级复杂性:假设有两个单独的代码路径在OVS代理中维护,以处理常规端口和参与到Trunk的端口,不提供从一个到另一个(或者相反)的转换能力,因此迁移是不需要的。这是以灵活性和维护复杂性为代价的实现。

  • 设计可重用性:Linux网桥mech驱动的支持VLAN Trunk的解决方案仍然需要,以避免扩大与Open vSwitch的间隙(例如,OVS有DVR,但LB没有)。

  • 性能惩罚:从性能的角度来看,采用Trunk网桥避免了代理使用内核的接口,从而解锁了快速数据包的全部处理潜力。也就是说,这只能与原生OVS防火墙联合使用。在编写此文时,唯一的DPDK防火墙驱动程序是基于学习的驱动程序,参见 networking-ovs-dpdk repo.

  • 功能兼容性:现有本地配置逻辑不会受到引入Trunk网桥的影响,因此,VM虚拟机通过常规端口连接到VLAN透明网络的用例仍然需要来自OVS的QinQ支持。

总结如下:

  • VLAN接口(A)具有吸引力,因为它以牺牲性能为代价换来了相对可控的工程成本。Open vSwitch社区需要参与才能交付VLAN透明功能。无论Open vSwitch是否选择该策略,这仍然是Linux网桥唯一可行的方法,因此解决Linux网桥对VLAN Trunk的支持。在某种程度上,此选项也可以被视为无法采用DPDK的OVS部署的备用策略。

  • Open Flow(B)是令人信服的,因为它将允许Neutron解锁Open vSwitch的全部潜能,代价是开发和运营难度。开发工作局限于Neutron社区,以解决VLAN感知和透明情况(作为两个不同的用例,单独采用)。

    在写作本文时,状态防火墙(基于OVS Conntrack)限制了DPDK的采用。但是基于学习的防火墙可以是合适的替代方案。显然,此解决方案不与iptables防火墙兼容。

  • Trunk网桥(C)尝试将选项A和B中的最佳部分组合在一起,以OVS的开发和性能为考虑点,但是以维护复杂性和灵活性为代价。仍然需要一个Linux网桥解决方案,QinQ支持仍需要用来解决VLAN透明场景。

所有考虑的因素中,就OVS而言,选项(C)是中期来看最有保证的。Trunk管理以及Trunk内端口的管理不同,首先,明智的做法是端口一旦绑定到特定的网桥(integration或者trunk)就要限制端口更新(即转换)。通过iptables规则实现的安全规则显然不受支持,而且永远不会被支持。

OVS的选项(A)可以与Linux网桥支持的结合可继续发展,如果这方面的努力已是特别低垂的果实。但是,基于此选项的工作方案将OVS代理定位为对于性能敏感的应用来说是性能次优的平台,相比于与其它基于加速或SDN控制器的解决方案。因为进一步的数据平面性能提高受到内核资源的额外使用的阻碍,从长远来看,这一选择完全没有吸引力。

从长远来看,接受选项(B)可能会比采用选项(C)更复杂。选项(C)和(B)分别涉及的开发和维护复杂性引出的问题是,是否在基于代理的架构上投入是有效的策略,尤其是如果最终结果看起来像其它已成熟的方案。

VLAN Interfaces 实现(选项 A)

此实现不需要任何vif-drivers的修改,因为Nova将以与传统端口相同的方式插入虚拟机的VIF。

Trunk 端口创建

生成一个VM,将与Trunk相关联的父端口的port-id传递给Nova。Nova/libvirt将创建tap接口并将其插入br-int网桥或防火墙网桥如果使用iptables防火墙的话。Nova将在端口的external-ids字段存储父端口的端口ID。OVS代理检测到新的VIF已插入。它得到新端口的细节和连接到网络。

代理以与传统端口相同的方式配置它:从VM发出的报文将使用与网络关联的内部VLAN ID打标签,发送到虚拟机的数据包将剥离VLAN ID。成功连接后,OVS代理将发送消息通知Neutron,父端口已UP起来。Neutron将发送Nova一个事件通知连接已成功。

如果父端口与一个或多个子端口关联,代理将如下一段所述处理它们。

子端口创建

如果子端口添加到父端口,但没有任何启动的VM使用该父端口,没有L2代理会处理它(因为此时父端口未绑定到任何主机)。

如果为父端口创建子端口时,使用该父端口的虚拟机已经在运行,OVS代理将在VM的TAP接口上创建一个VLAN接口,VLAN ID使用子端口的分段ID。会发生竞争的可能性很小:防火墙网桥可能在vif还不存在时创建并插入。在尝试创建子接口之前,OVS代理需要检查VIF是否存在。

让我们看看在使用iptables防火墙或OVS原生防火墙时,这些模型有什么不同。

Iptables 防火墙
     +----------------------------+|             VM             ||   eth0            eth0.100 |+-----+-----------------+----+||+---+---+       +-----+-----+| tap1  |-------|  tap1.100 |+---+---+       +-----+-----+|                 ||                 |+---+---+         +---+---+| qbr1  |         | qbr2  |+---+---+         +---+---+|                 ||                 |+-----+-----------------+----+|    port 1          port 2  ||   (tag 3)         (tag 5)  ||           br-int           |+----------------------------+

假设子端口在network2上,并使用分段ID 100。在混合插口的情况下,OVS代理必须创建防火墙网桥(qbr2),创建tap1.100并将其插入qbr2。它将把qbr2连接到br-int网桥并在端口2的external-ids成员中设置子端口ID。

  • VM视角的Inbound流量

无标签的流量将由port1通过qbr1到eth0. port2发出的流量,将剥除network2的内部VLAN ID. 报文将无标签通过qbr2,在其上iptables规则将过滤到流量。tap1.100将添加标签100,最终到达eth0.100接口。

  • VM视角的Outbound流量

未标记的流量将从eth0通过qbr1到port1,其中将应用防火墙规则。带有VLAN 100标签的流量将离开eth0.100,通过tap1.100,其中剥离VLAN 100。它将到达qbr2,在那里将应用iptables规则并转到端口2。network2网络的内部VLAN将被br-int网桥添加,因为数据包进入的port2是一个tag端口。

OVS 防火墙

::

     +----------------------------+|             VM             ||   eth0            eth0.100 |+-----+-----------------+----+||+---+---+       +-----+-----+| tap1  |-------|  tap1.100 |+---+---+       +-----+-----+|                 ||                 ||                 |+-----+-----------------+----+|    port 1          port 2  ||   (tag 3)         (tag 5)  ||           br-int           |+----------------------------+

创建子端口时,OVS代理将创建VLAN接口tap1.100,并将其插入br-int。假设子端口在network2网络上。

  • VM视角的Inbound流量

流量将无标签的由port1到eth0. 从port2发出的流量将被剥除赋予network2的VLAN ID。它将被防火墙安装的规则过滤,到达tap1.100端口。

tap1.100将为流量打上VLAN 100的标签,随后它到达VM的eth0.100接口。

  • VM视角的Outbound流量

无标签的流量达到port1,其中使用关联与网络的VLAN ID打标签。VLAN标签为100的流量离开eth0.100达到tap1.100,其中VLAN100将被剥离。随后达到port2. 被port2上防火墙安装的规则过滤。随后,报文使用内部关联与network2的VLAN打标签,因为port2是一个trunk接口.

父端口删除

禁止删除Trunk中的活动父端口。如果父端口没有关联的Trunk(它是一个“正常”端口),可以删除。OVS代理不需要执行任何操作,删除将导致删除数据库中的端口数据。

Trunk 删除

当Nova删除一个VM时,它会删除VM对应的Neutron端口,如果此端口由Nova在启动虚拟机时创建。在vlan-aware-vm虚拟机案例中,父端口被传递给Nova,因此删除VM后,端口数据将保留在数据库中。Nova将删除VM的VIF接口(示例中tap1)作为VM终止的一部分。OVS代理将检测到删除操作,并通知Neutron服务器父端口已关闭。OVS代理将清除相应的子端口,下一节中将解释。

不允许删除VM使用的Trunk。当VM不在使用父端口时,可以删除Trunk(父端口不受影响)。删除Trunk后,也可以删除父端口。

子端口删除

删除与未用于引导VM的父端口关联的子端口,从OVS代理的角度来看,任何从ovs代理的角度来看,是一个no-op。

当删除关联于引导VM的父端口的子端口时,如果使用iptables防火墙,并且端口在br-int上,OVS代理将负责删除防火墙网桥。

Trunk 网桥实现 (选项 C)

此实现基于 etherpad.

选项use_veth_interconnection=true将不在支持,将来可能会废弃,参见[1]。网桥使用的ID和端口名称有裁剪。

::

     +--------------------------------+|             VM                 ||   eth0               eth0.100  |+-----+--------------------+-----+||+-----+--------------------------+|    tap1                        ||          tbr-trunk-id          ||                                || tpt-parent-id   spt-subport-id ||                   (tag 100)    |+-----+-----------------+--------+|                 ||                 ||                 |+-----+-----------------+---------+| tpi-parent-id    spi-subport-id ||  (tag 3)           (tag 5)      ||                                 ||           br-int                |+---------------------------------+

tpt-parent-id: trunk bridge side of the patch port that implements a trunk.
tpi-parent-id: int bridge side of the patch port that implements a trunk.
spt-subport-id: trunk bridge side of the patch port that implements a subport.
spi-subport-id: int bridge side of the patch port that implements a subport.

[1] https://bugs.launchpad.net/neutron/+bug/1587296

Trunk 创建

VM生成,传递关联与Trunk的父端口的port-id到Nova。Neutron将VIF接口要插入的网桥作为VIF信息的一部分传递给Nova。如果在plug()时网桥不存在,os-vif驱动程序将创建Trunk网桥:tbr-trunk-id。OVS代理将监控Trunk网桥上端口的创建。当它检测到Trunk网桥上创建了新端口,它将执行以下操作:

ovs-vsctl add-port tbr-trunk-id tpt-parent-id -- set Interface tpt-parent-id type=patch options:peer=tpi-parent-id
ovs-vsctl add-port br-int tpi-parent-id tag=3 -- set Interface tpi-parent-id type=patch options:peer=tpt-parent-id

创建patch端口连接Trunk网桥和integration网桥。Trunk网桥侧的patch端口,tpt-parent-id,不关联任何标签。它将承载无标签流量。

br-int网桥侧的patch端口,tpi-parent-id,标签为VLAN 3. 我们假设Trunk在network1上,其在主机上关联与VLAN 3. OVS代理将会在tpt-parent-id 和 tpi-parent-id的externel-ids上设置Trunk ID。如果父端口关联与一个或多个子端口,代理将按照下节的描述处理它们。

子端口创建

如果子端口添加到还没有任何VM使用其启动的父端口上。代理将不处理此子端口(因为此时并没有任何节点关联与父端口)。

如果子端口添加到VM使用的父端口上,OVS代理将创建一个新的patch端口:

 ovs-vsctl add-port tbr-trunk-id spt-subport-id tag=100 -- set Interface spt-subport-id type=patch options:peer=spi-subport-idovs-vsctl add-port br-int spi-subport-id tag=5 -- set Interface spi-subport-id type=patch options:peer=spt-subport-id

此patch端口连接Trunk网桥到integration网桥。Trunk网桥侧的patch端口,spt-subport-id,标签使用VLAN 100. 我们假设子端口的分段ID是100. br-int网桥侧的patch端口,spi-subport-id,标签为VLAN 5. 我们假设此主机上的子端口在network2上使用VLAN 5. OVS代理将设置spt-subport-id 和 spi-subport-id的external-ids的值为子端口ID。

  • VM视角的Inbound流量

由tpi-parent-id发出的流量将由br-int剥除VLAN ID 3. 将以无标签的格式到达tpt-parent-id,随后时tap1。
由spi-subport-id发出的流量将由br-int剥除VLAN ID 5. 到达spt-subport-id之后,被打上标签VLAN 100,随后发送到tap1.

  • VM视角的Outbound流量

从tap1发出的无标签的流量将到达tpt-parent-id,随后时tpi-parent-id,此处打上标签VLAN 3。

从tap1发出的带标签的流量将到达spt-subport-id。由于spt-subport-id是tagged端口,将剥除流量的VLAN 100标签,报文到达spi-subport-id,此处打上标签VLAN 5。

父端口删除

删除Trunk中活动父端口时不允许的。如果父端口没有关联的Trunk,可以被删除。OVS代理不需要执行任何操作。

Trunk 删除

当Nova删除一个VM时,它会删除VM对应的Neutron端口,条件是这些端口是在启动VM时由Nova创建的。在vlan-aware-vm案例中,父端口被传递给Nova,因此删除VM后,端口数据将保留在数据库中。Nova将删除Trunk网桥上连接虚拟机的端口。L2代理将检测删除操作并删除Trunk网桥。它将通知Neutron服务器父端口已关闭。

不允许删除VM使用的Trunk。当父端口不被VM使用时,可以删除Trunk(保持父端口不变)。删除Trunk后,也可以删除父端口。

子端口删除

在子端口删除后,OVS代理将删除相应的patch端口。

代理重同步

在重新同步期间,代理应检查所有Trunk和子端口是否仍然有效。根据以上段落描述的实现流程,它将删除过期的Trunk和子端口。

进阶阅读

  • Darragh O’Reilly - The Open vSwitch plugin with VLANs

这篇关于OpenStack之OVS L2代理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511817

相关文章

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

proxy代理解决vue中跨域问题

vue.config.js module.exports = {...// webpack-dev-server 相关配置devServer: {host: '0.0.0.0',port: port,open: true,proxy: {'/api': {target: `https://vfadmin.insistence.tech/prod-api`,changeOrigin: true,p

OpenStack离线Train版安装系列—3控制节点-Keystone认证服务组件

本系列文章包含从OpenStack离线源制作到完成OpenStack安装的全部过程。 在本系列教程中使用的OpenStack的安装版本为第20个版本Train(简称T版本),2020年5月13日,OpenStack社区发布了第21个版本Ussuri(简称U版本)。 OpenStack部署系列文章 OpenStack Victoria版 安装部署系列教程 OpenStack Ussuri版

OpenStack离线Train版安装系列—2计算节点-环境准备

本系列文章包含从OpenStack离线源制作到完成OpenStack安装的全部过程。 在本系列教程中使用的OpenStack的安装版本为第20个版本Train(简称T版本),2020年5月13日,OpenStack社区发布了第21个版本Ussuri(简称U版本)。 OpenStack部署系列文章 OpenStack Victoria版 安装部署系列教程 OpenStack Ussuri版

OpenStack离线Train版安装系列—1控制节点-环境准备

本系列文章包含从OpenStack离线源制作到完成OpenStack安装的全部过程。 在本系列教程中使用的OpenStack的安装版本为第20个版本Train(简称T版本),2020年5月13日,OpenStack社区发布了第21个版本Ussuri(简称U版本)。 OpenStack部署系列文章 OpenStack Victoria版 安装部署系列教程 OpenStack Ussuri版

OpenStack离线Train版安装系列—0制作yum源

本系列文章包含从OpenStack离线源制作到完成OpenStack安装的全部过程。 在本系列教程中使用的OpenStack的安装版本为第20个版本Train(简称T版本),2020年5月13日,OpenStack社区发布了第21个版本Ussuri(简称U版本)。 OpenStack部署系列文章 OpenStack Victoria版 安装部署系列教程 OpenStack Ussuri版

OpenStack镜像制作系列5—Linux镜像

本系列文章主要对如何制作OpenStack镜像的过程进行描述记录 CSDN:OpenStack镜像制作教程指导(全) OpenStack镜像制作系列1—环境准备 OpenStack镜像制作系列2—Windows7镜像 OpenStack镜像制作系列3—Windows10镜像 OpenStack镜像制作系列4—Windows Server2019镜像 OpenStack镜像制作

OpenStack镜像制作系列4—Windows Server2019镜像

本系列文章主要对如何制作OpenStack镜像的过程进行描述记录  CSDN:OpenStack镜像制作教程指导(全) OpenStack镜像制作系列1—环境准备 OpenStack镜像制作系列2—Windows7镜像 OpenStack镜像制作系列3—Windows10镜像 OpenStack镜像制作系列4—Windows Server2019镜像 OpenStack镜像制作系

OpenStack镜像制作系列2—Windows7镜像

本系列文章主要对如何制作OpenStack镜像的过程进行描述记录 CSDN:OpenStack镜像制作教程指导(全) OpenStack镜像制作系列1—环境准备 OpenStack镜像制作系列2—Windows7镜像 OpenStack镜像制作系列3—Windows10镜像 OpenStack镜像制作系列4—Windows Server2019镜像 OpenStack镜像制作系列