Cilium系列-5-Cilium替換KubeProxy

2023-07-26 12:03:05

系列文章

前言

將 Kubernetes 的 CNI 從其他元件切換為 Cilium, 已經可以有效地提升網路的效能. 但是通過對 Cilium 不同模式的切換/功能的啟用, 可以進一步提升 Cilium 的網路效能. 具體調優項包括不限於:

  • 啟用本地路由(Native Routing)
  • 完全替換 KubeProxy
  • IP 地址偽裝(Masquerading)切換為基於 eBPF 的模式
  • Kubernetes NodePort 實現在 DSR(Direct Server Return) 模式下執行
  • 繞過 iptables 連線跟蹤(Bypass iptables Connection Tracking)
  • 主機路由(Host Routing)切換為給予 BPF 的模式 (需要 Linux Kernel >= 5.10)
  • 啟用 IPv6 BIG TCP (需要 Linux Kernel >= 5.19)
  • 禁用 Hubble(但是不建議, 可觀察性比一點點的效能提升更重要)
  • 修改 MTU 為巨型幀(jumbo frames) (需要網路條件允許)
  • 啟用頻寬管理器(Bandwidth Manager) (需要 Kernel >= 5.1)
  • 啟用 Pod 的 BBR 擁塞控制 (需要 Kernel >= 5.18)
  • 啟用 XDP 加速 (需要 支援本地 XDP 驅動程式)
  • (高階使用者可選)調整 eBPF Map Size
  • Linux Kernel 優化和升級
    • CONFIG_PREEMPT_NONE=y
  • 其他:
    • tuned network-* profiles, 如: tuned-adm profile network-latencynetwork-throughput
    • CPU 調為效能模式
    • 停止 irqbalance,將網路卡中斷引腳指向特定 CPU

在網路/網路卡裝置/OS等條件滿足的情況下, 我們儘可能多地啟用這些調優選項, 相關優化項會在後續文章逐一更新. 敬請期待.

上篇文章我們啟用了Cilium本地路由, 啟用後對網路吞吐量提升明顯.

今天我們來使用 Cilium 完全替換 KubeProxy, 建立一個沒有 KubeProxy 的 Kubernetes 叢集, 以此來大幅減少 iptables 規則鏈(還有 netfilter), 從而全方位提升網路效能.

測試環境

  • Cilium 1.13.4
  • K3s v1.26.6+k3s1
  • OS
    • 3臺 Ubuntu 23.04 VM, Kernel 6.2, x86

背景

Kubernetes 叢集中, 在 Kube Proxy 裡大量用到了 iptables, 在 Kubernetes 叢集規模較大的情況下, 數以千/萬計的 iptables 規則會極大地拖慢 Kubernetes 網路效能, 導致網路請求響應緩慢.

大量 IPTables 規則鏈的範例如下:

Kube Proxy 的用途

Kube Proxy 的負責以下幾個方面的流量路由:

  1. ClusterIP: 叢集內通過 ClusterIP 的存取
  2. NodePort: 叢集內外通過 NodePort 的存取
  3. ExternalIP: 叢集外通過 external IP 的存取
  4. LoadBalancer: 叢集外通過 LoadBalancer 的存取.

而 Cilium 完全實現了這些功能, 並做到了效能上的大幅提升, 具體 Cilium 官方測試結果如下:

啟用了 DSR 後效能會更強:

實施步驟

接下來我們開始實施替換, Cilium 的 eBPF kube-proxy 可在直接路由和隧道模式下進行替換。

重新安裝 K3s

# Server Node
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_EXEC='--write-kubeconfig-mode=644 --flannel-backend=none --disable-network-policy --disable=servicelb --prefer-bundled-bin --disable-kube-proxy' INSTALL_K3S_VERSION=v1.26.6+k3s1 sh -

說明如下:

  • --disable=servicelb K3s servicelb 不是 Kubernetes 的標準元件, 為了減少干擾, 先去掉它.
  • --disable-kube-proxy 禁用 Kube Proxy

重新安裝 Cilium

