邁入 Cilium+BGP 的雲原生網絡時代

語言: CN / TW / HK

This post also provides anEnglish version.

本文是我們的前一篇博客 Trip.com: First Step towards Cloud Native Networking 的後續,介紹自上一篇博客以來我們在基於 Cilium 的雲原生網絡和雲原生安全方面的一些 探索和實踐。

1 網絡演進:簡要回顧

從 2013 到 2018 年,我們經歷了物理機到虛擬機再到容器的基礎設施演進,但網絡技 術棧基本都是沿用 Neutron+OVS —— 即使對我們(前期)的 Kubernetes 集羣也是 如此。但業務開始往 Kubernetes 遷移之後,這套 Neutron+OVS 的網絡方案越來越捉襟見肘, 尤其是在部署密度更高、規模更大的容器面前,這種大二層網絡模型的軟件和硬件瓶頸暴露無遺 [1]。

為了解決這些問題,更重要地,為了滿足雲原生業務的各種需求(例如,支持Kubernetes 的 Service 模型),我們調研了很多較新的網絡方案,綜合評估之後,選擇了 Cilium+BGP 的組合 [3]。

Fig 1-1. Networking solutions over the past years [2]

Cilium+BGP 方案 2019 年底正式在我們生產環境落地,我們打通了 Cilium 網絡和現有網 絡,因此能灰度將容器從 Neutron 遷移到 Cilium。

2 雲原生網絡實踐

作為 Cilium 的早起用户之一,我們對 Cilium 的實現和部署做了一些修改或定製化,以使 這套方案能平滑地落地到我們現有的基礎設施之中,例如 [2],

  1. docker-compsoe + salt 來部署,而不是採用默認的 daemonset+configmap 方式。

    這樣每台 node 上的 cilium-agent 都有獨立配置,我們能完全控制發佈灰度,將 變更風險降到最低。

  2. BIRD 作為 BGP agent,而不是採用默認的 kube-router

    kube-router 開箱即用,但缺少對 ECMP、BFD 等高級功能的支持,不符合我們生產環境的要求。

  3. 為了保證某些業務的平滑遷移,我們開發了 StatefulSet/AdvancedStatefulSet 固定 IP 的支持(需要 sticky 調度配合)。

  4. 定製化了監控和吿警。

  5. 其他一些自定義配置。

我們之前的一篇文章 [2] 對此有較詳細的介紹,有興趣可以移步。 下面討論幾個之前介紹較少或者沒有覆蓋到的主題。

2.1 BGP 建連模型

Cilium+BIRD 方案中,以宿主機為界,網絡可以大致分為兩部分,如圖 2-1 所示,

Fig 2-1. High level topology of the Cilium+BGP solution [2]

  1. 宿主機內部網絡 :由 Cilium(及內核協議棧)負責,職責包括,
    • 為容器創建和刪除虛擬網絡。
    • 為容器生成、編譯和加載 eBPF。
    • 處理同宿主機內容器之間的網絡通信。
  2. 跨宿主機網絡 :由 BGP(及內核路由模塊)負責,職責包括,
    • 與數據中心網絡交換路由(PodCIDRs)。
    • 對出宿主機的流量進行路由。

對於跨宿主機部分,需要確定要採用哪種 BGP peering 模型,這個模型解決的問題包括 :

  1. BGP agent 的職責,是作為一個全功能路由控制服務,還是僅用作 BGP speaker?
  2. 宿主機和數據中心的哪些設備建立 BGP 鄰居?
  3. 使用哪種 BGP 協議,iBGP 還是 eBGP?
  4. 如何劃分自治域(AS),使用哪種 ASN(自治域系統編號)方案?

取決於具體的網絡需求,這套 BGP 方案可能很複雜。基於我們數據中心網絡能提供的能力 及實際的需求,我們採用的是一種相對比較簡單的模型,

  1. 每台 node 運行 BIRD,僅作為 BGP speaker,
    1. Node 在上線時會自動分配一個 /25/24 的 PodCIDR。
    2. BIRD 和數據中心網絡中的兩個鄰居建立 BGP 連接。
    3. BIRD 將 PodCIDR 通吿給鄰居 ,但 不從鄰居接受任何路由
  2. 數據中心網絡只從 node 接受 /25/24 路由宣吿,但不向 node 宣吿任何路由。
  3. 整張網絡是一張三層純路由網絡(pure L3 routing network)。

這種模型的簡單之處在於,

  1. 數據中心網絡從各 node 學習到 PodCIDR 路由,瞭解整張網絡的拓撲,因此 Pod 流量在數據中心可路由。
  2. Node 不從數據中心學習任何路由,所有出宿主機的流量直接走宿主機默認路由(到數據 中心網絡),因此宿主機內部的路由表不隨 node 規模膨脹,沒有路由條目數量導致的性能瓶頸。

Fig 2-2. BGP peering model in 3-tier network topology

在路由協議方面,

  • 老數據中心基於“接入-匯聚-核心”三級網絡架構,如圖 2-2 所示,
    • 節點和核心交換機建立 BGP 連接。
    • 使用 iBGP 協議交換路由。
  • 新數據中心基於 Spine-Leaf 架構,
    • 節點和直連的 Leaf 交換機(置頂交換機)建立 BGP 連接。
    • 使用 eBGP 協議交換路由。

