Skip to content

antony@notes:~/kubernetes$ cat "Run-Kube-vip-as-Cloud-Provider-on-HA-K3s.md"

Run Kube-vip as Cloud Provider on HA K3s

2025-05-10· kubernetes

Run Kube-vip as Cloud Provider on HA K3s

1. 先備知識

1.1. kube-vip 專案主要提供兩大功能領域:

  • 透過 control-plane 的 VIP 實現高可用(HA)的 Kubernetes 叢集
  • 提供 Kubernetes 中的 Service 類型: LoadBalancer

1.2. HA Kubernetes Control Plane

  • 重新實作負載平衡功能:由於先前的請求,HTTP 負載平衡功能被移除,只保留了控制平面的高可用(HA)功能。此功能將重新實作,可能會採用原本的 round-robin HTTP 請求方式,或是改用 IPVS。
  • 利用 Kubernetes API 判斷其他控制平面節點:一旦單節點叢集運行後,kube-vip 可以透過 Kubernetes API 來判斷其他控制平面成員。目前的做法是每個控制平面節點都必須由 Cluster-API provider 投放一份 static manifest。
  • 重新評估 Raft:kube-vip 最初的設計是作為獨立於 Kubernetes 的 raft 叢集,但因為像 CAPV(Cluster API Provider vSphere)這類元件在升級過程中存在的一些限制,使得改為在 Kubernetes 內部使用 leaderElection 成為更好的選擇。

1.3. Kubernetes service type:LoadBalancer

  • 每個 LoadBalancer 使用 ARP 的 LeaderElection:目前僅有被選為 leader 的單一 Pod 會處理某個 VIP 的所有流量。若能為每個 Service 產生一個獨立的 leaderElection token,將可讓服務在整個叢集中的所有 Pod 上分散部署與運作。

2. Install kube-vip as static pods

2.0. 本篇文章測試環境

NAME             STATUS   ROLES                              AGE   VERSION        INTERNAL-IP   EXTERNAL-IP   OS-IMAGE               KERNEL-VERSION     CONTAINER-RUNTIME
k3s-kubevip-a1   Ready    control-plane,etcd,master,worker   13h   v1.31.3+k3s1   172.20.6.61   <none>        SUSE Linux Micro 6.0   6.4.0-18-default   containerd://1.7.23-k3s2
k3s-kubevip-a2   Ready    control-plane,etcd,master,worker   13h   v1.31.3+k3s1   172.20.6.62   <none>        SUSE Linux Micro 6.0   6.4.0-18-default   containerd://1.7.23-k3s2
k3s-kubevip-a3   Ready    control-plane,etcd,master,worker   13h   v1.31.3+k3s1   172.20.6.63   <none>        SUSE Linux Micro 6.0   6.4.0-18-default   containerd://1.7.23-k3s2
  • 底層是一台 VM 分別在各自的實體機上
  • 實體網路速度 10 G
  • kubeproxy mode 為 ipvs
  • 可透過在 K3s 任一 node 執行以下指令檢查 : curl http://localhost:10249/proxyMode

2.1. Set configuration details

設定用於 control plane node 的 VIP 位址:

export VIP=172.20.6.60

將 INTERFACE 名稱設定為將宣布 VIP 的 control plane node 上的介面名稱。在許多 Linux 發行版中,可以使用 ip a 指令找到它。

export INTERFACE=eth0

透過解析 GitHub API 取得 kube-vip 版本的最新版本。此步驟需要安裝 jqcurl

