天天看點

Flomesh 服務網格采用 eBPF 實作更高效的流量攔截和通信

作者:opendotnet

在不久前釋出的 Flomesh 服務網格 1.3.3[1] 我們引入了 eBPF 功能,用以替代流量攔截方面的實作 iptables。由于 eBPF 對較新核心的依賴,iptables 的實作仍繼續提供。同時,得益于 eBPF 網絡方面的能力,我們也實作了同節點網絡通信的加速。

背景

在服務網格中,iptables 和 eBPF 是兩種比較常見的流量攔截方式。

iptables 是一種基于 Linux 核心的流量攔截工具,它可以通過過濾規則來對流量進行控制。它的優點包括:

  • • 通用性:iptables 工具已經在 Linux 作業系統中被廣泛使用,是以大多數的 Linux 使用者都熟悉它的使用方法;
  • • 穩定性:iptables 早已經成為了 Linux 核心的一部分,是以具有較高的穩定性;
  • • 靈活性:iptables 工具可以根據需要靈活地配置規則,以控制網絡流量。

然而,iptables 也存在一些缺點:

  • • 難以調試:由于 iptables 工具本身較為複雜,是以在進行調試時比較困難;
  • • 性能問題:iptables 會在核心空間中進行處理,這可能會對網絡性能産生影響;
  • • 處理複雜流量可能存在問題:當涉及到一些複雜的流量處理時,iptables 可能不太适合,因為其規則處理不夠靈活。

ebpf 是一種進階的流量攔截工具,它可以通過自定義的程式在 Linux 核心中進行流量攔截和分析。ebpf 的優點包括:

  • • 靈活性:ebpf 可以使用自定義的程式來攔截和分析流量,是以具有更高的靈活性;
  • • 可擴充性:ebpf 可以動态加載和解除安裝程式,是以具有更高的可擴充性;
  • • 高效性:ebpf 可以在核心空間中進行處理,是以具有更高的性能。

然而,ebpf 也存在一些缺點:

  • • 較高的學習曲線:ebpf 相對于 iptables 來說比較新,是以需要一些學習成本;
  • • 複雜性:ebpf 自定義程式的開發可能比較複雜;
  • • 安全性:由于 ebpf 可以直接操作核心,是以需要謹慎使用以確定安全。

綜合來看,iptables 更适合簡單的流量過濾和管理,而 ebpf 更适合需要更高靈活性和性能的複雜流量攔截和分析場景。

架構

在 1.3.3 中為了提供 eBPF 特性,Flomesh 服務網格提供了 CNI 實作 osm-cni 和運作于各個節點的 osm-interceptor,其中 osm-cni 可與主流的 CNI 插件相容。

當 kubelet 在節點上建立 pod,會通過容器運作時 CRI 實作調用 CNI 的接口建立 Pod 的網絡命名空間,osm-cni 會在 pod 網絡命名空間建立完成後調用 osm-interceptor 的接口加載 BPF 程式并将其附加到 hook 點上。除此以外 osm-interceptor 還會在 eBPF Maps 中維護 pod 資訊等内容。

Flomesh 服務網格采用 eBPF 實作更高效的流量攔截和通信

實作原理

接下來介紹下引入 eBPF 後帶來的兩個特性的實作原理,注意這裡會忽略很多的處理細節。

流量攔截

出站流量

下面的圖中展示的是出站(outbound)流量的攔截。将 BPF 程式附加到 socket 操作

connect

上,在程式中判斷目前 pod 是否被網格納管,也就是是否注入 sidecar,然後将目标位址修改為

127.0.0.1

、目标端口修改為 sidecar 的 outbound 端口

15003

。隻是修改還不夠,還要将原始目的位址和端口儲存在 map 中,使用 socket 的 cookie 作為 key。

當與 sidecar 的連接配接建立成功後,通過附加到挂載點

sock_ops

上的程式将原始目的地儲存在另一個 map 中,使用 本地位址 + 端口和遠端位址 + 端口 作為 key。後面 sidecar 通路目标應用時,通過 socket 的

getsockopt

操作獲得原始目的位址。沒錯,

getsockopt

上也附加了 BPF 程式,程式會從 map 中取出原地目的位址并傳回。

Flomesh 服務網格采用 eBPF 實作更高效的流量攔截和通信

入站流量

對于入站流量的攔截,主要是将原本通路應用端口的流量,轉發到 sidecar 的 inbound 端口

15003

。這裡有兩種情況:

  • • 第一種,請求方和服務方位于同一個節點,請求方 sidecar 的

    connect

    操作被攔截後會将目标端口改為

    15003

  • • 第二種,二者位于不同的節點,請求方 sidecar 使用原始端口建立連接配接,但握手的資料包到了服務方網絡命名空間時,被附加到 tc(traffic control)ingress 上的 BPF 程式将端口修改為

    15003

    ,實作類似 DNAT 的功能。

網絡通信加速

網絡資料包在 Kubernetes 的網絡中不可避免的要經過多次核心網絡協定棧的處理,eBPF 對網絡通信的加速則是通過跳過不必要的核心網絡協定棧,由互為對端的兩個 socket 直接交換資料。

在上面流量攔截的圖中有消息的發送和接收軌迹。當附加在

sock_ops

上的程式發現連接配接建立成功,會将 socket 儲存 map 中,同樣使用 本地位址 + 端口和遠端位址 + 端口 作為 key。而互為對端的兩個 socket,本地和遠端資訊正好 相反,是以一個 socket 發送消息時可以直接從 map 中尋址對端的 socket。

