Azure计算节点无法连上本地主节点,原来是MTU惹的祸

 

IBM Spectrum Symphony 是基于SOA架构的分布式计算框架,它能在自由伸缩的共享集群中,为计算密集型和数据密集型的应用提供强大的企业级管理。而且,Symphony能加快多个并行应用以更快地得出结果以及更好地使用所有可用资源。利用Symphony,企业可以提高IT性能,减少基础设施成本,以更快地满足商业需求。受益于Symphony底层优秀的资源调度框架EGO、由C++实现的中间件SOAM,Symphony的性能和可扩展性都极为优秀,在金融衍生品的定价以及风险模拟等金融领域得到广泛的应用。

在Cloud大势所趋的今天,越来越多的公司选择将应用迁移到Cloud上,目的无非都是为了降低IT成本,说白了就是为了省钱。从全球的角度来看,前几大Cloud大厂无非是Amazon的AWS,Microsoft的Azure,Google Cloud,IBM Cloud,阿里云,腾讯云,金山云,等等。Symphony的客户也在尝试将Symphony的应用跑到Cloud上,目前为止支持AWS,Azure和IBM Cloud。现如今已经有不少Symphony的客户将整个cluster都跑到AWS或Azure上。

 

近日,欧洲某大型商业银行在测试Symphony上Azure的时候,就遇到一个网络连接问题。他们的架构是Symphony master节点依然保留原本跑在本地的master,但是打算从Azure上租用数以千计的计算节点。在部署测试的时候,他们遇到的现象是,计算节点在cluster中显示为Unavailable的状态,而且计算节点上,启动Symphony之后只有LIM这个进程,连PEM都没有起来。参看下图了解Symphony的daemon关系图,PEM在计算节点上是负责关停其他进程的。

从计算节点上LIM的DEBUG日志来看,内容也很简单,把request发给master LIM之后就没有下文了。

May 18 17:27:43 2020 1732:3912 5 3.7.0 logMTU(): The MTU (Maximum Transmission Unit) size of the network interface <Microsoft Hyper-V Network Adapter> is 1500 bytes.
May 18 17:28:43 2020 1732:3912 7 3.7.0 LSF uses normal communication
May 18 17:28:43 2020 1732:3912 7 3.7.0 announceMasterToHost: Sending request to LIM on 192.168.31.13:7869

而master LIM的DEBUG log也没相关信息,

May 18 15:52:39 2020 9456:1552 5 3.7.0 logMTU(): The MTU (Maximum Transmission Unit) size of the network interface <Microsoft Network Adapter Multiplexor Driver> is 1500 bytes.
May 18 15:52:45 2020 9456:1552 4 3.7.0 initNewMaster: This is now the master host.

看两边的LIM log, 很明显MTU都一样,不是问题(两边MTU必须相同是Symphony明文规定的一个基本要求)。那会不会是Azure上或者本地内网的Firewalls把端口封了呢?我们首先怀疑是Firewalls导致网络不通,因为正常情况下,计算节点上的LIM log是如下的。很明显,计算节点上没有chanRcvDgramExt_,也就是说,计算节点没有接收到来自master LIM的cluster相关配置信息。

May 18 14:31:33 2020 5264:22512 7 3.8.0 announceMasterToHost: Sending request to LIM on 192.168.1.5:8000
May 18 14:31:33 2020 5264:22512 7 3.8.0 chanRcvDgramExt_: datagram comes from <192.168.1.5:8000>   <=======received datagram from the master
May 18 14:31:37 2020 5264:22512 6 3.8.0 recvConfFiles: the conf data from master lim is as follows:
LAST_UPDATE=1589837387.25741
MLIM_STARTUP=1589837387.25741
EGO_MASTER_LIST=master_hostname
EGO_KD_PORT=8001
EGO_PEM_PORT=8002
EGO_COMPONENTS_COLLECTION=Y
......

所以,第一步我们检测socket是否可以通信。

1. 分别从两端互ping对方IP,正常。

2. 两端启动了Symphony之后,分别从两端telnet对方的LIM的默认监听端口7869,也通。

看起来socket通信没问题啊,郁闷了。

 

