需求
外网有一个子网,子网的ip地址用完了,需要添加新的外网地址段,同一个内网子网上有两个虚拟机,分别绑定这两个外网子网的fip。
第一种办法新增加一个外网,第二种办法给原来的外网增加一个子网。第一种办法需要router同时能绑定两个外网,目前neutron不支持,如果修改router数据库表格式。第二种办法neutron数据库不用动,转发面有问题需要修改。
https://bugs.launchpad.net/neutron/ bug/1312467
社区有bug单,有人尝试修改,fip中的fg口和snat中的gq口在外网子网中随机分配一个ip地址,然后在snat和fip中的main路由表中配置到其它子网的路由,外网不同子网的之间强行通了,极不科学,子网之间不能直接通,虽然不科学但省ip,而且子网之间互通不用绕网关,如一个虚拟机没有绑定floating ip,一个虚拟机绑定了floating ip,没有绑定floating ip的虚拟机访问另一个虚拟机的floating ip,那么流量在snat查路由到了计算节点的fip中。
公有云
计算节点/网络节点l3-agent和硬件交换机之间运行OSPF路由协议,计算节点/网络节点的fg/qg口随便配置一个私网地址,交换机上也配置私网地址,计算节点/网络节点和交换机之间通过这个私网地址二层互通,建立路由协议邻居关系,计算节点/网络节点向交换机通告公网地址,下一跳到自己节点fg/gq的私网地址,把到自己的来自公网的流量吸引过来,绑定公网地址的虚拟机迁移,旧节点通告撤销路由新节点通告添加路由。计算节点/网络节点的fip/snat namespace下路由时default路由下一跳到交换机的私网地址。
好处就是把外网当作三层转发处理,计算节点/网络节点的fg/qg口节省公网IP地址,难处就是需要交换机配合。
ToB操作不现实,还是需要把外网当作二层转发处理,外网子网有子网网关,计算节点/网络节点的fip/snat中default路由下一跳到外网子网网关,流量出去时arp请求子网网关mac,流量进来时到了外网子网网关,外网子网网关ARP请求虚拟机绑定的公网IP地址,fip中配置32位路由,配置proxy_arp进行arp代答,有arp请求在就不怕虚拟机带着floating ip热迁移,只要fg口开启proxy_arp功能,即使floating ip和fg口配置的ip不在同一个subnet,因为在同一个vlan下,也能收到arp请求,也会进行arp应答。
办法
图中有两个外网子网,外网子网1红色,外网子网2绿色,三个内网,两个router,8台虚拟机分布在两个计算节点,一些虚拟机分别绑定了外网子网1和外网子网2的floating ip,绑定外网子网1就是红色,绑定外网子网2就是绿色。
社区bug的修改并没有把问题完全解决。第一个问题,fip中其它路由表中只有一条default路由,不管虚拟机绑定的floating ip是外网子网1的还是外网子网2的,流量只能到一个外网子网网关,流量在外网子网网关会丢或者来回不对称。第二个问题,绑定floating ip的虚拟机访问其它虚拟机的floating ip,流量在fip中的ip rule发给网关,网关查路由再绕回来,需要加ip rule让查main表,不要绕网关。第三问题,fip中fg口浪费公网IP地址的问题,fg口的ip唯一的作用流量出去时发arp请求网关mac时用作源ip,没有流量的目的ip是这个口,所以随便一个ip就行,推荐外网专门创建一个子网给fg口分配ip,subnet创建时gateway_ip=NULL,并且service_type设置为network:floatingip_agent_gateway,这样fg口就只能从这个子网中分配ip地址。
修改办法就是,需要ip rule区分流量,流量目的ip是外网子网的查main路由表,这个优先级要高,main路由表中有本机floating ip的32位路由,出接口是到qrouter的veth,下一跳是到qrouter中veth的ip,图中紫色的线,main路由表中外网子网subnet mask位的路由,出接口是fg口,下一跳直达。流量源ip是外网子网1的并且目的ip不是外网子网的查自定义路由表1,下一跳到外网子网1的网关。流量源ip是外网子网2的并且目的ip不是外网子网的查自定义路由表2,下一跳到外网子网2的网关。
snat namespace随便选择一个子网IP配置在qg口上,生成一条路由,社区为了修改bug引入了extra_subnet为其它外网子网生成了路由。
第一,fip namespace的fg口从service_type是network:floatingip_agent_gateway子网分配一个ip,沿用社区修改,用extra_subnet下其它subnet的路由。
第二,fip namespace目前ip rule只区分来自哪个qrouter,也就是哪个接口,但不区分是哪个外网子网的流量。
路由表中只有到一个外网子网网关的路由
修改代码ip rule区分qrouter出来的目的ip是外网流量,让这些流量查main路由表,优先级要高,否则就要绕外网网关一圈了,如图中黄色线条。
修改代码ip rule区分qrouter出为的外网子网1和子网2的流量,增加路由表,不同外网子网流量查不同的路由表,路由表中只有一条default到外网子网网关。
代码
配置变化或者动态变化不好处理,目前发现的问题修改neutron l3 agent配置use_veth_pair重启会导致网络不通,主要是qr是ovs internal还是veth pair,重启后并检查已经存在的口类型和配置是否一致,如果不一致需要删除旧类型口生成新类型port。
动态变化和配置变化一样,难以处理。router已经绑定external network,此时给external network添加一个subnet,fip namespace需要添加ip rule和路由表,反之删除一个subnet也一样。手动把router和external network解除绑定,再重新绑定,会导致流量中断,如果不想流量中断得代码处理。
subnet创建:
在fip中为了个subnet创建路由表创建ip rule。
subnet删除:
如果有port占用了subnet的ip地址,subnet是不允许删除。假如有办法update port把这个subnet的ip释放,恐怕l3 agent无法清除fip中这个subnet对应的ip rule和路由表,l3 agent配置了什么本地没有存储,重启后也不和fip中真正有的ip rule路由表同步。只有删除fg口,router和external network解绑才能清除fip中所有东西。
l3-agent处理流程:
process_external→create_dvr_external_gateway_on_agent-->get_agent_gateway_port调用到neutron-server的rpc创建get_agent_gw_port
neutron-server中get_agent_gw_port不存在就创建agent_gw_port,创建完_populate_mtu_and_subnets_for_ports
l3-agent调用connect_rtr_2_fip添加路由表和ip rule,修改connect_rtr_2_fip的代码,先获取agent_gw_port的所有subnet,给每个sbunet创建一个路由表,下default路由,没有gateway_ip不上,再添加ip rule,匹配subnet cidr和iif导流到不同的路由表。
最终代码在本人github上,如需要请自取。
https://github.com/huiweics/neutron/commit/9301b093c2e56bc3c66ed1009a36a66d0453d78cgithub.com
测试
测试环境如最上面的图:
1.router1绑定外网,生成fip fg和snat qg口,fip fg配置一个service_type subnet ip,snat gq口一个ip地址。
2.router1解绑定外网,回收资源。
3.router1绑定外网,router2绑定外网,router2解绑定外网,fip fg配置一个ip和snat gq口一个ip地址保持不变。
4.router1绑定外网,router2绑定外网,给vm1,vm2和vm4绑定floating ip,fip中ip rule和route table正确,虚拟能通过fip访问外网,解绑定floating ip,虚拟给通过snat访问外网。
5.给外网增加一个子网,给vm3给新子网中分配一个floating ip,fip中新加了subnet对应的东西,vm3解绑定floating ip,fg口ip保留。
6.增加一个外网,这个外网有两个子网,router1绑定外网1, router2绑定外网2。
结论
neutron中subnet真是个奇葩的存在,本来subnet就防止广播范围太大,结果openstack neutron subnet共用network的vlan id,流量并不隔离,内网subnet可以绑定router,生成各自的qr口,外网subnet就不行,只有一个qg/fg口。