邁入 Cilium+BGP 的雲原生網路時代
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],
-
用
docker-compsoe + salt
來部署,而不是採用預設的daemonset+configmap
方式。這樣每臺 node 上的 cilium-agent 都有獨立配置,我們能完全控制釋出灰度,將 變更風險降到最低。
-
用
BIRD
作為 BGP agent,而不是採用預設的kube-router
。kube-router
開箱即用,但缺少對 ECMP、BFD 等高階功能的支援,不符合我們生產環境的要求。 -
為了保證某些業務的平滑遷移,我們開發了 StatefulSet/AdvancedStatefulSet 固定 IP 的支援(需要 sticky 排程配合)。
-
定製化了監控和告警。
-
其他一些自定義配置。
我們之前的一篇文章 [2] 對此有較詳細的介紹,有興趣可以移步。 下面討論幾個之前介紹較少或者沒有覆蓋到的主題。
2.1 BGP 建連模型
Cilium+BIRD 方案中,以宿主機為界,網路可以大致分為兩部分,如圖 2-1 所示,
Fig 2-1. High level topology of the Cilium+BGP solution [2]
- 宿主機內部網路 :由 Cilium(及核心協議棧)負責,職責包括,
- 為容器建立和刪除虛擬網路。
- 為容器生成、編譯和載入 eBPF。
- 處理同宿主機內容器之間的網路通訊。
- 跨宿主機網路 :由 BGP(及核心路由模組)負責,職責包括,
- 與資料中心網路交換路由(PodCIDRs)。
- 對出宿主機的流量進行路由。
對於跨宿主機部分,需要確定要採用哪種 BGP peering 模型,這個模型解決的問題包括 :
- BGP agent 的職責,是作為一個全功能路由控制服務,還是僅用作 BGP speaker?
- 宿主機和資料中心的哪些裝置建立 BGP 鄰居?
- 使用哪種 BGP 協議,iBGP 還是 eBGP?
- 如何劃分自治域(AS),使用哪種 ASN(自治域系統編號)方案?
取決於具體的網路需求,這套 BGP 方案可能很複雜。基於我們資料中心網路能提供的能力 及實際的需求,我們採用的是一種相對比較簡單的模型,
- 每臺 node 執行 BIRD,僅作為 BGP speaker,
- Node 在上線時會自動分配一個
/25
或/24
的 PodCIDR。 - BIRD 和資料中心網路中的兩個鄰居建立 BGP 連線。
- BIRD 將 PodCIDR 通告給鄰居 ,但 不從鄰居接受任何路由 。
- Node 在上線時會自動分配一個
- 資料中心網路只從 node 接受
/25
或/24
路由宣告,但不向 node 宣告任何路由。 - 整張網路是一張三層純路由網路(pure L3 routing network)。
這種模型的簡單之處在於,
- 資料中心網路從各 node 學習到 PodCIDR 路由,瞭解整張網路的拓撲,因此 Pod 流量在資料中心可路由。
- 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]
主要步驟:
- 在 Node1 上的 Pod1 裡面訪問某個 Service (
curl <ServiceIP>:<port>
)。 - eBPF 處理 Service 抽象,做客戶端負載均衡 :選擇某個後端,然將包的目的 IP 地址從 ServiceIP 換成後端 PodIP(即執行 DNAT)。
- 核心路由決策 :查詢系統路由表,根據包的目的 IP 地址確定下一跳;對於這個例 子匹配到的是預設路由,應該通過宿主機網絡卡(或 bond)傳送出去。
- 包到達宿主機網絡卡(bond),通過預設路由傳送到宿主機的預設閘道器(配置在資料中心 網路裝置上)。
- 資料中心網路對包進行路由轉發 。由於此前資料中心網路已經從各 Node 學習到了 它們的 PodCIDR 路由,因此能根據目的 IP 地址判斷應該將包送到哪個 Node。
- 包達到 Node 2 的網絡卡(bond):一段 eBPF 程式碼負責提取包頭,根據 IP 資訊找到 另一段和目的 Pod 一一對應的 eBPF 程式碼,然後將包交給它。
- 後一段 eBPF 程式碼對包執行 入向策略檢查 ,如果允許通過,就將包交給 Pod4。
- 包到達 Pod4 的虛擬網絡卡,然後被收起。
我們有一篇專門的文章詳細介紹整個過程,見 [4]。
2.3 叢集邊界 L4/L7 入口解決方案
在 Kubernetes 的設計中, ServiceIP 只能在叢集內訪問 ,如果要 從叢集外訪問 Service 怎麼辦? 例如,從 baremetal 叢集、OpenStack 叢集,或者其他 Kubernetes 叢集訪問?這屬於叢集邊界問題。
K8s 為這些場景提供了兩種模型:
- L7 模型 :稱為 Ingress,支援以 7 層的方式從叢集外訪問 Service,例如通過 HTTP API 訪問。
- 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 提供的兩大核心能力:
- 基於 eBPF 的靈活、動態、高效能網路。
- 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:
- 建立一個 CNP,可以指定
name
和description
等描述欄位。 - 對帶
app=protected-db
標籤(labels)的 endpoints(pods)執行這個 CNP。 - 在執行 CNP 的時候,只對入向(
ingress
)流量做控制,並且限制如下流量來源:- 協議是
TCP
,並且埠是6379
. - 流量來自帶
app:employee
labels 的 endpoints(pods)。
- 協議是
可以看到,CNP 非常靈活,使用起來也很方便。但真實世界要遠比想象中複雜,要真正落地 Cilium 安全策略,還存在很多挑戰。
3.2 落地挑戰
下面舉兩個例子,相信這些問題在很多公司都需要面對,並不是我們獨有的。
多叢集問題
如果你所有的應用都執行在 Cilium 叢集中,並且客戶端和服務端都收斂到一個叢集(大部 分公有云廠商都推薦一個 region 只部署一套 K8s 叢集,所有訪問都收斂到這套叢集),那落 地起來會簡單很多。
但大部分有基礎設施演進的公司恐怕都不滿足這個假設,實際的情況很可能是:業務分散在多 個叢集。
混合基礎設施
多叢集還不是最大的問題,因為業界多少還有一些多叢集解決方案。
更嚴重的一個問題是:業務不僅分散在不同叢集,而且在不同平臺。例如對我們來說,現在 有:
- Bare metal 叢集
- OpenStack 叢集
- 基於 Neutron+OVS 的 Kubernetes 叢集
- 基於 Cilium+BGP 的 Kubernetes 叢集
雖然我們計劃將所有容器從 Neutron 網路遷移到 Cilium 網路,但另外兩種,bare metal 和 OpenStack 叢集,還是會長期存在的,雖然規模可能會逐漸減小。
3.3 整體方案設計
我們目前的一個整體方案: 在服務端容器做入向安全策略,客戶端可以來自任何平臺、任何叢集 :
- 這將範圍框定到了 已經在 Cilium 網路的服務端容器 ,是一個不錯的起點。
- 傳統網路裡的服務端容器,會逐漸遷移到 Cilium 網路。
- BM 和 VM 的 服務端例項 ,第一階段先不考慮安全控制。
那接下來的問題就是:服務端如何具備 對所有型別、所有叢集的客戶端進行限制的能力 ? 我們的解決方案是:
- 首先,用 Cilium 提供 ClusterMesh 將已有 Cilium 叢集連線起來;
- 然後,“擴充套件” 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 似乎是理所當然的,但其實它並不是我們當初的第一選擇。 因為多叢集還有其他方案,本質上做的事情就是如何在多個叢集之間同步元資料,並且做到 叢集變動的實時感知。
- 出於多個內部需求,當時有考慮自己做這樣一套元資料同步方案,它能解決包括 Cilium 在內的多個需求。
- 並未看到業界大規模使用 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 的雲原生網路和雲原生安全方面的一些 探索和實踐。更多技術細節,可參考下面一些連結。
參考文獻
- Ctrip Network Architecture Evolution in the Cloud Computing Era
- Trip.com: First Step towards Cloud Native Networking .
- Cilium Doc: Using BIRD to run BGP
- Life of a Packet in Cilium: Discovering the Pod-to-Service Traffic Path and BPF Processing Logics
- L4LB for Kubernetes: Theory and Practice with Cilium+BGP+ECMP
- Cilium ClusterMesh: A Hands-on Guide
- Cilium Doc: clustermesh
- [譯] 為 K8s workload 引入的一些 BPF datapath 擴充套件(LPC, 2021)
- [譯] [論文] 可虛擬化第三代(計算機)架構的規範化條件(ACM, 1974)
- [譯] NAT 穿透是如何工作的:技術原理及企業級實踐(Tailscale, 2020)
- [譯] 寫給工程師:關於證書(certificate)和公鑰基礎設施(PKI)的一切(SmallStep, 2018)
- [譯] 基於角色的訪問控制(RBAC):演進歷史、設計理念及簡潔實現(Tailscale, 2021)
- [譯] Control Group v2(cgroupv2 權威指南)(KernelDoc, 2021)
- [譯] Linux Socket Filtering (LSF, aka BPF)(KernelDoc,2021)
- [譯] LLVM eBPF 彙編程式設計(2020)
- [譯] Cilium:BPF 和 XDP 參考指南(2021)
- BPF 進階筆記(三):BPF Map 核心實現
- BPF 進階筆記(二):BPF Map 型別詳解:使用場景、程式示例
- BPF 進階筆記(一):BPF 程式型別詳解:使用場景、函式簽名、執行位置及程式示例
- 原始碼解析:K8s 建立 pod 時,背後發生了什麼(四)(2021)
- 原始碼解析:K8s 建立 pod 時,背後發生了什麼(三)(2021)
- [譯] 邁向完全可程式設計 tc 分類器(NetdevConf,2016)
- [譯] 雲原生世界中的資料包標記(packet mark)(LPC, 2020)
- [譯] 利用 eBPF 支撐大規模 K8s Service (LPC, 2019)
- 計算規模驅動下的網路方案演進
- 邁入 Cilium BGP 的雲原生網路時代
- [譯] BeyondProd:雲原生安全的一種新方法(Google, 2019)