但是,从正常的环境中我们发现,PEM启动之后,居然会跟master的VEMKD进程的EGO_KD_PORT(默认7870)建立一个TCP连接,而且PEM使用的居然是一个随机端口!而在Azure的Firewalls设置上,客户可是仅仅开放了某一段范围的端口,这就完全有可能PEM使用了一个规定范围外的端口去连VEMKD。我信心满满,认为找到了问题所在。

但是,怎么样才能使PEM使用规定范围内的端口呢?严格地说,这可以算是产品bug,因为在产品文档里罗列了所有Symphony进程会用到的端口,但并没有说明PEM会用一个随机端口跟VEMDK相连,而实际上PEM的确在这么做。因为之前Symphony集群运行的环境都是在内网,没有这种内网跟Cloud即外网结合的情况,极有可能没有任何客户遇到这种问题。面对这个问题,要么是改代码提供patch,时间必然相对较长;要么看看能不能从OS层面设定进程启动时的可用端口,算是workaround,能迅速解决问题。

 

果然,在Windows环境下,我们找到以下方法可以设定进程启动时的可用端口。

Limit Symphony to use ports from 7869 ~ 8168, totally 300 ports, on masters and all other compute hosts.
 
To set the port range, run below commands from master and compute hosts. Note, the minimum num is 255.
 
C:\symphony\soam>netsh int ipv4 set dynamic tcp start=7875 num=300
Ok.

C:\symphony\soam>netsh int ipv4 set dynamic udp start=7875 num=300
Ok.
 
 
Check the port range:
 
C:\symphony\soam>netsh int ipv4 show dynamicport tcp
Protocol tcp Dynamic Port Range
---------------------------------
Start Port      : 7875
Number of Ports : 300

C:\symphony\soam>netsh int ipv4 show dynamicport udp
Protocol udp Dynamic Port Range
---------------------------------
Start Port      : 7875
Number of Ports : 300

这样设置之后,Azure的Firewalls policy里只要开放端口范围7875~8174的入栈(inbound)和出栈(outbound)TCP和UDP连接即可.

 

然后,验证端口确实开放且数据包能到达。这里,我发现一个利器 --- nc.exe(netcat).

首先从https://joncraton.org/blog/46/netcat-for-windows/下载nc111nt_safe.zip,解压缩之后,运行下面的命令来开启监听端口,-L指定持续监听,-p指定监听端口,-u指定连接类型是UDP模式,-v指定详细输出,-l指定是inbound连接的监听模式。


 
C:\Users\Admin\Downloads\nc111nt_safe>nc.exe -L -p 8011 -u     <===listen on port 8011 in UDP mode  

把nc111nt_safe.zip复制到另外一个host,解压缩之后,打开一个CMD,尝试往对端socket发包。 


 
C:\Users\Admin\Downloads\nc111nt_safe>nc.exe -u -v 192.168.1.5 8011 < test.txt   <===in test.txt are some contents 
 

验证下来,发现socket通信没问题。所以防火墙是没有拦截指定端口范围的通信的。

 

我以为这次问题总该解决了吧。但是,计算节点上PEM依然没有起来,问题依旧。这就让人纳闷了。接下来还能做什么呢?

抓包看看吧,对,Wireshark抓包看看。Azure节点上不让安装Wireshark,那我们就先只从本地的master上抓包。果然发现数据包传输有问题,看下图。其中计算节点的IP是8.29,master节点IP是31.13.

 

解读一下以上截图。

第一帧,是master发给计算节点UDP包,很明显是被分割(fragment)成更小片了,每一片加上各种包头包尾大小才1410  --- 这里补充一下,一个帧(frame)里,帧头信息包括:目的主机MAC地址(6 byte),源主机MAC地址(6 byte),数据报类型(2 byte),一共14字节。展开帧详情,我们可以看到IP包大小是1396 byte,加上帧头14 byte,刚好是1410 byte。

