天天看點

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在計算節點上是負責關停其他程序的。

Azure計算節點無法連上本地主節點,原來是MTU惹的禍

從計算節點上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.

Azure計算節點無法連上本地主節點,原來是MTU惹的禍

解讀一下以上截圖。

第一幀,是master發給計算節點UDP包,很明顯是被分割(fragment)成更小片了,每一片加上各種標頭包尾大小才1410  --- 這裡補充一下,一個幀(frame)裡,幀頭資訊包括:目的主機MAC位址(6 byte),源主機MAC位址(6 byte),資料報類型(2 byte),一共14位元組。展開幀詳情,我們可以看到IP包大小是1396 byte,加上幀頭14 byte,剛好是1410 byte。

Azure計算節點無法連上本地主節點,原來是MTU惹的禍

然而,黑色底色的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節點了。

繼續閱讀