JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

胖樹與葉脊:為規模而生的拓撲

上一篇說明了為什麼資料中心網路自成一格:規模龐大、流量多半是東西向、而且任兩台伺服器之間的頻寬永遠不夠。這一篇則展示修好它的接線方式——胖樹與葉脊架構如何用許多小型交換器拼出一台巨大、便宜、近乎無阻塞的交換器,以及流量如何在其中眾多的路徑上分散。

接線必須解決的問題

上一篇留給我們一個尖銳的需求。一個資料中心網路連接數以萬計的伺服器,而它們的流量多半是東西向的——伺服器在建築物內部彼此交談——而不是往外通向網際網路的南北向流量。夢想是:任何一台伺服器都能同時以全速與任何另一台伺服器交談,彷彿每一台機器都插在同一台大得不可思議的交換器上。整個拓撲問題其實就是:當沒有任何一顆交換器晶片接近足夠大時,你要怎麼把那一台巨大的交換器造出來?

有兩個數字衡量你做得多好。第一個是對分頻寬(bisection bandwidth):想像把整個資料中心切成相等的兩半,然後把橫跨這道切口的每一條纜線的頻寬全部加總。那就是在兩半之間流動的流量的最壞情況容量,也是衡量「全體對全體」工作負載最誠實的尺。第二個是路徑多樣性:兩台伺服器之間存在多少條不同的路由,因為許多條短路徑意味著流量可以分散開來,而單一一台交換器故障也只是少了一條路徑,而非一場災難。

請抓住第一段位裡的一個區別,因為拓撲無法打破它。更多的纜線與更多的路徑,買到的是更多的頻寬——兩半之間每秒更多的位元組。它們買不到更低的延遲:一個封包仍然得穿過一定數目的交換器,而光速與每一跳的轉發時間設下了一個地板,再多的接線也打不破它。這裡的目標是「規模下的頻寬與韌性」;維持低延遲是另一場仗,主要靠減少跳數,以及後面幾篇會講的傳輸層技巧來打。

舊辦法:三層樹,以及它在哪裡掐住

經典的企業設計是三層拓撲:一棵樹。最底層,接取層(access)交換器坐在每個機架的頂端,連接那個機架裡的伺服器。它們向上連到中間的匯聚層(aggregation),匯聚層再向上連到最頂端、數量很少的核心層(core)。每一台伺服器要連到另一台,都得爬上樹中的某個共同祖先再爬下來——這對於往外通向網際網路的南北向流量來說沒問題,那正是這個形狀當初被設計來做的工作。

麻煩出在那個漏斗。一個機架可能裝了 40 台伺服器、每台有一條 10 Gbps 的鏈路,也就是可能有 400 Gbps 的流量要離開這個機架——但接取交換器往上一層的上行鏈路也許只有,比方說,80 Gbps。這個比例,400 比 80、也就是 5 比 1,就叫做超額認購(oversubscription):你在邊緣賣出去的容量,是核心實際能承載的五倍。當你往樹上爬,鏈路會變粗,卻很少粗到足夠,於是少數幾台核心交換器就成了所有跨資料中心流量都得擠過去的瓶頸

對東西向流量而言,這正好是錯的形狀。兩台位於不同機架、想為一個資料洗牌作業交換一個 TB 的伺服器,都必須把自己的位元組一路拉到爭用激烈的核心再拉下來,並與其他每一條跨機架的流爭奪那一點點薄薄的核心容量。昂貴的解法是去買越來越大、越來越特殊的核心交換器——這種垂直擴張的代價呈指數級攀升,而且仍然讓你只剩幾台機器,其中一台故障就能讓半棟樓癱瘓。

新辦法:用小交換器拼出一台大交換器

這個突破翻轉了經濟學。與其用少數幾台巨大的客製化核心交換器,不如用大量小型、便宜、一模一樣的商用交換器,並以一種巧妙的模式接線,使它們合起來表現得像一台龐大的無阻塞交換器。這個模式叫做 Clos 網路,是從 1950 年代的電話交換機借來的點子,而它在資料中心的形態就是胖樹(fat-tree)。「胖」這個字正是重點:在一棵普通的樹裡,鏈路越往上越細,但胖樹讓每一層往上的總容量都等於它下方的容量,於是漏斗消失了。

細鏈路怎麼會加起來變成一條粗的?不是靠把任何一條纜線弄粗,而是靠製造許多條平行路徑。普通的樹往上只有一條鏈路,胖樹卻有好幾條上行鏈路扇形散開,連到上一層的好幾台交換器。那些平行鏈路的總和,承載量等同於它下方的一切,於是一個從某機架到另一機架的封包,不再去爭搶一條稀缺的單一路徑——它從許多條一樣好的路由中挑一條往上、繞過去。容量靠橫向增添更多便宜機器來擴張(scale out),而不是靠買更大的機器(scale up)。

這個形狀還免費掉出另外兩項好處。第一,韌性:任兩個機架之間有數十條路徑,失去一台交換器只是從眾多路徑中拿掉一條,流量便繞過它而行——沒有任何單一故障能讓半張網路癱瘓。第二,成本與一致性:每一台交換器都是同一個小型的商用型號,所以買起來便宜、好備料當備品、管理起來也簡單。你把幾台不可替代的巨人,換成了一群可以互換的螞蟻。

