Nova Conductor


Conductor 服务作为 Nova 核心部件之一最初在 Grizzly 版本中发布,在整个 Nova 中充当着组织者的角色。主要提供了 3 个功能:


  • nova-conductor 连接了 nova-api、nova-compute 和 nova-scheduler 服务,提供了 长时任务编排(Task Orchestration)功能。Nova 将所有耗时长,跨节点,易出错但相对固定的处理流程抽象成 Task,包括启动虚拟机、冷迁移和热迁移等。Conductor 作为 Task 的组织者,在执行 Task 的时候 Conductor 会一直追踪它的状态,并且能够执行错误处理、恢复等一系列工作。


  • nova-conductor 为 nova-computee 提供了 数据库的代理访问机制。因为计算节点是 OpenStack 云环境中与用户业务接触最近、数据最多、最容易被攻击的目标,所以 nova-compute 被设计成无法直接访问数据库,以此来保障数据库的安全。


  • nova-conductor 为版本较旧的 nova-compute 提供了 数据库版本向下兼容性。OpenStack 作为一个分布式系统,不同的服务可能运行在不同的代码版本上,在这些模块通信的过程中,Conductor 为传输的数据对象提供了版本兼容功能。让处于不同版本的 Nova 服务能够识别 RPC 请求中数据对象的版本,并完成不兼容的对象转换。但需要注意的是,这种兼容性是向下的。


Conductor 的代码包目录结构


  1. [root@localhost nova]# tree conductor/

  2. conductor/

  3. ├── api.py

  4. ├── __init__.py

  5. ├── manager.py

  6. ├── rpcapi.py

  7. └── tasks

  8. ├── base.py

  9. ├── __init__.py

  10. ├── live_migrate.py

  11. └── migrate.py


其中,在 manager 模块中主要实现了 ComputeTaskManager 任务编排封装类和 ConductorManager 数据库访问代理封装类。


数据库访问代理机制


Conductor 实现的数据库访问代理机制带来的好处:


  • 为 nova-compute 服务的数据库访问提供了一层额外的安全保障。


  • 在保证 Conductor API 兼容性的前提下,数据库 schema 升级的同时不需要升级 nova-compute 的代码版本。


  • nova-compute 可以通过创建多个协程来使用非阻塞的 nova-conductor RPC 调用。


需要注意的是,nova-conductor 作为代理服务成为了 nova-compute 访问数据库速度的瓶颈,好在由于 Conductor 服务是无状态服务,所以在性能和稳定性要求较高的环境中可以任意横向扩展 Conductor 节点的数量。


Versioned Object Model 机制


Versioned Object Model(版本化对象模型)机制在 Icehouse 版本被引入。Object Model 将每一张数据库表都与一个 Object 对应,将对数据库表的操作都封装到 Object 实例中,需要通过 Object 实例来进行数据库的操作。这听起来了 ORM 很相似,但实际上 Object Model 是比 ORM 更上一层的封装。ORM 只是单纯将数据库表结构映射成为了一个 Python 对象,但 Object Model 是在此之上做了更多的数据库操作实践和优化,Object Model 使用面向对象的思想对数据进行了封装,并为封装的数据提供了数据库代理、RPC 向下版本兼容、流量优化等一些列高级特性。


  • Nova 数据库访问方式与对象构建过程解耦:Object Model 支持 remote 和 local 两种数据库访问方式,并非成功构建了 Object 就可以直接操作数据库,在 nova-compute 中通一个 Object 来执行数据库操作实际上是远程的,是一种间接的数据库访问。这是实现数据库访问代理功能的关键。


  • nova-compute 和数据库的在线升级:nova-compute 通过 RPC 传递 Object 到 nova-conductor 时,Object 会维护一个版本号。假设 nova-compute 传递的是一个低版本的 Object,那么 nova-conductor 就会对该 Object 进行版本兼容处理,使得低版本的 Object 也能如常对高版本 DB schema 进行操作。


  • 对象属性类型的声明:Python 是一门动态的强类型解释语言,Python 不具有对象类型声明语句,这是 Python 轻便简单的根本,也是 Python 难以支撑开发超大型项目的病因。在 Web API 层面,我们可以通过 validation.schema 和 view_builder 来严格规范输入、输出的数据结构。而在后端程序的内部实现,我们则可以通过 Object Model 来实现对 Object 每个属性字段的类型声明,这帮助我们编写出来的程序具有更好的可读性和稳定性。


  • 减少写入数据库的数据量:Object Model 支持增量更新数据库记录属性值,而无需将整个对象的所有属性都更新一遍,每个 Object 都具有一个 _change_field 实例属性用来记录变化的值。


  1. def remotable(fn):

  2. """Decorator for remotable object methods."""

  3. @six.wraps(fn)

  4. def wrapper(self, *args, **kwargs):

  5. ctxt = self._context

  6. if ctxt is None:

  7. raise exception.OrphanedObjectError(method=fn.__name__,

  8. objtype=self.obj_name())

  9. if self.indirection_api:

  10. # indirection_api: 间接数据库访问 API,就是 conductor_rpcapi.ConductorAPI()

  11. # object_action: remotable 装饰器中调用,Versioned Object 实例通过这个方法来远程访问数据库

  12. # 相对的,object_class_action_versions: remotable_classmethod 装饰器中调用,Versioned Object 类通过这个方法来远程访问数据库

  13. updates, result = self.indirection_api.object_action(

  14. ctxt, self, fn.__name__, args, kwargs)

  15. for key, value in six.iteritems(updates):

  16. if key in self.fields:

  17. field = self.fields[key]

  18. # NOTE(ndipanov): Since VersionedObjectSerializer will have

  19. # deserialized any object fields into objects already,

  20. # we do not try to deserialize them again here.

  21. if isinstance(value, VersionedObject):

  22. setattr(self, key, value)

  23. else:

  24. setattr(self, key,

  25. field.from_primitive(self, key, value))

  26. # obj_reset_changes 重新设置 change 数据

  27. # 相对的,obj_get_changes 获取 change 数据

  28. self.obj_reset_changes()

  29. # _changed_fields 存放增量更新的数据

  30. self._changed_fields = set(updates.get('obj_what_changed', []))

  31. return result

  32. else:

  33. return fn(self, *args, **kwargs)


  34. wrapper.remotable = True

  35. wrapper.original_fn = fn

  36. return wrapper


END

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。成立七年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。


技术周 | Nova Conductor Versioned Object Model 机制


点击“阅读原文”,了解九州云更多信息!

虚拟存储器系统


在早期的计算机系统中,程序员会直接对主存储器的物理地址进行操作,这种编程方式导致了当程序出现寻址错误时有可能会导致整个系统崩溃,当一个进程出现寻址错误时也可能会导致另一个进程崩溃。显然,直接操作主存的物理地址不是一个好的方法。而且,由于不存在分页或分段的存储空间管理手段,所以 CPU 寻址宽度就成为了存储容量的限制,例如:32 位 CPU 只支持 4GB 内存的寻址。这导致了该计算机无法运行存储空间需求大于实际内存容量的应用程序。


为了解决这些问题,现代计算机系统通过操作系统和硬件的结合,把主存储器和辅存储器从逻辑上统一成了一个整体,这就是虚拟存储器,或称为虚拟存储系统。虚拟存储器是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的和私有的地址空间。


虚拟存储器的两大特点:


  • 允许用户程序使用比实际主存空间大得多的空间来访问主存

  • 每次访存都要进行虚实地址转换。


理地址,即物理主存的地址空间主存被组织成一个由 M 个连续的、字节大小的单元组成的数组,每字节都有一个唯一的物理地址(Physical Address,PA)。第一个字节的地址为 0,接下来的字节的地址为 1,依此类推。给定这种简单的结构,CPU 访问存储器的最自然的方式就是使用物理地址,即物理寻址。


虚拟地址,即虚拟存储地址空间,它能够让应用程序误以为自己拥有一块连续可用的 “物理” 地址,但实际上从程序视角所见的都是虚拟地址,而且这些虚拟地址对应的物理主存空间通常可能是碎片的,甚至有部分数据还可能会被暂时储存在外部磁盘设备上,在需要时才进行数据交换。


虚拟存储器的核心思路是根据程序运行时的局部性原理,一个程序运行时,在一小段时间内,只会用到程序和数据的很小一部分,仅把这部分程序和数据装入主存即可,更多的部分可以在需要用到时随时从辅存调入主存。在操作系统和相应硬件的支持下,数据在辅存和主存之间按程序运行的需要自动成批量地完成交换。


页式虚拟存储器


在页式虚拟存储器中,通过将虚拟存储空间分割成为了大小固定的虚拟页(Vitual Page,VP),简称虚页,每个虚拟页的大小为 P=2^n 字节。类似地,物理存储空间被分割为物理页(Physical Page,PP)也称为页帧(Page Frame),简称实页,大小也为 P 字节。


同任何缓存设计一样,虚拟存储器系统必须有某种方法来判定一个虚拟页是否存放在物理主存的某个地方。如果存在,系统还必须确定这个虚拟页存放在哪个物理页中。如果物理主存不命中,系统必须判断这个虚拟页存放在磁盘的哪个位置中,并在物理主存中选择一个牺牲页,然后将目标虚拟页从磁盘拷贝到物理主存中,替换掉牺牲页。这些功能是由许多软硬件联合提供,包括操作系统软件,MMU(存储器管理单元)地址翻译硬件和一个存放在物理主存中的叫做页表(Page Table)的数据结构,页表将虚拟页映射到物理页。页表的本质就是一个页表条目(Page Table Entry,PTE)数组。


CPU 通过虚拟地址(Virtual Address,VA)来访问存储空间,这个虚拟地址在被送到存储器之前需要先转换成适当的物理地址。将一个虚拟地址转换为物理地址的任务叫做地址翻译(Address Translation)。就像异常处理一样,地址翻译需要 CPU 硬件和操作系统之间的紧密合作。比如:Linux 操作系统的交换空间(Swap Space)。如果当 CPU 寻址时发现虚拟地址找不到对应的物理地址,那么就会触发一个异常并挂起寻址错误的进程。在这个过程中,对其他进程没有任何影响。


虚拟地址与物理地址之间的转换主要有 CPU 芯片上内嵌的存储器管理单元(Memory Management Unit,MMU)完成,它是一个专用的硬件,利用存放在主存中的查询表(地址映射表)来动态翻译虚拟地址,该表的内容由操作系统管理。


当页表已经存放在主存中,那么当 CPU 访问(虚拟)存储器时,首先要查询页面得到物理主存地址之后再访问主存完成存取。显然,地址转换机制让 CPU 多了一次访问主存的操作,相当于访问速度下降一半。而且当发生页面失效时,还要进行主存-辅助的页面交换,那么 CPU 访问主存的次数就更多了。为了解决这个问题,在一些影响访问速度的关键地方引入了硬件的支持。例如:采用按内容查找的相联存储器并行查找。此外,还进一步提出了 “快表” 的概念。把页表中最活跃的部分存放在快速存储器中组成快表,是减少 CPU 访问时间开销的一种方法。


快表由硬件(门电路和触发器)组成,属于 MMU 的部件之一,通常称为转换旁路缓冲器(Translation lookaside buffer,TLB)。TLB 的本质也是一个 Cache,它比页表小得多,一般在 16 个条目 ~ 128 个条目之间,快表只是页表的一个小小的副本。查表时,带着虚页好同时差快表和慢表(原页面),当在快表中找打条目时,则马上返回主存物理地址到主存地址寄存器,并使慢表查询作废。此时,虽然使用了虚拟存储器但实际上 CPU 访问主存的速度几乎没有下降(CPU 不再需要多次访问主存)。如果快表不命中,则需要花费一个访主存时间查慢表,然后再返回主存物理地址到主存地址寄存器,并将此条目送入到快表中,替换到快表的某一行条目。


大页内存


在页式虚拟存储器中,会在虚拟存储空间和物理主存空间都分割为一个个固定大小的页,为线程分配内存是也是以页为单位。比如:页的大小为 4K,那么 4GB 存储空间就需要 4GB/4KB=1M 条记录,即有 100 多万个 4KB 的页。我们可以相待,如果页太小了,那么就会产生大量的页表条目,降低了查询速度的同时还浪费了存放页面的主存空间;但如果页太大了,又会容易造成浪费,原因就跟段式存储管理方式一般。所以 Linux 操作系统默认的页大小就是 4KB,可以通过指令查看:


  1. $ getconf PAGE_SIZE

  2. 4096


但在某些对性能要求非常苛刻的场景中,页面会被设置得非常的大,比如:1GB、甚至几十 GB,这些页被称之为 “大页”(Huge Page)。大页能够提升性能的主要原因有以下几点:


  • 减少页表条目,加快检索速度。


  • 提升 TLB 快表的命中率,TLB 一般拥有 16 ~ 128 个条目之间,也就是说当大页为 1GB 的时候,TLB 能够对应 16GB ~ 128GB 之间的存储空间。


值得注意的是,首先使用大页的同时一般会禁止主存-辅存页面交换,原因跟段式存储管理方式一样,大容量交换会让辅存读写成为 CPU 处理的瓶颈。再一个就是大页也会使得页内地址检索的速度变慢,所以并非是页面的容量越大越好,而是需要对应用程序进行大量的测试取得页面容量与性能的曲线峰值才对。


