Cilium Socket LB 特性

evescn發表於2024-07-10

Cilium Socket LB

一、環境資訊

主機 IP
ubuntu 172.16.94.141
軟體 版本
docker 26.1.4
helm v3.15.0-rc.2
kind 0.18.0
kubernetes 1.23.4
ubuntu os Ubuntu 20.04.6 LTS
kernel 5.11.5 核心升級文件

二、Cilium SocketLB 模式認知

負載均衡的實現方式通常有如下兩種:

  • 客戶端負載均衡,自行選擇目標端點。這樣做的好處是,在建立連線時預先支付負載均衡的成本,並且在連線的生命週期內不存在額外的開銷。這種方法的缺點是這對應用程式不透明。
  • 服務端負載均衡,透過一箇中間服務將請求轉換為對應服務 IP 來執行負載平衡。與客戶端負載平衡相比,此方法的優點是透明。應用程式本身不涉及。然而,缺點是每個網路資料包都需要在請求和響應上更改其 IP 地址。

Cilium 1.6 中,引入了基於套接字的負載平衡(socket-based load-balancing),它結合了兩種方法的優點:

img

  • Transparent: 負載平衡對應用程式保持 100% 透明。服務是使用標準 Kubernetes svc
  • Highly efficient: 透過轉換地址在socket層面執行負載平衡,LB的成本是在建立連線時預先支付的,並且在之後的連線持續時間內不需要額外的ip地址轉換。效能與應用程式直接與後端對話相同。

三、傳統 kube-proxy 實現 service 負載均衡

img

可以檢視此文件 Cilium Native Routing with kubeProxy 模式 中,Service 網路通訊,資料包轉發流程一致

四、Cilium Socket LB 如何實現東西向流量的負載均衡

kind 配置檔案資訊

root@kind:~# cat install.sh

#!/bin/bash
date
set -v

# 1.prep noCNI env
cat <<EOF | kind create cluster --name=cilium-socket-lb --image=kindest/node:v1.23.4 --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  # kind 預設使用 rancher cni,cni 我們需要自己建立
  disableDefaultCNI: true
  # kind 安裝 k8s 叢集需要禁用 kube-proxy 安裝,是 cilium 代替 kube-proxy 功能
  kubeProxyMode: "none"

nodes:
 - role: control-plane
 - role: worker
 - role: worker

containerdConfigPatches:
- |-
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.evescn.com"]
    endpoint = ["https://harbor.evescn.com"]
EOF

# 2.remove taints
controller_node_ip=`kubectl get node -o wide --no-headers | grep -E "control-plane|bpf1" | awk -F " " '{print $6}'`
#kubectl taint nodes $(kubectl get nodes -o name | grep control-plane) node-role.kubernetes.io/master:NoSchedule-
kubectl get nodes -o wide

# 3.install cni
helm repo add cilium https://helm.cilium.io > /dev/null 2>&1
helm repo update > /dev/null 2>&1

# Direct Routing Options(--set kubeProxyReplacement=strict --set tunnel=disabled --set autoDirectNodeRoutes=true --set ipv4NativeRoutingCIDR="10.0.0.0/8")
# Host Routing[EBPF](--set bpf.masquerade=true)

helm install cilium cilium/cilium \
   --set k8sServiceHost=$controller_node_ip \
   --set k8sServicePort=6443 \
   --version 1.13.0-rc5 \
   --namespace kube-system \
   --set debug.enabled=true \
   --set debug.verbose=datapath \
   --set monitorAggregation=none \
   --set ipam.mode=cluster-pool \
   --set cluster.name=cilium-socket-lb \
   --set kubeProxyReplacement=strict \
   --set tunnel=disabled \
   --set autoDirectNodeRoutes=true \
   --set ipv4NativeRoutingCIDR="10.0.0.0/8" \
   --set bpf.masquerade=true \
   --set installNoConntrackIptablesRules=true \
   --set socketLB.enabled=true


# 4.install necessary tools
for i in $(docker ps -a --format "table {{.Names}}" | grep cilium) 
do
    echo $i
    docker cp /usr/bin/ping $i:/usr/bin/ping
    docker exec -it $i bash -c "sed -i -e 's/jp.archive.ubuntu.com\|archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list"
    docker exec -it $i bash -c "apt-get -y update >/dev/null && apt-get -y install net-tools tcpdump lrzsz bridge-utils >/dev/null 2>&1"
done

