紧接上文记一次虚拟机无法获取内网 IP 问题的解决过程,我们来看一下路由及公网 IP。注意,这里的公网 IP 也称为浮动 IP(Floating IP)。
本文继续参考每天 5 分钟玩转 OpenStack系列。
路由器
我们这里的路由指的是由 Neutron L3 Agent 提供的软件路由
。除此之外,L3 Agent 也通过 iptables 提供防火墙和公网 IP 服务。
在后台用命令行也可以看到该 agent 的状态:
配置
利用 devstack 部署原始生成的一些默认网络配置比较符合平常在网上看到的资料的习惯,如可以通外网的网桥 br-ex,所以下文会在原有配置进行微小变更。
原有配置
其实,在刚部署好环境的时候,devstack 已经帮我们创建好了一个网络 public、网桥 br-ex,并且还用 iptables 做了网络 public 到网口 eth0 的 SNAT。下边我们详细看下。
网络及网桥
部署后生成的配置文件 /etc/neutron/l3_agent.ini 中的相关字段如下:
1 | [ml2_type_flat] |
也就是说,部署时会帮我们创建一个默认的 flat 类型的外部网络 public,并且该网络还映射到一个网桥 br-ex。
可从后台看到如下结果(已去掉不相关部分):
1 | stack@ubuntu-controller:~$ neutron net-list -F "name" -F "subnets" |
可以看到,devstack 还单独给网桥 br-ex 配了一个 IP;在该网桥挂接一个虚拟网卡 tap2278c047-6a。该虚拟网卡实际会通过 veth pair 连接到默认创建的路由器的 interface,且看下文。
1 | stack@ubuntu-controller:~$ neutron router-list -F "id" -F "name" -F "external_gateway_info" |
可以看出,OpenStack 会为路由器创建一个单独的 namespace(命名为qrouter+router_id
);而且在该 namespace 会创建一个虚拟网卡通过 veth pair 连接到 public 网络。
SNAT
既然部署创建的是一个可以连接到外网的网络 public,那它是如何能够连接到外网的?我们只要看一下防火墙的设置就很清楚了:
1 | stack@ubuntu-controller:~$ sudo iptables -t nat -S |
也就是说,public 网络到外网的通信是通过 SNAT 到 eth0 来实现的。
网络拓扑
至此,我们可以画出上边部署默认创建的一个网络拓扑(控制节点):
需要特别注意的是,public 网络虽然有 DHCP Agent,但并没有为该 Agent 创建独立的 namespace。
新的配置
因为在之前部署的时候,我们是规划 eth2 来作为 OpenStack 与外网通信的网卡的,所有在这里,我们需做一些修改,来达到该目的。
注意:下文不相关内容并没有展示出来
首先,将控制节点的 eth2 桥接到真正物理机上的网卡(eth0 和 eth2 实际在同一个子网中):
第二,为该网卡配置 IP:
1 | stack@ubuntu-controller:~$ sudo ip a add 192.168.0.20/24 dev eth2 |
第三,删除并新建新的 SNAT 规则:
1 | stack@ubuntu-controller:~$ sudo iptables -t nat -L -n --line-number |
第四,清除原有路由:
1 | stack@ubuntu-controller:~$ ip r |
现在最新的网络拓扑如下:
Vlan 间通信
为了让 Vlan 之间可以互相通信,我们需要为它们添加 router interface,最后添加完成后的网络拓扑如下(计算节点没有画出来):
Veth pair
我们可以在后台看下结果:
1 | stack@ubuntu-controller:~$ sudo ip netns exec qrouter-63c5bd1f-a00f-403c-96cc-0345fcb51df5 ip a |
从上图及后台结果可以看出一个重要的地方,就是路由器上的 interface 分别与各 Vlan 网络所在的网桥之间的通信都是 veth pair 来进行的,因为路由器被单独放在了一个 namespace 里边,这样是为了支持网络重叠。
对不同的网络,它们可能会存在相互重叠的子网,如果没有 namespace,不同子网之间的路由就可能会存在冲突。
博文 Why Namespace? 举了一个很形象的例子:
因为没有 namespace 隔离,router_100_101 和 router_102_103 的路由条目都只能记录到控制节点操作系统(root namespace)的路由表中,内容如下:
Destination Gateway Genmask Flags Metric Ref Use Iface
10.10.1.0 * 255.255.255.0 U 0 0 0 tap1
10.10.2.0 * 255.255.255.0 U 0 0 0 tap2
10.10.1.0 * 255.255.255.0 U 0 0 0 tap3
10.10.2.0 * 255.255.255.0 U 0 0 0 tap4
这样的路由表是无法工作的。
注:router_100_101 和 router_102_103 是作者用于说明问题的路由器。
而当路由有了 namespace,namespace 可以有自己独立的路由,这样就可以支持网络重叠了
:
router_100_101 的路由表内容如下:
Destination Gateway Genmask Flags Metric Ref Use Iface
10.10.1.0 * 255.255.255.0 U 0 0 0 qr-1
10.10.2.0 * 255.255.255.0 U 0 0 0 qr-2router_102_103 的路由表内容如下:
Destination Gateway Genmask Flags Metric Ref Use Iface
10.10.1.0 * 255.255.255.0 U 0 0 0 qr-3
10.10.2.0 * 255.255.255.0 U 0 0 0 qr-4这样的路由表是可以工作的。
另外,若路由器上的 veth 用于连接租户网络,命名格式为 qr-<port-id前11位>;若用于连接外部网络,命名格式为 qg-<port-id前11位>。
SNAT
我们还可以看一下 iptables 规则:
1 | stack@ubuntu-controller:~$ sudo ip netns exec qrouter-63c5bd1f-a00f-403c-96cc-0345fcb51df5 iptables -t nat -S |
这表示数据包从 router 连接外部网络接口 qg-2278c047-6a 发出的时候,都会做一次 SNAT,即将包的源地址修改为 router 的接口地址 172.24.4.2,这样就能保证目的端能够将应答的包发回给 router,然后再转发回源端虚拟机。
公网IP
接下来,我们为新建的虚拟机申请并绑定一个公网 IP 172.24.4.12/24,这样它就可以跟外边的公网通信了,我们可以简单测试一下:
注:192.168.0.3 是“物理”局域网(对于 OpenStack 来说是外网)内一台独立于 OpenStack 环境的 Windows 主机的 IP
这是怎么做到的?还是要看 iptables 规则:
1 | stack@ubuntu-controller:~$ sudo ip netns exec qrouter-63c5bd1f-a00f-403c-96cc-0345fcb51df5 ip a |
也就是,实际上,是 DNAT+SNAT 使得虚拟机可以连接到外网。