启用 HugePage 的优点


  • 无需交换,不存在页面由于内存空间不足而换入换出的问题。

  • 减轻 TLB Cache 的压力,也就是降低了 CPU Cache 可缓存的地址映射压力。

  • 降低 Page Table 的负载。

  • 消除 Page Table 地查找负载。

  • 提高内存的整体性能。


Linux 服务器的大页内存设置


在 Linux 中,物理内存是以页为单位来管理的。页的大小为 4096 字节。1MB 的内存能划分为 256 页;1GB 则等同于 256000 页。CPU 中有一个内置的内存管理单元(MMU),用于存储这些页的列表(页表),每页都有一个对应的入口地址。在这种情况下,内存管理单元的大小(注:实际上是页表的大小)决定了服务器能使用的最大内存大小。


如果为服务器分配的内存远大于现有内存管理单元能管理的量,则会造成内存的浪费。CentOS 6 中为解决这个问题,使用了大页面的方式。简单来说,大页面即大小为 2MB 或者 1GB 的页。2MB 的页适用于管理 GB 级单位的内存;1GB 的页适用于 TB 级单位的内存。


手动去管理大页面较麻烦,往往需要更改代码。为了便于系统管理员和开发人员使用, CentOS 引入了 transparent huge pages (简称 THP )的概念。THP 是一个抽象层,其自动化了创建,管理和使用大页面的大多数步骤。


大页面配置需要连续的内存空间,因此在开机时就分配是最可靠的设置方式。配置大页面的参数有:


  • hugepages :在内核中定义了开机启动时就分配的永久大页面的数量。默认为 0,即不分配。只有当系统有足够的连续可用页时,分配才会成功。由该参数保留的页不能用于其他用途。


  • hugepagesz:在内核中定义了开机启动时分配的大页面的大小。可选值为 2MB 和 1GB 。默认是 2MB 。


  • default_hugepagesz:在内核中定义了开机启动时分配的大页面的默认大小。


要调整页的尺寸,必须将配置以参数格式写入到 Linux 操作系统启动命令中。如要为系统配置 10 个 1GB 的大页面,则启动命令中要包含:default_hugepagesz=1Ghugepagesz=1Ghugepages=10。配置 1GB 的大页面,CPU 特性需要支持 pdpe1gb ,系统内核也需要支持。e.g.


  1. grub


  2. GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX default_hugepagesz=1G hugepagesz=1G hugepages=100"


查看大页的相关配置:


  1. $ sysctl -a | grep -I huge


  2. $ cat /proc/meminfo | grep -i Huge


配置大页面后,系统在开机启动时会首选尝试在内存中找到并预留连续的大小为 hugepages *hugepagesz的内存空间。如果内存空间不满足,则启动会报错 KernelPanic,Outof Memory 等错误。


使用大页面后,能减少系统管理和访问页的时间(扩大了 TLB 快页表查询的内存地址范围);内核中的 swap 守护进程也不会管理大页面占用的这部分空间。合理设置大页面能减少内存操作的负担,减少访问页表造成性能瓶颈的可能性,从而提升系统性能。


如只配置了一个大小的大页面,可以通过 /proc/meminfo 中的 HugepagesizeHugePages_Total 计算出大页面所在内存空间的大小。这部分空间会被算到已用的内存空间里,即使还未真正被使用。因此,用户可能观察到下面现象:使用 free 命令查看已用内存很大,但 top 或者 ps 中看到 %mem 的使用总量加起来却很少。


注意:一般情况下,配置的大页面可能主要供特定的应用程序或服务使用,其他进程是无法共享这部分空间的(如 Oracle SGA )。请根据系统物理内存和应用需求来设置合适的大小,避免大页面使用的浪费;以及造成其他进程因竞争剩余可用内存而出现内存溢出的错误,进而导致系统崩溃的现象。


Nova 虚拟机的大页内存设置


绝大多数现代 CPU 都支持多种内存页尺寸,从 4KB 到 2M/4M,最大可以达到 1GB。所有 CPU 都默认使用最小的 4KB 页,如果具有较大的内存可以选择启用大页内存进行分配,这将会明显减少 CPU 的页表项,因此会增加 TLB 页表缓存的命中率,降低内存访问延迟。如果操作系统使用默认的小页内存,随着运行时间,系统会出现越来越多的碎片,以至于后来难以申请到大页的内存。在大页内存大小越大时,该问题越严重。因此,如果当你有使用大页内存的需求时,最好的办法是在系统启动时就预留好内存空间。当前的 Linux 内核不允许针对特定的 NUMA 节点进行这样的设定,不过,在不久的将来这个限制将被取消。更进一步的限制是,由于 MMIO 空洞的存在,内存开始的 1GB 不能使用 1GB 的大页。


Linux 内核已经支持 THP(Transparent Huge Pages,透明巨型页)特性,该特性会尝试为应用程序预分配大页内存。依赖该特性的一个问题是,虚拟机的拥有者并不能保证给虚拟机使用的是大页内存还是小页内存。


内存块是直接指定给特定的 NUMA 单元的,这就意味着大页内存也是直接指定在 NUMA 单元上的。因此在 NUMA 单元上分配虚拟机时,计算服务需要考虑在 NUMA 单元或者主机上可能会用到的大页内存。为虚拟机内存启用大页内存时,可以不用考虑虚拟机操作系统是否会使用。


Linux 内核有一项特性,叫做内核共享存储(KSM),该特性使得不同的 CPU 可以共享相同内容的内存页。内核会主动扫描内存,合并内容相同的内存页。如果有 CPU 改变这个共享的内存页时,会采用写时复制(COW)的方式写入新的内存页。当一台主机上的多台虚拟机使用相同操作系统或者虚拟机使用很多相同内容内存页时,KSM 可以显著提高内存的利用率。因为内存扫描的消耗,使用 KSM 的代价是增加了 CPU 的负载,并且如果虚拟机突然做写操作时,会引发大量共享的页面,此时会存在潜在的内存压力峰值。虚拟化管理层必须因此积极地监控内存压力情况并做好现有虚拟机迁移到其他主机的准备,如果内存压力超过一定的水平限制,将会引发大量不可预知的 Swap 操作,甚至引发 OOM。所以在性能要求严格的场景中,我们建议关闭内存共享特性。


计算节点可以配置 CPU 与内存的超配比例,但是一旦使用了大页内存,内存便不能再进行超配。因为当使用大页内存时,虚拟机内存页必须与主机内存页一一映射,并且主机操作系统能通过 Swap 分区分配大页内存,这也排除了内存超配的可能。大页内存的使用,意味着需要支持内存作为专用资源的虚拟机类型。


尽管设置专用资源时,不会超配内存与 CPU,但是 CPU 与内存的资源仍然需要主机操作系统提前预留。如果使用大页内存,必须在主机操作系统中明确预留。对于 CPU 则有一些灵活性。因为尽管使用专用资源绑定 CPU,主机操作系统依然会使用这些 CPU 的一些时间。不管怎么样,最好可以预留一定的物理 CPU 专门为主机操作系统服务,以避免操作系统过多占用虚拟机 CPU,而造成对虚拟机性能的影响。Nova可以保留一部分 CPU 专门为操作系统服务,这部分功能将会在后续的设计中加强。


允许内存超配时,超出主机内存的部分将会使用到 Swap。ZSWAP 特性允许压缩内存页被写入 Swap 设备,这样可以大量减少 Swap 设备的 I/O 执行,减少了交换主机内存页面中固有的性能下降。Swap 会影响主机整体 I/O 性能,所以尽量不要把需要专用内存的虚拟机机与允许内存超配的虚拟机放在同一台物理主机上。如果专用 CPU 的虚拟机与允许超配的虚拟机竞争 CPU,由于 Cache 的影响,将会严重影响专用 CPU 的虚拟机的性能,特别在同一个 NUMA 单元上时。因此,最好将使用专用 CPU 的虚拟机与允许超配的虚拟机放在不同的主机上,其次是不同的 NUMA 单元上。


关于虚拟机在主机上的 CPU 与内存的布局决策,也会影响其他的主机资源分配。例如,PCI 设备与 NUMA 单元关系密切,PCI 设备的 DMA 操作使用的内存最好在本地 NUMA 单元上。因此,在哪个 NUMA 单元上分配虚拟机,将会影响到 PCI 设备的分配。


Nova 内存页设置


  1. openstack flavor set FLAVOR-NAME

  2. --property hw:mem_page_size=PAGE_SIZE


PAGE_SIZE


  • small (default):使用最小的 page size,e.g. x86 平台的 4KB。

  • large:只使用最大的 page size,e.g. x86 平台的 2MB 或 1GB。

  • any:取决于 Hypervisor 类型。如果是 Libvirt 的话,会根据服务器的大页内存设置进行决策,优先使用大的大页,依次递减。

  • pagesize:指定具体的 pape size,e.g. 4KB、2MB、2048、1GB。


e.g.

  1. openstack flavor create --vcpus 2 --ram 2048 --disk 40

  2. --property hw:mem_page_size=2MB Flavor1


  3. openstack flavor create --vcpus 2 --ram 2048 --disk 40

  4. --property hw:mem_page_size=1GB Flavor2


NOTELarge pages can be enabled for guest RAM without any regard to whether the guest OS will use them or not. If the guest OS chooses not to use huge pages, it will merely see small pages as before. Conversely, if a guest OS does intend to use huge pages, it is very important that the guest RAM be backed by huge pages. Otherwise, the guest OS will not be getting the performance benefit it is expecting.


