公司生产机房最近淘汰下来一批设备,不算太旧,普遍CPU两颗两核,少数四颗四核,但其中已经有若干台主机安装不了CentOS7的系统了。因为主机型号太旧,CentOS7已经不提供磁盘驱动的支持了。所以只好将就着装成了CentOS6.7。为了更好得发挥余热,着手部署了一套OpenStack系统,版本只能选用Ice House,因为受制于OS的版本。
系统的架构基本上是:
一个控制节点
一个网络节点
四个计算节点
以上六个主机节点全部带磁盘存储,部署GlusterFS系统,与Cinder集成提供云硬盘服务。
使用Neutron实现OpenStack网络服务时,发现云主机无法获取到DHCP地址。
问题分析:
1、DHCP Server是运行在网络节点上的,通过dnsmasq发放地址,因此云主机必须与网络节点上的DHCP服务能够通信;
2、云主机运行在计算节点上,使用GRE随道通信与网络节点进行交互,每新建一个云主机就会有一个新的专用的通信隧道的建立,在隧道两侧的网络接口名称基本上都是命令为类似于br-tun的形式;
3、隧道接口br-tun在收发来自隧道的数据后,进行了格式转换,转发给了br-int网络接口,翻译为云主机和网络节点的IP网络能够识别的格式;
4、使用tcpdump在网络节点和计算节点上,针对br-int接口进行抓包,应该能抓到双方进行DHCP交互的数据包;
#tcpdump -i br-int
在网络节点和计算节点上使用上面的命令开始抓包。
使用浏览器登录dashboard管理平台,进入测试使用的云主机Cirros中,手工执行获取dhcp地址的命令:
#sudo cirros-dhcpc up eth0
现象:
在计算节点上,立即就抓到了发出dhcp请求的广播数据包,证明云主机系统和计算节点网络正常。
而网络节点的br-int接口始终没获取到dhcp广播包。于是直接对网络节点的隧道通信网卡进行抓包:
#tcpdump -i eth1
虽然干扰数据有点多,但仍然很容易看到当云主机发出dhcp请求时,几乎同时,在网络节点的隧道通信网卡eth1上收到了dhcp广播数据包。
5、分析
dhcp请求已经进入了网络节点,但是没能经由隧道接口转给br-int内网接口,因此云主机的dhcp请求得不到响应。
在确认了网络节点的隧道接口br-tun和内网接口br-int的端口映射无误后,认为DHCP请求是卡在了网络节点的系统层面,即eth1可以拿到,br-tun和br-int不能拿到,除非是网络节点的iptables策略把数据包拦住了。
借用一张OpenStack官网的架构图来说明一下系统与网络部署结构:
6、我们看一下网络节点此时的iptables规则如下
[root@networker ~]# iptables --list
Chain INPUT (policy ACCEPT)
target prot opt source destination
neutron-openvswi-INPUT all -- anywhere anywhere
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT icmp -- anywhere anywhere
ACCEPT all -- anywhere anywhere
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
Chain FORWARD (policy ACCEPT)
target prot opt source destination
neutron-filter-top all -- anywhere anywhere
neutron-openvswi-FORWARD all -- anywhere anywhere
REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
neutron-filter-top all -- anywhere anywhere
neutron-openvswi-OUTPUT all -- anywhere anywhere
Chain neutron-filter-top (2 references)
target prot opt source destination
neutron-openvswi-local all -- anywhere anywhere
Chain neutron-openvswi-FORWARD (1 references)
target prot opt source destination
Chain neutron-openvswi-INPUT (1 references)
target prot opt source destination
Chain neutron-openvswi-OUTPUT (1 references)
target prot opt source destination
Chain neutron-openvswi-local (1 references)
target prot opt source destination
Chain neutron-openvswi-sg-chain (0 references)
target prot opt source destination
Chain neutron-openvswi-sg-fallback (0 references)
target prot opt source destination
DROP all -- anywhere anywhere
我们可以看到,数据包一进INPUT链,就全数转给了neutron-openvswi-INPUT链一份。
而neutron-openvswi-INPUT链正是处理网络节点的OVS虚拟交换机数据通信的入口策略。
我们继续向下看neutron-openvswi-INPUT链有哪些规则。奇怪的是规则为空。按上下文的理解,这里的规则为空,应该理解为全部ACCEPT。事实是这样吗?
为了验证这一点,我们可以手工针对neutron-openvswi-INPUT链加一条明确的接收全部进来的数据包的规则:
iptables -A neutron-openvswi-INPUT -p all -j ACCEPT
再使用云主机手工获取一次DHCP地址,结果云主机立即就拿到了地址。
再对网络节点的br-int接口抓包,重复云主机获取DHCP的操作,马上就抓到了进入网络节点br-int子网的DHCP广播包了。
7、结论与推测
网络节点的iptables规则,是由openvswitch服务动态维护的。上述新增的neutron-openvswi-INPUT链也正是启动ovs服务后,动态生成的。根据链表处理逻辑,不需要显示的设置这样一条接受数据包的规则,也应该可以实现设计中的网络通信能力。而事实却不是这样的。我没有在更多的linux系统和版本上进行测试,至少在CentOS6.7上,必须手工增加一条链表规则才行。
重启openvswitch服务后,手工加的这条规则会被清除掉。为了容易维护,可以先使用service iptables save把规则保存至文件中去。
在以后重启openvswitch服务后,立即执行一次service iptables reload即可。