如何構建 Sidecarless 模式的高性能服務網格

01 服務網格數據面的演進

以 Istio 爲代表的 Service Mesh 技術已經存在四五年的時間了,阿里雲也是第一批支持 Service Mesh 雲服務的廠商。在 Service Mesh 技術中,通過把服務治理的能力進行 Sidecar 化,實現與應用程序本身的解耦。這些若干個 Sidecar 代理就形成了一個網狀的數據平面,通過該數據平面可以處理和觀察所有應用服務間的流量。負責數據平面如何執行的管理組件稱爲控制平面。

可以看到,控制平面是服務網格的大腦,它爲網格使用人員提供公開 API,以便更容易地操縱網絡行爲。總之,通過 Service Mesh 技術,Dev/Ops/SRE 將以統一的、聲明的方式解決應用服務管理問題。

服務網格作爲一種應用感知的雲原生基礎設施,提供了雲原生應用感知的網絡管理能力。

網絡是 Kubernetes 的核心部分,涉及了 Pod 間通信、Pod 和服務間通信以及服務與外部系統間的通信等。Kubernetes 集羣中使用 CNI 插件來管理其容器網絡功能,使用 Kube-proxy 維護節點上的網絡規則,譬如使發往 Service 的流量(通過ClusterIP 和端口)負載均衡到正確的後端 Pod。

容器網絡成爲用戶使用 IaaS 網絡的新界面,以阿里雲 ACK Terway 網絡爲例,基於阿里雲 VPC 網絡直通彈性網卡,具備高性能特徵;同時無縫地跟阿里雲 IAAS 網絡對接;

然而,kube-proxy 設置是全局的,無法針對每個服務進行細粒度控制;而且 kube-proxy 只是專在網絡數據包級別上運行。它無法滿足現代應用程序的需求,如應用層流量管理、跟蹤、身份驗證等。

我們來看服務網格 Sidecar 代理模式下的雲原生應用網絡是如何解決這些問題的。服務網格通過 Sidecar 代理將流量控制從 Kubernetes 的服務層中解耦,將代理注入到每個 Pod;並通過控制平面操縱管理這些分佈式代理,從而可以更加精細地控制這些服務之間的流量。

那麼在 Sidecar 代理下的網絡數據包的傳輸是怎樣的過程?

當前 Istio 實現中涉及了 TCP/IP 堆棧的開銷,它使用了 Linux 內核的 netfilter 通過配置 iptables 來攔截流量,並根據配置的規則對進出 sidecar 代理的流量進行路由。客戶端 pod 到服務器端 pod 之間的典型路徑(即使在同一主機內)至少要遍歷 TCP/IP 堆棧 3 次(出站、客戶端 Sidecar Proxy 到服務器端 Sidecar Proxy、入站)。

爲了解決這個網絡數據路徑問題,業界通過引入 eBPF 繞過 Linux 內核中的 TCP/IP 網絡堆棧來實現網絡加速,這不但降低了延遲,同時也提升了吞吐量。當然,eBPF 並不會替換 Envoy 的七層代理能力,在七層流量管理的場景下,仍然是 4 層 eBPF + 7 層 Envoy 代理的融合模式。只有針對 4 層流量網絡的情況下,跳過代理 pod 直接進入網絡接口。

前面講述的是傳統的 Sidecar 代理模式,除此之外,業界開始在探索一種新型的數據平面模式。它的設計理念是:將數據平面分層,4 層用於基礎處理,特點是低資源、高效率;7 層用於高級流量處理,特點是功能豐富,當然需要更多的資源。這樣的話,用戶可以根據所需功能的範圍,以漸進增量的方式採用服務網格技術。具體來說:

在 4 層處理中,主要包括:

  • 流量管理:TCP 路由
  • 安全:面向 4 層的簡單授權策略、雙向 TLS
  • 可觀測:TCP 監控指標及日誌

在 7 層處理中,則主要包括:

  • 流量管理:HTTP 路由、負載均衡、熔斷、限流、故障容錯、重試、超時等
  • 安全:面向 7 層的精細化授權策略
  • 可觀測:HTTP 監控指標、訪問日誌、鏈路追蹤

