本地叢集使用 OpenELB 實現 Load Balancer 負載均衡

語言: CN / TW / HK

為了方便測試,準備為 Ingress 控制器配置一個 LoadBalaner 型別的 Service,由於我這是本地私有環境,所以需要部署一個支援該服務型別的負載均衡器,在社群中目前最流行的應該是 MetalLB 這個專案,現在也屬於 CNCF 沙箱專案,該專案在 2017 年底發起,經過 4 年的發展已經在社群被廣泛採用,但是我這邊在測試使用過程中一直表現不穩定,經常需要重啟控制器才能生效。所以將目光轉向了最近國內青雲開源的另外一個負載均衡器 OpenELB。

OpenELB 之前叫 PorterLB,是為物理機(Bare-metal)、邊緣(Edge)和私有化環境設計的負載均衡器外掛,可作為 Kubernetes、K3s、KubeSphere 的 LB 外掛對叢集外暴露 LoadBalancer 型別的服務,現階段是 CNCF 沙箱專案,核心功能包括:

  • 基於 BGP 與 Layer 2 模式的負載均衡
  • 基於路由器 ECMP 的負載均衡
  • IP 地址池管理
  • 使用 CRD 進行 BGP 配置
openelb

與 MetaLB 對比

OpenELB 作為後起之秀,採用了更加 Kubernetes-native 的實現方式,可以直接通過 CRD 進行配置管理,下面是關於 OpenELB 與 MetaLB 的簡單對比。

雲原生架構

在 OpenELB 中,不管是地址管理,還是 BGP 配置管理,你都可以使用 CRD 來配置。對於習慣了 Kubectl 的使用者而言, OpenELB 十分友好,在 MetalLB 中,需通過 ConfigMap 來配置,感知它們的狀態需要通過檢視監控或者日誌。

靈活的地址管理

OpenELB 通過 EIP 這個自定義資源物件來管理地址,它定義子資源 Status 來儲存地址分配狀態,這樣就不會存在分配地址時各副本發生衝突的情況。

使用 gobgp 釋出路由

不同於 MetalLB 自己實現 BGP 協議, OpenELB 採用標準的 gobgp 來發布路由,這樣做的好處如下:

  • 開發成本低,且有 gobgp 社群支援
  • 可以利用 gobgp 豐富特性
  • 通過 BgpConf/BgpPeer CRD 動態配置 gobgp,使用者無需重啟 OpenELB 即可動態載入最新的配置資訊
  • gobgp 作為 lib 使用時, 社群提供了基於 protobuf 的 API,OpenELB 在實現 BgpConf/BgpPeer CRD 時也是參照該 API,並保持相容
  • OpenELB 也提供 status 用於檢視 BGP neighbor 配置,狀態資訊豐富

架構簡單,資源佔用少

OpenELB 目前只用部署 Deployment 即可,通過多副本實現高可用,部分副本崩潰後並不會影響已建立的正常連線。

BGP 模式下, Deployment 不同副本都會與路由器建立連線用於釋出等價路由,所以正常情況下我們部署兩個副本即可。在 Layer 2 模式下,不同副本之間通過 Kubernetes 提供的 Leader Election 機制選舉 Leader,進而應答 ARP/NDP。

安裝

在 Kubernetes 叢集中,您只需要安裝一次 OpenELB。安裝完成後,叢集中會安裝一個 openelb-manager Deployment,其中包含一個 openelb-manager Pod。 openelb-manager Pod 為整個 Kubernetes 叢集實現了 OpenELB 的功能。 安裝完成後,可以擴充套件openelb-manager Deployment,將多個OpenELB副本(openelb-manager Pods)分配給多個叢集節點,保證高可用。有關詳細資訊,請參閱配置多個 OpenELB 副本。

要安裝使用 OpenELB 非常簡單,直接使用下面的命令即可一鍵安裝:

# 注意如果不能獲取k8s.gcr.io映象,需要替換其中的映象
☸ ➜ kubectl apply -f https://raw.githubusercontent.com/openelb/openelb/master/deploy/openelb.yaml