葉脊:人人實際接出來的那種胖樹

實務上,幾乎每一座現代資料中心都接成一種被壓扁的兩層胖樹,叫做葉脊架構(leaf-spine fabric)。(leaf)交換器坐在每個機架的頂端(每機架一到兩台,連接那個機架的伺服器)。(spine)交換器構成上層。它的定義規則殘酷地簡單、值得背下來:每一台葉都連到每一台脊,而葉與葉之間從不相連、脊與脊之間也從不相連。結果是任何一台伺服器都恰好離任何一台脊一跳,而任何另一台伺服器最多再遠兩跳——葉、上到某台脊、下到目的葉。

        SPINE 1        SPINE 2        SPINE 3        SPINE 4
          |  \   \   /  |  \   /  |  /   /  |   ...
          | (every leaf connects to EVERY spine)
        LEAF A        LEAF B        LEAF C        LEAF D
        /||\          /||\          /||\          /||\
     servers        servers       servers       servers
      rack A         rack B        rack C        rack D

  A server in rack A -> server in rack C  always = 3 hops:
     leaf A  ->  (any one) spine  ->  leaf C
  With 4 spines there are 4 equal-length paths to choose from.
一個葉脊架構。每一台葉都連到每一台脊,所以任何機架到機架的行程都是「葉、上到脊、下」:一個固定而短的跳數,每一台脊提供一條可供分散負載的路徑。

這種扁平正是東西向流量想要的。每一條跨機架的流都走同樣可預測的三跳,所以任兩台伺服器之間的延遲是均勻的——不像舊樹那樣有「近而便宜」對上「遠而昂貴」之分。而脊交換器的數目是一個你可以轉的旋鈕:更多的脊意味著更多的平行路徑、因而更多的對分頻寬,所以你可以把架構建成完全無阻塞的容量,或者,如果你的工作負載並不真的需要全體對全體的線速,就刻意地把脊配置得少一點、接受一個已知的超額認購比例,比如 3 比 1。要擴張這棟建築,就是增添葉(更多機架)與脊(更多頻寬),而永遠不必更換核心。

分散負載:ECMP 與它誠實的極限

所有那些平行路徑,除非流量真的用上它們,否則都是白搭。當一台葉有,比方說,四條等長的路徑往上通到四台不同的脊,它就用等價多路徑(ECMP)把流分散到它們之上。路由器的轉發表現在針對那個目的地列出好幾個成本相等的下一跳,而 ECMP 在它們之間挑選——於是不同的對話自然地在整張架構上扇形展開,而不是全部蜂擁衝下同一條鏈路。這就是把「許多路徑存在」變成「許多路徑被用上」的那個機制。

這裡有個聰明而謹慎的細節。ECMP 絕不能把單一一條流的封包打散到不同路徑上,因為不同路徑可能有些微不同的延遲,封包就會亂序抵達——而傳輸層會把重排讀成出問題的跡象。所以 ECMP 不是逐封包選擇,而是逐流選擇。它對封包標頭裡的幾個欄位做雜湊——通常是來源與目的的 IP 位址與通訊埠號,也就是這條連線「誰對誰」的身分——並用那個雜湊值去挑一條路徑。同一條流的每一個封包都雜湊到同一個數字,所以它們全都走同一條路由、保持有序,而不同的流則落在不同的路由上。

要誠實面對它保證了什麼、又沒保證什麼。ECMP 只在平均意義上把流均勻分散;它是一個雜湊,不是一個排程器,所以它對每一條流有多大一無所知。兩條龐大的「大象」流可能雜湊到同一條脊鏈路上、在那裡相撞,而另外三台脊卻閒著——這種雜湊碰撞會花掉你實實在在的頻寬。這是樸素 ECMP 一個真實而眾所周知的弱點,而你之後會遇到的那些修補(更細粒度的 flowlet 切換、感知壅塞的負載平衡,以及在端點上刻意把一條連線拆到多條路徑上的多路徑 TCP)全都正是為了補上這個缺口而存在。

把它湊起來

把一個封包從頭追到尾,整個設計就豁然開朗。機架 A 裡的一台伺服器送往機架 C 的一台伺服器。封包升上葉 A;葉 A 看見好幾條成本相等、往上通到脊的路徑,便對這條流的五元組做雜湊,挑出,比方說,脊 2;封包橫越到脊 2;脊 2 把它向下轉發給葉 C;葉 C 把它交給目的伺服器。三跳、許多台可互換的脊之一,而同一機架的一條姊妹流,可能就在同一瞬間走了脊 4。這就是規模、韌性與負載分散,全在一趟行程裡。

  1. 一個機架裡的伺服器連到它們的葉(機架頂端)交換器——這是伺服器唯一插入的地方。
  2. 每一台葉都連到每一台脊;葉與葉之間從不相觸、脊與脊之間也從不相觸。
  3. 任何機架到機架的路徑都是「葉 -> 脊 -> 葉」,永遠是同樣短的跳數,每一台脊提供一條候選路徑。
  4. ECMP 對每一條流的標頭欄位做雜湊,把它釘在那些路徑之一上,將許多條流分散到整張架構上,同時讓每一條流保持有序。
  5. 需要更多頻寬?加脊。需要更多伺服器?加葉。你永遠不必更換一台巨大的核心交換器。