視情況不同, 可能需要解除安裝 Cilium:

helm uninstall cilium -n kube-system

重新安裝, 重新安裝時直接加上 kubeProxyReplacement 引數:

helm install cilium cilium/cilium --version 1.13.4 \
   --namespace kube-system \
   --set operator.replicas=1 \
   --set k8sServiceHost=192.168.2.43 \
   --set k8sServicePort=6443 \
   --set hubble.relay.enabled=true \
   --set hubble.ui.enabled=true \
   --set tunnel=disabled \
   --set autoDirectNodeRoutes=true \
   --set ipv4NativeRoutingCIDR=10.0.0.0/22 \
   --set kubeProxyReplacement=strict

說明如下:

  • kubeProxyReplacement=strict Kube Proxy 替換使用嚴格模式. 而在預設情況下, Helm 會設定 kubeProxyReplacement=disabled,這隻會啟用 ClusterIP 服務的群集內負載平衡。

基本資訊驗證

執行完成後進行驗證:

$ kubectl -n kube-system exec ds/cilium -- cilium status | grep KubeProxyReplacement
KubeProxyReplacement:    Strict   [eth0 192.168.2.3 (Direct Routing)]

使用 --verbose 檢視全部細節:

$ kubectl -n kube-system exec ds/cilium -- cilium status --verbose
...
KubeProxyReplacement Details:
  Status:                 Strict
  Socket LB:              Enabled
  Socket LB Tracing:      Enabled
  Socket LB Coverage:     Full
  Devices:                eth0 192.168.2.3 (Direct Routing)
  Mode:                   SNAT
  Backend Selection:      Random
  Session Affinity:       Enabled
  Graceful Termination:   Enabled
  NAT46/64 Support:       Disabled
  XDP Acceleration:       Disabled
  Services:
  - ClusterIP:      Enabled
  - NodePort:       Enabled (Range: 30000-32767)
  - LoadBalancer:   Enabled
  - externalIPs:    Enabled
  - HostPort:       Enabled

實戰驗證

接下來, 我們可以建立一個 Nginx 部署。然後,建立一個新的 NodePort 服務,並驗證 Cilium 是否正確安裝了該服務。

建立 Nginx Deploy:

cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

下一步,為這兩個範例建立一個 NodePort 服務:

$ kubectl expose deployment my-nginx --type=NodePort --port=80
service/my-nginx exposed

檢視 NodePort 伺服器埠等資訊:

$ kubectl get svc my-nginx
NAME       TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
my-nginx   NodePort   10.43.204.231   <none>        80:32727/TCP   96s

藉助 cilium service list 命令,我們可以驗證 Cilium 的 eBPF kube-proxy 替代程式是否建立了新的 NodePort 服務。在本例中,建立了埠號為 32727 的服務(位於網路卡裝置 eth0):

$ kubectl -n kube-system exec ds/cilium -- cilium service list
ID   Frontend             Service Type   Backend
...
32   192.168.2.3:32727    NodePort       1 => 10.0.0.70:80 (active)
                                         2 => 10.0.2.96:80 (active)
33   0.0.0.0:32727        NodePort       1 => 10.0.0.70:0 (active)
                                         2 => 10.0.2.96:80 (active)

同時,我們還可以使用主機名空間中的 iptables 驗證是否存在針對該服務的 iptables 規則:

casey@cilium-62-1:~$ sudo iptables-save | grep KUBE-SVC
[sudo] casey 的密碼:
casey@cilium-62-1:~$

上方結果為空, 證明已經沒有了 KUBE-SVC 相關的 IPTables 規則.

我們可以使用 curl 對 NodePort ClusterIP PodIP 等進行測試:

node_port=$(kubectl get svc my-nginx -o=jsonpath='{@.spec.ports[0].nodePort}')
# localhost+NodePort
curl 127.0.0.1:$node_port
# eth0+NodePort
curl 192.168.2.3:$node_port
# ClusterIP
curl 10.43.204.231:80
# 本機PodIP
curl 10.0.0.70:80
# 其他Node PodIP
curl 10.0.2.96:80