雲原生網絡利器 --Cilium Service Mesh
微服務在技術領域已經算是比較 “歷史悠久” 的話題,不管是在沒有容器的傳統領域,還是在雲原生時代的今天,微服務都是軟件領域一個重要的版塊。但無論是在傳統領域,還是在雲原生背景下,微服務的體系建設和能力都是爲了解決應用服務的治理能力,問題域是一致的,只是使用到技術會有區別,整體主要包含服務發現,服務註冊、配置中心、負載均衡、斷路器、智能路由等等。接下來,**本篇技術文章主要從 Cilium 的技術視角出發,通過分析 Service Mesh 的相關技術,來了解一下基於 eBPF 技術的網絡方案 Cilium,擁有怎樣處理微服務治理的相關能力。**對 Cilium 的一些前置瞭解,可以參見之前的系列文章,《雲原生網絡利器 --Cilium 總覽》和《雲原生網絡利器 --Cilium 之 eBPF 篇》。
常見方案
SpringCloud: Java 體系的微服務框架。
Dubbo: Java 體系的微服務框架。
Istio: 基於 Envoy 數據面能力的服務網格。
Linkerd: 基於 Envoy 數據面能力的服務網格。
技術語言
Service Mesh (服務網格): 服務網格的概念中,突出的是網格這個概念。在服務網格中,服務本身是獨立的業務的運行單元,可以是運行在物理機,虛擬機,容器裏。這些服務本身運行的位置是複雜的拓撲,服務和服務之間的通信不是直接連接的,而是通過每一個服務自己的邊車程序,來完成流量的攔截和轉發,數據包在所有的邊車之間傳遞,從而看起來像是構成一個網狀的拓撲。目前還有一種模式是無邊車模式的服務網格,這個也是本篇重點介紹的模式。
邊車模式/無邊車模式: 邊車模式是指的 Pod 都被動態注入了一個 Sidecar Proxy,通信線路爲 ServiceA → Sidecar Proxy → Sidecar Proxy → ServiceB。無邊車模式是指不會爲 Pod 注入 Sidecar Proxy,同一個機器上的 Pod 共享一個 Proxy,通信線路爲 ServiceA → Proxy → ServiceB。
Envoy (數據面 LB): 基於 C++ 實現的負載均衡軟件。也是目前比較流行的服務網格的數據面的負載均衡組件。主要作用是從控制平面同步配置信息到自己的配置中,完成服務之間流量代理和治理能力。這裏有一個需要注意的是,Cilium 本身對 Envoy 是進行深度集成的,有一個 Envoy 內置的能力被去除了,同時也擴展了一些和 Cilium 服務網格業務相關的能力,以及和 Cilium 網絡能力進行了集成。
XDS (配置發現服務): XDS 是 Envoy 中基於 grpc stream 實現的動態配置發現能力框架,其中具體的 DS 包含了 LDS (Listener Discovery Service) 、RSD (Route Discovery Service)、CDS (Cluster Discovery Service)、EDS (Endpoint Discovery Service)、SDS (Secret Discovery Service) 等,具體 Listener、Route、Cluster、Endpoint、Secret 在後面會提到。
Ingress (訪問入口): Ingress 本身是 Kubernetes 中定義的資源對象,也就是數據模型的規範,不同的提供商可以根據自己的技術棧實現自己的 Ingress 能力,來提供容器平臺中南北向流量的能力,暴露容器服務的訪問地址到容器環境以外。
Listener (監聽器): 這是 Envoy 中的負責接收數據流量的入口,它會在主機上監聽一個端口,這個監聽的端口可以主機上沒有被佔用的端口。
Listener Filter (監聽器過濾器): 顧名思義就是對監聽器本身做一些處理能力的過濾器,常用在處理連接的元數據信息。如在客戶端連接建立的過程中做一些處理。Envoy 內置了一些監聽器過濾器,如獲取連接的目的地址的 OriginalDst 和源地址的 OriginalSrc 等。也支持自定義監聽器過濾器,如 Cilium 擴展出了 cilium.BpfMetadata 這個監聽器過濾器。
Filter Chains (過濾器鏈): 在監聽器過濾器處理結束,接下來就要進入網絡過濾器的處理階段,網絡過濾器包含很多種類型的 Filters,而這些 Filter 是以 Chains 的方式一個一個執行下去,Chains 中的每一個節點都是一個 Filter,這裏包含常見的 Dubbo proxy、Connection Limit Filter、Local rate limit、Redis Proxy、Dubbo Proxy、Tcp Proxy、Mongo Proxy、Zookeeper Proxy、HttpConnectionManager 等等。
Filter (過濾器): 過濾器是 Envoy 中提供的擴展能力之一,可以在接收到數據包之後,對其進行數據包的一些處理,其支持插件機制,可以根據自己對數據的處理能力去擴展新的 Filter。同時 Envoy 的 Chains 中也內置了一些常用的 Filter,如包含常見的處理 http 請求的 7 層協議的 HttpConnectionManager,也有處理 4 層協議的 DubboProxy,RedisProxy 等。也有自定義擴展的 Filter,如 Cilium 基於 Envoy 擴展的 cilium.network。
Http Filter (Http 過濾器): Envoy 常見的使用場景是 7 層代理的能力比較多,在 Envoy 中內置了處理 http 請求的 7 層協議的 HttpConnectionManager,對於 7 層協議的處理功能點有很多,所以 HttpConnectionManager 這個過濾器本身也包含了很多 Filter,如常見的 Router、Rate limit、Admission Control、Fault Injection、gRPC HTTP/1.1 bridge、Lua、OAuth2、Stateful session、Health check、Compressor、Wasm 、CORS 等等。Cilium 基於 Envoy 擴展了 cilium.l7policy 這個 http Filter。
Route (路由): 路由是用來在 Envoy 中控制代理的服務訪問的一些路由配置信息。如負責的 domains 是什麼;匹配什麼樣的 path 後,流量被什麼集羣去處理,這裏的集羣是 Envoy 中的概念。
Cluster (集羣): Envoy 的 Cluster 在 Kubernetes 中看,可以理解成在一個 Kubernetes 中的 Service,Service 對應的 Endpoints 就是 Cluster 的集羣成員。
Service (服務): 這裏指的就是 Kubernetes 中的 Service 資源對象。
LoadBalancer (負載均衡): 這裏指的就是 Kubernetes 中的 LoadBalancer 類型的 Service 資源對象。可以提供一個固定不變的訪問地址,這也是公有云常見的 Kubernetes 暴露服務訪問的方式。當然私有云也有一些實現,如使用的比較多的 MetalLB。
Endpoint (端點): 實現邏輯服務的網絡節點。它們組成集羣。在 Kubernetes 中就是指的是 Pod,在 Envoy 中,集羣中的端點就是 Envoy 代理的上游。在 Kubernetes 的發現模式下,是基於動態發現的 EDS,同時在 Envoy 中也支持給集羣配置靜態的 Endpoints。
TProxy (透明代理): 這裏用於在 Cilium 中完成流量攔截後,數據包進入到 Envoy 的網絡路徑。從連接信息來看,就像客戶端和服務端直接連接是一樣的,但實際中間是有一個代理在處理數據包,可以基於 iptables 的方式來達到這個能力,也可以使用 Cilium 使用 eBPF 實現的透明代理的能力。
MetalLB: 這裏是用於提供實現了 LoadBalancer 類型的 Service 的具體的 Provider 實現,用於在 Cilium 中的南北向的 Ingress 中會需要用到。MetalLB 支持 layer2 方式的 arp 和 bgp 的協議來達到廣播被暴露的地址,不管是哪種方式,目的都是爲了引流數據包到 Kubernetes 的集羣中。這裏有一個注意點,很多 LoadBalancer 的 Provider 在實現的時候,都會爲每一個 Service 同時申請一個 NodePort,比如 MetalLB、AWS 的都是這樣,但是也有例外的,比如 GCP 平臺就沒有 NodePort。但是最終目的都是引流,如果是有 NodePort 的就引流到 Kubernetes 的主機的 NodePort,如果沒有 NodePort,那就引流到對應的 Pod,這些要根據 Provider 自身的設計和實現。
eBPF Section from-container: 處理容器出口流量的 eBPF 程序,在這個場景中,可以根據訪問服務是不是需要走 Envoy 完成服務治理,來發送數據包 Envoy。
eBPF Section from-netdev: 處理主機外部的入口流量的 eBPF 程序,在這個場景中,可以根據訪問服務是不是需要走 Envoy 完成服務治理,來發送數據包 Envoy。
eBPF Section from-host: 處理主機上程序發起服務訪問的的流量處理的 eBPF 程序,在這個場景中,可以根據訪問服務是不是需要走 Envoy 完成服務治理,來發送數據包 Envoy。
eBPF Section to-netdev: 處理從當前主機要出去的數據包的這樣一個 eBPF 程序。,主要完成一些 SNAT 等相關的操作,後經過物理網卡發往遠程的主機。
eBPF ipv4_local_delivery: 處理本地的數據包到 Pod 的核心方法。不管是 Pod 到 Pod 的,還是 cilium_host 到 Pod 的,還是 overlay 到 Pod 的,還是物理網卡到 Pod 的,只要是確定了是本地的 Endpoint,那數據包要進入 Pod 就是要這個方法來處理的,主要的作用就是校驗 policy,看能不能進入到 Pod,方向對於 Pod 來說是 ingress 方向。
流量攔截: 服務網格的重要特點之一就是要應用無感知的情況下對應用的服務進行管理,而在其中重要的問題域就是怎麼解決流量攔截的技術。在 istio 的實現中,是基於 iptables 對流量進行 redirect 到 Envoy 的指定監聽的 Port 上,在出口的地方同樣攔截流量到負責出口的 Envoy 的指定監聽的 Port 上。在 Cilium 中是基於本身的網絡的 datapath 上進行根據是不是要攔截的策略進行流量的 redirect 到 Envoy 的某一個監聽的 Port 上,具體的監聽 Port 會根據不同的 CiliumEnvoyConfig 而不同。
Upstream / Downstream: ServiceA→ Proxy→ ServiceB, 那這裏的 ServiceB 就是 Upstream,ServiceA 就是 Downstream。
本地 Endpoint / 遠程 Endpoint: 本地 Endpoint 指的是請求的後端服務恰好在被訪問的機器上,反之遠程 Endpoint 是請求的後端服務不在被訪問的機器上,這個時候需要將請求的數據包轉發到遠程的 Endpoint 所在的機器,去完成請求處理。
控制平面
最近 Cilium 發佈的 1.12.0 版本,在其中比較重點的部分是提出了 Cilium Service Mesh 能力。本篇文章先不考慮和 Istio 集成或者對接的場景。Cilium Service Mesh 提供了服務網格中的南北向和東西向的能力,主要包括南北向的 Ingress 和 CiliumEnvoyConfig。CiliumEnvoyConfig 不但可以完成東西向的能力,也可以完成基於 NodePort 的方式的南北向能力。這裏主要從對比常見服務網格的視角來理解。
- Ingress: Kubernetes 標準的資源對象。只是 Cilium 基於 ingressClassName 擴展實現了自己的 Ingress 南北向流量的能力。Ingress 創建成功之後,可以基於 Ingress 的 external IP 去訪問 Kubernetes 的服務。
- CiliumClusterwideEnvoyConfig/CiliumEnvoyConfig: 這兩個數據結構和作用基本是相同的,區別是一個是 namespace 級別的,一個是 cluster 級別的。主要的作用包含兩個大的部分能力,一部分是對一些指定的 Kubernetes Service,讓其在 Cilium 網絡層面訪問這些 Service 的時候會走到 Envoy 的能力,這部分的定義是在 spec 的 services 中指定,這裏的 services 中包含的 services 會被 Cilium Service Mesh 認爲是需要服務治理能力,所有這些服務的訪問都會被 redirect 到 Cilium 內置的 Envoy 中,由 Envoy 中配置的各種規則完成服務的治理之後,代理到真正的 Backend,所以前面提到也可以基於 NodePort 的方式提供南北向,同時服務還可以有服務治理的能力,作用類似 Istio 中的 istio-injection=enabled,只是 Istio 這種是基於 namespace 級別的動態注入,Cilium 這種是以更小的 Service 爲管理單位;還有一部分的能力是給 Envoy 下發配置信息,如上述的 Listener、Filter、Route、Cluster、Endpoint 等,其中 Cluster 就是 Kubernetes 的 Service,這個和 Istio 是類似的,這部分的定義是在 spec 的 resources 中指定。這兩部分的中間的網絡橋樑就是上述提到的透明代理 TProxy。
架構介紹
下圖包含了 Cilium Service Mesh 中的南北入口的 Ingress,也包含了東西方向的 CiliumClusterwideEnvoyConfig/CiliumEnvoyConfig。以下以 Native Routing 的模式展示數據流部分,所以不會有 Overlay 相關的部分。
Ingress 控制流:1 → 2 → 3 → 4 → 5 → 6 → 7。包含 CiliumEnvoyConfig 的處理和 LoadBalancer 類似的 Service 的處理。下文會詳細介紹。
Ingress 數據流:
-
本地 Endpoint: 8 → 9 → 10 → 11 。對於集羣外部訪問 Ingress 入口,會經過負責處理外部流量的 from-netdev 的 eBPF 程序,經過 TProxy 代理 Envoy,再由 Envoy 代理,轉發到真正的 Pod 中去。其中從主機上進入到 Cilium 中,需要經過 from-host eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,驗證通過了就 ctx redirect,數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
-
遠程 Endpoint: 8 → 9 → 13 → 15 → 16 。對於集羣外部訪問 Ingress 入口,會經過負責處理外部流量的 from-netdev eBPF 程序,經過 TProxy 代理到 Envoy,Envoy 經過 CDS 和 EDS 的能力,找到遠程的 Endpoint 的 IP 地址後,數據包經過內核進行路由的查找,經過 to-netdev eBPF 程序處理後發送到遠程的主機,數據包到達遠程主機,由遠程主機的 from-netdev eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,通過驗證通過了就 ctx redirect 數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
CiliumEnvoyConfig 控制流: 3 → 4 → 6 → 7。包含 CiliumEnvoyConfig 的處理,下文會詳細介紹。
CiliumEnvoyConfig 數據流:
- 從容器內訪問服務:
-
本地 Endpoint:12 → 10→ 11。從 Pod 內部發出訪問服務的請求,首先會由處理容器出口流量的 from-container eBPF 程序處理,發現是需要轉發到 Envoy 的數據包,經過 TProxy 代理 Envoy,再由 Envoy 代理,轉發到真正的 Pod 中去。其中從主機上進入到 Cilium 中,需要經過 from-host eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,通過驗證通過了就 ctx redirect 數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
-
遠程 Endpoint:12 → 13 → 15 → 16。從 Pod 內部發出訪問服務的請求,首先會由處理容器出口流量的 from-container eBPF 程序處理,發現是需要轉發到 Envoy 的數據包,經過 TProxy 代理 Envoy,Envoy 經過 CDS 和 EDS 的能力,找到遠程的 Endpoint 的 IP 地址後,數據包經過內核進行路由的查找,經過 to-netdev eBPF 程序處理後發送到遠程的主機,數據包到達遠程主機,由遠程主機的 from-netdev eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,驗證通過了就 ctx redirect 數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
- 從主機上訪問服務:
-
本地 Endpoint:14 → 9 → 10→ 11。從主機上發送服務的請求,如直接訪問 ClusterIP,首先需要知道是,所有由主機的進程發出的請求,都是首先需要被 from-host eBPF 程序處理,發現是需要轉發到 Envoy 的數據包,經過 TProxy 代理 Envoy,再由 Envoy 代理,轉發到真正的 Pod 中去。其中從主機上進入到 Cilium 中,需要經過 from-host eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,通過驗證通過了就 ctx redirect 數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
-
遠程 Endpoint:14 → 9 → 13 → 15 → 16。從主機上發送服務的請求,如直接訪問 ClusterIP,首先需要知道是,所有由主機的進程發出的請求,都是首先需要被 from-host eBPF 程序處理,發現是需要轉發到 Envoy 的數據包,經過 TProxy 代理 Envoy,Envoy 經過 CDS 和 EDS 的能力,找到遠程的 Endpoint 的 IP 地址後,數據包經過內核進行路由的查找,經過 to-netdev eBPF 程序處理後發送到遠程的主機,數據包到達遠程主機,由遠程主機的 from-netdev eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,通過驗證通過了就 ctx redirect 數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
- 從機器外訪問服務 (NodePort):
-
本地 Endpoint:8→ 9 → 10→ 11。對於集羣外部訪問 NodePort 類型的 Service,會經過負責處理外部流量的 from-netdev eBPF 程序,經過 TProxy 代理 Envoy,再由 Envoy 代理,轉發到真正的 Pod 中去。其中從主機上進入到 Cilium 中,需要經過 from-host eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,驗證通過了就 ctx redirect 數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
-
遠程 Endpoint:8 → 9 → 13 → 15 → 16。對於集羣外部訪問 NodePort 類型的 Service,會經過負責處理外部流量的 from-netdev 的 eBPF 程序,經過 TProxy 代理到 Envoy,Envoy 經過 CDS 和 EDS 的能力,找到遠程的 Endpoint 的 IP 地址後,數據包經過內核進行路由的查找,經過 to-netdev eBPF 程序處理後發送到遠程的主機,數據包到達遠程主機,由遠程主機的 from-netdev eBPF 程序處理之後,通過 eBPF 的 tail call 方式調用 ipv4_local_delivery eBPF 程序,最後由 ipv4_local_delivery eBPF 程序處理,主要是對進入容器的數據包做一些 policy 驗證類的工作,判斷數據包能不能進入到 Pod 中去,驗證通過了就 ctx redirect 數據包到 Pod 的 lxc-xxx,可以理解成數據包被轉發到了 Pod 了。
備註:具體的數據路徑會隨着內核的版本而使用不同的 eBPF 能力,導致數據流會有稍微的不同。
CiliumEnvoyConfig實現
總體流程: 整體上包含了 Envoy 配置下發和 Cilium 流量攔截兩大部分。方法 UpsertEnvoyResources() 完成 Envoy 配置的轉換和下發動作。addK8sServiceRedirects() 方法負責給 Service 對象配置對應的標誌,用於在 Cilium 的 eBPF 程序中判斷當前的 Service 被訪問時,是需要被流量攔截,以及對應的 Proxy Port。對於這個 Proxy Port 是怎麼來的,後面會介紹。
Envoy 配置下發:首先會根據 CRD CiliumClusterwideEnvoyConfig / CiliumEnvoyConfig 的 cr 對象的定義,將 resources 部分的定義轉變成 Envoy 可以認識的各種對象,包含 Listener、Route、Cluster、Endpoint、Secret。轉換成功之後,保存到 xDS 的 cache 中,這個 cache 後面會通過 xDS 的 grpc 的 stream 下發到 Envoy 中去。
Cilium 流量攔截 - 控制面邏輯: 主要邏輯分爲幾個步驟。步驟一:registerL7LBService() 方法完成 Service 對象的 Proxy Port 的設置,以及對應的 Envoy 的 Listener 是哪一個;步驟二:UpsertService() 方法將設置好的 Service,通過 go 程序和 eBPF 的通信,將 Service 信息寫入到 eBPF 程序會訪問的 lb map 中,這裏的 lb map 就是 eBPF 支持的數據結構,用於保存和 Service 相關的信息,很多 eBPF 程序中都會用到這個 lb map 的數據。
Cilium 流量攔截 - 數據面邏輯: 主要邏輯是判斷 Service 是不是一個需要被攔截流量,redirect 到 Envoy 中去,是的話就通過透明代理的方式代理流量到 Envoy。lb4_svc_is_l7loadbalancer() 方法用於判斷 Service 是不是一個需要被攔截流量;ctx_redirect_to_proxy_hairpin_ipv4() 方法完成代理流量到 Envoy。ipv4_l3() 方法用於設置 ctx 的 ip 的 ttl,用於定期檢查 icmp 的有效性,以及設置源 mac 地址和目的 mac 地址。ctx_redirect() 方法的能力是基於 eBPF,由內核提供的核心方法,主要作用就是將數據包轉發到某一個網絡設備,可以是虛擬的網絡設備,也可以是物理的網卡。
Envoy 的監聽端口: Cilium 在引入了 Envoy 之後,給 Envoy 分配了 10000-20000 的一個端口範圍,用於爲 Envoy 分配 Listener 端口用的。在沒有開始支持 Service Mesh 的時候,這個能力已經存在,主要用於 NetworkPolicy 和觀測能力。NetworkPolicy 可以理解成使用 Envoy 的一些配置來控制訪問。至於爲什麼能觀測,後面會提到。對於每一個 CiliumClusterwideEnvoyConfig / CiliumEnvoyConfig 的 cr 對象,Cilium 都會爲其動態的自動分配對應的 Port,用於 Envoy 的 Listener 端口。每一個機器的 cilium agent 都會爲 CiliumClusterwideEnvoyConfig / CiliumEnvoyConfig 的 cr 對象分配一個 Port,而且不同的機器上爲其分配的 Port 都是根據每個機器的端口分配情況,各自分配的,所以不同的機器上爲 CiliumClusterwideEnvoyConfig / CiliumEnvoyConfig 的 cr 對象分配的端口會不同。
支持的 Envoy 能力: Cilium 深度集成了 Envoy,去掉很多 Envoy 默認自帶的很多能力,目前是保留了一下的能力,如果要使用的時候,需要關注支持的能力,目前 Cilium 對於下發的資源對象的格式沒有驗證,不會在下發的時候進行檢查,只會在下發到 Envoy 之後,從底層查看錯誤的日誌,纔會知道哪裏配置有問題,這個地方也是目前版本做的不是很友好的地方。後期會根據客戶的反饋,逐步的添加回這些被刪除的能力。
Ingress 實現
這裏的 Ingress 就是指的是 Kubernetes 的 Ingress 資源對象,Cilium 的 Ingress 的實現主要依賴於兩部分,一部分就是上面重點分析的 CiliumEnvoyConfig,基於 CiliumEnvoyConfig 可以完成內部的東西向的代理能力;一部分是 LoadBalancer 的 Kubernetes 的 Service。那接下來就要解決的是南北向的流量怎樣引流到 Kubernetes 的集羣中。在 Cilium 中使用了 LoadBalancer 的 Kubernetes 的 Service 方式來引流到 Kubernetes 集羣中。這裏的 MetalLB 是 Cilium 使用了 MetalLB 的程序包自己實現和集成了 MetalLB 的 LoadBalancer 能力。
-
處理 CiliumEnvoyConfig 相關: 1 → 2 → 3 → 4
-
處理 LoadBalancer Service 相關: 5→ 6 → 7 → 8
- Ingress 對象處理: 這裏有 Cilium Operator 組件完成 Ingress 資源對象的 watch,當有 Ingress 對象被添加,會爲 Ingress 對象創建,CiliumEnvoyConfig cr 對象,這個 cr 對象最終由 cilium agent 去 watch 並生成 Envoy 的配置後,下發到 cilium agent 機器所在的 Envoy 中,由於每臺機器都有 cilium agent,所以每一個 CiliumEnvoyConfig 的定義都會在所有的機器上的 Envoy 中配置。接下來,會創建一個 LoadBalancer 類型的 Service,這裏中間件還有一步創建一個 Endpoint 的步驟,在下面說,但是這個 Endpoint 是爲 LoadBalancer 類型的 Service 創建的 Endpoint。
- CiliumEnvoyConfig 創建: 首先根據配置生成 Ingress 類型對應的 CiliumEnvoyConfig 配置,調用 Kubernetes 的 API,如果有就更新,沒有就創建。
- Service 的 Endpoint: 由於這裏創建的 Service 是沒有 selector 的,所有 Service 是沒有 Endpoint 的,這裏需要創建一個 dummy 的 Endpoint,目的就是爲了讓 Service 有一個 Endpoint,這樣 Service 的信息纔可以被寫到 eBPF 的 lb map 中,這裏的 lb map 就是在 Cilium 中保存了 Kubernetes Service 對應信息的一種 map 類型的數據結構。這個 lb map,Cilium 的控制平面的 go 程序可以讀寫,eBPF 的數據面處理程序也可以讀寫。只有寫到 lb map 中的 Service 纔會根據是不是需要代理到 Envoy 中而進行處理數據包,不會有真正的數據包會經過這個 Endpoint 處理。
- LoadBalancer 類型的 Service: 這裏就是創建一個 LoadBalancer 類型的 Service,這裏要注意的是,Service 的 name 和創建的 Endpoint 的 name 是一樣的,這樣就對上述的 Endpoint 關聯到了自己的 Service 中,接下來會被寫到 eBPF 的 lb map 中,在數據包達到 eBPF 的處理程序中的時候,會根據這個 Service 是不是被設置了需要服務治理的標記,來判斷是不是要將數據包通過 TProxy 代理到 Envoy 中,從而完成 Ingress 南北向進入到 Cilium 的能力。
對比社區 Istio
本篇文章先不考慮和社區的 Istio 集成或者對接的場景。主要聚焦在 Cilium 自己的控制平面 CiliumEnvoyConfig 和社區標準的 Istio 作一些對比。
-
在對接 Envoy 方面,Cilium 內置了 Envoy,同時對 Envoy 進行的大量的定製化,來適配 Cilium 的網絡。刪除了一些能力,也增加了一些能力。
-
在運行模式方面,Cilium Service Mesh 採用了無邊車模式的模式,也就是不會在爲每一個 Pod 去動態的注入一個 Envoy 這樣的 Sidecar Proxy,而是每臺機器上的所有 Pod 共享一個 Envoy。
-
在實現方面,Cilium 對業務流量的攔截不再基於 iptables 的各種規則來 redirect 到 Envoy 的監聽 Port,而是基於 Cilium 網絡數據面的 code 中內置實現了基於 TProxy 的透明代理的方式 redirect 數據包到 Envoy 的監聽 Port,而且 Envoy 的監聽 Port 也是可以自己手動指定的,而在 Istio 是不允許指定的,固定在 15006 上。
-
在安全方面,Cilium 內置實現了 NetworkPolicy 來管理服務的訪問控制。不僅可以完成 L3/L4 的安全控制,還可以完成 L7 的安全控制,可以在整體上更完整的控制網絡的完全。
-
在使用方面,Cilium 還沒有定義像 Istio 中提供的 VirtualService、DestinationRule、Gateway、WorkloadEntry、ServiceEntry 等面向用戶友好的服務治理的業務 API。只有 Ingress 和 CiliumEnvoyConfig 兩種類型。
-
在性能方面,由於 Cilium 中使用無邊車模式模式,本身是少了一次代理,每一次代理基本會加大 400-500us 左右的延遲,對於延遲要求很高的系統是無法接受的。以及 eBPF 傳遞數據的 CTX Redirect / CTX Redirect Peer / CTX Redirect Neight 的能力 (eBPF 加速 packet 傳遞,bypass 內核網絡協議棧,不會使用 iptables,加速網絡,降低延時) 等。所以性能上 Cilium Service Mesh 是比默認沒有任何加速方案的 Istio (基於 iptables 和內核網絡棧的方案) 要好。
-
在觀測性上,由於 Cilium 內置了 Hubble 這樣的觀測能力,天然支持了 L3/L4 層,同時在集成 Envoy 的情況下,支持了 L7 層的觀測,整體上觀測的數據比較完整。而 Istio 重點是 L7 層的觀測數據比較偏重。同時從額外帶來的延遲上看,Cilium Service Mesh 是比 Istio 中延遲要低,畢竟少了一次代理。
-
在資源消耗方面,在邊車模式下,每一個需要服務治理的 Pod 都會注入一個 Envoy 的容器,Pod 很多情況下,對資源消耗是不可小覷。但是 Cilium Service Mesh 的無邊車模式,不會爲 Pod 注入 Envoy 容器,可以大量的節省資源。
-
在配置下發方法,在邊車模式下,每一個需要服務治理的 Pod 都會注入一個 Envoy 的容器,Pod 很多情況下,所有的 Envoy Sidecar 都需要下發 Envoy 的配置,這部分的網絡開銷和壓力也是不可小覷。但是 Cilium Service Mesh 的無邊車模式,不會爲 Pod 注意 Envoy,也就不會爲每一個 Pod 去下發配置,而是以機器爲單位的下發,而不是 Pod 爲單位下發配置,可以節省這部分的網絡消耗和減少這部分的服務壓力。
-
在 CNI 依賴方面,Cilium Service Mesh 是強依賴 Cilium 本身的 CNI 網絡能力的。Istio 是 CNI 獨立的 Service Mesh,理論上都支持,具體要看 CNI 是不是有和 Istio 能力衝突的地方,特別是 eBPF 類型,用戶態類型的 CNI,因爲這些類型的 CNI 會影響到 Istio 中基於 iptables 規則攔截流量的方式。
總結
從整體上,Cilium Service Mesh 目前的能力更多體現在數據平面上,同時提供了 Ingress 和 CiliumEnvoyConfig 兩個控制平面的能力,但是相比於 Istio 控制平面以及業務 API 的能力還是比較欠缺。但是 Cilium 本身和 Istio 不衝突,可以藉助於 Cilium 的網絡能力,在 Cilium 之上運行 Istio 也是一種選擇。
相關鏈接:
https://github.com/cilium/cilium
https://docs.cilium.io/en/v1.12/gettingstarted/servicemesh/l7-traffic-management/
https://isovalent.com/blog/post/cilium-service-mesh/
本文作者
熊中祥
「DaoCloud 道客」技術合夥人
雲原生技術專家
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/taPzBp6uDKRv9FGbbVVPhA