KVVERSION=$(curl -sL https://api.github.com/repos/kube-vip/kube-vip/releases | jq -r ".[0].name")

2.2. Creating the manifest

現在已經設定好輸入值,我們可以拉取並執行 kube-vip image,並提供所需的 flags 與參數。一旦依照你選擇的方法(ARP 或 BGP)產生出靜態 Pod 的 manifest,如果你是運行多個 control plane 節點,請確保將該 manifest 放入每個 control plane 節點的 static manifest 目錄中(預設為 /etc/kubernetes/manifests)。

根據使用的 container runtime,請使用兩個別名命令中的其中一個來建立 kube-vip 命令,以 app container 的方式執行 kube-vip 映像檔。

若使用的是 containerd,請執行以下命令:

alias kube-vip="ctr image pull ghcr.io/kube-vip/kube-vip:$KVVERSION; ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:$KVVERSION vip /kube-vip"

2.2.1. ARP

在設定好輸入參數與別名命令後,我們可以執行 kube-vip container 產生一個 Static Pod 的 manifest,並將其寫入 /var/lib/rancher/k3s/agent/pod-manifests/kube-vip.yaml 檔案中。因此,這個動作預期是在第一個 control plane 節點上執行。

這個設定會建立一個 manifest,啟動 kube-vip,以使用 leaderElection 方法與 ARP 提供 control plane 的虛擬 IP(VIP)以及 Kubernetes Service 的管理功能。當該實例被選為 leader 時,它會將 VIP 綁定到指定的網路介面上。這個行為與 Kubernetes 中的 LoadBalancer 類型 Service 相同。

kube-vip manifest pod \
    --interface $INTERFACE \
    --address $VIP \
    --controlplane \
    --services \
    --arp \
    --leaderElection \
    --vipSubnet 16 \
    --servicesElection \
    --enableLoadBalancer \
    --k8sConfigPath /etc/rancher/k3s/k3s.yaml | tee /var/lib/rancher/k3s/agent/pod-manifests/kube-vip.yaml

🔍 參數解析

  • --interface $INTERFACE:指定 kube-vip 綁定的網路介面,例如 eth0
  • --address $VIP:設定虛擬 IP(VIP),作為控制平面和服務的入口點。
  • --controlplane:啟用控制平面 VIP 功能,提供高可用的 Kubernetes 控制平面。
  • --services:啟用對 Kubernetes 中 LoadBalancer 類型服務的支援。
  • --arp:使用 ARP 模式廣播 VIP,適用於 Layer 2 網路環境。
  • --leaderElection:啟用領導者選舉機制,確保在多個節點中只有一個節點持有 VIP。
  • --vipSubnet 16:設定 VIP 的子網掩碼為 /16
  • --servicesElection:為每個服務啟用獨立的領導者選舉,允許服務在不同節點上分佈(也就是說這允許 kube-vip 在多個節點之間分配 loadBalancer 位址)。
  • --enableLoadBalancer:啟用 IPVS 負載平衡器,將流量分發到後端 Pod。
  • --k8sConfigPath /etc/rancher/k3s/k3s.yaml:指定 kube-vip 使用的 Kubernetes 配置檔案路徑。

📌 注意事項

  • 確保 $INTERFACE$VIP 變數已正確設定,並符合您的網路環境。
  • --vipSubnet 的設定應根據您的網路規劃進行調整,常見的值有 /32/24 等。
  • 在使用 ARP 模式時,確保網路設備允許 ARP 廣播。
  • --servicesElection 功能需要 kube-vip 版本 ≥ 0.5.0。
  • --serviceInterface 可以讓 loadbalancer service 的 ip 跑在這參數指定的網卡上。

:::spoiler 適合 kubeproxy mode 為 iptables 的 YAML

kube-vip manifest pod \
    --interface $INTERFACE \
    --address $VIP \
    --controlplane \
    --services \
    --arp \
    --leaderElection \
    --vipSubnet 16 \
    --k8sConfigPath /etc/rancher/k3s/k3s.yaml | tee /var/lib/rancher/k3s/agent/pod-manifests/kube-vip.yaml

:::

2.3. 檢視 kube-vip pod 狀態

kubectl -n kube-system get pods -o wide

執行結果 :

NAME                                      READY   STATUS    RESTARTS       AGE     IP            NODE             NOMINATED NODE   READINESS GATES
coredns-6cb54596b5-fp52d                  1/1     Running   2 (116m ago)   13h     10.42.0.4     k3s-kubevip-a1   <none>           <none>
kube-vip-cloud-provider-6ffc794f6-x9zr8   1/1     Running   0              36m     10.42.1.5     k3s-kubevip-a3   <none>           <none>
kube-vip-k3s-kubevip-a1                   1/1     Running   0              5m42s   172.20.6.61   k3s-kubevip-a1   <none>           <none>
local-path-provisioner-598c97774d-dbfhd   1/1     Running   2 (116m ago)   13h     10.42.0.5     k3s-kubevip-a1   <none>           <none>
metrics-server-66f4d95f59-r9kxn           1/1     Running   2 (116m ago)   13h     10.42.0.3     k3s-kubevip-a1   <none>           <none>

2.4. 檢視網卡

連線到 kube-vip pod 所在的 node

ssh <user>@<node ip>

檢視 eth0 網卡資訊

ip a s eth0

執行結果 :

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether bc:24:11:0b:f6:e5 brd ff:ff:ff:ff:ff:ff
    altname enp0s18
    altname ens18
    inet 172.20.6.61/16 brd 172.20.255.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 172.20.6.60/32 scope global eth0
       valid_lft forever preferred_lft forever

可以看到這張網卡多了一個 IP 位址

2.5. 到其他 Control-plane node 重複執行 2.1 ~ 2.3 的操作步驟

2.6. 再次檢視 kube-vip pod 狀態

kubectl -n kube-system get pods -o wide

執行結果 :

NAME                                      READY   STATUS    RESTARTS       AGE     IP            NODE             NOMINATED NODE   READINESS GATES
coredns-6cb54596b5-fp52d                  1/1     Running   2 (116m ago)   13h     10.42.0.4     k3s-kubevip-a1   <none>           <none>
kube-vip-cloud-provider-6ffc794f6-x9zr8   1/1     Running   0              36m     10.42.1.5     k3s-kubevip-a3   <none>           <none>
kube-vip-k3s-kubevip-a1                   1/1     Running   0              5m42s   172.20.6.61   k3s-kubevip-a1   <none>           <none>
kube-vip-k3s-kubevip-a2                   1/1     Running   0              2m8s    172.20.6.62   k3s-kubevip-a2   <none>           <none>
kube-vip-k3s-kubevip-a3                   1/1     Running   0              35s     172.20.6.63   k3s-kubevip-a3   <none>           <none>
local-path-provisioner-598c97774d-dbfhd   1/1     Running   2 (116m ago)   13h     10.42.0.5     k3s-kubevip-a1   <none>           <none>
metrics-server-66f4d95f59-r9kxn           1/1     Running   2 (116m ago)   13h     10.42.0.3     k3s-kubevip-a1   <none>           <none>

3. Install the kube-vip Cloud Provider

kubectl apply -f https://raw.githubusercontent.com/kube-vip/kube-vip-cloud-provider/main/manifest/kube-vip-cloud-controller.yaml

檢查

kubectl -n kube-system get pods -l "app=kube-vip"

執行結果如下 :

NAME                                     READY   STATUS    RESTARTS   AGE
kube-vip-cloud-provider-88689889-62msw   1/1     Running   0          8m40s

4. Create a global CIDR or IP Range

為了讓 kube-vip 能夠為類型為 LoadBalancerService 設定 IP 位址,必須先提供可供分配的 IP 位址範圍。這些資訊儲存在 Kubernetes 的 ConfigMap 中,kube-vip 可以存取該 ConfigMap。您可以透過 ConfigMap 中的 key 來控制 IP 分配的範圍。可以指定 CIDR 區段或 IP 範圍,並將其設定為全域(整個叢集)或特定 Namespace 使用。

若要允許 kube-vip 在任何 Namespace 中為類型為 LoadBalancerService 分配 IP 位址,您可以建立一個名為 kubevip 的 ConfigMap,並設定 key 為 cidr-global,其值為您環境中可用的 CIDR 區段。例如,以下指令建立了一個全域的 CIDR 區段 172.20.6.65/32,kube-vip 將從中分配 IP 位址。

kubectl create configmap -n kube-system kubevip --from-literal cidr-global=172.20.6.65/32

其他指定 IP 的方式,請參考以下連結 : The kube-vip Cloud Provider ConfigMap - Kube-vip Docs

5. 建立測試的應用程式

kube-vip 的「平均」故障轉移時間約為 3 秒,但這可能會因底層基礎設施而有很大差異,例如虛擬和實體交換器可能會阻止或限制 kube-vip 更新網路。要開始評估您的基礎設施效能,可以進行以下最簡單的測試:

kubectl create deployment source-ip-app \
  --image=registry.k8s.io/echoserver:1.10 \
  --replicas 3

5.1. 檢視 pod 狀態

kubectl get pods -o wide

執行結果 :

NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE             NOMINATED NODE   READINESS GATES
source-ip-app-78477d56f4-cqmhh   1/1     Running   0          78s   10.42.0.10   k3s-kubevip-a1   <none>           <none>
source-ip-app-78477d56f4-fw6d7   1/1     Running   0          78s   10.42.2.2    k3s-kubevip-a2   <none>           <none>
source-ip-app-78477d56f4-hsgrv   1/1     Running   0          78s   10.42.1.8    k3s-kubevip-a3   <none>           <none>

5.2. 建一個 loadbalancer service

kubectl expose deployment source-ip-app \
  --name=loadbalancer \
  --port=80 \
  --target-port=8080 \
  --type=LoadBalancer

5.3. 檢視 SVC 的 EXTERNAL-IP

kubectl get svc

執行結果 :

NAME           TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes     ClusterIP      10.43.0.1       <none>        443/TCP        13h
loadbalancer   LoadBalancer   10.43.123.204   172.20.6.65   80:32303/TCP   2m11s

5.4. 查詢 kube-vip 為服務選舉的 leader 節點

kubectl get lease -n kube-system plndr-svcs-lock

執行結果 :

NAME              HOLDER           AGE
plndr-svcs-lock   k3s-kubevip-a3   5h17m

5.5. 檢視網卡

連線到 kube-vip pod 所在的 node

ssh <user>@<node ip>

檢視 eth0 網卡資訊

ip a s eth0

執行結果 :

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether bc:24:11:70:32:5c brd ff:ff:ff:ff:ff:ff
    altname enp0s18
    altname ens18
    inet 172.20.6.63/16 brd 172.20.255.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 172.20.6.65/16 brd 172.20.255.255 scope global secondary eth0
       valid_lft forever preferred_lft forever

可以看到這張網卡多了一個 IP 位址

5.6 . 測試 Failover

在同網路的其他機器執行以下命令

while true; do start=$(date +%s%3N); wget --timeout=1 -qO - 172.20.6.65 | grep Hostname || echo "wget error"; end=$(date +%s%3N); echo "耗時: $((end - start)) 毫秒"; sleep 1; done

執行結果 :

Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 7 毫秒
Hostname: source-ip-app-78477d56f4-sgm2x
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-sgm2x
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-sgm2x
耗時: 5 毫秒
...

將 kube-vip leader pod 所在的節點關機用以模擬故障

ssh <user>@<NodeIP>
sudo poweroff

執行結果 :

Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-sgm2x
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5007 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 2006 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 2007 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 2006 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 2006 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 2006 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 2006 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 2007 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒
Hostname: source-ip-app-78477d56f4-jjmhj
耗時: 6 毫秒
Hostname: source-ip-app-78477d56f4-wf8kw
耗時: 5 毫秒

實測下來 vip failover 的時間約 5 秒 如果該節點同時是 cp 和 service 的 leader,failover 的時間為 9 秒左右

5.7 再次查詢 kube-vip leader

kubectl get lease -n kube-system plndr-svcs-lock

執行結果 :

NAME              HOLDER           AGE
plndr-svcs-lock   k3s-kubevip-a3   5h17m

有發現 kube-vip 無法更新這個 lease,但 VIP 實際上已移轉至其他節點

5.8. 驗證網卡資訊

連線到 kube-vip pod 所在的 node

ssh <user>@<node ip>

檢視 eth0 網卡資訊

ip a s eth0

執行結果 :

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether bc:24:11:d7:a9:4d brd ff:ff:ff:ff:ff:ff
    altname enp0s18
    altname ens18
    inet 172.20.6.62/16 brd 172.20.255.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 172.20.6.65/16 brd 172.20.255.255 scope global secondary eth0
       valid_lft forever preferred_lft forever

6. 清除環境

# 1. 移除測試應用
kubectl delete deploy source-ip-app; kubectl delete svc loadbalancer

# 2. 移除 kube-vip cloud provider
kubectl apply -f https://raw.githubusercontent.com/kube-vip/kube-vip-cloud-provider/main/manifest/kube-vip-cloud-controller.yaml

# 3. 到每台 Contol-plane 移除 kube-vip static pod yaml
rm /var/lib/rancher/k3s/agent/pod-manifests/kube-vip.yaml

7. 參考文件