--set 引數解釋

  1. --set kubeProxyReplacement=strict

    • 含義: 啟用 kube-proxy 替代功能,並以嚴格模式執行。
    • 用途: Cilium 將完全替代 kube-proxy 實現服務負載均衡,提供更高效的流量轉發和網路策略管理。
  2. --set tunnel=disabled

    • 含義: 禁用隧道模式。
    • 用途: 禁用後,Cilium 將不使用 vxlan 技術,直接在主機之間路由資料包,即 direct-routing 模式。
  3. --set autoDirectNodeRoutes=true

    • 含義: 啟用自動直接節點路由。
    • 用途: 使 Cilium 自動設定直接節點路由,最佳化網路流量。
  4. --set ipv4NativeRoutingCIDR="10.0.0.0/8"

    • 含義: 指定用於 IPv4 本地路由的 CIDR 範圍,這裡是 10.0.0.0/8
    • 用途: 配置 Cilium 使其知道哪些 IP 地址範圍應該透過本地路由進行處理,不做 snat , Cilium 預設會對所用地址做 snat。
  5. --set bpf.masquerade

    • 含義: 啟用 eBPF 功能。
    • 用途: 使用 eBPF 實現資料路由,提供更高效和靈活的網路地址轉換功能。
  6. --set installNoConntrackIptablesRules=true:

    • 安裝無連線跟蹤的 iptables 規則,這樣可以減少 iptables 規則集中的連線跟蹤負擔。
  7. --set socketLB.enabled=true:

    • 啟用 Socket Load Balancer(SLB),用於最佳化服務之間的負載均衡。
  • 安裝 k8s 叢集和 cilium 服務
root@kind:~# ./install.sh

Creating cluster "cilium-socket-lb" ...
 ✓ Ensuring node image (kindest/node:v1.23.4) 🖼
 ✓ Preparing nodes 📦 📦 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing StorageClass 💾 
 ✓ Joining worker nodes 🚜 
Set kubectl context to "kind-cilium-socket-lb"
You can now use your cluster with:

kubectl cluster-info --context kind-cilium-socket-lb

Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/

cilium 配置資訊

root@kind:~# kubectl -n kube-system exec -it ds/cilium -- cilium status

KVStore:                 Ok   Disabled
Kubernetes:              Ok   1.23 (v1.23.4) [linux/amd64]
Kubernetes APIs:         ["cilium/v2::CiliumClusterwideNetworkPolicy", "cilium/v2::CiliumEndpoint", "cilium/v2::CiliumNetworkPolicy", "cilium/v2::CiliumNode", "core/v1::Namespace", "core/v1::Node", "core/v1::Pods", "core/v1::Service", "discovery/v1::EndpointSlice", "networking.k8s.io/v1::NetworkPolicy"]
KubeProxyReplacement:    Strict   [eth0 172.18.0.3 (Direct Routing)]
Host firewall:           Disabled
CNI Chaining:            none
CNI Config file:         CNI configuration file management disabled
Cilium:                  Ok   1.13.0-rc5 (v1.13.0-rc5-dc22a46f)
NodeMonitor:             Listening for events on 128 CPUs with 64x4096 of shared memory
Cilium health daemon:    Ok   
IPAM:                    IPv4: 6/254 allocated from 10.0.0.0/24, 
IPv6 BIG TCP:            Disabled
BandwidthManager:        Disabled
Host Routing:            BPF
Masquerading:            BPF   [eth0]   10.0.0.0/8 [IPv4: Enabled, IPv6: Disabled]
Controller Status:       35/35 healthy
Proxy Status:            OK, ip 10.0.0.231, 0 redirects active on ports 10000-20000
Global Identity Range:   min 256, max 65535
Hubble:                  Ok   Current/Max Flows: 4095/4095 (100.00%), Flows/s: 6.85   Metrics: Disabled
Encryption:              Disabled
Cluster health:          3/3 reachable   (2024-06-27T09:06:44Z)

root@kind:~# kubectl -n kube-system exec -it ds/cilium -- cilium status --verbose
......
KubeProxyReplacement Details:
  Status:                 Strict
  # 啟用了 Socket LB 功能
  Socket LB:              Enabled
  Socket LB Tracing:      Enabled
......
  • KubeProxyReplacement: Strict [eth0 172.18.0.3 (Direct Routing)]
    • Cilium 完全接管所有 kube-proxy 功能,包括服務負載均衡、NodePort 和其他網路策略管理。這種配置適用於你希望最大限度利用 Cilium 的高階網路功能,並完全替代 kube-proxy 的場景。此模式提供更高效的流量轉發和更強大的網路策略管理。
  • Host Routing: BPF
    • 使用 BPF 進行主機路由。
  • Masquerading: BPF [eth0] 10.0.0.0/8 [IPv4: Enabled, IPv6: Disabled]
    • 使用 BPF 進行 IP 偽裝(NAT),介面 eth0,IP 範圍 10.0.0.0/8 不回進行 NAT。IPv4 偽裝啟用,IPv6 偽裝禁用。
  • Socket LB: Enabled
    • 啟用了 Socket LB 功能,Service 服務訪問時,之間使用後端 pod ip port 進行資料返回。

