從美圖容器優化實踐談Kubernetes網路方案設計

weixin_33843409發表於2018-12-14

本文通過介紹美圖線上容器化的實踐經驗,包括線上遇到的實際問題,來探討 Kubernetes 環境下的網路方案設計。值得正在轉型 K8S 的架構師學習和借鑑。

李連榮,美圖高階系統研發工程師,曾建立支援千萬的長連線服務,從零開始在建立美圖的容器化服務,並主導完成美圖容器化的網路方案。在網路、儲存方面有非常深厚的造詣。

目前,我們的 Kubernetes 叢集選擇使用 Calico 作為基礎網路方案。


選擇 Calico 網路方案的挑戰

Calico 是一套基於路由(BGP)的 SDN,它通過路由轉發的方式實現容器的跨主機通訊。Calico 將每個節點虛擬為一個“路由器”併為之分配獨立的虛擬網段,該路由器為當前節點上的容器提供路由服務。

更多 Calico 專案介紹可參閱 https://www.projectcalico.org/ 下面以具體網路為例介紹其中的設計與難點。

以上圖為例,如果節點 192.168.1.2 分配的虛擬網段是 10.233.1.0/24,其上執行了一個容器 10.233.1.2,其路由資訊如下:

10.233.1.2 0.0.0.0 255.255.255.255 UH 0 0 0 cali814214d5913

當物理機收到目標地址為10.233.1.2 的IP 資料包時會轉發到網口 cali814214d5913,而 cali814214d5913 是通過 veth-pair 建立的網路卡,它與本機上 IP 地址為 10.233.1.2 的容器互通,因此,IP 地址為 10.233.1.2 容器就可以收到相應的 IP 資料包。

當位於節點 192.168.1.3 上的容器 10.233.2.2 給 10.233.1.2 傳送 IP 資料包時,需要知道 10.233.1.2 所在的物理節點的 IP,並新增以下路由規則:

10.233.1.0/16 192.168.1.2 eth0

Calico 通過 BGP 實現節點間相互學習路由規則。節點 192.168.1.2與節點192.168.1.3 建立 BGP 鄰居,節點 192.168.1.3 可以通過 BGP 學習到上面的路由規則。當容器 10.233.2.2 傳送 IP 資料包給 10.233.1.2 時,會根據上面的路由規則轉發到節點 192.168.1.2,並由節點 192.168.12 轉發給 10.233.1.2,進而實現容器的跨主機通訊。

但是當兩個容器所在的節點處於不同的子網時,如10.233.3.2,其所在的節點192.168.2.2與節點192.168.1.2處於不同的子網,此時無法在192.168.2.2上新增以下路由:

10.233.1.0/16 192.168.1.2 eth0

這是因為物理機 192.168.2.2與物理機 192.168.1.2 鏈路層不通。為了解決這個問題,Calico 選擇了 IPIP。IPIP 是將虛擬網路的 IP 資料包封裝到物理網路的 IP 資料包裡傳輸。啟用 IPIP 後,節點上會出現相應的虛擬網路卡,通常是 tunl0,節點1 92.168.2.2 可以新增以下路由規則:

10.233.1.0/16 192.168.1.2 tunl0

與之前的路由的區別在網口換成了 tunl0。當 10.233.3.2 傳送 IP 資料包給10.233.1.2 時,其所在的節點會將 IP 資料包轉發到網口 tunl0,轉發到 tunl0 的 IP 資料包會被 IPIP 驅動接管。IPIP 驅動會將每個 IP 資料包封裝到物理網路的 IP 資料包內(目標地址是下一跳地址,即 192.168.1.2,Payload 是虛擬機器發出的原始 IP 資料包)傳送出去。

由於該 IP 資料包的目標地址是節點 192.168.1.2,因此,可以經過物理閘道器進行轉發。執行在節點 192.168.1.2 上 IPIP 服務接收到該物理網路 IP 資料包後將其 Payload 取出,再根據節點 192.168.1.2 上路由規則轉發給相應的容器,進而實現了容器的跨子網通訊。

Calico 網路方案存在的問題

