現代資料中心安裝了數百個主動設備,涵蓋不同類型的監控。 但即使是擁有完美監控能力的理想工程師也能夠在短短幾分鐘內正確回應網路故障。 在Next Hop 2020會議的報告中,我提出了一種DC網路設計方法,它有一個獨特的功能——資料中心在毫秒內自我修復。 更準確地說,工程師冷靜地解決問題,而服務人員根本沒有註意到它。
對許多網路工程師來說,資料中心網路當然是從 ToR 開始的,也就是機架中的交換器。 ToR 通常有兩種類型的連結。 小的進入伺服器,其他的-有N倍多-進入第一層的主幹,也就是它的上行鏈路。 上行鏈路通常被認為是相等的,上行鏈路之間的流量基於 5 元組的哈希進行平衡,其中包括 proto、src_ip、dst_ip、src_port、dst_port。 這裡沒有什麼驚喜。
接下來,規劃架構是什麼樣的呢? 第一層的脊椎彼此不連接,而是透過超級脊椎連接。 字母 X 將負責超級脊椎;它幾乎就像一個交叉連接。
另一方面,很明顯,托里與第一層的所有脊柱相連。 這張圖片中重要的是什麼? 如果我們在機架內部進行交互,那麼交互當然會透過 ToR。 如果交互發生在模組內部,則交互通過第一級脊椎發生。 如果交互作用是模組間的(如此處的 ToR 1 和 ToR 2),則交互作用將通過第一層和第二層的脊柱。
理論上,這樣的架構很容易擴展。 如果我們有連接埠容量、資料中心的空閒空間和預敷光纖,那麼通道數量總是可以增加,從而增加系統的整體容量。 這在紙上很容易做到。 生活中也會如此。 但今天的故事不是這個。
我希望得出正確的結論。 我們在資料中心內部有很多路徑。 他們是有條件獨立的。 資料中心內部的一條路徑只能在 ToR 內部進行。 在模組內部,路徑的數量等於車道的數量。 模組之間的路徑數量等於平面數量與每個平面中超級脊椎數量的乘積。 為了更清楚地了解規模,我將給出對 Yandex 資料中心之一有效的數字。
有八個平面,每個平面有 32 個超級脊椎。 結果,模組內部有 256 條路徑,透過模組間交互,路徑已經有 XNUMX 條。
也就是說,如果我們正在開發Cookbook,試圖學習如何建立自我修復的容錯資料中心,那麼平面架構就是正確的選擇。 它解決了擴展問題,理論上很容易。 有許多獨立的路徑。 問題仍然是:這樣的架構如何在失敗中存活下來? 有各種各樣的失敗。 我們現在將討論這個問題。
讓我們的超級脊柱之一“生病”。 這裡我又回到了兩平面架構。 我們將繼續以這些作為範例,因為使用較少的移動部件可以更容易地了解發生了什麼。 讓X11生病吧。 這將如何影響資料中心內的服務? 很大程度取決於失敗的實際情況。
如果故障是好的,它被捕獲在同一 BFD 的自動化級別,自動化會愉快地放置有問題的接頭並隔離問題,然後一切都很好。 我們有很多路徑,流量會立即重新路由到替代路線,並且服務不會注意到任何事情。 這是一個好劇本。
一個糟糕的情況是,如果我們不斷遭受損失,而自動化卻沒有註意到這個問題。 為了了解這對應用程式有何影響,我們必須花一些時間討論 TCP 的工作原理。
我希望我不會讓任何人感到震驚:TCP 是一種傳輸確認協定。 也就是說,在最簡單的情況下,發送方發送兩個資料包並收到對它們的累積確認:“我收到了兩個資料包。”
之後,他會再發送兩個資料包,情況又會重複。 對於一些簡化,我提前表示歉意。 如果視窗(傳輸中的資料包數量)為 XNUMX,則這種情況是正確的。 當然,一般情況不一定是如此。 但視窗大小不影響封包轉送上下文。
如果我們遺失資料包 3 會發生什麼事? 在這種情況下,接收者將收到資料包 1、2 和 4。並且他將使用 SACK 選項明確地告訴發送者:“你知道,三個到達了,但中間丟失了。” 他說:“Ack 2,SACK 4。”
此時,發送方可以毫無問題地重複遺失的資料包。
但如果視窗中的最後一個資料包遺失,情況就會看起來完全不同。
接收方收到前三個資料包,首先開始等待。 由於 Linux 核心的 TCP 堆疊中的一些最佳化,它將等待配對的資料包,除非標誌明確指示它是最後一個資料包或類似的東西。 它將等到延遲 ACK 逾時到期,然後發送前三個資料包的確認。 但現在寄件者將等待。 他不知道第四個包裹是遺失了還是即將到達。 為了不讓網路過載,它將嘗試等待封包遺失的明確指示,或等待 RTO 逾時到期。
什麼是RTO超時? 這是由 TCP 堆疊計算的 RTT 的最大值和一些常數。 這是一個什麼樣的常數,我們現在來討論。
但重要的是,如果我們再次不走運,第四個資料包再次遺失,那麼 RTO 就會翻倍。 也就是說,每次不成功的嘗試都意味著超時時間加倍。
現在讓我們來看看這個基數等於什麼。 預設情況下,最小RTO為200毫秒。 這是資料包的最小 RTO。 對於 SYN 封包,則不同,為 1 秒。 正如您所看到的,即使是第一次嘗試重新發送資料包,也會花費比資料中心內的 RTT 長 100 倍的時間。
現在讓我們回到我們的場景。 服務怎麼了? 服務開始遺失資料包。 首先讓服務有條件地幸運,並在視窗中間丟失一些東西,然後它收到 SACK 並重新發送丟失的資料包。
但如果壞運氣重演,那麼我們就會面臨 RTO。 這裡重要的是什麼? 是的,我們的網路中有很多路徑。 但某個特定 TCP 連線的 TCP 流量將繼續通過同一個損壞的堆疊。 丟包,只要我們這個神奇的X11不會自行出去,就不會導致流量流入沒有問題的區域。 我們正在嘗試透過同一個損壞的堆疊傳遞資料包。 這會導致級聯故障:資料中心是一組互動的應用程序,所有這些應用程式的一些 TCP 連接開始降級 - 因為超級主幹會影響資料中心內存在的所有應用程式。 俗話說:不掌蹄,馬跛; 馬跛了——報告沒有送達; 報告沒有交付——我們輸掉了戰爭。 只是這裡的計數是從問題出現到服務開始下降的階段的秒數。 這意味著用戶可能會錯過某些地方的東西。
有兩種相輔相成的經典解決方案。 第一個是試圖投入救命稻草並解決問題的服務,如下所示:「讓我們在 TCP 堆疊中調整一些東西。 讓我們透過內部健康檢查在應用程式層級或長期 TCP 會話上設定逾時。” 問題是這樣的解決方案:a)根本無法擴展; b) 檢查非常差。 也就是說,即使服務意外地以使其更好的方式配置了 TCP 堆疊,首先,它不太可能適用於所有應用程式和所有資料中心,其次,很可能,它不會理解它已完成正確與否。 也就是說,它可以工作,但效果很差並且無法擴展。 如果出現網路問題,誰該負責? 當然,國家奧委會。 國家奧委會是做什麼的?
許多軍種認為,在 NOC 工作中會發生類似的事情。 但說實話,不只如此。
經典方案中的NOC從事許多監控系統的開發。 這些都是黑盒和白盒監控。 關於黑盒子脊椎監控的範例
你真正想收到什麼? 我們有很多方法。 問題的出現正是因為不幸的 TCP 流繼續使用相同的路由。 我們需要允許我們在單一 TCP 連線中使用多個路由的東西。 看來我們有一個解決方案。 還有TCP,稱為多路徑TCP,即多路徑的TCP。 確實,它是為完全不同的任務而開發的 - 用於具有多個網路設備的智慧型手機。 為了最大化傳輸或建立主/備份模式,開發了一種機制,該機制可以對應用程式透明地建立多個執行緒(會話),並允許您在發生故障時在它們之間進行切換。 或者,正如我所說,最大化連勝。
但這裡有一個細微差別。 為了理解它是什麼,我們必須看看線程是如何建立的。
線程是按順序安裝的。 首先安裝第一個線程。 然後使用該線程內已商定的 cookie 來設定後續線程。 問題就在這裡。
問題是,如果第一個執行緒沒有自行建立,那麼第二個和第三個執行緒將永遠不會出現。 也就是說,多路徑TCP並不能解決第一流中SYN封包遺失的問題。 如果 SYN 遺失,多路徑 TCP 就會變成常規 TCP。 這意味著在資料中心環境中它不會幫助我們解決工廠中的損失問題並學會在發生故障時使用多條路徑。
有什麼可以幫助我們呢? 你們中的一些人已經從標題中猜到,我們接下來的故事中的一個重要欄位將是 IPv6 流標籤標頭欄位。 確實,這是v6中出現的字段,v4中沒有,它佔用20位,而且長期以來關於它的使用一直存在爭議。 這非常有趣 - 存在爭議,RFC 中修復了某些內容,同時 Linux 核心中出現了一個實現,但沒有任何地方記錄。
我邀請你和我一起去進行一個小小的調查。 讓我們來看看過去幾年Linux核心發生了什麼。
2014 年。 來自一家受人尊敬的大公司的工程師將流標籤值對套接字哈希的依賴性添加到 Linux 核心的功能中。 他們想在這裡解決什麼問題? 這與 RFC 6438 相關,RFC 4 討論了以下問題。 在資料中心內部,IPv6常常封裝在IPv6資料包中,因為工廠本身就是IPv4,但外部必須以某種方式給出IPv5。 很長一段時間以來,交換器都存在無法查看兩個 IP 標頭以到達 TCP 或 UDP 並找到 src_ports、dst_ports 的問題。 事實證明,如果你查看前兩個 IP 標頭,雜湊值幾乎是固定的。 為了避免這種情況,以便該封裝流量的平衡正常運作,建議將 XNUMX 元組封裝封包的雜湊新增至流標籤欄位的值。 對於其他封裝方案,UDP、GRE 也做了大致相同的事情,後者使用了 GRE 金鑰欄位。 無論如何,這裡的目標是明確的。 至少在那個時候它們是有用的。
2015 年,同一位受人尊敬的工程師發布了新補丁。 他很有趣。 它說明了以下內容 - 我們將在出現負面路由事件的情況下隨機化雜湊值。 什麼是負面路由事件? 這就是我們前面討論的RTO,也就是視窗尾部遺失是一個真正負面的事件。 確實,要猜到就是這樣是相對困難的。
2016年,又一家有信譽的公司,也很大。 它分解了最後的拐杖,並使得我們之前隨機生成的哈希值現在在每次 SYN 重傳和每次 RTO 逾時後都會發生變化。 在這封信中,第一次也是最後一次闡述了最終目標——確保流量在遺失或頻道擁塞的情況下能夠軟性重新路由並使用多條路徑。 當然,這之後還有很多出版物,你很容易找到。
雖然不可以,但你不能,因為還沒有關於這個主題的出版品。 但我們知道!
如果你不完全明白做了什麼,我現在就告訴你。
Linux 核心做了什麼,增加了哪些功能? 每次 RTO 事件後,txhash 都會變更為隨機值。 這是路由帶來的非常負面的結果。 哈希取決於這個 txhash,流標籤取決於 skb 哈希。 這裡有一些函數的計算,所有的細節不可能放在一張投影片上。 如果有人好奇,你可以透過核心程式碼檢查一下。
這裡重要的是什麼? 流標籤欄位的值在每個 RTO 之後變更為隨機數。 這對我們不幸的 TCP 流有何影響?
如果發生 SACK,則不會發生任何變化,因為我們正在嘗試重新發送已知遺失的資料包。 到目前為止,一切都很好。
但在 RTO 的情況下,如果我們在 ToR 上的雜湊函數中加入了流標籤,流量可能會採取不同的路線。 通道越多,找到不受特定設備故障影響的路徑的機會就越大。
仍然存在一個問題——RTO。 當然,還有另外一條路線,但在這上面浪費了很多時間。 200 毫秒已經很多了。 一秒鐘絕對是瘋狂的。 之前,我談到了服務配置的超時。 所以,一秒鐘就是一個超時,這通常是由服務在應用層面配置的,這樣服務甚至會比較正確。 此外,我再說一遍,現代資料中心內的真實 RTT 約為 1 毫秒。
您可以利用 RTO 逾時做什麼? 逾時負責在資料包遺失時的 RTO,可以從用戶空間相對輕鬆地配置:有一個 IP 實用程序,其參數之一包含相同的 rto_min。 當然,考慮到RTO不需要全域調整,而是針對給定的前綴,這樣的機制看起來相當可行。
確實,使用 SYN_RTO 一切都會變得更糟。 自然就被釘牢了。 內核有一個固定值 1 秒,僅此而已。 您無法從用戶空間到達那裡。 只有一種方法。
eBPF 來救援。 簡單來說,這些都是小C程序,它們可以在內核堆疊和TCP堆疊的執行過程中的不同地方插入鉤子,用它們可以改變非常多的設置。 總的來說,eBPF是一個長期趨勢。 該運動不是削減數十個新的 sysctl 參數並擴展 IP 實用程序,而是轉向 eBPF 並擴展其功能。 使用 eBPF,您可以動態變更擁塞控制和各種其他 TCP 設定。
但對我們來說很重要的是它可以用來更改 SYN_RTO 值。 另外,還有一個公開的例子:
我們已經知道什麼? 事實上,平面架構允許擴展,當我們在 ToR 上啟用流標籤並獲得繞過問題區域的能力時,它對我們非常有用。 降低RTO和SYN-RTO值的最佳方法是使用eBPF程式。 問題仍然是:使用流標籤進行平衡是否安全? 這裡有一個細微差別。
假設您的網路上有一項以選播方式存在的服務。 不幸的是,我沒有時間詳細介紹什麼是選播,但它是一種分散式服務,可以透過相同的 IP 位址存取不同的實體伺服器。 這裡有一個可能的問題:RTO 事件不僅會在流量通過結構時發生。 它也可能發生在 ToR 緩衝區層級:當發生 incast 事件時,甚至可能在主機溢出某些內容時發生在主機上。 當 RTO 事件發生並且它更改流標籤時。 在這種情況下,流量可以流向另一個任播執行個體。 我們假設這是一個有狀態的任播,它包含一個連線狀態 - 它可能是一個 L3 Balancer 或一些其他服務。 那麼問題就出現了,因為在RTO之後,TCP連線到達伺服器,而伺服器對這個TCP連線一無所知。 如果我們在選播伺服器之間沒有狀態共享,那麼此類流量將被丟棄,TCP 連線將中斷。
你在這裡能做什麼? 在啟用流標籤平衡的受控環境中,您需要在存取任播伺服器時記錄流標籤的值。 最簡單的方法是透過相同的 eBPF 程序來完成此操作。 但這裡有一個非常重要的點——如果你不是運營資料中心網絡,而是電信運營商怎麼辦? 這也是您的問題:從 Juniper 和 Arista 的某些版本開始,它們默認在哈希函數中包含流標籤 - 坦率地說,出於我不清楚的原因。 這可能會導致您中斷通過您網路的使用者的 TCP 連線。 因此,我強烈建議您在此處檢查路由器設定。
無論如何,在我看來,我們已經準備好繼續進行實驗了。
當我們在 ToR 上啟用串流標籤,準備好 eBPF 代理程式(現在它位於主機上)時,我們決定不等待下一次重大故障,而是進行受控爆炸。 我們採用了具有四個上行鏈路的 ToR,並在其中一個上設置了分站。 他們制定了一條規則並說 - 現在你將丟失所有資料包。 正如您在左側看到的,我們有逐包監控,已下降到 75%,即有 25% 的資料包遺失。 右側是該 ToR 背後的服務圖表。 本質上,這些是機架內伺服器介面的流量圖。 正如你所看到的,它們跌得更低。 為什麼它們會下降——不是下降 25%,而是在某些情況下下降了 3-4 倍? 如果 TCP 連線不走運,它會繼續嘗試透過損壞的連接點進行連線。 DC 內部服務的典型行為加劇了這種情況 - 對於一個使用者請求,會產生 N 個對內部服務的請求,並且當所有資料來源回應時,或者當應用程式發生逾時時,回應將發送給使用者level,仍然需要配置。 也就是說,一切都非常非常糟糕。
現在進行相同的實驗,但啟用了流標籤值。 正如您所看到的,在左側,我們的大量監控也下降了 25%。 這是絕對正確的,因為它不知道有關重傳的任何信息,它發送資料包並簡單地計算已發送和丟失的資料包數量的比率。
右邊是服務時間表。 您不會在這裡發現有問題的接頭的影響。 在同一毫秒內,流量從問題區域流向其餘三個未受問題影響的上行鏈路。 我們有一個可以自我修復的網路。
這是我的最後一張投影片,是時候總結一下了。 現在,我希望您知道如何建立一個自我修復的資料中心網路。 您不需要瀏覽 Linux 核心存檔並在那裡查找特殊補丁;您知道本例中的 Flow 標籤可以解決問題,但您需要仔細處理此機制。 我再次強調,如果你是電信業者,你不應該使用流標籤作為雜湊函數,否則你會擾亂用戶的會話。
網路工程師必須經歷概念上的轉變:網路不是從ToR、不是從網路設備開始,而是從主機開始。 一個相當引人注目的例子是我們如何使用 eBPF 來更改 RTO 並修復選播服務的串流標籤。
流標籤機制當然適用於受控管理段內的其他應用。 這可以是資料中心之間的流量,或者您可以以特殊方式使用此類機制來管理傳出流量。 但我希望下次我會告訴你這一點。 非常感謝您的關注。
來源: www.habr.com