用户可能会发现,所有网络服务都在网络节点上进行,这意味着大量的流量和处理,给网络节点带来了很大的压力。
这些处理的核心是路由器服务。那么如果计算节点上自己就能实现路由器服务的话,无疑是更为合理的。这个思路是正确的,但具体实施起来有诸多的考量和细节问题。
为了降低网络节点的负载,同时提高可扩展性,OpenStack 自 Juno 版本开始正式引入了分布式路由(Distributed Virtual Router,DVR)服务,来让计算节点自己来处理原先的大量的东西向流量和 DNAT 流量(有 floating IP 的 vm 跟外面的通信)。
这样网络节点只需要处理占到一部分的 SNAT (无 floating IP 的 vm 跟外面的通信)流量,大大降低了负载和整个系统对网络节点的依赖。
DHCP 服务和 VPN 服务仍然需要集中在网络节点上进行。
优势:
(1)东西向流量吞吐量增加。
(2)高东西流量下VM平均带宽增加。
(3)南北向流量和东西向流量不再互相干扰。
(4)当东西向流量在同一个Hypervisor上,就不会走过不必要的路径。
典型场景
从网络的访问看,涉及到路由服务的至少是需要跨子网的访问,又包括是否是同一机器、是否是涉及到外网(东西向 vs 南北向)。
考虑下面几个典型的场景。
1.东西向
(1)同一机器
路由器直接在 br-int 上转发,不经过外部网桥。
(2)不同机器
如图所示,租户 T1 的两个不同子网的 vm:VM1 和 VM4 在不同机器上,VM1 要访问 VM4,请求过程是计算节点 1 上的 IR1 起到路由器功能。返程的网包,则在计算节点 2 上的路由器 IR2 起作用。
两个路由器的 id、内部接口、功能等其实都是一样的。即同一个路由器,但是实际上在多个计算节点上存在。
但是同样的路由器,如果都暴露在外部网络上,会出现冲突。例如当请求包离开计算节点 1 时,带的源应该是目标子网网关的 mac,但这个 mac 在计算节点 2 上同样存在。
因此,在 br-int 上进行拦截,修改其源 mac 为 tunnel 端口的 mac。同样的,计算节点 2 在 br-int 上拦截源mac 为这个 tunnel 端口的 mac,替换为正常的子网网关的 mac,直接扔给目标虚拟机。
2.南北向
(1)无 floating IP
这种情况(即 SNAT)下,跟传统模式很类似。
租户 T2 在外部,通过默认的 SNAT 网关访问内部子网的 vm VM1。此时,网络节点上的 T2-SNAT 起到路由器的作用
反过来,租户 T2 内部子网的 vm VM1 试图访问外部网络,则仍然经过网络节点上的 T2-SNAT 路由器。
为何这种情况下必须从网络节点走?这是因为对于外部网络来说,看到的都是外部接口的地址,这个地址只有一个。
当然,如果以后每个计算节点上都可以带有这样一个 SNAT 默认外部地址的话,这种情况下的流量也是可以直接从计算节点出去的。
(2)有 floating IP
这种情况下,计算节点上的专门负责的外部路由器将负责进行转发,即计算节点 1 上的 IR2 和计算节点 2上的 IR1。
网络节点
服务基本没变动,除了 L3 服务需要配置为 dvr_snat 模式。
命名空间上会多一个专门的 snat-xxx 命名空间,处理来自计算节点的无 floating IP 的南北向流量。
计算节点
需要额外启用 l3_agent(dvr 模式),以及 metadata agent。
其实,跟传统情况下的网络节点十分类似。每个路由器有自己的命名空间,进行跨子网的转发。
所不同的是,单独有一个 qfloat-XXX 路由器(也在一个独立命名空间中)来负责处理带有 floating IP 的南北向流量。
这里面比较重要的地方,是可能有多个同样的网关存在于多个计算节点上,因为同一个子网的虚机是可能存在在多个计算节点的。
要解决这个问题,也不难,合理处理好本地的 ARP 请求,让本地对网关的请求拦截发给本地的路由器即可。