vatt'ghern jaskier's ballads
本文 1 個互動圖表在手機上以重點摘要呈現,互動版請以桌面瀏覽器開啟。

2011 年的 vServer 只有原生 Linux bridge 與靜態路由,每台 host 最多 1 Gb/s,最後大約跑了 25,000 個 instance。到了 2026 年,Hetzner 的 Open vSwitch 資料面服務超過一百萬台雲端伺服器,而團隊想用更專門的網路堆疊,來改善它的擴展性、韌性與彈性。

Hetzner Cloud 網路堆疊的演進史

這篇文章的作者是 Hetzner 的 SDN 團隊主管 Maximilian Wilhelm,發表於 2026 年 10 月 8 日,是一個系列的第一部分,講 Hetzner Cloud 網路堆疊的演進。範圍收在雲端伺服器與虛擬化主機(VM host);負責管理所有伺服器、Load Balancer 與 Private Networks 的 Cloud Control Plane,這一篇明說不談。

本文照時間走。每一段回答同一個問題:上一步做完,團隊接著得做什麼。年表在前,接著是 OVS 之上長出的零件與限制,最後是原文還沒展開的下一步。

drag the handle along the timeline · 7 events from 2011 to 2021

2011 vServer 2015.09 BGP · 1:1 NAT 2016 2x 10 Gb/s 2018 Cloud 上線 · 無 NAT 2019.07 OVS · VXLAN 2020.11 公網遷入 OVS 2021.03 stateful 防火牆 2011
2011:vServer 上線。HDD、原生 Linux bridge、靜態路由,雙協定連線每台 host 最多 1 Gb/s,最後大約跑了 25,000 個 instance。

橋接與靜態路由,到 BGP 與 1:1 NAT

2011 年上線的 vServer 是起點:HDD、原生 Linux bridge、靜態路由,雙協定(dual-stack)連線,每台 host 最多 1 Gb/s。這套設計最後撐了大約 25,000 個 instance。靜態路由的每條路徑都由人事先寫好,下一步動的正是路由。

2015 年 9 月上線的後繼產品是 hyper-converged、以 Ceph 為基礎的架構,網路換了三件事:路由改成透過 BGP 的動態路由;host 內仍是 Linux bridge,IPv4 做 1:1 NAT;IPv6 則是完整路由的前綴(fully routed prefixes)。原文給的理由只有一句:IP 更容易搬動,host 維護也不必影響客戶。這是年表上的第一個轉折,路由從人事先寫好,變成由 BGP 動態維護。2016 年,連到 host 的鏈路升級為 2x 10 Gb/s,這套架構一路撐到 2018 年,長到約 50,000 個 instance。

這一代有個不對稱:IPv4 做 1:1 NAT,IPv6 卻是完整路由的前綴,同一套網路上兩個協定走兩種模型。兩個規模數字也分屬兩個產品,都是各自架構的收尾描述:舊的收在約 25,000,新的到 2018 年約 50,000,兩者不構成同一條成長曲線。鏈路從 1 Gb/s 到 2x 10 Gb/s,量級上跳了一級,兩個數字的測量口徑是否相同,原文沒有交代。

2018 年 Hetzner Cloud 上線,網路只改了一件事:放棄 NAT,改為直接的 IPv4 路由。1:1 NAT 讓每個 instance 的內外位址不同,直接路由把這層對應拿掉。IPv4 從此和 IPv6 走同一種模型。

2019 年 7 月,資料面換成 Open vSwitch

第二個轉折的日期很具體:2019 年 7 月,團隊導入以 Open vSwitch(OVS)為基礎的資料面,同時支援以 VXLAN 封裝的私有雲端網路。OVS 是新底座,Private Networks 是第一個蓋在上面的功能。每個私有網路有一個獨一無二的 Virtual Network Identifier(VNI);VXLAN 把二層封包包進 UDP,VNI 就是標頭裡區分網路的欄位,這一段是通用背景。

