需要额外启用 l3_agent(dvr 模式),以及 metadata agent。
其实,跟传统情况下的网络节点十分类似。每个东西向路由器有自己的命名空间,负责跨子网的转发。另外,多一个 floating 路由器,专门负责经由 floating 地址的南北向转发。
如上图所示,租户两个子网,红色和绿色,分别有 vm1 和 vm2,位于节点 cn1 和 cn2 上。
vm1 访问 vm2 的网包如步骤 1-6,整个过程 ip 保持不变。
r1 red mac
;r1 grn mac
,目的 mac 改为 vm2 mac
,并且带上绿网的本地 vlan tag;dvr cn1 mac
,之后带着目标子网(绿网)的 外部 tunnel id 扔出去(实现可以为 vlan、vxlan、gre 等,功能都是一样的);返回包的过程正好是反过来。虽然实现上略复杂,但整个过程还是比较清晰的,保证 vm1 和 vm2 感觉到的都是直接跟路由器的接口相连(分别为红网网关接口和绿网网关接口)。
所不同的是,单独有一个 qfloat-XXX 路由器(也在一个独立命名空间中)来负责处理带有 floating IP 的南北向流量。
为了实现上面描述的这些功能,需要下面几种流表规则。
在 tunnel 网桥上看到的一律丢弃。
DVR PROCESS Table 1 (New table for dvr):
table=1, priority=4, dl_vlan= red1-L-vlan, dl_type=arp, ar_tpa= r1-red-ip actions: drop
table=1, priority=4, dl_vlan= grn1-L-vlan, dl_type=arp, ar_tpa= r1-grn-ip actions: drop
在 tunnel 网桥上看到的一律丢弃。
DVR PROCESS Table 1 (New table for dvr):
table=1, priority=2, dl_vlan=red2_L_vlan, dl_dst=r1-red-mac, actions: drop
table=1, priority=2 , dl_vlan=grn2_L_vlan, dl_dst=r1-grn-mac, actions: drop
在 tunnel 网桥上进行处理。
DVR PROCESS Table 1 (New table for dvr):
table=1, priority=1, dl_vlan=red2_L_vlan, dl_src=r1-red-mac, actions: mod_dl_src=dvr-cn1-mac, resubmit(,2)
table=1, priority=1, dl_vlan=grn2_L_vlan, dl_src=r1-grn-mac, actions: mod_dl_src=dvr-cn1-mac, resubmit (,2)
在 integration 网桥上进行处理。
Table 0: (Local switching table)
table=0, priority=2, in_port=patch-tun, dl_src=dvr-cn1-mac actions: goto table 1
table=0, priority=1, actions: output->NORMAL
Table 1: (DVR_TO_LOCALMAC table)
table=1, priority=2, dl_vlan=grn2-L-vlan, nw_dst=grn-subnet actions: strip_vlan, mod_dl_src=r1-grn-MAC,output->port-vm2
table=1, priority=1 actions: drop
此外,还有一些优化措施,包括:
采用 L2 pre-population 技术,提前把相关计算节点的地址关系放到本地的 FDB 表中,减少外部广播。
在 integration 网桥上,采用组表来调整规则顺序等。
Table 1: (DVR_TO_LOCALMAC table)
table=1, priority=2, dl_vlan=grn2-L-vlan, nw_dst=grn-subnet actions: strip_vlan, mod_dl_src=r1-grn-MAC,output->port-vm2
这里面比较重要的地方,是允许有多个同样的网关存在于多个计算节点(同一子网跨多个物理节点导致)的情况下,保证:
要解决这两个问题,首先是合理处理好本地的 ARP 请求,让本地对网关的请求拦截发给本地的路由器。同时,路由器路由后的网包,发到外部网络之前,先用一个跟节点绑定的特殊 mac 替换掉源 mac。这个 mac 地址是控制器为每个计算节点单独分配的唯一地址。
如果不允许同一子网跨多个物理节点,则每个节点上的路由器就不会冲突,设计起来就简单的多,比如 kubernetes 中的网络。