我們已經將這方面的實踐整理成文檔,見 Using BIRD to run BGP [3]。

2.2 典型流量轉發路徑:從 Pod 訪問 Service

來看一下在這套方案中,典型的流量轉發路徑。

假設從一個 Pod 內訪問某個 Service,這個 Service 的後端位於另一台 Node,如下圖所示,

Fig 2-3. Traffic path: accessing Service from a Pod [4]

主要步驟:

  1. 在 Node1 上的 Pod1 裏面訪問某個 Service ( curl <ServiceIP>:<port> )。
  2. eBPF 處理 Service 抽象,做客户端負載均衡 :選擇某個後端,然將包的目的 IP 地址從 ServiceIP 換成後端 PodIP(即執行 DNAT)。
  3. 內核路由決策 :查詢系統路由表,根據包的目的 IP 地址確定下一跳;對於這個例 子匹配到的是默認路由,應該通過宿主機網卡(或 bond)發送出去。
  4. 包到達宿主機網卡(bond),通過默認路由發送到宿主機的默認網關(配置在數據中心 網絡設備上)。
  5. 數據中心網絡對包進行路由轉發 。由於此前數據中心網絡已經從各 Node 學習到了 它們的 PodCIDR 路由,因此能根據目的 IP 地址判斷應該將包送到哪個 Node。
  6. 包達到 Node 2 的網卡(bond):一段 eBPF 代碼負責提取包頭,根據 IP 信息找到 另一段和目的 Pod 一一對應的 eBPF 代碼,然後將包交給它。
  7. 後一段 eBPF 代碼對包執行 入向策略檢查 ,如果允許通過,就將包交給 Pod4。
  8. 包到達 Pod4 的虛擬網卡,然後被收起。

我們有一篇專門的文章詳細介紹整個過程,見 [4]。

2.3 集羣邊界 L4/L7 入口解決方案

在 Kubernetes 的設計中, ServiceIP 只能在集羣內訪問 ,如果要 從集羣外訪問 Service 怎麼辦? 例如,從 baremetal 集羣、OpenStack 集羣,或者其他 Kubernetes 集羣訪問?這屬於集羣邊界問題。

K8s 為這些場景提供了兩種模型:

  1. L7 模型 :稱為 Ingress,支持以 7 層的方式從集羣外訪問 Service,例如通過 HTTP API 訪問。
  2. L4 模型 : 包括 externalIPs Service、LoadBalancer Service,支持以 4 層的方 式訪問 Service,例如通過 VIP+Port。

但是, K8s 只提供了模型,沒提供實現 ,具體的實現是留給各廠商的。例如,假如你使 用的是 AWS,它提供的 ALB 和 ELB 就分別對應上面的 L7 和 L4 模型。在私有云,就需要 我們自己解決。

我們基於 Cilium+BGP+ECMP 設計了一套四層入口方案。本質上這是一套四層負載均衡器( L4LB),它提供一組 VIP,可以將這些 VIP 配置到 externalIPs 類型或 LoadBalancer 類 型的 Service,然後就可以從集羣外訪問了。

Fig 2-4. L4LB solution with Cilium+BGP+ECMP [5]

基於這套四層入口方案部署 istio ingress-gateway,就解決了七層入口問題。從集羣外訪 問時,典型的數據轉發路由如下:

Fig 2-5. Traffic path when accesing Service from outside the Kubernetes cluster [5]

我們之前有篇博客詳細介紹這個主題,見 [5]。

3 雲原生安全嘗試

Cilium 提供的兩大核心能力:

  1. 基於 eBPF 的靈活、動態、高性能網絡。
  2. L3-L7 安全策略:CiliumNetworkPolicy 是對 K8s 的 NetworkPolicy 的擴展。

在落地了網絡功能後,針對安全需求,我們在嘗試落地基於 Cilium 的安全。

3.1 Cilium 安全策略

首先來看一個簡單的例子,看看 CiliumNetworkPolicy (CNP) 長什麼樣 [6]:

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "clustermesh-ingress-l4-policy"
  description: "demo: allow only employee to access protected-db"
spec:
  endpointSelector:
    matchLabels:
      app: protected-db
  ingress:
  - toPorts:
    - ports:
      - port: "6379"
        protocol: TCP
    fromEndpoints:
      - matchLabels:
          app: employee

上面的 yaml:

  1. 創建一個 CNP,可以指定 namedescription 等描述字段。
  2. 對帶 app=protected-db 標籤(labels)的 endpoints(pods)執行這個 CNP。
  3. 在執行 CNP 的時候,只對入向( ingress )流量做控制,並且限制如下流量來源:
    • 協議是 TCP ,並且端口是 6379 .
    • 流量來自帶 app:employee labels 的 endpoints(pods)。

可以看到,CNP 非常靈活,使用起來也很方便。但真實世界要遠比想象中複雜,要真正落地 Cilium 安全策略,還存在很多挑戰。

3.2 落地挑戰