然而,黑色底色的ICMP包明显报分片重组超时了(Fragment erassembly time exceeded),也就说,从master发出的UDP包分片之后,不是所有分片都及时到达计算节点并合成一个完整的UDP包。须知,路由器或网卡根据MTU设置执行的行为是,只要有一个分片没有到达目的地,目的地端会把所有接收到的分片都丢弃。所以,事实上,计算节点并没有收到master发送的大于1410 byte的UDP包。而master LIM经UDP发送给计算节点LIM的cluster配置内容,是明显大于1410的,一般都大约5000多字节。

 

好了,问题清楚了,即是虽然节点两端的MTU都是1500,但是由于Azure的网络提供的线路中间的设备,比方说,路由器,所设置的MTU小于1500 --- 实际上,根据以上帧的详情,基本断定链路上的MTU是1400 bytes(MTU 1400 bytes的设置,IP层的最大有效负载就是1380 = 1400 - 20。但是1380/8 = 172.5,不能整除,所以必须是仅小于1380又能整除8的数字,即1376/8 = 172. 所以UDP包分片的时候,每片IP最大有效负载就是1376 bytes + 20 bytes IP头 = 1396 bytes),也就是上层IP层的最大传输值(IP头算在内)。

果不其然,后来客户跟Azure那边确认到,隧道配置的MTU就是1400!

Between datacenter and azure-Prod environment they are running over an expressRoute with an ipsec 
encryption ontop.The crypto tunnel is configured with an IP MTU of 1400.

 

那为什么分片之后有些包就到达不了另一端呢?经查阅,是因为跟路由器或交换机的ACL处理有关。简单说就是,假设问题中master发送给计算节点LIM的UDP包大小是 5620 bytes,分成1480+1480+1480+1180四个分片发出去,最后一个1180的分片加上20的IB头部信息因为小于1400,无需分片直接pass;但是1480的分片就要被路由器或交换机分成1396+84。问题就出在这,1396这个分片,叫initial fragment,84这个分片叫non-initial fragment. initial fragment分片会有一个Flag,即More Fragments,如图中所示;最重要的是,一般来说,initial fragment会携带上层协议(本例中是UDP)的头部信息,因此就包含有目的端口等信息;但是non-initial fragment一般不会携带上层协议的头部信息,这就导致分片之后,设备上设置的ACL也可能对non-initial fragment做了其他处理,因为它不带端口号,就极有可能命中ACL中的另外一条规则,设备从而做出异于initial fragment的操作,比方说,丢弃。这方面的知识,请参考Cisco的官方文档说明:https://www.cisco.com/c/en/us/support/docs/ip/generic-routing-encapsulation-gre/25885-pmtud-ipfrag.html   部分摘抄如下:

Firewalls that filter or manipulate packets based on Layer 4 (L4) through Layer 7 (L7) information in the packet might have trouble processing IPv4 fragments correctly. If the IPv4 fragments are out of order, a firewall might block the non-initial fragments because they do not carry the information that would match the packet filter. This would mean that the original IPv4 datagram could not be reassembled by the receiving host. If the firewall is configured to allow non-initial fragments with insufficient information to properly match the filter, then a non-initial fragment attack through the firewall could occur. Also, some network devices (such as Content Switch Engines) direct packets based on L4 through L7 information, and if a packet spans multiple fragments, then the device might have trouble enforcing its policies.

 

问题清楚之后就可以提出解决方案了,要么是改大隧道的MTU值,以跟两边终端一致;要么是改小两边终端的MTU的值(Windows更改MTU的命令是“netsh interface ipv4 set subinterface xxx mtu=1400 store=persistent”, xxx是网卡)。至于MTU值多少为合适,还需要考虑隧道加密用的是哪种IPSec协议和加密算法,因为协议和加密算法不同造成的overhead自然不同。

最终解决问题的方案是做了两点修改,一是客户把两边终端的MTU改小到1400;同时客户联系微软换了一条隧道,由expressRoute换到了另外一条经过Internet的隧道。虽然两条隧道都配置了IP MTU 1400,而且使用的IPSec的IKEv2和Security Association参数都一样,但是隧道的网关、所属的HUBVnet以及经过的路由自然有所不同。这样更改之后,跑在Azure上的Symphony的计算节点就可以连上本地的master节点了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章