對于同一個節點上的兩個 pod 通信,該方案也同樣适用。

Flomesh 服務網格采用 eBPF 實作更高效的流量攔截和通信

快速體驗

  • • Ubuntu 20.04
  • • Kernel 5.15.0-1034
  • • 2c4g 虛拟機 * 3:master、node1、node2

安裝 CNI 插件

在所有的節點上執行下面的指令,下載下傳 CNI 插件。

sudo mkdir -p /opt/cni/bin
curl -sSL https://github.com/containernetworking/plugins/releases/download/v1.1.1/cni-plugins-linux-amd64-v1.1.1.tgz | sudo tar -zxf - -C /opt/cni/bin           

Master 節點

擷取 master 節點的 IP 位址。

export MASTER_IP=10.0.2.6           

Kubernetes 叢集使用 k3s 發行版,但在安裝叢集的時候,需要禁用 k3s 內建的 flannel,使用獨立安裝的 flannel 進行驗證。這是因為 k3s 的 CNI bin 目錄在

/var/lib/rancher/k3s/data/xxx/bin

下,而非

/opt/cni/bin

curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb --flannel-backend=none --advertise-address $MASTER_IP --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config           

安裝 Flannel。這裡注意,Flannel 預設的 Pod CIDR 是

10.244.0.0/16

,我們将其修改為 k3s 預設的

10.42.0.0/16

curl -s https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml | sed 's|10.244.0.0/16|10.42.0.0/16|g' | kubectl apply -f -           

擷取 apiserver 的通路 token,用于初始化工作節點。

sudo cat /var/lib/rancher/k3s/server/node-token           

工作節點

使用 master 節點的 IP 位址以及前面擷取的 token 初始化節點。

export INSTALL_K3S_VERSION=v1.23.8+k3s2
export NODE_TOKEN=K107c1890ae060d191d347504740566f9c506b95ea908ba4795a7a82ea2c816e5dc::server:2757787ec4f9975ab46b5beadda446b7
curl -sfL https://get.k3s.io | K3S_URL=https://${MASTER_IP}:6443 K3S_TOKEN=${NODE_TOKEN} sh -           

下載下傳 osm-edge CLI

system=$(uname -s | tr [:upper:] [:lower:])
arch=$(dpkg --print-architecture)
release=v1.3.3
curl -L https://github.com/flomesh-io/osm-edge/releases/download/${release}/osm-edge-${release}-${system}-${arch}.tar.gz | tar -vxzf -
./${system}-${arch}/osm version
sudo cp ./${system}-${arch}/osm /usr/local/bin/           

安裝 osm-edge

export osm_namespace=osm-system 
export osm_mesh_name=osm 

osm install \
 --mesh-name "$osm_mesh_name" \
 --osm-namespace "$osm_namespace" \
 --set=osm.trafficInterceptionMode=ebpf \
 --set=osm.osmInterceptor.debug=true \
 --timeout=900s           

部署示例應用

#模拟業務服務
kubectl create namespace ebpf
osm namespace add ebpf
kubectl apply -n ebpf -f https://raw.githubusercontent.com/cybwan/osm-edge-start-demo/main/demo/interceptor/curl.yaml
kubectl apply -n ebpf -f https://raw.githubusercontent.com/cybwan/osm-edge-start-demo/main/demo/interceptor/pipy-ok.yaml

#讓 Pod 排程到不同的 node 上
kubectl patch deployments curl -n ebpf -p '{"spec":{"template":{"spec":{"nodeName":"node1"}}}}'
kubectl patch deployments pipy-ok-v1 -n ebpf -p '{"spec":{"template":{"spec":{"nodeName":"node1"}}}}'
kubectl patch deployments pipy-ok-v2 -n ebpf -p '{"spec":{"template":{"spec":{"nodeName":"node2"}}}}'

sleep 5

#等待依賴的 POD 正常啟動
kubectl wait --for=condition=ready pod -n ebpf -l app=curl --timeout=180s
kubectl wait --for=condition=ready pod -n ebpf -l app=pipy-ok -l version=v1 --timeout=180s
kubectl wait --for=condition=ready pod -n ebpf -l app=pipy-ok -l version=v2 --timeout=180s           

測試

測試時可通過指令檢視工作節點上的核心 tracing 日志檢視 BPF 程式執行的 debug 日志。為了避免幹擾 sidecar 與控制平面通信産生的幹擾,先擷取控制面的 IP 位址。

kubectl get svc -n osm-system osm-controller -o jsonpath='{.spec.clusterIP}'
10.43.241.189           

在兩個工作節點執行下面的指令。

sudo cat /sys/kernel/debug/tracing/trace_pipe | grep bpf_trace_printk | grep -v '10.43.241.189'           

執行下面的指令發起請求。

curl_client="$(kubectl get pod -n ebpf -l app=curl -o jsonpath='{.items[0].metadata.name}')"
kubectl exec ${curl_client} -n ebpf -c curl -- curl -s pipy-ok:8080           

正常會收到類似下面的結果,同時核心 tracing 日志也會相應地輸出 BPF 程式的 debug 日志(内容較多,這裡不做展示)。

Hi, I am pipy ok v1 !
Hi, I am pipy ok v2 !           

引用連結

[1]

Flomesh 服務網格 1.3.3: https://github.com/flomesh-io/osm-edge/releases/tag/v1.3.3

繼續閱讀