那麼數據面 L4 與 L7 代理的解耦模式下,Service Mesh 的網絡拓撲將是什麼形式?

一方面,將 L4 Proxy 能力下移到 CNI 組件中,L4 Proxy 組件以 DaemonSet 的形式運行,分別運行在每個節點上。這意味着它是爲一個節點上運行的所有 pod 提供服務的共享基礎組件。

另一方面,L7 代理不再以 Sidecar 模式存在,而是解耦出來,我們稱之爲 Waypoint Proxy, 它是爲每一個 Service Account 創建的 7 層代理 pod。

4 層和 7 層代理的配置仍然保持通過 Service Mesh 控制面組件來進行下發管理。

總之,通過這種解耦模式,實現了 Service Mesh 數據平面與應用程序之間的進一步解耦分離。

那麼,在 Istio 的具體實現中,可以分爲 3 個部分:

  • Waypoint 代理L7 組件完全獨立於應用程序運行,安全性更高;每個身份(Kubernetes 中的服務帳戶)都有自己專用的 L7 代理(Waypoint 代理),避免多租戶 L7 代理模式引入的複雜度與不穩定性;同時,通過 K8s Gateway CRD 定義觸發啓用;
  • ztunnel將 L4 處理下沉到 CNI 級別,來自工作負載的流量被重定向到 ztunnel,然後ztunnel 識別工作負載並選擇正確的證書來處理;
  • 與 Sidecar 模式兼容
    Sidecar 模式仍然是網格的一等公民,Waypoint 代理可以與部署了 Sidecar 的工作負載進行本地通信。

不影響應用程序是使 Ambient Mesh 比傳統的 Sidecar 模式具備更少侵入性的原因之一。與採用 Sidecar 模式時必須將 Sidecar 代理注入到每個應用程序部署中相比,Ambient 模式下無需以任何方式重新部署或修改現有應用程序。通過不重新部署和直接修改應用程序,可以有效地降低落地風險並簡化採用 Mesh 的落地曲線。

Ambient Mesh 的設計是非侵入式的,並且僅對存在特定標記的命名空間並使現有應用程序成爲 Ambient Mesh 的一部分,可以逐步採用。一旦應用程序成爲 Ambient Mesh 的一部分,它立即獲得 mTLS 和 L4 可觀察性功能。

02 網絡 CNI 插件爲節點進行網絡命名空間配置

Sidecarless 模式中 CNI 插件在兩個位置進行網絡命名空間的配置,其中一個就是在所處的節點上,另一個是在 ztunnel pod 上。

具體如下:

  • 節點:在每個節點上配置網絡命名空間,以將進出節點的流量透明地路由到節點,並相應地將其路由到 ztunnel 代理。
  • ztunnel:在 ztunnel pod 的網絡命名空間中配置路由,將進出流量路由到 ztunnel 代理上的特定端口。

CNI 插件在每個節點上初始化路由並設置 iptables 和 ipset。

注意:與 Sidecar 模式下不同,CNI 插件所做的配置不會直接影響任何工作負載 pod。更改僅在節點網絡命名空間和 ztunnel pod 的網絡命名空間中進行,與流量重定向機制無關。

網格 CNI 插件爲節點進行配置的內容可以分爲 4 個部分:

1. 創建名爲 ztunnel-pods-ips 的 IP 集(ipset)

2. 由 Mesh CNI 網絡插件創建網絡接口(Geneve Device)

3. 設置網絡接口的參數

4. 設置 iptables、路由表和路由規則具體來說:

1. 創建名爲 ztunnel-pods-ips 的 IP 集(ipset)

節點上創建了一個名爲 ztunnel-pods-ips 的 IP 集,用於存儲當前節點中啓用Ambient 模式的 pod 的 IP 地址。每次添加一個 pod 時,CNI 插件將 pod 的 IP 地址添加到節點上的 ztunnel-pods-ips IP 集(ipset)中。

隨着 pod 的添加或刪除(即它們的 IP 地址發生變化),CNI 插件會讓節點上的 ipset 保持最新。