2018 年放棄 NAT 與 2019 年換底座,原文都只寫了動作,沒有寫動機。年表因此有日期、有順序、有結果;下文談到原因時,用的是原文明寫的設計取向。

賭注看得出來。底座換在已經對外服務的產品線下面:從 Cloud 在 2018 年上線,到導入 OVS 不到兩年。順序是 2019 年 7 月 OVS 先承載新增的私有網路,2020 年 11 月才「開始」遷移公網,間隔 16 個月;「開始」兩個字暗示那時公網還走舊路徑。新功能先上,既有流量後搬,每一步的代價都比上一步大。

這 16 個月裡,OVS 承載的只有私有網路,公網仍在舊路徑上,也就是說 Hetzner 同時維運兩套資料面。這是從「開始遷移」四個字讀出來的,遷移期間兩者如何並存,原文沒有描述。2020 年 11 月之後,公網才一塊一塊進入 OVS,防火牆要等到這一步走完才上線。

公網遷完,2021 年 3 月,團隊推出建立在 OVS flows 與 netfilter 之上的 stateful cloud firewall。原文的因果寫得直接:公網遷到 OVS,讓團隊得以推出這個產品。

drag the divider to compare 2018 with 2019 to 2021 · 2 layers

2018 · Hetzner Cloud 上線 VM IPv4 直接路由 不再做 NAT 這一格之前的資料面細節, 文章沒有展開。 2019 至 2021 · OVS 資料面 2019.07 · Private Networks VXLAN 封裝,每網路一個 VNI 2020.11 起 · 公網遷入 OVS 完成日期文章未載明 2021.03 · stateful 防火牆 OVS flows 加 netfilter

互動圖表

2018 年前是無 NAT 的直接路由;2019 到 2021 年間,OVS 資料面依序承接 VXLAN 私有網路、公網與 stateful 防火牆。

從 2019 年 7 月到 2021 年 3 月約 20 個月,OVS 依序接手新增的私有網路、既有的公網,最後是壓在公網路徑上的防火牆。每接手一塊,需要的零件就多一樣。

底座換了,flows 要有人寫

OVS 的資料路徑由 OpenFlow 的 flows 驅動。原文把 OpenFlow 定義為一個為了設定網路裝置資料面而生的開放協定(an open protocol built to configure the data plane of network devices)。flows 是一條條規則,OVS 依規則決定每個封包的去向,所以寫規則與轉送封包是兩份工作。2019 年底座一換,寫規則這份工作就落到團隊自己身上。

常見的選擇是 OVN。團隊決定不走這條路,自己做了 Flusskrebs。原文給的是一般立場,團隊相信自己擁有、自己能操作的方案,沒有針對 OVN 另給理由。虛擬化同樣如此:KVM 搭配自製工具(custom-built tooling),沒有採用現成平台。自製的另一面是維護由自己扛,原文沒有討論,這一點是推斷。

Flusskrebs 用 Python 寫成,提供 REST API,給 Cloud control plane 在 host 本地的那一部分呼叫,替每台 VM 或 Load Balancer 設好正常運作所需的 flows。私有網路介面的 DHCP 也歸它,它是各 host 上的中央 DHCP 伺服器。

這讓 Flusskrebs 成為兩層之間的接縫:上面是 Cloud control plane 的 host 本地部分,只需要呼叫 REST API;下面是 OVS,只需要執行 flows。每台 VM 或 Load Balancer 要動哪些 flows,由 Flusskrebs 決定。防火牆建在 OVS flows 之上,所以防火牆規則最後也要經過這個接縫,這是把兩段說法接起來的讀法。

把規則的編排放在 host 上,與作者前段交代的取向一致:「the moving parts are on the hosts」,而不是一套聰明而複雜的實體網路。實體網路只給 host 穩定的 IP 連線;防火牆、Private Networks、基礎設施服務都由 host 提供。這樣一來,2019 年之後每加一項功能,動的是 host 這一側,實體網路維持原樣。代價在另一側:每台 host 都得完整攜帶這些功能,更新一項就要碰每一台。