手动为 KVM 虚拟机中设置大页内存(http://www.linux-kvm.org/page/UsingLargePages):


  1. # 2G 内存设置 256

  2. # 4G 内存设置 512

  3. # 8G 内存设置 1024


  4. echo 1024 > /proc/sys/vm/nr_hugepages


END

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。成立七年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。


技术周 | OpenStack 高性能虚拟机之大页内存


点击“阅读原文”,了解九州云更多信息!

概览


技术周 | Kuryr对接之Baremetal containers networking


packstack安装openstack


准备


控制节点与计算节点同时运行。

关闭防火墙

1.  systemctl stop firewalld  

2.  systemctl disable firewalld  

 

关闭SELinux

1.  sed -i ‘s#SELINUX=enforcing#SELINUX=disabled#g’ /etc/selinux/config  

2.  setenforce 0  

 

修改yum源

1.  yum -y install wget  

2.  wget http://mirrors.aliyun.com/repo/Centos-7.repo  

3.  yum update  

 

下载安装包


控制节点与计算节点同时运行。

1.  yum install -y centos-release-openstack-rocky  

2.  yum install -y openstack-packstack  

 

控制节点生成并修改answer文件


控制节点运行下述命令生成answer file。

1.  packstack –gen-answer-file answer.txt  

修改生成的answer.txt,根据实际控制节点与计算节点的IP,修改下述信息。

1.  #关闭非必须组件减少安装时间  

2.  CONFIG_CINDER_INSTALL=n  

3.  CONFIG_MANILA_INSTALL=n  

4.  CONFIG_SWIFT_INSTALL=n  

5.  CONFIG_AODH_INSTALL=n  

6.  CONFIG_CONTROLLER_HOST=<控制、网络节点IP>  

7.  CONFIG_COMPUTE_HOSTS=<计算节点IP>  

8.  CONFIG_NETWORK_HOSTS=<控制、网络节点IP>  

9.  #启用LB  

10.CONFIG_LBAAS_INSTALL=y  

11.CONFIG_NEUTRON_METERING_AGENT_INSTALL=n  

12.CONFIG_NEUTRON_ML2_TYPE_DRIVERS=vxlan,flat  

13.CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=vxlan  

14.#将默认的OVN backend切换为ovs  

15.CONFIG_NEUTRON_ML2_MECHANISM_DRIVERS=openvswitch  

16.CONFIG_NEUTRON_L2_AGENT=openvswitch  

 

控制节点运行安装


1.  packstack –answer-file answer.txt  


准备openstack中K8S所需项目、用户及网络信


创建K8S所需资源可以在dashboard中进行,也可以直接通过命令行操作。


创建K8S专用的项目,用户信息

1.  openstack project create k8s  

2.  openstack user create –password 99cloud k8s-user  

3.  openstack role add –project k8s –user k8s-user admin  


在K8S项目下创建pod 网络子网及service 网络子网

1.  openstack network create pod_network  

2.  openstack network create service_network  

3.  openstack subnet create –ip-version 4 –subnet-range 10.1.0.0/16 –network pod_network pod_subnet  

4.  openstack subnet create –ip-version 4 –subnet-range 10.2.0.0/16 –network service_network service_subnet  


在K8S项目路由器连接pod网络子网与service网络子网


1.  openstack router create k8s-router  

2.  openstack router add subnet k8s-router pod_subnet  

3.  openstack router add subnet k8s-router service_subnet  


在K8S项目创建pod安全组


1.  openstack security group create service_pod_access_sg  

2.  openstack security group rule create –remote-ip 10.1.0.0/16 –ethertype IPv4 –protocol tcp service_pod_access_sg  

3.  openstack security group rule create –remote-ip 10.2.0.0/16 –ethertype IPv4 –protocol tcp service_pod_access_sg  


注意,这里的创建的project id,user name,password,pod subnet id,service subnet id,security group id均需要记录,用于后期配置kuryr controller。

 

kubeadm安装Kubernetes

准备

master节点


以下操作在master节点和work 节点均执行。

关闭虚拟内存

1.  swapoff -a  

2.  sed -i ‘s/.*swap.*/#&/’ /etc/fstab  

配置转发参数

1.  cat <<EOF >  /etc/sysctl.d/k8s.conf  

2.  net.bridge.bridge-nf-call-ip6tables = 1  

3.  net.bridge.bridge-nf-call-iptables = 1  

4.  EOF  

5.  sysctl –system  

配置kubernetes阿里源

1.  cat <<EOF > /etc/yum.repos.d/kubernetes.repo  

2.  [kubernetes]  

3.  name=Kubernetes  

4.  baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/  

5.  enabled=1  

6.  gpgcheck=1  

7.  repo_gpgcheck=1  

8.  gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg  

9.  EOF  

设置docker源

1.  wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -P /etc/yum.repos.d/  

安装docker

1.  yum install docker-ce-18.06.1.ce -y  

 

设置镜像仓库加速

1.  sudo mkdir -p /etc/docker  

2.  sudo tee /etc/docker/daemon.json <<-‘EOF’  

3.  {  

4.    “registry-mirrors”: [“https://hdi5v8p1.mirror.aliyuncs.com”]  

5.  }  

6.  EOF  

 

启动docker

1.  systemctl daemon-reload  

2.  systemctl enable docker  

3.  systemctl start docker  

安装kubernetes相关组件

1.  yum install kubelet-1.12.2 kubeadm-1.12.2 kubectl-1.12.2 kubernetes-cni-0.6.0 -y  

2.  systemctl enable kubelet && systemctl start kubelet  

开启IPVS

加载ipvs内核,使node节点kube-proxy支持ipvs代理规则。

1.  modprobe ip_vs_rr  

2.  modprobe ip_vs_wrr  

3.  modprobe ip_vs_sh  

4.    

5.  cat <<EOF >> /etc/rc.local  

6.  modprobe ip_vs_rr  

7.  modprobe ip_vs_wrr  

8.  modprobe ip_vs_sh  

9.  EOF  

 

下载镜像


1.  cat <<EOF >> master.sh  

2.  #!/bin/bash  

3.  kube_version=:v1.12.2  

4.  kube_images=(kube-proxy kube-scheduler kube-controller-manager kube-apiserver)  

5.  addon_images=(etcd-amd64:3.2.24 coredns:1.2.2 pause-amd64:3.1)  

6.    

7.  for imageName in ${kube_images[@]} ; do  

8.    docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName-amd64$kube_version  

9.    docker image tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName-amd64$kube_version k8s.gcr.io/$imageName$kube_version  

10.  docker image rm registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName-amd64$kube_version  

11.done  

12.  

13.for imageName in ${addon_images[@]} ; do  

14.  docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName  

15.  docker image tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName  

16.  docker image rm registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName  

17.done  

18.  

19.docker tag k8s.gcr.io/etcd-amd64:3.2.24 k8s.gcr.io/etcd:3.2.24  

20.docker image rm k8s.gcr.io/etcd-amd64:3.2.24  

21.docker tag k8s.gcr.io/pause-amd64:3.1 k8s.gcr.io/pause:3.1  

22.docker image rm k8s.gcr.io/pause-amd64:3.1  

23.EOF  

24.  

25.chmod u+x master.sh  

26../master.sh  


kubeadm init安装master节点


注意这里的pod network cidr与service cidr必须和前文中创建的openstack中pod subnet cidr及service subnet cidr保持一致。


1.kubeadm init –kubernetes-version=v1.12.2 –pod-network-cidr=10.1.0.0/16 –service-cidr=10.2.0.0/16  在成功运行该命令后会输出类似如下图所示的结果:


技术周 | Kuryr对接之Baremetal containers networking


①  表示kubernetes master节点安装成功。

②  根据命令执行同样操作。

③  记录该指令,用于后期添加kubernetes node节点。


配置flannel


这里配置flannel网络插件,仅用于对接kuryr前简单验证kubernetes功能是否正常。


下载配置文件


1.  curl -LO https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml  

 

修改pod network cidr

1.  sed -i “s/10.244.0.0/10.1.0.0/g” kube-flannel.yml  

 

启动flannel

1.  kubectl apply -f kube-flannel.yml  


设置API代理

1.  kubectl proxy –port=8080 –accept-hosts=’.*’ –address=’0.0.0.0′  

 

node节点


下载镜像

1.  cat <<EOF >> node01.sh  

2.  #!/bin/bash  

3.  kube_version=:v1.12.2  

4.  coredns_version=1.2.2  

5.  pause_version=3.1  

6.    

7.  docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64$kube_version  

8.  docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64$kube_version k8s.gcr.io/kube-proxy$kube_version  

9.  docker image rm registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64$kube_version  

10.  

11.docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:$pause_version  

12.docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:$pause_version k8s.gcr.io/pause:$pause_version  

13.docker image rm registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:$pause_version  

14.  

15.docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:$coredns_version  

16.docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:$coredns_version k8s.gcr.io/coredns:$coredns_version  

17.docker image rm registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:$coredns_version  

18.EOF  

19.  

20.chmod u+x node01.sh  

21../node01.sh  

 

添加node节点


使用在master节点安装成功后保存的hubedem join命令,添加node节点。

1.  kubeadm join 192.168.1.12:6443 –token lgbnlx.ehciqy1p1rpu6g6g –discovery-token-ca-cert-hash sha256:0acd9f6c7afcab4f4a0ebc1a1dd064f32ef09113829a30ce51dea9822d2f4afd  

 

验证


在master节点运行下述命令可以看到node节点被正确加入

1.  [root@kuryra-1 ~]# kubectl get nodes  

2.  NAME       STATUS   ROLES    AGE   VERSION  

3.  kuryra-1   Ready    master   21h   v1.12.2  

4.  kuryra-2   Ready    <none>   21h   v1.12.2  

 

安装配置kuryr


安装kuryr-k8s-controller


安装

1.  mkdir kuryr-k8s-controller  

2.  cd kuryr-k8s-controller  

3.  virtualenv env  

4.  git clone https://git.openstack.org/openstack/kuryr-kubernetes -b stable/rocky  

5.  . env/bin/activate  

6.  pip install -e kuryr-kubernetes  

 

配置

1.  cd kuryr-kubernetes  

2.  ./tools/generate_config_file_samples.sh  

3.  mkdir /etc/kuryr  

4.  cp etc/kuryr.conf.sample /etc/kuryr/kuryr.conf  

这里需要修改配置文件kuryr.conf。具体配置项需根据上文中在openstack内创建的用于K8S环境的信息填写。

1.  [DEFAULT]  

2.  use_stderr = true  

3.  bindir = /usr/local/libexec/kuryr  

4.    

5.  [kubernetes]  

6.  api_root = http://192.168.1.11:8080  

7.    

8.  [neutron]  

9.  # 需根据上文章节中创建的项目、用户信息填写  

10.auth_url = http://192.168.1.11:5000/v3  

11.username = k8s-user  

12.user_domain_name = Default  

13.password = 99cloud  

14.project_name = ks  

15.project_domain_name = Default  

16.auth_type = password  

17.  

18.[neutron_defaults]  

19.ovs_bridge = br-int  

20.# 下面的网络资源ID需根据上文章节中创建的资源ID填写  

21.pod_security_groups = a19813e3-f5bc-41d9-9a1f-54133facb6da  

22.pod_subnet = 53f5b742-482b-40b6-b5d0-bf041e98270c  

23.project = aced9738cfd44562a22235b1cb6f7993  

24.service_subnet = d69dae26-d750-42f0-b844-5eb78a6bb873  

 

运行

1.  kuryr-k8s-controller –config-file /etc/kuryr/kuryr.conf -d  


安装kuryr-cni


安装

1.  mkdir kuryr-k8s-cni  

2.  cd kuryr-k8s-cni  

3.  virtualenv env  

4.  . env/bin/activate  

5.  git clone https://git.openstack.org/openstack/kuryr-kubernetes -b stable/rocky  

6.  pip install -e kuryr-kubernetes  

 

配置

1.  cd kuryr-kubernetes  

2.  ./tools/generate_config_file_samples.sh  

3.  mkdir /etc/kuryr  

4.  cp etc/kuryr.conf.sample /etc/kuryr/kuryr.conf  

此处需要修改配置文件kuryr.conf。

1.  [DEFAULT]  

2.  use_stderr = true  

3.  bindir = /usr/local/libexec/kuryr  

4.  lock_path=/var/lib/kuryr/tmp  

5.     

6.  [kubernetes]  

7.  # 填k8s master的IP  

8.  api_root = http://192.168.1.11:8080  

 

修改cni配置


执行下述命令。

1.  mkdir -p /opt/cni/bin  

2.  ln -s $(which kuryr-cni) /opt/cni/bin/  

由于前文安装k8s时已经安装过flannel,这里/opt/cni/bin目录已经存在。新增/etc/cni/net.d/10-kuryr.conf文件,按如下信息修改配置文件,同时删除同目录下flannel的配置文件。

1.  {  

2.      “cniVersion”: “0.3.1”,  

3.      “name”: “kuryr”,  

4.      “type”: “kuryr-cni”,  

5.      “kuryr_conf”: “/etc/kuryr/kuryr.conf”,  

6.      “debug”: true  

7.  }  

安装相应依赖包

1.  sudo pip install ‘oslo.privsep>=1.20.0’ ‘os-vif>=1.5.0’  

运行

1.  kuryr-daemon –config-file /etc/kuryr/kuryr.conf -d  

 


END

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。成立七年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。


技术周 | Kuryr对接之Baremetal containers networking


点击“阅读原文”,了解九州云更多信息!

昨日,官方正式公布了OpenInfra上海峰会的完整议程,九州云携手中国联通、中国移动研究院、诺基亚、中兴、戴尔、Verizon等合作伙伴提交的五大议题全数入选,向业界分享开源最佳实践和创新。

——导言


重磅消息!九州云五大议题入选OpenInfra上海峰会!


11月4-6日,OpenInfra全球峰会将首次来到中国,在上海世博中心向来自全球的IT领袖、服务提供商、应用开发者、社区贡献者、电信运营商等展现开源的风采。


据官方透露,此次OpenInfra上海峰会将展示一系列开源项目,如Ceph, Kubernetes, ONAP,OPNFV等,包括OpenStack基金会托管的开源项目:OpenStack、Airship、Kata Containers、StarlingX和Zuul。


峰会议程包括10个专题:人工智能(AI)、机器学习(ML)和高性能计算(HPC);容器基础设施;持续集成/持续交付(CI/CD);5G、网络功能虚拟化(NFV)、边缘计算;新手入门;开放式开发;私有云和混合云;公有云;安全性;实践工坊。涵盖了OpenStack、Kata Containers、Kubernetes、Hadoop以及StarlingX等30+开源项目的应用案例、专题报告和现场演示。  


在此次OpenInfra上海峰会现场,九州云将分享五大议题,与业界共同谋划开源基础设施的未来。


1


重磅消息!九州云五大议题入选OpenInfra上海峰会!

范桂飓

重磅消息!九州云五大议题入选OpenInfra上海峰会!

俞谦

重磅消息!九州云五大议题入选OpenInfra上海峰会!

白永君

      

OpenStack和Kubernetes的统一监控和秒级告警方案

Unified monitoring and second-level alarm scheme for OpenStack and Kubernetes


演讲者:范桂飓、俞谦、白永君

内容简介:在愈演愈烈的多云趋势下,统一的、松耦合的、迅速响应的监控告警方案,是复杂多样的云生产环境所亟需的。传统 OpenStack 的 Telemetry 架构无法延伸开来对诸如底层物理基础架构、Kubernetes 资源和其他云集群的监控,同时随着集群规模的增大,如何进行快速的告警响应也是关键问题。


我们提出了一种体系结构,通过对 Prometheus-operator 的优化,对多云下的各种资源进行统一采集管理,解决了存储的持久化问题,能够对节点、集群服务、虚拟机整个纵向栈的故障进行秒级告警,并提供了多种 Exporter 及整套监控的自动化部署方案。目前,我们已将其应用到具有 500 台物理服务器的生产环境中。


2


重磅消息!九州云五大议题入选OpenInfra上海峰会!

黄舒泉

重磅消息!九州云五大议题入选OpenInfra上海峰会!

   Ildiko Vancsa

重磅消息!九州云五大议题入选OpenInfra上海峰会!

   David Paterson

重磅消息!九州云五大议题入选OpenInfra上海峰会!

Gergely Csatari

重磅消息!九州云五大议题入选OpenInfra上海峰会!

Beth Cohen

                            

扩大范围:边缘计算工作组更新

Expanding Scope: Edge Computing Working Group Update


演讲者:黄舒泉、Ildiko Vancsa(OpenStack基金会)、David Paterson(Dell EMC)、Gergely Csatari(Nokia)、Beth Cohen(Verizon)


内容简介:边缘计算工作组继续致力于定义边缘计算的使用案例,明确不同项目之间的协同,并且基于已定义的使用案例来构建出可供参考的边缘应用架构。你可以在工作组讨论的时候加入我们,了解我们在过去的一年中是如何与 OpenStack、Open Infrastructure 以及其他开源社区进行合作的,还可以了解我们将在明年开展的工作。


3

 

重磅消息!九州云五大议题入选OpenInfra上海峰会!

黄舒泉

重磅消息!九州云五大议题入选OpenInfra上海峰会!

Qihui Zhao

重磅消息!九州云五大议题入选OpenInfra上海峰会!

tao jiang

 

如何实现电信营运商多异构边缘云的协同与管理?

How to achieve coordination and management of heterogeneous and multi-vendor cloud on telco edge?


演讲者:黄舒泉、Qihui Zhao(中国移动研究院)、tao jiang(中兴)


内容简介:电信运营商可以在边缘提供网络服务、MEC 计算能力、IaaS、PaaS、SaaS 和许多其他的服务。边缘基础设施则是所有这些上层服务的底层支撑。电信的边缘基础设施与核心基础设施有很大的不同,后者只会拥有少量的云和解决方案,相比之下边缘基础设施则更加的开放和多样化。电信边缘云的数量和类型都要多于核心云。如何管理这些异构的,来自多家云服务提供商的边缘云将会是电信运营商在边缘上提供各类云和服务时需要面临的问题。


在本次会议中,我们将会介绍边缘云的体系结构,还会介绍几个现存的与异构边缘云相关的解决方案,以及我们在探索边缘云管理和集中式 O&M 解决方案时所遇到的一些问题和经验。


4


重磅消息!九州云五大议题入选OpenInfra上海峰会!

黄舒泉

重磅消息!九州云五大议题入选OpenInfra上海峰会!

陈丹

重磅消息!九州云五大议题入选OpenInfra上海峰会!

Qihui Zhao

重磅消息!九州云五大议题入选OpenInfra上海峰会!

Yongbing Fan

重磅消息!九州云五大议题入选OpenInfra上海峰会!

Wei Hu

   

中国电信运营商是如何看待边缘计算的?

How China Telco Carriers think of Edge Computing?


演讲者:黄舒泉、陈丹(中国联通)、Qihui Zhao(中国移动研究院)、Yongbing Fan、Wei Hu


内容简介:在数字化转型浪潮与 5G 到来的前夕,传统网络通信架构面临着如何处理海量边缘数据的巨大压力。同时,大数据和人工智能(AI)等创新型应用也需要在边缘网络上提供更加强大的支持。


中国移动,中国联通和中国电信,三大中国电信运营商都在积极地推动着基于自身业务特点所探索出来的边缘计算实践经验,为即将到来的 5G 时代做好准备。


我们希望邀请这些运营商的代表来讲述他们对即将到来的边缘计算和 5G 时代的看法,并分享一些这方面的实践,如:StarlingX 功能评测和 POCs。


5


重磅消息!九州云五大议题入选OpenInfra上海峰会!

李开

重磅消息!九州云五大议题入选OpenInfra上海峰会!

陈丹

重磅消息!九州云五大议题入选OpenInfra上海峰会!

Wei Hu

  

中国联通使用边缘编排系统在 5G 环境中管理 StarlingX、K8S 和 OpenStack 边缘数据中心

China Unicom’s edge Orchestration system in 5G to manage StarlingX, K8S and OpenStack edgeDC


演讲者:李开、陈丹(中国联通)、Wei Hu


内容简介:该主题旨在演示中国联通的边缘管理与编排(Edge-MANO)系统如何在 5G 环境中管理基于 StarlingX、K8S 和 OpenStack 的 3 类边缘站点。我们将演示其中的一些功能,例如:TOSCA(The de-facto edge app descriptor from carriers)的软件包分发,边缘资源编排和边缘应用程序的生命周期管理(deployment, scaling-up, scaling-down and seamless upgrading)能力。我们还将介绍一些与边缘相关的后端设计,例如:怎么为 VM-Based 或 Cloud-Native 的应用程序设计 MEAD(Mobile Edge Application Descriptor),以及针对 5G PNFs/VNFs 的 NFVO/VNFM 协同设计。


从以上五大议题可以看到,大多为偏向边缘计算内容。随着5G概念的出现,逐渐成熟强大的OpenStack开始从云计算延伸到了边缘计算领域,奠定了边缘计算的架构和基础。而在OpenStack方面领先的九州云也敏锐地捕捉到了边缘计算的“风口”。


近两年九州云确立了边缘计算的战略方向,并将自身在OpenStack领域的优势延续到了边缘计算领域。实现了边缘IaaS、边缘CaaS、边缘PaaS、边缘MANO、边缘SD-WAN等多项技术突破,并深化了“边缘云解决方案”赛道,成长为边缘计算领域的一匹“黑马”。


在此次峰会上,除OpenStack相关话题外,九州云将更多的向业界同行分享边缘计算的生产需求和实战经验, 与业界共建开源基础设施新生态。


OpenInfra上海峰会完整议程:请点击这里

九州云五大议题详情全览:请点击这里



END

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。成立七年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。


重磅消息!九州云五大议题入选OpenInfra上海峰会!


点击“阅读原文”,查看OpenInfra上海峰会完整议程!

技术周 | StarlingX overview和功能点详解

介绍StarlingX之前需要先介绍一下StarlingX的前身。早在2014年风河就成立了TitaniumCloud生态系统项目,设计目标就是要成为电信网络应用软件理想的云操作系统平台,以最高的可靠性地运行网络虚拟化功能。StarlingX是面向边缘计算场景的多功能性的基础设施堆栈,由于StarlingX是从WindRiver产品开源产生的,而WindRiver的这款Titanium Cloud已经从成立到现在有5年时间之久的发展,Titanium Cloud算是一款比较成熟的产品了,因此即使到今年7月份为止,StarlingX仅仅发布了第一个版本,StarlingX也具备一定条件的生产化使用基础。

 

StarlingX既是一个开发项目又是一个集成项目。它将新服务与更多开源项目结合到一个总体边缘云软件堆栈中。由于风河Titanium Cloud平台在性能、可用性上都有了比较高的提升。目前风河的软件可以在通用服务器硬件上实现电信级的性能;提供的NFV平台可以达到6个9,完全满足电信级要求。而StarlingX基于该风河的商业产品进行开源优化形成,因此StaringX也继承了Titanium Cloud的优点。

 

StarlingX主要面向的场景是工业IoT、电信、视频业务等对延迟要求较高的业务。并且基于Titanium Cloud,StarlingX提供了性能兼顾高可用的特点。

 

StarlingX最初的代码是由英特尔和Wind River提供,目前StarlingX项目由OpenStack Foundation托管代码,StarlingX目前与领先的开源项目(包含OpenStack、Ceph和OVS)相结合。StarlingX在2018年10月才release了第一个版本,因此StarlingX是一个非常年轻的项目。

 

对于一些想了解边缘计算云更详细的资料的同学,OpenStack社区发表了边缘计算的白皮书,介绍了边缘计算云的起因,挑战,解决思路和应用场景,链接如下:

https://www.openstack.org/edge-computing/cloud-edge-computing-beyond-the-data-center?lang=en_US

 

白皮书总结到,一个成熟的边缘计算云是面向于开发者和应用的,并把基础设施的资源下沉到边缘网络上。那么如何定义边缘计算呢?最重要的一点就是延迟,即从各类终端、最终应用到边缘云的网络延迟需要小于20ms。

 

第二部分介绍的是StarlingX的功能点, StarlingX由于包含了OpenStack,因此OpenStack的功能点StarlingX同样具备,但是以下功能点是与OpenStack不一样的地方。

技术周 | StarlingX overview和功能点详解

Configuration Management

配置管理功能在边缘云基础设施架构中变得非常重要,特别是在管理大量的远端节点的时候,因为有些远处的节点,不太方便直接对其进行配置。因此借助于Configuration Management功能点,可以方便地对远端的物理服务器进行配置管理,配置管理中包含了CPU、GPU、内存、Huge pages,crypto/compression PCIE配置等。

 

Fault Management

这个组件是可以统计报警和查看log,并且同时包括了中心云和边缘云的物理资源和虚拟资源,并且在Horizon上都可以进行查看,监控的方面比OpenStack更广。

 

Host Management

这个组件可以检查虚拟主机的状态,并在主机关机的情况下尝试自动重启,并根据集群状态、关键进程、资源的阈值、物理主机的故障等来使用不同的调度策略来进行对虚拟机的重启。

 

Service Management

该功能点提供了服务的高可用,使用了多路通道来避免通信的断开和服务的脑裂问题,基于StarlingX本身服务的active/passive状态的切换来保障服务的高可用,并对服务的状态进行监控。

 

Software Management

从kernel到OpenStack服务的全栈软件包升级,该功能可以实现滚动升级,比如在需要对物理服务器关机的情况下实现对虚拟机的热迁移的情况,该功能在StarlingX中仅需要在horizon界面上进行操作,该热迁移可以自动把需要更新软件包主机上的虚拟机或者容器事先迁移到可用的主机,并在更新完成之后,再自动将资源分配到更新完成的主机上,该功能提供了对升级时候的虚拟机关机问题的生命周期管理的机制。

 

第三部分是StarlingX的整体架构,StarlingX基于OpenStack组件,并进行功能的增强,提供了上述5个核心功能点的能力。

 

OSS全称为Operation support system是指运营支撑系统,BSS全称为Businesssupport system,BSS系统包括客户关系管理、数据采集系统、计费帐务、综合结算、营销支撑这些功能模块。StarlingX的上述功能点可以赋能这些系统,提供更全功能的基础设施架构。

 

在计算节点上对底层的KVM进行了优化,在网络部分引进了SR-IOV、OVS-DPDK、Intel网络加速方案,使得在计算节点的能力有了质的提供,如果说上述几个功能点提供了鲁棒性和高可用性,对底层组件的优化则是提升了整体边缘云的性能。存储节点仍然是集成了业界优秀的分布式存储方案Ceph,并提供了多种存储解决方案,可以通过分布式、集中式和商务SAN存储的融合,来保障运营商级别的存储高可用。

技术周 | StarlingX overview和功能点详解

分组核心网EPC(EvolvedPacket Core),该系统的特点为仅有分组域而无电路域、基于全IP结构、控制与承载分离且网络结构扁平化,其中主要包含MME、SGW、PGW、PCRF等网元。其中SGW和PGW常常合设并被称为SAE-GW。

 

CPE,英文全称为Customer Premise Equipment ,实际是一种接收移动信号并以无线WIFI信号转发出来的的移动信号接入设备,它也是一种将高速4G或者5G信号转换成WiFi信号的设备,可支持同时上网的移动终端数量也较多。CPE可大量应用于农村,城镇,医院,单位,工厂,小区等无线网络接入,能节省铺设有线网络的费用。

 

在对于上层的虚拟网元接口VNFs方面,StarlingX可以提供通过在虚拟机中部署虚拟的EPC、CPE来实现对电信网元的支撑。

 

StarlingX社区目前在进行对容器的支持,以后可以同时在边缘处给用户提供虚拟机和容器两种资源,同时还将开发在不同操作系统中部署StarlingX系统的能力。

 

 

END

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。成立七年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。

技术周 | StarlingX overview和功能点详解

点击“阅读原文”,了解九州云更多信息!

前文

我非要捅穿这 Neutron(一)底层网络实现模型篇

Neutron 的资源模型

Network

Network 是 Network Connectivity as s Service 的 “根” 操作对象,是 Neutron 对二层网络的抽象,包含了用户对「大二层」网络的一切想象,支持 Local、 Flat、 VLAN、 VXLAN、 GRE、 Geneve 等多种网络类型并不断扩充。如果将 Network 映射到现实世界的话,它就相当于一个巨大的交换机:从介质的角度,它拥有许多端口;从网络的角度,它划分广播域;从功能的角度,它提供了 “隔离” 和 “转发”。

在《Networking 基本术语/概念》一文中,我们已经记录了关于大二层网络的介绍,这里不再赘述。

Neutron 网络概念的定义和类型花样之多,令人眼花缭乱。我们姑且从几个不同的方面对 “网络” 做一个分类归纳,区分理解。

从网络实现模型分层的角度看

  • 本地网络(br-int)

  • 租户网络(br-ethX/br-tun)

  • 外部网络(br-ex)

从网络实现技术的角度看

  • 非隧道网络(Local、Flat、VLAN)

  • 隧道网络(VxLAN、GRE、Geneve)

从网络从属关系的角度看

  • 运营商(物理)网络

  • 租户网络

从 Neutron 所追求的「多租户隔离多平面网络」实现来看,我们可以感受到每一个 Network 都应该具有以下 3 个核心要素,我将其称之为 “核心三要素”:

  • Network Type

  • VID Range

  • Physical Network Mapping

接下来的内容,主要就是围绕这三个核心要素展开。

运营商网络和租户网络

本章节我们要使用的就是第三种视角。所谓 “从属” 即是 “属于谁”:由 Neutron 创建的,属于 Neutron 管理范畴的网络,我们称之为租户网络(Tenant Network);属于运营商(e.g. OpenStack 平台的运营者)原有的,Neutron 无法管理、只能映射(记录)的网络,称之为运营商网络( Provider Network)。两种网络都采用了 Network 资源模型,从模型的角度来看,两者的区别在于是否会持久化下列三个字段属性值(运营商网络具有,租户网络反之):

  • provider:network_ type (string) — The type of physical network that this network is mapped to.

  • provider:physical_network (string) — The physical network where this network is implemented.

  • provider:segmentation_id (int) — The ID of the isolated segment on the physical network.

首先需要提出的问题是:为什么要区分租户网络和运营商网络?这是因为单存的 Neutron(虚拟网络)本身只是一座孤岛,如果希望与外界取得联系,就需要依赖于公共设施(e.g. 移动通信网络、桥梁)的支持。虽然 Neutron 对这些公共设施没有控制权,但却可以使用(e.g. 购买手机号码)它们,所以 Neutron 要创建一个 Network 资源对象来存储这些(运营商网络)信息。而存储的地方就是上述的 3 个字段了。

举例说明运营商网络的应用场景:用户希望 OpenStack 中的 VMs 与产品部(VLAN 10=100)的若干台个人电脑处于同一个二层网络,那么用户可以为这些 VMs 创建一个 VLAN ID=100 的运营商网络 p-network1,并且填入下列字段的值。

  • provider:network_ type ==> VLAN

  • provider:physical_network ==> “产品部网络”

  • provider:segmentation_id ==> 100

NOTE:以上只是举例,实际这三个字段的值并非如此。

需要注意的是,上述例子中无论是 VLAN 类型的网络,还是 VLAN ID=100 都并非是 Neutron 创建的,而是运营商本来就已经存在的网络,Neutron 只是创建了一个 Network 资源类型并记录下这些运营商网络的信息,继而进一步使用这个运营商网络的特性而已。

简而言之,运营商网络的作用就是为了让 Neutron 内部的虚拟网络可以与物理网络连接起来。运营商网络是运营商的某个物理网络在 Neutron 上的延伸,Neutron 租户无法管理这个物理网络的生命周期

创建运营商网络

创建运营商网络有两个要点:一是只能有管理员创建,二是需要手动填写 “核心三要素”。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

上图为通过 admin 创建一个 VLAN 类型的运营商网络,填写的三个表单就对应了 “核心三要素” 并存储到数据库中 networks 表的 provider:network_ type、provider:physicalnetwork、provider:segmentationid 字段中。“核心三要素” 中的 Network Type 和 VID Range 很好理解,两者描述了运营商网络的特征。但 Physical Network Mapping 的意义你或许会感到迷惑,其实它描述的是 Neutron Network 应该如何接入运营商实际的物理网络。直白的说,就是 Neutron Network 要通过哪一张 “网卡” 来接入到实际的运营商网络中。

但为什么上图 “物理网络” 表单项填写的 “网卡” 名称是 “public” 呢?其实 “public” 是 Neutron 实现的一种 Label 机制,本质是一个 Key-Value Mapping,将便于人类理解的 Label Name 与实际的 Physical Network 隐射起来,这就是所谓的 Physical Network Mapping。几乎所有的物理网络是使用 Label 的方式来标的。而这个 Physical Network Mapping 就定义在 Neutron OvS(本文以 OvS 为例)的配置文件中。e.g.

  1. # /etc/neutron/plugins/ml2/openvswitch_agent.ini


  2. bridge_mappings = physnet1:br-ethx1, physnet2:br-ethx2

那为什么 Lable “public” 对应的 Physical Network 是 “br-ethx1” 而不是一张具体的物理网卡名称(e.g. eth0)呢?这个问题其实我们在 Neutron 的网络实现模型已经提到过,因为 OvS Bridge 会将物理服务器上的物理网卡挂载到 OvS br-ethX(IP 地址也在此),所以 Physical Network 填入的是 br-ethX 而不是 eth0。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

上图为创建一个 VxLAN 类型的运营商网络。奇怪的是为什么没有了 “物理网络” 这个表单项呢?这还是要说到 Physical Network Mapping(provider:physical_network)描述的是 Neutron Network 应该如何接入到实际的运营商网络,由于 Flat、VLAN 等非隧道网络类型本质是一个二层网络,所以需要指定 “网卡” 才能让二层的数据帧流入运营商网络。但像 VxLAN、GRE 之类的隧道网络类型的实现原理是基于三层 IP 协议的,所以隧道网络能否与运营商网络互通的关键是隧道外层封装 IP 地址是否填写正确,而非一张 “网卡”。

再次强调一下创建运营商网络的要点:

1. 只能由管理员创建

2. 需要手动填写 “核心三要素”,并持久化到数据库中

  • 非隧道网络类型:需要传入 Physical Network Mapping(provider:physical_network)字段值,描述 Neutron Network 接入运营商网络的 “网卡(br-ethX)”。

  • 隧道网络类型:只需要保证节点上已经定义好了与运营商网络对应的 Tunnel 端口 IP 地址并且三层网络通信无障碍即可。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

上图为创建一个 Flat 类型的运营商网络。Flat(扁平)类型网络是没有 VID 的,不想 VLAN 类型在网络包从 Bridge 发出前还是打 VLAN tag 并进行内外 VID 转换。Flat 的网络包就是一个 Untag 网络包,直接接入物理网络,只需要告诉它用哪一张 “网卡(物理网络)” 即可。

创建租户网络

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

上图为创建一个租户网络,可见并没有填入 “核心三要素” 的表单项。这是因为 Neutron 认为:对于租户创建自己的网络而言,租户希望得到的只是一个二层网络,至于这个二层网络是由 VLAN 提供的或由 VxLAN 提供的并无所谓,VID 是多少也并无所谓,Physical Network 是怎么 Mapping 的就更无所谓了。

一言以蔽之,租户无需关心这个 Network(二层网络)的底层实现细节。Neutron 只有做到了这一点,才能称之为 “服务”!这不禁让我想起柯达公司的一句经典广告语 —— 你只管快门,其余我来!

但话又说回来,租户自然是不必操心这些细节,但云平台管理员可不行。云平台管理员是可以通过 Neutron 的配置文件来声明定义 “核心三要素” 的。e.g.

  1. [ml2]

  2. ...

  3. tenant_network_types = vlan,vxlan

  4. mechanism_drivers = openvswitch,linuxbridge


  5. [securitygroup]

  6. firewall_driver = openvswitch


  7. [ovs]

  8. datapath_type = system

  9. bridge_mappings = public:br-ex,

  10. tunnel_bridge = br-tun

  11. local_ip = 172.18.22.200


  12. [ml2_type_flat]

  13. flat_networks = public,


  14. [ml2_type_vlan]

  15. network_vlan_ranges = public:3001:4000,


  16. [ml2_type_geneve]

  17. vni_ranges = 1:1000


  18. [ml2_type_gre]

  19. tunnel_id_ranges = 1:1000


  20. [ml2_type_vxlan]

  21. vni_ranges = 1:1000

所以,就租户网络而言,其 “核心三要素” 是不需要持久化到数据库中的,也就不具有 provider:network_ type、provider:physicalnetwork、provider:segmentationid 这三个字段值了。再一个就是从配置中可以看出,只有非隧道类型是需要填写 “物理网络” Lable 的。

创建外部网络

创建外部网络(能够访问公网的网络)属于运营商网络的另一种应用场景。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

实际上所谓的外部网络和运营商网络的底层实现并无区别,不同之处在于外部网络具有分配 Floating IP 的功能,即外部网络在网络节点的 qrouter-XXX namespace 中配置的 iptables 的 NAT(SNAT/DNAT)功能,使得内部虚拟机得以访问到外部公网。

Network 小结

Network 是 Neutron 的二层网络资源模型,对外提供二层网络的服务接口(创建、删除二层网络)。租户可以通过这个接口来创建专属于自己的租户网络,管理员可以通过这个接口来创建(对接)Neutron 所无法管理的运营商的物理网络与外部网络。

从资源的角度来看,租户网络和运营商网络的主要区别在于「可控性」:租户网络是 Neutron 完全可控的,基于 Neutron 网络实现模型支撑的多租户隔离多平面网络需求,租户网络的 “核心三要素” 由云管人员通过 Neutron 的 ML2 配置文件定义;而运营商网络却是 Neutron 所无法管理的运营商的物理网络在 Neutron 上的一种延伸,其 “核心三要素” 只是对运营商物理网络信息的一个记录,并无管理性质。

运营商网络的两种典型应用场景:一是打通 Neutron Network 与运营商内部的物理网络,使得两者之间的虚拟机可以互相通信;再一个是打通 Neutron Network 与 Internet,使得 Neutron Network 中的虚拟机可以访问公网。第二种应用场景也就是 Neutron 的 Floating IP 功能,是通过网络节点上 qrouter-XXX 中的 iptables NAT 实现的。

Subnet

Subnet 下属于 Network,是 Neutron 对三层子网的抽象,它作为一个具体网段(CIDR)的 IP 地址池,同时为使用接入到该子网的 VMs 提供 IP 核心网络服务,还负责保证三层网络之间的路由互通。在通常情况下,Subnet 符合人们对 “子网” 的常规理解。

  • IP VERSION

  • IPADDR

  • NETMASK

  • GATEWAY

  • ROUTE

  • DNS

  • DHCP

  • IPAM

NOTE:在某些特定的应用场景中(e.g. Multi-Segments),Subnet 被赋予了更加深厚的含义,但由于非常少见,这里我们暂且不谈。

IP 核心网络服务

IP 核心网络服务(IP CoreNetwork Services),又称 DDI(DNS、DHCP、IPAM)服务。

  • DNS — Domain Name System,域名系统

  • DHCP — Dynamic Host Configuration Protocol,动态主机设置协议

  • IPAM — IP Address Manager,IP 地址管理

DNS 和 DHCP 相信大家不会陌生,这里介绍一下 IPAM 服务。IPAM 用于发现、监视、审核和管理企业网络上使用的 IP 地址空间,IPAM 还可以对运行的 DHCP 和 DNS 服务器进行管理和监视。简而言之,IPAM 就是一种 IP 地址的管理手段,目的是让 IP 地址的分配、使用更加便利

Subnet 资源模型中与 DDI 服务相关的字段

– enable_dhcp:布尔类型,表示是否为 Subnet 启用 DHCP 服务,若启用,再会在 qdhcp-XXX namesapce 中启动 dnsmasq 服务进程。

– allocation_pools:是一个数组,表示 DHCP 服务可分配的 IP 地址池列表,每个地址池的格式为 [start IP,endIP]。若没有配置则以 Subnet 的 cidr 作为地址池。

– dns_nameservers:是一个数组,用于指定一批 DNS Server 的地址。这里仅记录地址,实际的 DNS 服务并不由 Neutron 提供。

– subnetpool_id:是 tables subnetpools 的外键,指向 SubnetPools 资源模型。

NOTE:SubnetPools 与 allocation_pools 是两个 “同类不同源” 的功能。两者均为 IP 地址的资源池,而前者是对后者的一种优化,在 Kilo 版本引入,为了更好的管理子网网络资源池(e.g. 提供访问接口)。

小结一下,IP 核心网络服务(DNS、DHCP、IPAM)是 Subnet 资源模型提供的服务,而服务的对象是使用该 Subnet 的 VMs。可见,Subnet 不仅仅是一个抽象资源接口,其具有一定的管理功能。这一点非常重要,因为我们在启动虚拟机时,时常选择的是一个 Network 而非 Subnet。e.g.

  1. $ openstack server create -h

  2. ...

  3. [--nic <net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,port-id=port-uuid,auto,none>]

这会让我们产生一种错觉,虚拟机网络来自于 Network,从表面看并没有问题,也是 Neutron 有意为之(对用户隐藏复杂细节的表现)。但对于开发者而言,如果带着这种认知去阅读源码就难免会产生疑惑,IP 核心网络服务的代码逻辑处理对象实际是 Subnet 而非 Network,这一点完全契合 Subnet 的资源模型设计。所以再次强调,Network 是 NaaS 的根操作对象,表示一个二层网络;而 Subnet 下属于 Network,为 Network 中的 VMs 提供 IP 核心服务

SubnetPools 资源模型

你是否考虑过,用户真的需要关心一个 Subnet 的网段是 192.X、还是 172.X、还是 10.X 吗?用户要关心的仅仅是这个 Subnet 可以提供多少个 IP 地址而已!出于这样的需求,Network 引入了 SubnetPools 资源模型,通过 subnetpool_id 字段与 Subnet 关联。SubnetPools 和 Sunbet 的关系就相当于:前者定义了一个大的(不重复的)网段,后者从中分配了一个小的网段。e.g.

  1. $ openstack subnet create -h

  2. ...

  3. [--subnet-pool <subnet-pool> | --use-prefix-delegation USE_PREFIX_DELEGATION | --use-default-subnet-pool]

NOTE:在较新的版本(Rocky)中,配合 OpenStack Placement Project,SubnetPools 还可以被一个第三方的网络软件提供的外部 IP 资源池代替。

  1. # 记录了一个 SubnetPool 的子网网段划分规则

  2. MariaDB [neutron]> select * from subnetpoolsG;

  3. *************************** 1. row ***************************

  4. project_id: 031cec3e2df143259d302aa1993fd410

  5. id: 5bbd5cab-c6a0-4851-b2d7-1137742e6adf

  6. name: shared-default-subnetpool-v4

  7. ip_version: 4

  8. default_prefixlen: 26

  9. min_prefixlen: 8

  10. max_prefixlen: 32

  11. shared: 1

  12. default_quota: NULL

  13. hash: 27e49575-31f7-4012-b57b-55c990cb6d82

  14. address_scope_id: NULL

  15. is_default: 1

  16. standard_attr_id: 1


  17. # 记录了一个 SubnetPool 的子网网段信息

  18. MariaDB [neutron]> select * from subnetpoolprefixes;

  19. +----------------+--------------------------------------+

  20. | cidr | subnetpool_id |

  21. +----------------+--------------------------------------+

  22. | 192.168.1.0/24 | 5bbd5cab-c6a0-4851-b2d7-1137742e6adf |

  23. +----------------+--------------------------------------+

Multi-Segments

在上文中我们反复提到,Network 是一个二层网络的抽象,Subnet 是一个三层子网的抽象。这是大多数 SDN 软件设计的常规定义,创建一个 Network 就相当于获得了一个广播域,创建一个 Subnet 就相当于获得了一个三层 IP 地址池。对用户的理解非常友好。

不过在实际的生产需求中 Neutron 面临一个问题:如果一个 Network 内的 VMs/Hosts 数量太多,就会出现诸如二层广播风暴、网络内交换机 ARP 表项容量不足等情况。简单来说,这就是 Network 作为云计算平台中的大二层网络却无法支撑起 “无限” 多个 VMs 的问题。为了满足这一需求 Neutron 引入了 Multi-Segments 设计。

Multi-Segments 解决这一问题的办法就是将 Network 的定义进一步升级为 “多个二层网络的容器”,同时也将 Subnet 的定义进一步升级为 “一个二层网络以及基于此的三层子网”,Subnet 之间通过三层路由互通。

Multi-Segments 顾名思义是一个 Network 具有多个 Segments,而一个 Segment 其实是一个运营商网络,这一点可以从 Segment 资源模型看出(每条 Segment 记录都包含一个运营商网络的 “核心三要素”)。

显然,天下没有免费的午餐,Multi-Segments 的设计实际上具有非常大的局限性 —— 需要规划整个物理、虚拟网络拓扑。它是一种通过 “物理手段” 来完成的 “Network 升级”。比如:Routed Network 应用场景。

所谓 Routed Network,就是一个 Network 内的 Subnet(对应运营商的物理网络)之间使用物理路由器连通,Subnet 的 gateway_ip 指向物理路由器直连网口的 IP 地址。在现实机房中,一个机柜接入物理路由器的网口基本固定,网口的 IP 地址也不会轻易改动,这意味着 Subnet 的三层子网需要与该物理路由器网口的 IP 处于同一个网段,否则无法通信。也就是说,使用 Routed Network 要求管理员提前做好物理、虚拟网络、每个机柜的网关 IP、DHCP Server 的全盘规划。

Multi-Segments 的应用场景除了 Routed Network 还有「VTEP 位于 TOR 交换机上」这一场景,暂不作介绍。可以感受到 Multi-Segments 的应用大多需要结合实际的物理网络拓扑对机房网络进行全盘规划,虽然都并非是一种通用的方案,但 Multi-Segments 的存在也的确让 Neutron 落地物理机房多了一些更加灵活的配置选项。不过,很可惜的是,至今为止笔者依然没有碰见过在生产环境中落地的案例,有时候难免让人怀疑 Multi-Segments 存在的必要性,这是一个大家都对其 “讳莫如深” 的话题。在本章节中提出也只是为了作一个了解而已。

Routed provider networks 官方示例文档:https://docs.openstack.org/newton/networking-guide/config-routed-networks.html

NOTE:有趣的是,经过上述文档的实验发现 Multi-Segments 无法实现预期的 Segments(Subnets)之间的二层隔离,这是因为 VMs 虽然在不同 Segments(Subnets)上,但 VMs 连接到同一个(计算节点的)OvS br-int 上的 qvo-XXX 却具有相同的 Tag ID,自然就无法进行隔离了。由于 Multi-Segments 的场景实在少见,笔者也就不再深究了,只是做出提醒,了解就好。

创建 Subnet

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

创建 Subnet 可以手动输入你预期的网段,也可以应用 SubnetPools 资源模型,直接选择一个 IP 地址资源池。显然后者要来的更加简便一些,只需要通过 “网络掩码” 表单来描述你想要的 IP 地址数量即可(e.g. NETMASK=24 即 2**8 == 256 个 IP 地址)。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

创建 Subnet 时你还可以 “手动输入网络地址”,该表单对应 Subnet 资源模型的 allocationpools 属性,所以 “手动输入网络地址” 是无法和 SubnetPools 共存的,只有通过手动指定网段的方式才可以使用。“主机路由” 对应 hostroutes 属性,格式正如上文所说: [目标网络,下一跳IP 地址]。“DNS 服务器” 对应 dns_nameservers 属性,是一个数值类型,填入 DNS Server 的 IP 地址。

NOTE:同一个 Network 不可以同时具有从资源池中分配(SubnetPools)和手动输入网络地址(allocation_pools)两种类型的 Subnet — Subnets hosted on the same network must be allocated from the same subnet pool.

Network 与 Subnet 的一对多关系

Network 和 Subnet 是一对多的关系,这跟应用了 Multi-Segments 与否无关。Network 下属的 Subnets 可以有不同的 IP 网段(CIDR)且不能重叠,但不同 Networks 的 Subnets 之间可以具有相同的 CIDR。这得益于 Neutron L3 Router 应用了 qrouter-XXX network namespace,解决了 Networks 之间 CIDR 冲突的问题。对于不同 Networks 的 Subnets 之间具有相同 CIDR 时,会有以下两种情况:

  1. 若两个 Subnets 通过同一个 Router 路由,根据 Router 的配置,只有指定的一个 Subnet 可被路由。

  2. 若两个 Subnets 通过不同的 Router 路由,因为 Router 的路由表隔离,所以两个 Subnets 都可以被路由。

我们知道常规的 Network 就是二层广播域,那么你是否想过,为什么 Network 与 Subnet 的聚合关系被设计成一对多?为什么 Neutron 允许在同一个二层广播域之上可以配置多个不同的子网?其实这并非出于什么特殊的意图,只是一对多的关系使得 Neutron 的网络应用更加灵活。通常情况下,用户大概率只会在一个 Network 下创建一个 Subnet,让它们尽可能维护一个 “表面上” 的一比一关系。但总有特殊的情况,例如:一个 Subnet 的 IP 地址用完了;例如:我要启用 Multi-Segments。无论从操作层面还是从架构设计层面上看,Network 与 Subnet 的一对多关系都让 Neutron 变得更加灵活。

那么选定一个具有多个 Subnets 的 Network 来启动一个虚拟机时 Neutron 会怎么处理呢(Nova 不支持选定 Subnet 来启动虚拟机)?Neutron 会按照 Subnets 的顺序选定第一个可用的 Subnet 来给虚拟机使用,知道该 Subnet 的 IP 被分配完毕为止。由此可以感受到,如果不是有特殊需求,还是尽量让 Network 与 Subnet 维持 “表面上” 的一比一关系吧。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

NOTE:虽然 Network 下属有多个 Subnet,但为整个 Network 服务的 DHCP 仍然只有一个。e.g.

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

Port

Network 是二层网络的抽象,Subnet 是三层子网的抽象,Port 就是网卡(NIC)的抽象,是连接 Nova 虚拟机与 Neutron Subnet 的桥梁,也是 Subnet 接入 L3 Router 的桥梁。

直白的说,Port 就是一个虚拟网卡(vNIC),Port 的关键属性就是 IP 地址和 MAC 地址。虚拟机需要绑定 Port,路由器也要绑定 Port。

Port 与 Subnet 一样下属于 Network,Port 与 Subnet 的关系是水平的,为多对多。Port 的 IP 地址来源于 Subnet,也就是说一个 Port 可以具有多个 IP 地址。不过通常情况下,Port 有且只有一个 MAC 地址,对应 Port 资源模型的 mac_address 属性。

Neutron 安全组(Security Group)

Neutron 提供了 Security Group 和 FWaaS 两种安全机制,前者的作用域是单个虚拟机或 Port,后者的作用域是一个具体的网络。而 Security Group 又分为 Nova Security Group 和 Neutron Security Group,这里我们主要讨论的是 Neutron(Port)Security Group。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

当用户将一个 Port 挂载到虚拟机时,底层逻辑会在该虚拟机所处的计算节点上创建一个 Tap 设备并将 Port 的特征信息隐射到这个 Tap 设备,从而实现了为虚拟机添上一张虚拟网卡(vNIC)。我们在 Neutron 网络实现模型的章节中提到过,虚拟机的 vNIC(Tap 设备)并非是直连到 OvS br-int(综合网桥)的,之间还存在一个安全网桥(Linux Bridge qbr-XXX)层。e.g.

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

如上图,Linux Bridge qbr-XXX 就是 Neutron Security Group 的底层支撑。当用户为指定 Port 设定一系列 Security Group Rules 时,Neutron 实际上是通过 CLI 方式调用操作系统的 iptables 指令为 qbr-xxx 上的 Tap 设备配置了相应的 iptables rules。这就是所谓的 “安全层”。

那么问题来了:为什么不能直接在 OvS br-int 上为虚拟机 Port(Tap 设备)设定安全组规则?非得莫名的增加一个安全层?这是因为 OvS Bridge 本身不支持对连接到其自身的 Tap 设备使用 iptables。

直到,OvS 2.5 提出了 “基于流表” 的 Security Group 特性,它应用 Linux 内核的 conntrack(CT)模块实现了对网络连接状态识别和处理。OpenStack 则从 M 版开始应用 OvS 这一新特性,支持 openvswitch securitygroup driver,支持使用 OvS 来实现 “有状态防火墙” 功能。可以通过修改 Neutron 的 ML2 配置文件来选择 Neutron Security Group 的驱动类型。

  1. # /etc/neutron/plugins/ml2/ml2_conf.ini


  2. [securitygroup]

  3. firewall_driver = openvswitch

当然了,如果选择了 openvswitch securitygroup driver,那么 Neutron 的网络实现模型就不再需要 qbr-XXX 安全层了。相应了少了一层转发之后,网络性能也会有所提高,尤其在安全组规则数量巨大的时候。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

关于 OvS Security Group 的实现细节,这里暂不作过多讨论,感兴趣的小伙伴可以浏览《OVS实现安全组,你需要知道这些!

可用地址对(Allowed address pairs)

Neutron Security Group 不仅仅只会一味的按照安全组规则来对虚拟机进行保护,Security Group 本身的存在(启用)就已经设定了一些安全策略,例如:为了防止虚拟机被 ARP Spoofing(ARP 欺诈)、 DHCP Spoofing 的 Port IP/MAC 地址绑定安全策略。由此,默认情况下,一个 Port 的 IP 地址和 MAC 地址是一一对应且绑定,也就是说对这个 Port 的 IP 地址进行添加、修改、删除都会被 Neutron Security Group 判定为非法行为。这也是之所以在 Neutron 网络环境中无法直接使用 Keepalived 来做虚拟机高可用的原因,这是一个无处安放的 VIP 的问题。

NOTE:ARP 反欺诈有很多方案,将 IP/MAC 地址一一绑定是最直接简单的办法。

但就像上文提到的 Keepalived 例子,一个 Port 具有多个 IP 地址的情况总是存在需求的,Port 资源模型的 fixedips 属性就被设计为一个数组类型(Port 可具有多个 IP 地址,1 个 MAC 地址)。针对这样的场景一般有两个办法:关闭 Port Security(portsecurityenabled,不建议使用)或使用 Allowed address pairs(allowedaddress_pairs)机制。

每个 Port 都具有自己的 allowedaddresspairs 数组,它记录了若干对合法的 IP/MAC 地址映射,以此来支持允许多个 IP 与 Port 连通,从而满足一个 Port 具有多个 IP 地址的需求。

更多可用地址对的详情请浏览官方介绍:https://docs.openstack.org/developer/dragonflow/specs/allowedaddresspairs.html

创建一个 Port

NOTE:Port 是依托于 Network 的,所以首先需要创建一个 Network。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

上图创建了一个简单的 Port:

  • Port 的 Fixed IP 地址会从 Subnet1 的 IP 地址池中随机分配,对应 fixed_ips 属性。

  • 开启了 Port Security,现在 Port 的 IP/MAC 地址一一绑定,对应 portsecurityenabled 属性。

  • VNIC 类型为正常(normal),对应 binding:vnic_type 属性。

  • 没有指定 “设备 ID(deviceid)” 和 “设备所属者(deviceowner)”,所以现在设备的 bindingviftype 状态为 unbound。binding:viftype 除了用来标识 Port 的绑定状态(unbound,boundfailed)之外,还用于标识这个 Port 的 Mechanism 类型(e.g. ovs、bridge、macvtap)。

  • 在创建 Port 的同时可以指定这个 Port 的安全组规则

  1. [root@localhost ~]# openstack port show Port1

  2. +-------------------------+-----------------------------------------------------------------------------+

  3. | Field | Value |

  4. +-------------------------+-----------------------------------------------------------------------------+

  5. | admin_state_up | UP |

  6. | allowed_address_pairs | |

  7. | binding_host_id | |

  8. | binding_profile | |

  9. | binding_vif_details | |

  10. | binding_vif_type | unbound |

  11. | binding_vnic_type | normal |

  12. | created_at | 2019-03-08T03:17:23Z |

  13. | data_plane_status | None |

  14. | description | |

  15. | device_id | |

  16. | device_owner | |

  17. | dns_assignment | None |

  18. | dns_domain | None |

  19. | dns_name | None |

  20. | extra_dhcp_opts | |

  21. | fixed_ips | ip_address='192.168.1.27', subnet_id='96f33568-70cd-47a2-a0b5-a32a853caa11' |

  22. | id | 07995f4e-b6b2-493f-9ce5-b1d945a13807 |

  23. | location | None |

  24. | mac_address | fa:16:3e:0f:ff:74 |

  25. | name | Port1 |

  26. | network_id | e28bd712-352f-439d-88ea-35a994a4a765 |

  27. | port_security_enabled | True |

  28. | project_id | 031cec3e2df143259d302aa1993fd410 |

  29. | propagate_uplink_status | None |

  30. | qos_policy_id | None |

  31. | resource_request | None |

  32. | revision_number | 1 |

  33. | security_group_ids | 12519ba0-d1f1-46e3-a0b2-48e6639ee8ad |

  34. | status | DOWN |

  35. | tags | |

  36. | trunk_details | None |

  37. | updated_at | 2019-03-08T03:17:23Z |

  38. +-------------------------+-----------------------------------------------------------------------------+

这里强调一下 “设备 ID(deviceid)” 和 “设备所属者(deviceowner)” 两个字段,它们共同标识了 Port 的绑定实体。比如:Port 绑定到了一台虚拟机,那么 device_id=<instance_uuid>, device_owner=compute:nova。常见的 device_owner 还有:

  • network:dhcp — Neutron DHCP Agent 使用的端口,为 Network 提供 DHCP 服务

  • network:router_interface — Neutron L3 Router Agent 使用的端口,将 Subnet 接入路由器

  • network:router_gateway — Neutron L3 Router Agent 使用的接口,外部网络接入路由器的网管接口

还需要强调一下的是 Port 的类型,Neutron 支持多种类型的 Port,不同类型的 Port 底层可能由不同的 Agent 实现(e.g. ovs-agent、sriov-agent)。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇只需要提供用户预期的 IP/MAC 地址,即可为 Port 添加可用地址对。在可用地址对清单中的 IP/MAC 都可以通过自动或手动的方式应用到这个 Port 所对应的 vNIC(Tap 设备)上。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

还需要注意一点,fixe_ips 属性是一个数值类型,就是说加入一个 Network 下属具有多个 Subnet,那么这个 Port 就可以从多个 Subnets 中获取多个 IP 地址。e.g.

  1. [root@localhost ~]# openstack port create --network Net2 --fixed-ip subnet=Subnet2-1 --fixed-ip subnet=Subnet2-2 --enable-port-security Port2-1

  2. +-------------------------+--------------------------------------------------------------------------------+

  3. | Field | Value |

  4. +-------------------------+--------------------------------------------------------------------------------+

  5. | admin_state_up | UP |

  6. | allowed_address_pairs | |

  7. | binding_host_id | |

  8. | binding_profile | |

  9. | binding_vif_details | |

  10. | binding_vif_type | unbound |

  11. | binding_vnic_type | normal |

  12. | created_at | 2019-03-08T04:21:56Z |

  13. | data_plane_status | None |

  14. | description | |

  15. | device_id | |

  16. | device_owner | |

  17. | dns_assignment | None |

  18. | dns_domain | None |

  19. | dns_name | None |

  20. | extra_dhcp_opts | |

  21. | fixed_ips | ip_address='172.16.100.38', subnet_id='0f15d289-26f2-4c83-9538-fae158bf3153' |

  22. | | ip_address='192.168.100.125', subnet_id='7a2fa4b5-c8ca-48e9-94fb-c74f5e59510f' |

  23. | id | 51383a86-56b5-4907-8bd2-80801351fc1b |

  24. | location | None |

  25. | mac_address | fa:16:3e:df:03:8c |

  26. | name | Port2-1 |

  27. | network_id | 4472e95b-f2e1-4ff5-8bce-429e1997f3cf |

  28. | port_security_enabled | True |

  29. | project_id | 031cec3e2df143259d302aa1993fd410 |

  30. | propagate_uplink_status | None |

  31. | qos_policy_id | None |

  32. | resource_request | None |

  33. | revision_number | 1 |

  34. | security_group_ids | 12519ba0-d1f1-46e3-a0b2-48e6639ee8ad |

  35. | status | DOWN |

  36. | tags | |

  37. | trunk_details | None |

  38. | updated_at | 2019-03-08T04:21:57Z |

  39. +-------------------------+--------------------------------------------------------------------------------+

挂载一个 Port

使用 Port 来启动一个虚拟机

  1. openstack server create --flavor cirros256 --image cirros-0.3.4-x86_64-disk --port Port1 VM1

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

需要注意的是,即便 Port 具有多个可用地址对,但虚拟机网卡原生的 IP/MAC 依旧是 Port 的原生 fixedips/macaddress。可用地址对的含义是可以被 Port 使用的 IP/MAC,而非一定会被使用的 IP/MAC。

Port 挂载到虚拟机之后,其自身的信息也会被更新

  1. [root@localhost ~]# openstack port show Port1

  2. +-------------------------+-------------------------------------------------------------------------------------------+

  3. | Field | Value |

  4. +-------------------------+-------------------------------------------------------------------------------------------+

  5. | admin_state_up | UP |

  6. | allowed_address_pairs | ip_address='192.168.1.28', mac_address='fa:16:3e:0f:ff:28' |

  7. | | ip_address='192.168.1.29', mac_address='fa:16:3e:0f:ff:29' |

  8. | binding_host_id | localhost.localdomain |

  9. | binding_profile | |

  10. | binding_vif_details | bridge_name='br-int', datapath_type='system', ovs_hybrid_plug='False', port_filter='True' |

  11. | binding_vif_type | ovs |

  12. | binding_vnic_type | normal |

  13. | created_at | 2019-03-08T03:17:23Z |

  14. | data_plane_status | None |

  15. | description | |

  16. | device_id | 6da255cb-402a-4273-844f-5aad549d65e7 |

  17. | device_owner | compute:nova |

  18. | dns_assignment | None |

  19. | dns_domain | None |

  20. | dns_name | None |

  21. | extra_dhcp_opts | |

  22. | fixed_ips | ip_address='192.168.1.27', subnet_id='96f33568-70cd-47a2-a0b5-a32a853caa11' |

  23. | id | 07995f4e-b6b2-493f-9ce5-b1d945a13807 |

  24. | location | None |

  25. | mac_address | fa:16:3e:0f:ff:74 |

  26. | name | Port1 |

  27. | network_id | e28bd712-352f-439d-88ea-35a994a4a765 |

  28. | port_security_enabled | True |

  29. | project_id | 031cec3e2df143259d302aa1993fd410 |

  30. | propagate_uplink_status | None |

  31. | qos_policy_id | None |

  32. | resource_request | None |

  33. | revision_number | 6 |

  34. | security_group_ids | 12519ba0-d1f1-46e3-a0b2-48e6639ee8ad |

  35. | status | ACTIVE |

  36. | tags | |

  37. | trunk_details | None |

  38. | updated_at | 2019-03-08T03:51:05Z |

  39. +-------------------------+-------------------------------------------------------------------------------------------+

  • bindingviftype — 从 unbound 变成 ovs,表示底层使用的是 OvS Mechanism Driver。

  • bindinghostid — Port 对应的 Tap 设备所在的主机,也就是 VM 所在的计算节点。

  • bindingvifdetails — 记录了一些 Port 挂载后的信息,例如 Tap 设备加入的 OvS Bridge 名称,OvS Bridge 的类型。

  • deviceid & deviceowner — 指定绑定 Port 的实体对象(虚拟机)和类型(compute:nova)。

NOTE:从上述信息可以看出,Port 对应的 Tap 设备实际上实在 “绑定” 之后再创建的,Tap 设备的命名规则为 tap[port-uuid]。e.g.

  1. [root@localhost ~]# ovs-vsctl show

  2. ccb13b70-cffb-4a64-97b1-734bf9040abc

  3. Manager "ptcp:6640:127.0.0.1"

  4. is_connected: true

  5. ...

  6. Bridge br-int

  7. Controller "tcp:127.0.0.1:6633"

  8. is_connected: true

  9. fail_mode: secure

  10. ...

  11. Port "tap07995f4e-b6"

  12. tag: 5

  13. Interface "tap07995f4e-b6"

  14. ...

  15. ovs_version: "2.9.0"

使用具有多个 fixed_ips 的 Port 来启动一个虚拟机

  1. [root@localhost ~]# openstack server create --flavor cirros256 --image cirros-0.3.4-x86_64-disk --port Port2-1 VM2

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

奇怪为什么 Port 和虚拟机明明有两个 IP 地址,但 GuestOS 起来后却只看见了一个?其实虚拟机启动获取 IP 地址时是随机挑选一个的,不会把 Port 的所有 IP 都配置上去,当然你也可以手动的将两个 IP 地址配置上去。总的来说这种做法和应用可用地址对的效果差不多,如果你只是单纯的希望虚拟机具有多个 IP,还是建议你使用可用地址对。但如果你希望虚拟机可以实现跨网段中断,那么就可以使用具有多个 Subnet IP 的 Port。

Router

Router 是 Neutron 实现的路由器抽象。

需要注意的是,Router 只是 Neutron 对 Linux 服务器路由功能的抽象,而非 Neutron 实现了一个完整的 vRouter 软件。这之间有着巨大的区别。Neutron Router 对 Linux 路由功能进行了封装,并由 L3 Agent 服务进程负责处理。通常的,L3 Agent 只运行在网络节点中,所以所有的跨网段访问流量、公网访问流量都会流经网络节点。由此,网络节点成为了 Neutron 三层网络的性能瓶颈,为了解决这个问题,Neutron 提出了 DVR(分布式路由器)机制,让每个计算节点都具有三层网络功能。

简而言之,对于上层应用而言,只需要关注 Router 的 端口网关路由表 即可。

NOTE:关于 Linux 路由功能在《Linux 的路由功能》一文中已经介绍过,这里不再赘述。

外部网关

Neutron 语义环境中的 “外部网关” 有两重含义:

  • 实际的外部网关(下文称为物理外部网关),在 Neutron 的管理范畴之外。如下图的 Router_2 上的 Port2。

  • Neutron 的外部网关(下文成为 Neutron 外部网关,以作区别),在 Neutron 的管理范畴之内。如下图 Router_1 上的 Port1。

我们思考一个问题:为什么 Neutron 内部网络需要通过 Router1 再与 Router2 进行连接?而不将内部网络直连 Router_2 然后访问公网呢?首先明确一点,“Neutron 管理的内部网络” 指的是租户网络而非运营商网络。运营商网络当然可以映射到一个直连物理外部网关的物理网络,这是因为云管人员手动的为运营商网络提供了物理网络的 “核心三要素”。同理,如果租户网络希望直连到物理外部网关,那么就需要云管人员提供连接到物理外部网关的 “核心要素(e.g. 外部网关的 IP 地址)”。但是,让云管人员为每个租户网络都手动的 “填入”(实际上并非填入,而是获取) “核心要素” 显然是不明智的,再者物理路由器很可能没有足够的接口数量供租户网络使用。

出于这样的前提,Neutron 在租户网络和外部网关之间引入了一个内部路由层。每个租户都可以创建专属的内部路由层,包含若干个 L3 Router 实例对象。租户可以随意的将租户网络接入内部路由层,然后再由内部路由层统一对接物理外部网关。这样物理路由器的端口就能得到更高效的利用。

NOTE:创建多个 L3 Router 实例对象并非是说需要多个 Linux 服务器,Neutron 通过 qrouter-XXX network namespace 在网络节点上隔离出了多个 “虚拟路由器”。每创建一个 Router,运行在网络节点的 L3 Agent 就会新建一个 qrouter-XXX。由于 qrouter-XXX 具有自己独立的路由表,所以同一租户下不同的 Network 之间允许具有相同 IP 网段(CIDR)的 Subnet,只要这些 Subnet 只要不连接到同一个 Router 上就不会出现 IP 地址重叠的问题。

  1. destination next_hop out_interface

  2. 104.20.110.0/24 182.24.4.1 Port1(182.21.4.6)

这条路由表项的关键信息有三:物理外部网关 IP(182.24.4.1)、Neutron 外部网关 IP(182.21.4.6)以及隐藏信息 —— 运营商网络(外部网络)182.24.4.0/24。

可见,Neutron 内部网络要想连接公网,关键在于如何获取上述 3 个关键信息。办法很简单,只需要创建一个运营商网络(外部网络)即可,例如:182.24.4.0/24。物理外部网关 IP(182.24.4.1)就是运营商网络(外部网络)对应的 Subnet 的 gatewayip 属性,而 Neutron 外部网关 IP(182.21.4.6)就是该 Subnet 分配的一个子网 IP 地址。存放这些信息的 Router 资源模型属性就是 externalgateway_info:

  1. {

  2. "external_gateway_info": {

  3. "enable_snat": true,

  4. "external_fixed_ips": [{

  5. "ip_address": "182.24.4.6",

  6. "subnet_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf"

  7. }],

  8. "network_id": "ae34051f-aa6c-4c75-abf5-50dc9ac99ef3"

  9. }

  10. }

  • network_id — 运营商网络(外部网络)

  • subnetid — 运营商网络(外部网络)的 Subnet(其 gatewayip 就是物理外部网关 IP 地址)

  • ip_address — Neutron 外部网关 IP 地址

  • enable_snat — Neutron 外部网关端口是否开启 SNAT 功能

如果开启了 SNAT,Neutron 就会在 qroute-XXX namespace 中添加这样的一条 iptables rule,所有从 qg-426c2bcd-f4(如上图 Port 1)出去的数据包,无论你来自哪里(源 IP 地址)或者要去哪里(目的 IP 地址),数据包的源 IP 地址都会被转换为 172.18.22.210。

  1. [root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f iptables -nvL -t nat

  2. ...


  3. Chain neutron-l3-agent-snat (1 references)

  4. pkts bytes target prot opt in out source destination

  5. 2 142 neutron-l3-agent-float-snat all -- * * 0.0.0.0/0 0.0.0.0/0

  6. 2 142 SNAT all -- * qg-426c2bcd-f4 0.0.0.0/0 0.0.0.0/0 to:172.18.22.210

  7. 0 0 SNAT all -- * * 0.0.0.0/0 0.0.0.0/0 mark match ! 0x2/0xffff ctstate DNAT to:172.18.22.210

NOTE:添加这条 SNAT 的原因是,Linux 作为路由器时仅仅开启了内核的路由转发功能是不够的,还需要添加这么一条 SNAT。那为什么不需要添加 DNAT 呢?因为作为路由器而言,所有的数据包都只是 SNAT(送出去)而已。

新建 Router 和外部网关端口

从上文可一直,当我们希望 Neutron 内部网络连通公网时,就创建一个运营商网络(外部网络),然后使用 L3 Router 将内部网络与运营商网络(外部网络)直连起来即可。e.g.

  • 新建 Router。如果此时用户选择了 “外部网络”,那么 Neutron 会自动的创建 Router1 与 public 直连的外部网关端口。需要注意的是,只有这一种情况下 Neutron 会自动创建路由端口,否则都需要用户手动创建。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

  • 内部网络 Net1 和运营商网络(外部网络)public 通过 Router 直连

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

  • public 有一个端口连接到 Router,类型为 network:router_gateway,IP 为 172.18.22.210(Neutron 外部网关 IP)。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

  • public 的 Subnet public-subnet 具有物理外部网关 IP 地址 172.18.22.1。

  1. [root@localhost ~]# openstack subnet show public-subnet

  2. +-------------------+--------------------------------------+

  3. | Field | Value |

  4. +-------------------+--------------------------------------+

  5. | allocation_pools | 172.18.22.201-172.18.22.210 |

  6. | cidr | 172.18.22.0/24 |

  7. | created_at | 2019-02-13T03:41:09Z |

  8. | description | |

  9. | dns_nameservers | |

  10. | enable_dhcp | False |

  11. | gateway_ip | 172.18.22.1 |

  12. | host_routes | |

  13. | id | 99749410-a6b0-418b-9ebc-fd060e1a746e |

  14. | ip_version | 4 |

  15. | ipv6_address_mode | None |

  16. | ipv6_ra_mode | None |

  17. | location | None |

  18. | name | public-subnet |

  19. | network_id | 282e146e-6948-436f-992c-f2d50588e357 |

  20. | project_id | 031cec3e2df143259d302aa1993fd410 |

  21. | revision_number | 0 |

  22. | segment_id | None |

  23. | service_types | |

  24. | subnetpool_id | None |

  25. | tags | |

  26. | updated_at | 2019-02-13T03:41:09Z |

  27. +-------------------+--------------------------------------+

  • Router 具有两个端口,一个连接 Net1,IP 地址是 Net1 的网关 IP 192.168.1.1;另一个连接 public,IP 是 Neutron 外部网关 IP 172.18.22.210。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

  • Router1 的路由表和网络设备:

  1. [root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f ifconfig

  2. lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536

  3. inet 127.0.0.1 netmask 255.0.0.0

  4. inet6 ::1 prefixlen 128 scopeid 0x10<host>

  5. loop txqueuelen 1000 (Local Loopback)

  6. RX packets 0 bytes 0 (0.0 B)

  7. RX errors 0 dropped 0 overruns 0 frame 0

  8. TX packets 0 bytes 0 (0.0 B)

  9. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0


  10. qg-426c2bcd-f4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

  11. inet 172.18.22.210 netmask 255.255.255.0 broadcast 172.18.22.255

  12. inet6 fe80::f816:3eff:fe0b:c722 prefixlen 64 scopeid 0x20<link>

  13. ether fa:16:3e:0b:c7:22 txqueuelen 1000 (Ethernet)

  14. RX packets 765265 bytes 36064217 (34.3 MiB)

  15. RX errors 0 dropped 0 overruns 0 frame 0

  16. TX packets 90 bytes 5316 (5.1 KiB)

  17. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0


  18. qr-cf018461-bc: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

  19. inet 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255

  20. inet6 fe80::f816:3eff:fea5:b2bb prefixlen 64 scopeid 0x20<link>

  21. ether fa:16:3e:a5:b2:bb txqueuelen 1000 (Ethernet)

  22. RX packets 216 bytes 18796 (18.3 KiB)

  23. RX errors 0 dropped 0 overruns 0 frame 0

  24. TX packets 139 bytes 13870 (13.5 KiB)

  25. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0


  26. [root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f route -nne

  27. Kernel IP routing table

  28. Destination Gateway Genmask Flags MSS Window irtt Iface

  29. 0.0.0.0 172.18.22.1 0.0.0.0 UG 0 0 0 qg-426c2bcd-f4

  30. 172.18.22.0 0.0.0.0 255.255.255.0 U 0 0 0 qg-426c2bcd-f4

  31. 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-cf018461-bc

其中 qr-XXX 和 qg-YYY 在 qroute-XXX namespace 中用于连接 br-int 和 br-ex,从第二、三条路由表项是由链路层协议发现并创建的,分别是 Net1 和 public 子网的直连路由,由设备 qr-XXX 和 qg-YYY 转发。而第一条路由表项,则是由 Neutron 添加的默认静态路由,Neutron 从 public 的 gateway_ip 获取到物理外部网关 IP 地址,所有非直连路由的子网访问流量,都会使用默认静态路由进行转发。

Router 的路由表

我们知道路由器的路由表项分配静态路由和动态路由,Neutron 中所有的路由表项均为静态路由(不遵循动态路由协议),又细分为:

  • 静态路由

  • 静态默认路由

  • 直连路由

其中只有 “静态路由” 一种类型会被 Neutron 记录在 Router 资源模型的 routes(数值类型)属性中。

  1. MariaDB [neutron]> desc routerroutes;

  2. +-------------+-------------+------+-----+---------+-------+

  3. | Field | Type | Null | Key | Default | Extra |

  4. +-------------+-------------+------+-----+---------+-------+

  5. | destination | varchar(64) | NO | PRI | NULL | |

  6. | nexthop | varchar(64) | NO | PRI | NULL | | # 目的网段( CIDR)

  7. | router_id | varchar(36) | NO | PRI | NULL | | # 下 一 跳 IP(下一个路由器的端口 IP)

  8. +-------------+-------------+------+-----+---------+-------+

后面两种路由表项类型为什么不被持久化呢?因为静态默认路由的信息来自外部网络的 gateway_ip 属性,而直连路由则由链路层协议自动发现、创建。所以不需要额外的持久化。

那么静态路由又是什么路由,为什么要持久化呢?

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

  1. +----------------+-------------+--------------------------------------+

  2. | destination | nexthop | router_id |

  3. +----------------+-------------+--------------------------------------+

  4. | 192.168.1.2/32 | 172.18.22.1 | a1adb970-dba9-49f0-ba4b-4294f0d07f6f |

  5. +----------------+-------------+--------------------------------------+

如上,Router 的静态路由其实就是常规的主机路由或网络路由,使用户自定义的路由表项。但需要注意的是,“下一跳” 表单不能随意乱填,而是限制在 Router 够得着的子网 IP 地址。否则就会 the nexthop is not connected with router

NOTE:在讲述 Subnet 资源模型的章节中你或许会对 Subnet 的 gatewayip 和 hostroutes 属性感到疑惑,通本章的举例,或许你能够更好的理解这些字段所具有的功能和含义。

Floating IP

从功能的角度可以将 Neutron 的 IP 分为:

  • Fixed IP:由租户网络 Subnet 分配,用于虚拟机的内部通讯,创建虚拟机时自动分配。

  • Floating IP:有外部网络分配,用于虚拟机访问公网,需要手动分配。

前文中我们提到过,一般的运营商网络和勾选了 “外部网络” 的运营商网络的主要区别在于后者可以分配 Floating IP,实现的原理就是内部网络与外部网络之间的 Router(qroute-XXX)的 NAT(SNAT&DNAT)功能。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

上图可见,外部网络 public 成为了一个 Floating IP 地址池。创建一个 Floating IP 会相应的创建一条数据库记录:

  1. MariaDB [neutron]> select * from floatingipsG;

  2. *************************** 1. row ***************************

  3. project_id: 031cec3e2df143259d302aa1993fd410

  4. id: 67272b76-493a-4a60-b63a-22e5aefebfc0

  5. floating_ip_address: 172.18.22.204

  6. floating_network_id: 282e146e-6948-436f-992c-f2d50588e357

  7. floating_port_id: 04aceef8-a3b2-46e4-a815-3104a2031ed7

  8. fixed_port_id: NULL

  9. fixed_ip_address: NULL

  10. router_id: NULL

  11. last_known_router_id: NULL

  12. status: DOWN

  13. standard_attr_id: 82

  14. 1 row in set (0.01 sec)

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇 为虚拟机绑定这个 Floating IP 之后,数据库记录会被更改:

  1. MariaDB [neutron]> select * from floatingipsG;

  2. *************************** 1. row ***************************

  3. project_id: 031cec3e2df143259d302aa1993fd410

  4. id: 67272b76-493a-4a60-b63a-22e5aefebfc0

  5. floating_ip_address: 172.18.22.204

  6. floating_network_id: 282e146e-6948-436f-992c-f2d50588e357

  7. floating_port_id: 04aceef8-a3b2-46e4-a815-3104a2031ed7

  8. fixed_port_id: 07995f4e-b6b2-493f-9ce5-b1d945a13807

  9. fixed_ip_address: 192.168.1.27

  10. router_id: a1adb970-dba9-49f0-ba4b-4294f0d07f6f

  11. last_known_router_id: NULL

  12. status: ACTIVE

  13. standard_attr_id: 82

  14. 1 row in set (0.00 sec)

qroute-XXX 中的 iptables rules 也会相同的添加 SNAT、DNAT 规则:

  1. [root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f iptables -nvL -t nat

  2. ...


  3. # 到目的 IP 地址 172.18.22.204 的数据包 DNAT 至 192.168.1.27

  4. Chain neutron-l3-agent-PREROUTING (1 references)

  5. pkts bytes target prot opt in out source destination

  6. ...

  7. 0 0 DNAT all -- * * 0.0.0.0/0 172.18.22.204 to:192.168.1.27

  8. ...


  9. # 从源 IP 地址 192.168.1.27 来的数据包 SNAT 至 172.18.22.204

  10. Chain neutron-l3-agent-float-snat (1 references)

  11. pkts bytes target prot opt in out source destination

  12. 0 0 SNAT all -- * * 192.168.1.27 0.0.0.0/0 to:172.18.22.204

  13. ...

这一切都发生在 qroute-XXX network namespace 中,实现了不同 Router 之间的网络隔离。


关于九州云99Cloud

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。公司成立六年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各大行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。

5月技术周 | 我非要捅穿这 Neutron(二)上层资源模型篇

前言

OpenStake 自动化部署工具一向被视为 OpenStack 发展的重点,现已被市场认可的有 Kolla、TripleO 等优秀工具。但今天我们不聊自动化部署,有兴趣的小伙伴可以浏览《Kolla 让 OpenStack 部署更贴心》。本篇的内容将一反常规,追求极致简约的 OpenStack 手动部署,抛开一切外在因素窥探 OpenStack 的本真。是一篇科普向的 OpenStack 入门介绍。

BTW,OpenStack 研发工程师不能过于依赖自动化部署工具,这会使得对 OpenStack 的理解流于表面。不妨花点时间试着手动部署一次,看看最原始的 OpenStack 到底长什么样子。

OpenStack 架构

Conceptual architecture

5月技术周 | 手动部署 OpenStack Rocky 双节点

Logical architecture

5月技术周 | 手动部署 OpenStack Rocky 双节点

网络选型