k8s 叢集安裝 Pod 測試網路

# cat cni.yaml

apiVersion: apps/v1
kind: DaemonSet
#kind: Deployment
metadata:
  labels:
    app: cni
  name: cni
spec:
  #replicas: 1
  selector:
    matchLabels:
      app: cni
  template:
    metadata:
      labels:
        app: cni
    spec:
      containers:
      - image: harbor.dayuan1997.com/devops/nettool:0.9
        name: nettoolbox
        securityContext:
          privileged: true

---
apiVersion: v1
kind: Service
metadata:
  name: serversvc
spec:
  type: NodePort
  selector:
    app: cni
  ports:
  - name: cni
    port: 80
    targetPort: 80
    nodePort: 32000
root@kind:~# kubectl apply -f cni.yaml
daemonset.apps/cni created
service/serversvc created

root@kind:~# kubectl run net --image=harbor.dayuan1997.com/devops/nettool:0.9
pod/net created
  • 檢視安裝服務資訊
root@kind:~# kubectl get pods -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP           NODE                       NOMINATED NODE   READINESS GATES
cni-gzclc   1/1     Running   0          32s   10.0.2.6     cilium-socket-lb-worker2   <none>           <none>
cni-sd4vv   1/1     Running   0          32s   10.0.1.145   cilium-socket-lb-worker    <none>           <none>
net         1/1     Running   0          20s   10.0.2.40    cilium-socket-lb-worker2   <none>           <none>

Service 網路通訊

  • 檢視 Service 資訊
root@kind:~# kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        31m
serversvc    NodePort    10.96.195.193   <none>        80:32000/TCP   43s
  • net 服務上請求 Pod 所在 Node 節點 32000
root@kind:~# kubectl exec -ti net -- curl 172.18.0.2:32000
PodName: cni-sd4vv | PodIP: eth0 10.0.1.145/32

並在 net 服務 eth0 網路卡 抓包檢視

net~$ tcpdump -pne -i eth0
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:20:26.931025 1a:fc:47:2b:d4:54 > 7a:b0:1f:00:ee:0a, ethertype IPv4 (0x0800), length 74: 10.0.2.40.36664 > 10.0.1.145.80: Flags [S], seq 1753200349, win 64240, options [mss 1460,sackOK,TS val 1674156063 ecr 0,nop,wscale 7], length 0
10:20:26.931401 7a:b0:1f:00:ee:0a > 1a:fc:47:2b:d4:54, ethertype IPv4 (0x0800), length 74: 10.0.1.145.80 > 10.0.2.40.36664: Flags [S.], seq 2087706195, ack 1753200350, win 65160, options [mss 1460,sackOK,TS val 3868244688 ecr 1674156063,nop,wscale 7], length 0
10:20:26.931546 1a:fc:47:2b:d4:54 > 7a:b0:1f:00:ee:0a, ethertype IPv4 (0x0800), length 66: 10.0.2.40.36664 > 10.0.1.145.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 1674156064 ecr 3868244688], length 0
10:20:26.931965 1a:fc:47:2b:d4:54 > 7a:b0:1f:00:ee:0a, ethertype IPv4 (0x0800), length 146: 10.0.2.40.36664 > 10.0.1.145.80: Flags [P.], seq 1:81, ack 1, win 502, options [nop,nop,TS val 1674156064 ecr 3868244688], length 80: HTTP: GET / HTTP/1.1
10:20:26.932279 7a:b0:1f:00:ee:0a > 1a:fc:47:2b:d4:54, ethertype IPv4 (0x0800), length 66: 10.0.1.145.80 > 10.0.2.40.36664: Flags [.], ack 81, win 509, options [nop,nop,TS val 3868244689 ecr 1674156064], length 0
10:20:26.932588 7a:b0:1f:00:ee:0a > 1a:fc:47:2b:d4:54, ethertype IPv4 (0x0800), length 302: 10.0.1.145.80 > 10.0.2.40.36664: Flags [P.], seq 1:237, ack 81, win 509, options [nop,nop,TS val 3868244689 ecr 1674156064], length 236: HTTP: HTTP/1.1 200 OK
10:20:26.932851 7a:b0:1f:00:ee:0a > 1a:fc:47:2b:d4:54, ethertype IPv4 (0x0800), length 113: 10.0.1.145.80 > 10.0.2.40.36664: Flags [P.], seq 237:284, ack 81, win 509, options [nop,nop,TS val 3868244690 ecr 1674156064], length 47: HTTP
10:20:26.933138 1a:fc:47:2b:d4:54 > 7a:b0:1f:00:ee:0a, ethertype IPv4 (0x0800), length 66: 10.0.2.40.36664 > 10.0.1.145.80: Flags [.], ack 237, win 501, options [nop,nop,TS val 1674156065 ecr 3868244689], length 0
10:20:26.933425 1a:fc:47:2b:d4:54 > 7a:b0:1f:00:ee:0a, ethertype IPv4 (0x0800), length 66: 10.0.2.40.36664 > 10.0.1.145.80: Flags [.], ack 284, win 501, options [nop,nop,TS val 1674156065 ecr 3868244690], length 0
10:20:26.936328 1a:fc:47:2b:d4:54 > 7a:b0:1f:00:ee:0a, ethertype IPv4 (0x0800), length 66: 10.0.2.40.36664 > 10.0.1.145.80: Flags [F.], seq 81, ack 284, win 501, options [nop,nop,TS val 1674156068 ecr 3868244690], length 0
10:20:26.936954 7a:b0:1f:00:ee:0a > 1a:fc:47:2b:d4:54, ethertype IPv4 (0x0800), length 66: 10.0.1.145.80 > 10.0.2.40.36664: Flags [F.], seq 284, ack 82, win 509, options [nop,nop,TS val 3868244694 ecr 1674156068], length 0
10:20:26.937084 1a:fc:47:2b:d4:54 > 7a:b0:1f:00:ee:0a, ethertype IPv4 (0x0800), length 66: 10.0.2.40.36664 > 10.0.1.145.80: Flags [.], ack 285, win 501, options [nop,nop,TS val 1674156069 ecr 3868244694], length 0