一台 VM host 要提供的東西,原文列得很清楚:連到網際網路、Private Networks、cloud Firewalls、給 IPv4 設定用的 DHCP 伺服器,還有位址 169.254.169.254 的 metadata 伺服器,cloud-init 與其他工具都會去問它。DHCP 與 metadata 都跑在每台 VM host 本機,理由是追求最大的韌性與可用性:開機後的伺服器問路、問自己是誰,答案來自同一台實體 host,不必跨網路找中央服務。公網介面的 DHCP 是每台伺服器專屬的服務,只負責那台伺服器,跑在獨立的 Linux network namespace。

本機服務的選擇也有它的邏輯。伺服器開機後最先需要的兩樣東西,是 IPv4 設定和 metadata,原文把兩者都放在本機,給的理由是最大的韌性與可用性。具體在哪種失效情境下這個設計會救場,原文沒有列舉;能確定的是,這兩項服務的可用性只跟那一台 host 綁在一起。

唯一落在 host 之外的是 Cloud Control Plane,也就是管理所有伺服器、Load Balancer 與 Private Networks 的那一層,原文開頭就說這一篇不談。呼叫 Flusskrebs 的 host 本地部分和中央部分如何分工,這篇看不到。

click a component to read what it owns · 5 components

上行鏈路 LACP bond 遷移中:BGP 不直連 中央 OVS bridge OpenFlow flows 處理資料路徑 伺服器介面 1 個公網介面 加最多 3 個私有 Flusskrebs 安裝 flows network namespace 服務

上行鏈路 · 實體網路的入口

多數 host 用兩條 10 Gb/s 的上行鏈路組成 LACP bond,團隊已開始遷移到兩條以 BGP 路由的原生上行。上行介面不直接接在 OVS bridge 上。

實體網路的分工:提供穩定的 IP 連線給 host,其餘功能不放在這一層。

中央 OVS bridge · 資料路徑

每台伺服器的介面都接在這座 bridge 上,資料路徑的大部分由 OpenFlow 的 flows 處理。防火牆建立在 OVS flows 與 netfilter 之上。

邊界:bridge 只執行 flows,flows 從哪裡來由 Flusskrebs 決定。

伺服器介面 · 公網與私有

每台伺服器最多一個公網介面,另加最多三個私有介面,全部接在中央 OVS bridge。私有網路以 VXLAN 封裝,每個私有網路有獨一無二的 VNI。

Flusskrebs · 自製編排器

以 Python 寫成,提供 REST API 給 Cloud control plane 在 host 本地的部分使用。設定每台 VM 或 Load Balancer 所需的 flows,並兼任私有網路介面的中央 DHCP 伺服器。

取捨:文章只說團隊決定不採用 OVN;合理的推測是,這換來自己能操作的方案,也由自己承擔維護。

network namespace 服務 · 每台伺服器的基礎設施

每台伺服器專屬的基礎設施服務,例如公網 DHCP 伺服器,跑在獨立的 Linux network namespace 裡。DHCP 與 metadata 兩個服務都跑在每台 VM host 本機。

上行鏈路分現況和方向。多數 host 是兩條 10 Gb/s 綁成 LACP bond;團隊已開始遷移到兩條各自以 BGP 路由的原生上行,每條上行自己成為一條路由。2015 年那一代已經用 BGP 做動態路由,到這裡 BGP 又往 host 的上行延伸了一步。要注意的是接法:上行介面不直接接在 OVS bridge 上。推斷:host 自身的可達性因此不依賴 OVS,OVS 出問題時 host 仍連得到、仍能維運。另一個對照是速度:2016 年的 2x 10 Gb/s,和今天多數 host 的兩條 10 Gb/s 數字相同,變的是路由方式,從 bond 走向 BGP;兩者是否為同一批鏈路,原文沒有說。

