KubeCon 2021|使用 eBPF 代替 iptables 優化服務網格資料面效能
作者簡介
劉旭
騰訊雲高階工程師,專注容器雲原生領域,有多年大規模 Kubernetes 叢集管理及微服務治理經驗,現負責騰訊雲服務網格 TCM 資料面產品架構設計和研發工作。
引言
目前以 Istio[1] 為代表的服務網格普遍使用 Sidecar 架構,並使用 iptables 將流量劫持到 Sidecar 代理,優點是對應用程式無侵入,但是 Sidecar 代理會增加請求時延和資源佔用。
效能一直是使用者十分關心的一個點,也是使用者評估是否使用服務網格產品的關鍵因素,騰訊雲 TCM 團隊一直致力於優化服務網格效能,上週我們在 KubeCon 分享了 使用 eBPF 代替 iptables 優化服務網格資料面效能的方案 。
iptables 實現流量劫持
首先看一下當前社群使用的基於 iptables 的流量劫持方案,下圖是一個 Pod 的建立過程,sidecar injector 會向 Pod 中注入兩個容器,istio-init 和 istio-proxy。
-
istio-init 是一個 init container,負責建立流量劫持相關的 iptables 規則,在建立完成後會退出;
-
istio-proxy 中執行著 envoy,負責代理 Pod 的網路流量,iptables 會將請求劫持到 istio-proxy 處理。
下圖展示了 iptables 完成流量劫持的整個過程,這裡簡單說明下,感興趣的同學可以檢視[2]
-
Inboundiptables 將入流量重定向到 15006 埠,也就是 envoy 的 VirtualInboundListener,envoy 會根據請求的原始目的地址轉發到應用程式的指定埠;
-
Outboundiptables 將出流量重定向到 15001 埠,也就是 envoy 的 VirtualOutboundListener,envoy 會根據請求的原始目的地址以及 Host URL 等資訊路由到指定後端。
eBPF 實現流量劫持
eBPF(extended Berkeley Packet Filter) 是一種可以在 Linux 核心中執行使用者編寫的程式,而不需要修改核心程式碼或載入核心模組的技術,目前被廣泛用於網路、安全、監控等領域。在 Kubernetes 社群最早也是最有影響的基於 eBPF 專案是 Cilium[4],Cilium 使用 eBPF 代替 iptables 優化 Service 效能。
Inbound
首先來看一下對入流量的劫持,對入流量的劫持主要使用 eBPF 程式 hook bind 系統呼叫完成。
eBPF 程式會劫持 bind 系統呼叫並修改地址,例如應用程式 bind 0.0.0.0:80 會被修改為 127.0.0.1:80,應用程式還有可能 bind ipv6 的地址,所以這裡有兩個 eBPF 程式分別處理 ipv4 和 ipv6 的 bind。
和 iptables 不同,iptables 可以針對每個 netns 單獨設定規則,eBPF 程式 attach 到指定 hook 點後,會對整個系統都生效,例如 attach 到 bind 系統呼叫後,所有 Pod 內以及節點上程序呼叫 bind 都會觸發 eBPF 程式,我們需要區分哪些呼叫是來自需要由 eBPF 完成流量劫持的 Pod。
在 K8s 中,除了 hostnetwork 的情況,每個 Pod 都有獨立的 netns,而每個 netns 都有唯一的 cookie,因此我們將需要使用 eBPF 完成流量劫持的 Pod 對應的 netns cookie 儲存在 cookie_map
中,eBPF 程式通過判斷當前 socket 的 netns cookie 是否在 cookie_map
中來決定是否修改 bind 地址。
修改應用程式的 bind 地址後,還需要下發 pod_ip:80 listener 配置到 envoy,pod_ip:80 listener 會將請求轉發到 127.0.0.1:80 也就是應用程式監聽的地址,這樣就實現了對入流量的劫持。但是這裡有一個問題,由於 istio 使用 istio-proxy 使用者啟動 envoy,預設情況下非 root 使用者不能 bind 1024 以下的特權埠,我們通過 istio-init 修改核心引數 sysctl net.ipv4.ip_unprivileged_port_start=0
解決了這個問題。
對比 iptables 和 eBPF 對入流量的劫持,iptables 方案每個包都需要 conntrack 處理,而 eBPF 方案只有在應用程式呼叫 bind 時執行一次,之後不會再執行,減少了效能開銷。
Outbound
再來看一下對出流量的劫持,對出流量的劫持比較複雜, 根據協議分為 TCP 和 UDP 兩種情況。
TCP 流量劫持
對 TCP 的出流量劫持過程:
-
_coonect4
通過劫持 connect 系統呼叫將目的地址修改為127.0.0.1:15001,也就是 envoy 的 VirtualOutboundListerer,同時將連線的原始目的地址儲存在sk_storage_map;
-
在 TCP 連線建立完成後,
sockops
會讀取sk_storage_map
中的資料,並以四元組(源IP、目的IP、源埠、目的埠)為 key 將原始目的地址儲存在origin_dst_map;
-
_getsockopt
通過劫持 getsockopt 系統呼叫,讀取origin_dst_map
中的資料將原始目的地址返回給 envoy。
UDP 流量劫持
istio 在 1.8 版本支援了智慧 DNS 代理[5],開啟後 iptables 會將 DNS 請求劫持到 Sidecar 處理,我們也需要用 eBPF 實現相同邏輯,對於 TCP DNS 的劫持和上面類似, 對 UDP DNS 的劫持見下圖:
對 UDP 的出流量劫持過程:
-
_connect4
和_sendmsg4
都是負責修改 UDP 的目的地址為 127.0.0.1:15053 並儲存原始的目的地址到sk_storage_map
,因為 Linux 提供兩種傳送 UDP 資料的方式 -
先呼叫 connect 再呼叫 send,這種情況由
_connect4
處理 -
直接呼叫 sendto,這種情況由
_sendmsg4
處理 -
recvmsg4
通過讀取sk_storage_map
將回包的源地址改為原始的目的地址,這是因為有些應用程式,例如 nslookup 會校驗回包的源地址。
對於 TCP 和 connected UDP,iptables 方案每個包都需要 conntrack 處理,而eBPF 方案的開銷是一次性的,只需要在 socket 建立時執行一次,降低了效能開銷。
Sockmap
使用 sockmap 優化服務網格效能的方案最早由 cilium 提出,我們的方案也參考了 cilium,這裡 借用 cilium 的兩張圖來說明下優化效果。
優化前 Sidecar 代理與應用程式間的網路通訊都需要經過 TCP/IP 協議棧處理。
優化後 Sidecar 代理與應用程式間的網路通訊繞過了 TCP/IP 協議棧,如果兩個 Pod 在同一節點上,兩個 Pod 間的網路通訊也可以被優化。這裡簡單說明下 sockmap 的優化原理,感興趣的同學可以檢視[6][7]。
-
sock_hash
是一個儲存 socket 資訊的 eBPF map,key 是四元組(源IP、目的IP、源埠、目的埠); -
_sockops
負責監聽 socket 事件,並將 socket 資訊儲存在sock_hash;
-
_sk_msg
會攔截 sendmsg 系統呼叫,然後到sock_hash
中查詢對端 socket,如果找到會呼叫bpf_msg_redirect_hash
直接將資料傳送給對端 socket。
問題
但是用四元組做為 key 可能會存在衝突的問題,例如在同一節點上的兩個 Pod 中,envoy 使用同一源埠 50000 請求應用程式的 80 埠。
為了解決這個問題,我們在 key 中添加了 netns cookie,同時對於非 localhost 的請求將 cookie 設定為 0,這樣既保證了 key 不會衝突,又可以加速同一節點上兩個 Pod 間的網路通訊。
但是之前版本的核心不支援在 sockops
和 sk_msg
這兩種 eBPF 程式中獲取 netns cookie 資訊,因此我們提交了兩個 patch [8 ][9]到核心社群,目前已合入 5.15 版本。
架構
整個方案的架構如圖所示,istio-ebpf 以 DaemonSet 的形式執行在節點上,負責 load/attach eBPF 程式和建立 eBPF map。istio-init 容器仍然保留,但是不再建立 iptables 規則,而是更新 eBPF map,istio-init 會將 Pod 的 netns cookie 儲存在 cookie_map 中。同時我們也修改了 istiod,istiod 會根據 Pod 的流量劫持模式(iptables/eBPF)下發不同的 xDS 配置。
效能對比
測試環境:Ubuntu 21.04 5.15.7
-
同等條件下,使用 eBPF 可減少 20% 的 System CPU 佔用;
-
同等條件下,使用 eBPF 可提高 20% QPS;
-
同等條件下,使用 eBPF 可降低請求時延。
總結
服務網格的 Sidecar 架構不可避免的會增加請求時延和資源佔用,我們通過使用 eBPF 代替 iptables 實現流量劫持,同時使用 sockmap 加速 Sidecar 代理和應用程式間的網路通訊,在 一定程度上降低了請求時延和資源開銷 ,由於核心版本等限制這一方案預計會在明年初上線,TCM 團隊將持續探索新的效能優化方向。
參考資料
[1] http://istio.io
[2] http://jimmysong.io/blog/sidecar-injection-iptables-and-traffic-routing
[3] http://ebpf.io
[4] http://cilium.io
[5] http://istio.io/latest/blog/2020/dns-proxy
[6] http://arthurchiao.art/blog/socket-acceleration-with-ebpf-zh
[7] http://github.com/cilium/cilium/tree/v1.11.0/bpf/sockops
[8] http://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=6cf1770d
[9] http://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=fab60e29f
- 騰訊正式開源 Spring Cloud Tencent,打造一站式微服務解決方案
- 騰訊正式開源Spring Cloud Tencent,打造一站式微服務解決方案
- 億級月活全民K歌feed業務在騰訊雲cmongo中的應用及優化實踐
- Polaris-Sidecar:更低成本的內網DNS實現
- Polaris-Sidecar:更低成本的內網DNS實現
- KubeCon 2021|使用 eBPF 代替 iptables 優化服務網格資料面效能
- 分散式任務排程:你知道和不知道的事
- 分散式任務排程:你知道與不知道的事
- 分散式任務排程:你知道和不知道的事
- Apache Pulsar 技術系列 - Pulsar事務實現原理
- 雲原生時代的Java應用優化實踐
- Apache Pulsar 技術系列 - Pulsar事務實現原理
- 雲原生時代的Java應用優化實踐
- 展望中介軟體的 2022:更加雲原生化,尋求標準化
- 騰訊雲訊息佇列TDMQ又一系列產品正式開啟公測,戳文檢視吧!
- Message deduplication 這裡的去重與你想的可能不一樣|Apache Pulsar 技術系列
- Message deduplication 這裡的去重與你想的可能不一樣|Apache Pulsar 技術系列
- 騰訊雲訊息佇列TDMQ RabbitMQ 版開啟公測,文末有驚喜!
- 騰訊雲訊息佇列TDMQ RabbitMQ 版開啟公測,文末有驚喜!
- 全面擁抱Go社群:PolarisMesh全功能對接gRPC-Go | PolarisMesh12月月報