下面舉兩個例子,相信這些問題在很多公司都需要面對,並不是我們獨有的。

多集羣問題

如果你所有的應用都運行在 Cilium 集羣中,並且客户端和服務端都收斂到一個集羣(大部 分公有云廠商都推薦一個 region 只部署一套 K8s 集羣,所有訪問都收斂到這套集羣),那落 地起來會簡單很多。

但大部分有基礎設施演進的公司恐怕都不滿足這個假設,實際的情況很可能是:業務分散在多 個集羣。

混合基礎設施

多集羣還不是最大的問題,因為業界多少還有一些多集羣解決方案。

更嚴重的一個問題是:業務不僅分散在不同集羣,而且在不同平台。例如對我們來説,現在 有:

  1. Bare metal 集羣
  2. OpenStack 集羣
  3. 基於 Neutron+OVS 的 Kubernetes 集羣
  4. 基於 Cilium+BGP 的 Kubernetes 集羣

雖然我們計劃將所有容器從 Neutron 網絡遷移到 Cilium 網絡,但另外兩種,bare metal 和 OpenStack 集羣,還是會長期存在的,雖然規模可能會逐漸減小。

3.3 整體方案設計

我們目前的一個整體方案: 在服務端容器做入向安全策略,客户端可以來自任何平台、任何集羣

  1. 這將範圍框定到了 已經在 Cilium 網絡的服務端容器 ,是一個不錯的起點。
  2. 傳統網絡裏的服務端容器,會逐漸遷移到 Cilium 網絡。
  3. BM 和 VM 的 服務端實例 ,第一階段先不考慮安全控制。

那接下來的問題就是:服務端如何具備 對所有類型、所有集羣的客户端進行限制的能力 ? 我們的解決方案是:

  1. 首先,用 Cilium 提供 ClusterMesh 將已有 Cilium 集羣連接起來;
  2. 然後,“擴展” ClusterMesh,讓它能感知到 mesh 之外的 endpoints,即 BM、BM 和 Neutron Pods。

下面分別解釋這兩點。

3.3.1 用 ClusterMesh 做 Cilium 集羣互連

Fig 3-1. Vanilla Cilium ClusterMesh [6]

ClusterMesh [7] 是 Cilium 自帶的一個多集羣解決方案。如果所有應用都在 Cilium 集羣 裏,那這種方式可以解決跨集羣的安全策略問題,即,application 實例可以分佈在不同的集羣。

這樣説來,使用 ClusterMesh 似乎是理所當然的,但其實它並不是我們當初的第一選擇。 因為多集羣還有其他方案,本質上做的事情就是如何在多個集羣之間同步元數據,並且做到 集羣變動的實時感知。

  1. 出於多個內部需求,當時有考慮自己做這樣一套元數據同步方案,它能解決包括 Cilium 在內的多個需求。
  2. 並未看到業界大規模使用 ClusterMesh 的案例,所以對它的可用性還存疑。

但後來綜合對比了幾種選項之後,覺得 ClusterMesh 還是值得嘗試的。

關於 ClusterMesh 的實地(功能)測試,可以參考我們之前的一篇博客 [6]。

3.3.2 擴展 ClusterMesh,感知 mesh 外實例

這裏的外部實例(external endpoints)包括 Neutron Pod、VM、BM。

基於對 Cilium 的理解,我們判斷只要 將外部實例信息以 Cilium 能感知的方式(格式)同 步到 Cilium 集羣 ,那在入向(inbound),Cilium 對這些實例的控制能力,與對原生 Cilium 實例的控制能力並無區別。換句話説,我們“騙一下” Cilium,讓它認為這些實例都是 Cilium endpoints/pods。

為此我們開發了一個組件,使得 OpenStack 平台、Bare metal 平台和Neutron-powered Kubernetes 平台能將它們的實例創建/銷燬/更新信息同步更新到 Cilium 集羣,如下圖所示:

Fig 3-2. Proposed security solution over hybrid infrastructures

結合 3.3.1 & 3.3.2,在這套“擴展之後的” ClusterMesh 中, 每個 Cilium agent 都對 全局實例(container/vm/bm)有着完整的、一致的視圖 ,因此能在 Cilium Pod 的入向 對各種類型的客户端做安全控制。目前計劃支持的是 L3-L4 安全策略,未來考慮支持 L7。

這套方案已經通過了功能驗證,正在進行正式開發和測試,計劃年底開始灰度上線。

4 總結

本文總結了我們自上一篇博客以來,在基於 Cilium 的雲原生網絡和雲原生安全方面的一些 探索和實踐。更多技術細節,可參考下面一些鏈接。

參考文獻

  1. Ctrip Network Architecture Evolution in the Cloud Computing Era
  2. Trip.com: First Step towards Cloud Native Networking .
  3. Cilium Doc: Using BIRD to run BGP
  4. Life of a Packet in Cilium: Discovering the Pod-to-Service Traffic Path and BPF Processing Logics
  5. L4LB for Kubernetes: Theory and Practice with Cilium+BGP+ECMP
  6. Cilium ClusterMesh: A Hands-on Guide
  7. Cilium Doc: clustermesh