私有網路的另一端,是前面提到的 VNI。每個私有網路一個 VNI,而每台伺服器最多接三個私有介面,所以一台伺服器同時所屬的私有網路數目有上限。這是把兩個數字放在一起得到的推算,原文沒有直接寫出,也沒有說明三這個數字的由來。

再往上,每台伺服器最多一個公網介面、另加最多三個私有介面,全部接在中央 OVS bridge。三個私有介面的上限是原文給的數字,沒有附理由。

click any layer to read where the host stops depending on it · 4 layers

從實體網路往上,host 依賴的四層

從實體網路往上,host 依賴的四層 L4 · 每台伺服器的服務 公網 DHCP 等服務在獨立的 network namespace L3 · 中央 OVS bridge 所有伺服器介面接在這裡,flows 決定資料路徑 L2 · 上行鏈路 兩條 10 Gb/s 的 LACP bond,遷移中改為兩條 BGP 路由的上行 L1 · 實體網路 只提供穩定的 IP 連線給 host

L1 · 實體網路

文章的設計取向把複雜度推離這一層:實體網路只負責穩定的 IP 連線,防火牆、Private Networks 與基礎設施服務都不在這裡。

L2 · 上行鏈路

多數 host 是兩條 10 Gb/s 的 LACP bond,已開始遷移到兩條以 BGP 路由的原生上行。上行介面不直接接在 OVS bridge 上。

合理的推測是,這讓 host 的可達性不必經過 OVS。

L3 · 中央 OVS bridge

每台伺服器最多一個公網介面與三個私有介面接在這座 bridge 上。防火牆與私有網路的功能都在這一層以 flows 實作。

L4 · 每台伺服器的服務

公網 DHCP 伺服器只負責伺服器的公網介面,跑在獨立的 network namespace。DHCP 與 metadata 都在每台 VM host 本機執行,追求最大的韌性與可用性。

四層並排來看:實體網路只給 IP 連線,上行鏈路在 LACP bond 與 BGP 之間遷移,bridge 承載全部伺服器介面與 flows,服務各住各的 network namespace。被刻意切開的一條邊,在上行與 OVS bridge 之間。

2021 年:防火牆上線,連線追蹤要有上限

bridge 上的 flows 不只轉送,也驗證。出站方向,flows 檢查封包是不是從該伺服器合法的身分發出;原文舉的規則只放行帶有與該伺服器相關聯的 IPv4 或 IPv6 位址的封包。入站方向,host 在 VM 眼中是一個固定的虛擬閘道 MAC(virtual gateway MAC)。下面的分頁依序展開這兩項,以及第三項:連線追蹤上限。

switch tabs to follow a packet through three checks · 3 tabs

封包從伺服器的介面進入 bridge 時,flows 檢查它的來源。文章舉的規則是:只放行帶有與該伺服器相關聯的 IPv4 或 IPv6 位址的封包。

合理的推測是,這一關擋的是偽造來源位址;文章只給了規則本身,沒有說明它要防範的情境。

入站方向,host 對 VM 表明身分的方式是一個固定、約定好的虛擬閘道 MAC(virtual gateway MAC)。

文章在這裡只交代了這個 MAC 的存在,沒有展開 VM 如何使用它。

連線追蹤表是全域共用的,所以每台伺服器有一個上限:最多 80,000 條活躍的並行連線。目的是避免表被塞滿,並讓所有伺服器與客戶公平使用。

合理的推測是,stateful 防火牆需要記住連線狀態,這個上限與 2021 年 3 月的防火牆產品有關;文章沒有把兩者直接連起來。

這兩項檢查發生的位置,各有一個細節。出站的規則看的是伺服器自己帶的位址,入站的設計讓 VM 面對的永遠是同一個虛擬閘道 MAC,也就是 host 在 VM 眼中是固定的下一跳。兩者都由 host 上的 bridge 執行,沒有一項要求實體網路出力,這與前面「實體網路只給穩定的 IP 連線」的取向是同一條線。