在節點上可以通過運行 ipset list 命令查看作爲 ipset 中的 IP 地址:

通過 kubectl 查看納入到 Ambient 模式中的本節點下的 Pod 列表,例如:

對比分析可以看到,在節點 172.17.0.3 上,納入到 Ambient 模式的 Pod 有 2 個,IP 地址分別爲 172.17.0.48 和 172.17.0.66。這 2 個地址也被寫入到 ztunnel-pods-ips IP 集 (ipset) 中。

2. 由 Mesh CNI 網絡插件創建網絡接口(Geneve Device)

在每個節點上,設置了兩個虛擬網絡接口(Geneve Device) - istioin 和 istioout。

由 Mesh CNI 網絡插件創建:

執行前面三行的命令後,Linux 系統會將 IP 地址 192.168.126.1 和子網掩碼 255.255.255.252 添加到 istioin 網絡接口中,以便該網絡接口可以用於與該 IP 地址所在的子網中的其他主機進行通信。然後將 istioin 網絡接口啓用,以便該網絡接口可以用於網絡通信和數據傳輸。網絡接口啓用後,它可以接收和發送數據包,與其他網絡設備進行通信和交換數據。

同樣的,後面三行命名執行後,Linux 系統會將 IP 地址 192.168.127.1 和子網掩碼 255.255.255.252 添加到 istioout 網絡接口中,以便該網絡接口可以用於與該 IP 地址所在的子網中的其他主機進行通信。然後將 istioout 網絡接口啓用,以便該網絡接口可以用於網絡通信和數據傳輸。網絡接口啓用後,它可以接收和發送數據包,與其他網絡設備進行通信和交換數據。

在 Linux 系統中,網絡接口啓用後通常會被標記爲 “UP” 狀態,可以通過 ip link show 命令查看網絡接口的狀態信息。例如, 在節點上運行命令 ip link show,顯示網絡接口的鏈路層信息,可以得到類似如下內容:

  • 結合節點上的 iptables 規則和路由表,確保來自 ambient pods 的流量被攔截,並根據方向(入站或出站)分別發送到 istioout 或 istioin。
  • 使用下面的命令顯示網絡接口的網絡層信息,可以查看節點上 istioin 網絡接口的詳細信息:

  • 發送到這些接口的數據包最終會到達在同一節點上運行的 ztunnel pod 的 pistioout 或 pistioin。
  • 使用下面的命令查看節點上 istioout 網絡接口的詳細信息,可以看到對應於同一節點上 ztunnel pod 的 IP 地址。

3. 設置網絡接口的參數

  • 將網絡接口的 rp_filter 參數設置爲 0,關閉反向路徑過濾,繞過數據包源 IP 地址的校驗;
  • 將網絡接口的 accept_local 參數設置爲 1,以接受本地生成的數據包;

4. 設置 iptables、路由表和路由規則

節點上的另一個更改是 iptables 鏈,它將數據包從現有的表(NAT 和 MANGLE)中的標準鏈(例如 PREROUTING,OUTPUT 等)重定向到自定義的 ztunnel 鏈。如下圖所示:

 

具體來說,對應執行如下命名:

 

此外,通過執行如下命令,用於在 mangle 表和 nat 表中爲自定義鏈 ztunnel-PREROUTING、ztunnel-FORWARD、ztunnel-INPUT、ztunnel-OUTPUT 和 ztunnel-POSTROUTING 添加一些規則,具體含義如下:

以及用於在 mangle 表中的自定義鏈 ztunnel-PREROUTING 中添加一些規則,具體含義如下:

任何標記爲 0x100/0x100 的數據包(即來自 Ambient pod)根據對應的路由表中的規則進行路由。創建這些路由表所需要執行的命令如下:

  • 要查看路由表 100,請運行下面的命令:

  • 要查看路由表 101,請運行下面的命令:

具體描述如下:

  • 路由表項 default via 192.168.127.2 dev istioout :默認路由,通過 istioout 接口將流量發送到 IP 地址 192.168.127.2(ztunnel 上的 pistioout)。
  • 路由表項 172.17.0.59 dev cali9af644900ca scope link :任何發送到 172.17.0.59 的流量(即 ztunnel pod 的 IP 地址)應直接通過虛擬接口(calixxxx)發送。
  • istioout 接口(192.168.127.1)使用 GENEVE 隧道連接到在同一節點上運行的ztunnel上的接口 pistioout(IP 爲 192.168.127.2)。也就是說,當通過istioout接口向 192.168.127.2 發送請求,數據包將最終到達稱爲 pistioout(注意前綴 p)的 ztunnel 出站接口,從而通過 ztunnel 路由網格中的 pod 的出站流量。
  • 要查看路由表 102,請運行下面的命令:

  • 第一行:所有目的 IP 地址不在本地子網中的數據包都會被路由到指定的下一跳路由器,該數據包會通過 ZTUNNELINTERFACE網絡接口發送到下一跳路由器(地址爲ZTUNNELINTERFACE網絡接口發送到下一跳路由器(地址爲{ZTUNNEL_INTERFACE} 網絡接口發送到下一跳路由器(地址爲 {ZTUNNEL_IP})。
  • 第二行:所有目的 IP 地址爲 ZTUNNELIP的數據包都會被路由到指定的下一跳路由器,該數據包會通過ZTUNNELIP的數據包都會被路由到指定的下一跳路由器,該數據包會通過{ZTUNNEL_IP} 的數據包都會被路由到指定的下一跳路由器,該數據包會通過 {ZTUNNEL_INTERFACE} 網絡接口發送到下一跳路由器,該路由器位於本地網絡中,不需要通過其他路由器進行轉發。

配置 IP 路由規則

  • ztunnel-pods-ips IP 集作爲 iptables 規則的一部分,IP 地址會相應地被標記。例如,如果數據包來自 Ambient Pod,並通過 istioout 接口重定向到 ztunnel,則會使用 0x100/0x100 標記數據包。
  • 在節點上可以使用 ip rule list 命令查看如何使用不同的標記設置路由表:

  • 對應的添加命令如下:

具體來說,對於標記爲 0x200/0x200 的數據包,跳轉到主路由表(編號爲 32766)進行路由。

對於標記爲 0x100/0x100 的數據包,在路由表 101 中查找路由信息進行路由。

對於標記爲 0x040/0x040 的數據包,在路由表 102 中查找路由信息進行路由。

將路由表 100 作爲本地主機的默認路由表。

出站標記以及數據包標記的說明如下:

Ambient 網格內部 Pod 的流量請求所經過的數據包流向,大致如下:

  • Ambient 模式下的應用 Pod 會被 CNI 插件將其 IP 地址寫入到 ipset 中,當發起請求時,流量數據包進入到該節點上對應的 veth 接口。
  • 數據包來自 Ambient Pod,會被 iptables 攔截。
  • 使用 0x100/0x100 標記數據包,並進入到 istioout 網絡接口。
  • 通過 istioout 接口將流量透明劫持到 pistioout 網絡接口,其中 pistioout 用於接收 Geneve 隧道中的發來的數據包。

03 網格 CNI 插件爲 ztunnel Pod 配置網絡命名空間

網格 CNI 插件爲 ztunnel Pod 進行配置的內容可以分爲 3 個部分:

1. 由 Mesh CNI 網絡插件創建網絡接口(Geneve Device)

2. 設置網絡接口的參數

3. 爲 ztunnel pod 設置 iptables、路由表和路由規則

具體來說:

1. 由 Mesh CNI 網絡插件創建網絡接口(Geneve Device)

在每個 ztunnel Pod 上,設置了兩個虛擬網絡接口(Geneve Device)- pistioin 和 pistioout。由 Mesh CNI 網絡插件在 ztunnel pod 裏創建:

在 ztunnel Pod 裏運行命令 ip link show,可以得到類似如下內容:

  • 使用下面的命令查看 ztunnel Pod 裏的 pistioin 網絡接口的詳細信息:

  • 使用下面的命令查看 ztunnel Pod 裏的 pistioout 網絡接口的詳細信息,可以看到對應節點的 IP 地址 172.17.0.3。

2. 設置網絡接口的參數

類似地,網格 CNI 插件將爲 ztunnel pod 設置網絡接口,rp_filter 參數設置爲 0,關閉反向路徑過濾,繞過數據包源 IP 地址的校驗;

3. 爲 ztunnel pod 設置 iptables、路由表和路由規則

在 ztunnel pod 上,pistioin 接口接收到的任何內容都會被轉發到端口 15008(HBONE)和 15006(純文本)。

同樣,pistioout 接口接收到的數據包最終會到達端口 15001。

ztunnel 還捕獲端口 15053 上的 DNS 請求,以提高網格的性能和可用性。請注意,僅當在 DNS 代理中指定的 ISTIO_META_DNS_CAPTURE 設置爲 true 時,纔會創建配置到 15053 的路由規則。

  • 網格 CNI 插件爲 ztunnel Pod 裏設置路由表,效果類似如下命令的執行結果:

  • 要查看該路由表,請運行下面的命令:

網格 CNI 插件爲 ztunnel Pod 裏設置 ztunnel Pod 也設置了路由規則,可以使用 ip rule list 命令查看路由規則:

  • 對應的添加命令如下:

對應的含義指,當數據包的標記值在 0x400~0xfff 範圍內時,將會匹配這條規則,並將數據包發送到編號爲 100 的路由表中進行操作,優先級爲 20000。

當數據包的標記值恰好等於 0x4d3~0xfff 範圍內時,將會匹配這條規則,並將數據包發送到編號爲 100 的路由表中進行操作,優先級爲 20003。

此外,網格 CNI 插件爲 ztunnel 配置 iptables,定義如下:

可以看到,iptables 規則使用 TPROXY 標記(0x400/0xfff)標記來自入站或出站隧道的數據包,並將其定向到相應的 ztunnel 入站和出站端口。爲什麼使用 TPROXY 方式呢?TPROXY 是 Linux 內核的一個功能,允許在傳輸層透明地攔截和重定向網絡流量,相比之下,使用 REDIRECT 目標修改數據包以更改目標地址。使用 TPROXY,這樣請求從pod通過隧道到 ztunnel 代理的所有跳躍中,流量的原始源 IP 和端口被保留,允許目標服務器看到原始客戶端的 IP 地址和端口。

04 節點與 ztunnel 通過 Geneve 隧道連接

Geneve(Generic Network Virtualization Encapsulation)是一種網絡虛擬化封裝(隧道)協議,它的設計的初衷是爲了解決當前數據傳輸缺乏靈活性和安全性的問題。

Geneve 相較於 VXLAN 封裝,具有更加靈活、安全、擴展和運維的特點,適用於更加複雜和安全性要求高的虛擬化網絡環境。

veth 設備與 netlink 機制之間存在關聯。netlink 是 Linux 內核中的一種通信機制,用於內核與用戶空間之間的通信。通過 netlink 機制,用戶空間可以向內核發送請求,獲取網絡設備、路由表、套接字等信息,實現網絡配置和管理等功能。

在容器化技術中,veth 設備的創建和配置通常是通過 netlink 機制實現的。前面的介紹中提及可以使用以下類似命令創建一個名爲 istioin 的 Geneve 設備:

ip link add name istioin type geneve id 1000 remote "${ZTUNNEL_IP}"

其中,type geneve 表示創建一個 Geneve 設備,id 1000 表示設備的虛擬網絡標識符,remote IP 地址表示設備的遠程目的地址。

CNI 插件在每個節點上初始化路由並設置 iptables 和 ipset 規則。在每個節點上,設置了兩個虛擬接口 - istioin 和 istioout,用於處理節點上的入站 (istioin) 和出站 (istioout) 流量。其中,虛擬接口 istioin 和 istioout 的 IP 地址分別爲 192.168.126.1 和 192.168.127.1。

這兩個接口使用 GENEVE(通用網絡虛擬化封裝)隧道連接到在同一節點上運行的 ztunnel pod 的接口 pistioin 和 pistioout 上。其中,虛擬接口 pistioin 和 pistioout的 IP 地址分別爲 192.168.126.2 和 192.168.127.2。

結合節點上的 iptables 規則和路由表,確保來自 ambient pods 的流量被攔截,並根據方向(入站或出站)分別發送到 istioout 或 istioin。發送到這些接口的數據包最終會到達在同一節點上運行的 ztunnel pod 的 pistioout 或 pistioin。

具體來說,使用 Geneve 隧道連接到節點上的 istioout 接口 ---> ztunnel 端的 pistioout 接口,如下所示:

使用 Geneve 隧道連接到節點上的 istioin 接口 ---> ztunnel 端的 pistioin 接口,如下所示:

05 L4 與 L7 的融合及端到端的流量路徑

對於僅涉及到 4 層請求的業務場景來說,L4 請求處理下的端到端的流量路徑如下所示:

1. Ambient 模式下的應用 Pod 會被 CNI 插件將其 IP 地址寫入到 ipset 中,當發起請求時,流量數據包進入到該節點上對應的 veth 接口。

2. sleep app pod 的請求被節點上的規則和 iptables 配置捕獲。

3. 因爲該 pod 是環境網格的一部分,它的 IP 地址被添加到節點上的 IP 集合中,並且數據包被標記爲 0x100。

4. 節點上的規則指定任何標記爲 0x100 的數據包都要通過 istio 出口接口定向到目標 192.168.127.2。

5. ztunnel 代理上的規則透明地代理來自 pistioout 的數據包到 ztunnel 出站端口 15001。

6. ztunnel 處理數據包並將其發送到目標服務(httpbin)的 IP 地址。該地址在節點 B 上爲 httpbin創建專用接口 veth,請求在該接口上被捕獲。

7. 入站流量的規則確保數據包被路由到 istioin 接口。

8. istioin 和 pistioin 之間的隧道使數據包落在 ztunnel pod 上。

9. iptables 配置捕獲來自 pistioin 的數據包,並根據標記將它們定向到端口 15008。

10. ztunnel pod 處理數據包並將其發送到目標 pod。而如果涉及到 7 層請求的業務場景來說,端到端的流量路徑則增加了 Waypoint Proxy 的部分,如下所示:

在 Waypoint Proxy 中執行的流程如下:

1. 進入到 waypoint proxy 之後, 通過流量總入口監聽 connect_terminate 接收來自 HBONE 的流量,包括身份認證,HBONE 解包等。然後將流量轉給 main_internal 這個主監聽器。

2. 主監聽器裏面有匹配邏輯,根據 service ip + port 來匹配。匹配上之後,執行各種 L7 流量策略。然後轉發給對應的 cluster。cluster 並沒有把流量直接轉發出去,而是轉發給了connect_originate。

3. connect_originate 以 HBONE 的方式,向上遊轉發數據。

06 Sidecarless 與 Sidecar 模式融合的服務網格

作爲業內首個全託管 Istio 兼容的阿里雲服務網格 ASM 產品,在不同維度提供了企業級產品功能:

1)在控制面維度,實現了全面託管機制,爲用戶降低運維複雜度;爲數據面提供了統一標準化的接入方式;支持開箱即用的 EnvoyFilter 插件市場,支持 Serverless/Knative 及 AI Serving/KServe 生態等。

2)在數據面維度,支持形態與功能多樣化,支持不同形態的計算基礎設施,包括 K8s 集羣、Serverless ECI 節點、邊緣集羣、註冊集羣等;支持不同的數據面網絡形態;支持異構服務統一治理、精細化的多協議流量控制與全鏈路灰度管理。

3)在性能維度,實現軟硬一體的端到端網格優化,支持自適應配置推送優化,實現了 AVX 指令集提升 TLS 加解密、以及資源超賣模式下的支持等。更多內容可以參考:《企業級服務網格優化中心:優化 Service Mesh 以提高性能和高可用性》

近期阿里雲服務網格 ASM 產品即將推出業界首個 Sidecarless 與 Sidecar 模式融合的服務網格平臺,歡迎試用與交流!

可以通過:https://www.aliyun.com/product/servicemesh 查看具體的內容介紹。

作者:王夕寧 阿里雲服務網格負責人,以下內容基於 2023 全球軟件工程創新技術峯會的演講內容整編而成

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章