通過 Calico 的工作原理可以看出,Calico 存在以下問題:

  • 使用 IPIP 時,需要巢狀 IP 協議,多餘的打包和拆包動作會帶來的效能開銷。
  • 使用 IPIP 時,巢狀的 IP 協議頭導致實際有效的 MTU 長度變小,也會影響實際的頻寬利用率。
  • 由於叢集外的節點無法學習叢集內的路由資訊,故無法直接訪問叢集內的容器。

根據 Calico 的工作原理可知,Calico 為了解決容器的跨子網通訊選擇了 IPIP,也正是因為引入了 IPIP 才引發了一系列的效能問題,那麼,為什麼 Calico 會選擇 IPIP 協議呢?

為了理解這個問題,我們先看一下傳統的物理網路是如何解決跨子網通訊的。仍以上圖為例,物理機 192.168.2.3 訪問物理機 192.168.1.2 的步驟如下:

  • 物理機 192.168.2.3 檢測到目標 IP 與自己處於不同的子網,因此,通過預設路由規則傳送給其所在閘道器 192.168.2.1。
  • 物理閘道器 192.168.2.1 通過路由協議可以知道閘道器 192.168.2.1 可以轉發 IP 包給物理 192.168.1.2,因此,將相應的 IP 包轉發給閘道器 192.168.1.1。
  • 物理閘道器 192.168.1.1 再將 IP 包轉發給物理機 192.168.1.2。

我們再來看一下 Calico 網路。如果沒有 IPIP,容器 10.233.4.2 訪問容器 10.233.1.2 的步驟如下:

  • 宿主機 192.168.2.3 檢測到目標 IP 與自己處於不同的子網,因此,會將目標地址為 10.233.1.2 的 IP 包轉發給其所在閘道器 192.168.2.1。
  • 閘道器 192.168.2.1 沒有匹配的路由規則,因此 drop 該 IP 包並會返回目標不可達。

如果引入 IPIP,容器 10.233.4.2 訪問容器 10.233.1.2 的步驟如下:

  • 宿主機 192.168.2.3 匹配到路由規則 “10.233.1.0/16 192.168.1.2 tunl0”。
  • 宿主機 192.168.2.3 將該容器發出的 IP 包通過 tunl0 埠轉發到物理機192.168.1.2。
  • IPIP 驅動將容器發出的 IP 包(目標地址 10.233.1.2)作為物理網路 IP 包(目標地址 192.168.1.2)的 Payload 發出。
  • 宿主機 192.168.2.3 按照物理網路的傳輸方式將物理網路 IP 包傳送到宿主機 192.168.1.2。
  • 宿主機 192.168.1.2 上 IPIP 驅動接收到該 IP 包之後解包並將該 Payload 作為 IP 包轉發給容器 10.233.1.2。

因此,在物理閘道器不能為虛擬網路提供路由服務的前提下,Calico 選擇使用 IPIP 的方式來解決容器的跨子網通訊。

如果通過某種方法,讓物理閘道器能夠學習到 Calico 虛擬網路的路由規則,那麼物理閘道器就可以為虛擬網路提供路由服務,Calico 就可以在不引入 IPIP 的前提下實現容器的跨子網通訊,但是,Calico 為什麼沒有選擇這種方式呢?

這是因為 Calico 的主要應用場景是公有云,公有云具備以下特性:

  • 多數公有云廠商可以提供一個穩定的大二層環境,其內的主機可以工作在同一個子網段,也就不存在跨子網通訊的問題
  • 不是所有的公有云廠商都可以提供 BGP 路由學習的介面,如果雲廠商不提供 BGP 介面,Calico就無法同步虛擬網路路由規則給公有云

因此,在這個前提下,Calico 選擇 IPIP 方式是非常合理的。

對於私有云場景:

  • 不是所有的私有云環境都支援大二層,對 Calico 跨子網通訊的需求是非常強烈的。
  • 所有硬體(包括閘道器)都在可控範圍內,只要物理閘道器支援 BGP 協議,Calico 就可以同步路由規則給物理閘道器。