連線追蹤上限,是 2021 年 3 月那個 stateful 防火牆帶出來的限制。stateful 表示防火牆要記住每條連線,記憶住在 netfilter 的連線追蹤表裡。這張表是全域共用的,所以每台伺服器最多 80,000 條活躍的並行連線,由 ctcount 強制執行,單位是連線數,不是速率。原文沒有把防火牆與這個上限直接連起來,兩者的關係是我們把年表和機制接在一起的讀法。

上限的推理可以走一遍。所有伺服器的連線記錄住在同一張表裡,若沒有額度,任何一台的連線數暴增,都會擠占別人的空間。原文給了兩個目的:避免表溢位,保護的是表本身;讓所有伺服器與客戶公平使用,保護的是其他人。ctcount 把這兩個目的化成每台伺服器一個數字,所以一台被攻擊或失控的 VM 用完的,是自己的額度。超過上限時封包如何處理,原文沒有描述。

一百萬台之後,下一套堆疊

回頭看,這篇裡的零件幾乎都落在 host 上:flows 在 host 的 bridge 上,Flusskrebs 在 host 本地,DHCP 與 metadata 在每台 VM host 本機,連線額度按台算。原文給的限制數字也都以單一伺服器為單位:最多一個公網介面、最多三個私有介面、每台 80,000 條連線。

把年表上的轉折再排一次,host 這個單位一直都在。2015 年的理由是 host 維護不必影響客戶;2019 年 flows 裝在 host 的 bridge 上;2020 年 11 月公網遷進同一座 bridge;2021 年 3 月防火牆的狀態記在 host 的連線追蹤表裡。每一次換的東西不同,落點都是 host。

這是同一個取向的兩面。作者說,把 moving parts 放在 host 上,能把「大多數」錯誤的影響範圍限制在單一 host(blast radius to a single host),括號裡的 (most) 是他自己加的限定。實體網路因此可以保持簡單,只負責穩定的 IP 連線,上行鏈路也刻意不接 OVS bridge。

收尾是一句帶分寸的評價。作者說這個以 OVS 為基礎的資料面「has served us well and mostly still does today」,即使面對超過一百萬台雲端伺服器;隨後寫團隊想用更專門的網路堆疊,改善擴展性(scalability)、韌性(resiliency)與彈性(flexibility)。「mostly」是作者自己留的縫。三個面向原文一項都沒有量化,也沒有指出吃緊的是 flows、Flusskrebs,還是那張共用的連線追蹤表。

年表上有兩次大換:2015 年路由變動態,2019 年資料面換成 OVS;現在宣告的是第三次,內容留給系列的後續篇章。複雜度會不會繼續放在 host,原文沒說。

這是系列的第一部分,作者也把 Cloud Control Plane 排除在範圍之外。所以讀完這篇,手上有的是 host 這一側從 2011 年到現在的完整輪廓;host 之外的那一層,以及取代 OVS 資料面的堆疊會長成什麼樣子,都要等後續篇章。

如果你的零件也放在 host 上,這個取向的帳單可以對著自家環境查一遍。這份清單全部來自原文已寫明的位置:額度的執行者、失敗的邊界、上行的接法,沒有一項需要猜 Hetzner 內部沒公開的設計。連線追蹤表或同類的共用狀態表,是全域共用還是按租戶分開,額度由誰在哪裡執行;Hetzner 的答案是 ctcount 按台計。影響範圍停在哪,Hetzner 寫的是大多數錯誤停在單一 host,DHCP 與 metadata 就為此留在本機,你的是在本機還是在中央服務。最後是管理路徑有沒有穿過資料面:Hetzner 讓上行不直接接 OVS,你的 host 在資料面壞掉時還連得到嗎。

What to check:找出自家共用表的每租戶額度寫在哪裡,以及管理路徑有沒有經過資料面;Hetzner 這兩題的答案是 ctcount,和不接 OVS 的上行。