上面的資源清單會部署一個名為 openelb-manager 的 Deployment 資源物件,openelb-manager 的 Pod 為整個 Kubernetes 叢集實現了 OpenELB 的功能,為保證高可用,可以將該控制器擴充套件為兩個副本。第一次安裝的時候還會為 admission webhook 配置 https 證書,安裝完成後檢視 Pod 的狀態是否正常:

☸ ➜ kubectl get pods -n openelb-system              
NAME                                READY   STATUS      RESTARTS      AGE
openelb-admission-create--1-cf857   0/1     Completed   0             58m
openelb-admission-patch--1-dhgrq    0/1     Completed   2             58m
openelb-manager-848495684-nppkr     1/1     Running     1 (35m ago)   48m
openelb-manager-848495684-svn7z     1/1     Running     1 (35m ago)   48m
☸ ➜ kubectl get validatingwebhookconfiguration       
NAME                                      WEBHOOKS   AGE
openelb-admission                         1          62m
☸ ➜ kubectl get mutatingwebhookconfigurations        
NAME                                    WEBHOOKS   AGE
openelb-admission                       1          62m

此外還會安裝幾個相關的 CRD 使用者 OpenELB 配置:

☸ ➜ kubectl get crd |grep kubesphere
bgpconfs.network.kubesphere.io           2022-04-10T08:01:18Z
bgppeers.network.kubesphere.io           2022-04-10T08:01:18Z
eips.network.kubesphere.io               2022-04-10T08:01:18Z

配置

接下來我們來演示下如何使用 layer2 模式的 OpenELB,首先需要保證所有 Kubernetes 叢集節點必須在同一個二層網路(在同一個路由器下),我測試的環境一共3個節點,節點資訊如下所示:

☸ ➜ kubectl get nodes -o wide      
NAME      STATUS   ROLES                  AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION                CONTAINER-RUNTIME
master1   Ready    control-plane,master   15d   v1.22.8   192.168.0.111   <none>        CentOS Linux 7 (Core)   3.10.0-1160.25.1.el7.x86_64   containerd://1.5.5
node1     Ready    <none>                 15d   v1.22.8   192.168.0.110   <none>        CentOS Linux 7 (Core)   3.10.0-1160.25.1.el7.x86_64   containerd://1.5.5
node2     Ready    <none>                 15d   v1.22.8   192.168.0.109   <none>        CentOS Linux 7 (Core)   3.10.0-1160.25.1.el7.x86_64   containerd://1.5.5

3個節點IP地址分別為 192.168.0.109、192.168.0.110、192.168.0.111。

首先需要為 kube-proxy 啟用 strictARP ,以便 Kubernetes 叢集中的所有網絡卡停止響應其他網絡卡的 ARP 請求,而由 OpenELB 處理 ARP 請求。

☸ ➜ kubectl edit configmap kube-proxy -n kube-system
......
ipvs:
  strictARP: true
......

然後執行下面的命令重啟 kube-proxy 元件即可:

☸ ➜ kubectl rollout restart daemonset kube-proxy -n kube-system

如果安裝 OpenELB 的節點有多個網絡卡,則需要指定 OpenELB 在二層模式下使用的網絡卡,如果節點只有一個網絡卡,則可以跳過此步驟,假設安裝了 OpenELB 的 master1 節點有兩個網絡卡(eth0 192.168.0.2 和 ens33 192.168.0.111),並且 eth0 192.168.0.2 將用於 OpenELB,那麼需要為 master1 節點新增一個 annotation 來指定網絡卡:

☸ ➜ kubectl annotate nodes master1 layer2.openelb.kubesphere.io/v1alpha1="192.168.0.2"

接下來就可以建立一個 Eip 物件來充當 OpenELB 的 IP 地址池了,建立一個如下所示的資源物件:

apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
  name: eip-pool
spec:
  address: 192.168.0.100-192.168.0.108
  protocol: layer2
  disable: false
  interface: ens33