抓包資料顯示, net 服務使用隨機埠和 10.0.2.202 80 埠進行 tcp 通訊。

  • 三次握手的過程, srcIPpodIP 地址, dstIP 不再是 node 節點 IP 地址,在 cilium socket LB 的加持下,將serviceIP 轉換成了後端 podIP 地址。
  • 透過 ebpf 的劫持,沒有必要將 nat 的過程傳遞到 root namespace 中去了,不再需要 root namespace 中的 iptables 規則了,節省了網路開銷。
  • 在資料包從源 pod 中出去的時候,已經做了替換,進入 root namespace 的時候,就變成了一個簡單的跨節點 pod 間的通訊,相比基於 iptable 來說效率更高
  • 不再依賴 iptables 規則

那麼 cilium 是如何查詢 service 資訊,並返回後端 Pod ip 地址給請求方的?其實 cilium 把資料儲存在自身內部,使用 cilium 子命令可以查詢到 service 資訊

  • cilium 查詢 service 資訊
root@kind:~# kubectl -n kube-system exec cilium-mphpk -- cilium service list
ID   Frontend           Service Type   Backend                         
1    10.96.36.130:443   ClusterIP      1 => 172.18.0.2:4244 (active)   
2    10.96.0.1:443      ClusterIP      1 => 172.18.0.3:6443 (active)   
3    10.96.0.10:53      ClusterIP      1 => 10.0.0.112:53 (active)     
                                       2 => 10.0.0.63:53 (active)      
4    10.96.0.10:9153    ClusterIP      1 => 10.0.0.63:9153 (active)    
                                       2 => 10.0.0.112:9153 (active)   
8    10.96.195.193:80   ClusterIP      1 => 10.0.2.6:80 (active)       
                                       2 => 10.0.1.145:80 (active)     
9    172.18.0.2:32000   NodePort       1 => 10.0.2.6:80 (active)       
                                       2 => 10.0.1.145:80 (active)     
10   0.0.0.0:32000      NodePort       1 => 10.0.2.6:80 (active)       
                                       2 => 10.0.1.145:80 (active) 

檢視上面的 service 資訊得到, 172.18.0.2:32000 後端有 2ip 地址資訊,並且後端埠為 80cilium 劫持到 Pod 需要訪問 service 資訊,即會查詢該 service 對應的後端 Pod 地址和埠返回給客戶端,讓客戶端使用此地址發起 http 請求

五、參考部落格

https://bbs.huaweicloud.com/blogs/417788

相關文章