因此,對於私有云場景,通過讓 Calico 同步虛擬網路路由規則給物理閘道器的方式來解決跨子網通訊會是更好的選擇。

效能提升方案

其實 Calico 的文件中提到了一種同步路由規則給物理閘道器的方式,具體可以參考如下連結:

https://docs.projectcalico.org/v2.6/usage/external-connectivity

本文參考這種設計,將虛擬網路路由規則同步給物理閘道器,虛擬網路與物理網路建立 BGP 鄰居的方式有兩種:

  • 方案一:各節點分別物理閘道器建立 BGP 鄰居
  • 方案二:中心化元件與物理閘道器建立 BGP 鄰居

下面針對兩種方案進行分析。

方案一

如下圖所示,方案一需要每個 SDN 節點分別與其所在的物理閘道器建立 BGP 鄰居。由於每個 SDN 節點作為本機容器的閘道器,如果執行在該物理機上 Calico 服務能夠與物理閘道器建立 BGP,並將自己的路由規則同步給物理閘道器,那麼,物理閘道器就可以學習虛擬網路的路由規則。

執行在節點上的 SDN 服務可以通過編碼或者指令碼實現自動建立 BGP 鄰居的邏輯,但是,這也需要物理閘道器的支援。如果物理閘道器不支援自動建立 BGP 鄰居,僅僅是在 SDN 端實現是沒有意義的。

傳統的 BGP 路由器需要逐個配置 BGP 鄰居,這對容器化叢集來說是不可接受到。因為容器化叢集的規模一般都比較大,在叢集使用過程中也會經常的調整節點,如擴容、縮容、機器故障等。如果每次調整節點都需要運維手動完成 BGP 鄰居的配置,運維成本是巨大的,而且容易因誤操作而影響叢集穩定性,因此,需要閘道器支援自動建立 BGP 鄰居。

要實現自動建立 BGP 鄰居需要使用支援 Dynamic Neighbors 功能的路由器。傳統的路由器在配置 BGP 鄰居時需要指定明確的 IP 地址,而支援 Dynamic Neighbors 的路由器可以指定一個 IP 網段,該路由器可以自動接受指定網段內BGP裝置發起的建立鄰居的請求。

叢集部署前先配置好路由器的 Dynamic Neighbors,調整叢集節點時執行在節點上的 SDN 服務主動與之建立 BGP 鄰居,並將其所在節點的路由資訊同步給物理閘道器。例如,執行在物理機 192.168.1.2 上的 SDN 服務自動與物理閘道器 192.168.1.1 建立 BGP 鄰居(eBGP)並將以下路由規則同步給物理閘道器:

10.233.1.0/24 192.168.1.2 eth0

物理閘道器 192.168.1.1 學習到以上規則之後會通過 BGP 或者其它路由同步協議同步給物理閘道器 192.168.2.1,通過這種方式,整個物理網路都可以學習上以上規則。叢集的容器要傳送資料到 10.233.1.0/24 網段時,直接通過其宿主機將資料包傳送給物理閘道器,物理閘道器即可根據其學到的虛擬網路路由資訊轉發到目標主機,最後,再由目標主機轉發給相應的容器。

方案二

方案二需要引入中心化元件,本文稱之為 BGP Speaker,其網路結構如下:

BGP Speaker 執行在 SDN 叢集內,它負責收集 SDN 叢集內的路由資訊並通過BGP 同步給物理閘道器。由於該模組可以收集 SDN 叢集內的全部路由資訊,故不再需要每個 SDN 節點單獨與物理閘道器建立 BGP 鄰居,也就不再依賴物理閘道器的的 Dynamic Neighbors 功能。

如下圖所示,BGP Speaker 主要分為兩部分:observer 和 publisher。

  • observer:負責收集 SDN 叢集的路由資訊。
  • publisher:將 observer 收集到的路由資訊同步給物理閘道器。

