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節點了。

 

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