這裡我們通過 address 屬性指定了 IP 地址池,可以填寫一個或多個 IP 地址(要注意不同 Eip 物件中的 IP 段不能重疊),將被 OpenELB 使用。值格式可以是:

  • IP地址,例如 192.168.0.100
  • IP地址/子網掩碼,例如 192.168.0.0/24
  • IP地址1-IP地址2,例如192.168.0.91-192.168.0.100

protocol 屬性用來指定 Eip 物件用於哪種 OpenELB 模式,可以配置為 layer2 或 bgp,預設為 bgp 模式,我們這裡想使用 layer2 模式,所以需要顯示指定 interface 是用來指定 OpenELB 監聽 ARP 或 NDP 請求的網絡卡,該欄位僅在協議設定為 layer2 時有效,我這裡的環境是 ens33 網絡卡 disable 表示是否禁用 Eip 物件

建立完成 Eip 物件後可以通過 Status 來檢視該 IP 池的具體狀態:

☸ ➜ kubectl get eip          
NAME       CIDR                          USAGE   TOTAL
eip-pool   192.168.0.100-192.168.0.108   0       9
☸ ➜ kubectl get eip eip-pool -oyaml
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
  finalizers:
  - finalizer.ipam.kubesphere.io/v1alpha1
  name: eip-pool
spec:
  address: 192.168.0.100-192.168.0.108
  interface: ens33
  protocol: layer2
status:
  firstIP: 192.168.0.100
  lastIP: 192.168.0.108
  poolSize: 9
  ready: true
  v4: true

到這裡 LB 的地址池就準備好了,接下來我們建立一個簡單的服務,通過 LB 來進行暴露,如下所示:

# openelb-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:  
    matchLabels:
      app: nginx
  template:  
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

這裡部署一個簡單的 nginx 服務:

☸ ➜ kubectl apply -f openelb-nginx.yaml 
☸ ➜ kubectl get pods                  
NAME                     READY   STATUS    RESTARTS      AGE
nginx-7848d4b86f-zmm8l   1/1     Running   0             42s

然後建立一個 LoadBalancer 型別的 Service 來暴露我們的 nginx 服務,如下所示:

# openelb-nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
  annotations:
    lb.kubesphere.io/v1alpha1: openelb
    protocol.openelb.kubesphere.io/v1alpha1: layer2
    eip.openelb.kubesphere.io/v1alpha2: eip-pool
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
    - name: http
      port: 80
      targetPort: 80

注意這裡我們為 Service 添加了幾個 annotations 註解:

  • lb.kubesphere.io/v1alpha1: openelb 用來指定該 Service 使用 OpenELB
  • protocol.openelb.kubesphere.io/v1alpha1: layer2 表示指定 OpenELB 用於 Layer2 模式
  • eip.openelb.kubesphere.io/v1alpha2: eip-pool 用來指定了 OpenELB 使用的 Eip 物件,如果未配置此註解,OpenELB 會自動使用與協議匹配的第一個可用 Eip 物件,此外也可以刪除此註解並新增 spec:loadBalancerIP 欄位(例如 spec:loadBalancerIP: 192.168.0.108)以將特定 IP 地址分配給 Service。

同樣直接建立上面的 Service:

☸ ➜ kubectl apply -f openelb-nginx-svc.yaml                
service/nginx created
☸ ➜ kubectl get svc nginx                   
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
nginx   LoadBalancer   10.100.126.91   192.168.0.101   80:31555/TCP   4s

建立完成後可以看到 Service 服務被分配了一個 EXTERNAL-IP ,然後我們就可以通過該地址來訪問上面的 nginx 服務了:

☸ ➜ curl 192.168.0.101
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

此外 OpenElb 還支援 BGP 模式以及叢集多路由的場景,更新使用方法可以檢視官方文件 https://openelb.github.io/docs/ 瞭解更多相關資訊。

參考文件

微信公眾號

掃描下面的二維碼關注我們的微信公眾帳號,在微信公眾帳號中回覆◉加群◉即可加入到我們的 kubernetes 討論群裡面共同學習。