Calico 將 SDN 叢集相關的資訊(包括配置資訊、每個節點的虛擬網段等)儲存在 etcd 裡,而 etcd 的支援 watch,因此,observer 可以通過watch方式實時獲取 Calico 叢集的節點資訊。observer 主要關注每個 SDN 節點劃分的虛擬網段資訊,並根據獲取到的虛擬網段資訊生成對應的路由規則,如節點 192.168.1.2 劃分的虛擬網段是 10.233.1.0/24,observer 會生成以下路由規則:

10.233.1.0/24 192.168.1.2 eth0

publisher 實現了 BGP 協議,它與物理閘道器建立 BGP 鄰居(eBGP),主要工作是將 observer 收集到的路由資訊同步給物理閘道器。由於 publisher 需要將不同節點的路由資訊同步給物理閘道器,因此,publisher 對物理閘道器來講,實際上是個 BGP Route Reflector。

gobgp 提供了開源的 BGP 協議庫,支援完整的 BGP 協議,可以作為 publisher的基礎庫。另外,BGP 是雙向的,publisher 不僅可以將自己持有的路由資訊同步給物理路由器,物理路由器也會同步路由資訊給 publisher,由於 SDN 不需要了解物理網路的路由資訊,因此,publisher 可以過濾掉這些路由資訊。

由於 BGP Speaker 是中心化部署的,因此 BGP Spaker 的高可用程度會直接影響 SDN 叢集的穩定性。BGP Speaker 直接從 SDN 叢集的 etcd 儲存中獲取叢集資訊,其自身不需要儲存任何資料(包括生成的路由資訊),故 BGP Speaker是無狀態的,因此,只需要在 SDN 叢集內部署多套 BGP Speaker 即可實現高可用。

多套 BGP Speaker 之間相互不感知,各自獨立執行。部署 BGP Speaker時,還可以根據物理網路的拓撲結構將 BGP Speaker 部署在不同的機架或者機房,保證某個機架或機房出現故障時 SDN 叢集仍可正常工作。

安全隱患

以上兩種方案都可以實現物理網路與虛擬網路的互通,但兩種方案都是虛擬網路通過 BGP 協議自動的同步路由規則給物理閘道器,如果虛擬網路產生了錯誤的路由規則或者產生的路由規則與物理網路衝突,就會影響叢集所處物理網路的執行狀態;如果同一物理網路內部署了多套 SDN 叢集且這多套 SDN 的網段有衝突時,也會造成虛擬網路的路由規則紊亂,影響 SDN 的穩定性。

因此,還需要採取一些措施來保證 SDN 之間以及 SDN 與物理網路之間不衝突。

確保 SDN 與物理網路不衝突

現在的物理閘道器大多都支援路由過濾,也就是說當閘道器通過 BGP 學習到新的路由規則時,可以根據一定的規則進行過濾,只有滿足要求的路由規則,才會同步到自己的路由表。部署容器化叢集時,應先規劃好 SDN 的網路地址,要保證 SDN 的網路地址與物理網路不衝突。

配置物理閘道器時,可以通過設定過濾規則來保證 SDN 不能修改其所屬網段以外的任何任何路由規則(例如,限制 SDN 只能更新目標地址處於 10.233.0.0/16 網段內的路由規則),通過這種方式,可以確保 SDN 不會影響物理網路穩定性。

確保 SDN 之間不衝突

物理網路與虛擬網路互通後,部署在同一個物理網路內的多個 SDN 叢集的路由資訊都會同步到物理閘道器,如果不同 SDN 的網路地址有重疊,就會引發衝突,導致 SDN 網路無法穩定工作,因此,在部署 SDN 叢集時,還需要為不同的 SDN 叢集分配不同的網路地址(這些地址都與物理網路不衝突)。配置閘道器時,限制每個 SDN 只能同步處於自己網路地址內的路由規則。

總結

Calico 存在效能問題的根本原因是物理網路與虛擬網路不互通,本文設計了兩種方案實現物理網路與虛擬網路的互通,解決了 Calico 引入 IPIP 和 NAT 帶來的效能問題,也帶來了一定的安全隱患,但是,可以通過額外的保障措施排除安全隱患,保證物理網路與 SDN 網路的穩定執行。

本文轉自kubernetes中文社群-從美圖容器優化實踐談Kubernetes網路方案設計

相關文章