antony@notes:~/kubernetes$ cat "Run-Kube-vip-as-Cloud-Provider-on-HA-K3s.md"
Run Kube-vip as Cloud Provider on HA K3s
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 版本的最新版本。此步驟需要安裝 jq 和 curl。
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 8m40s4. Create a global CIDR or IP Range
為了讓 kube-vip 能夠為類型為 LoadBalancer 的 Service 設定 IP 位址,必須先提供可供分配的 IP 位址範圍。這些資訊儲存在 Kubernetes 的 ConfigMap 中,kube-vip 可以存取該 ConfigMap。您可以透過 ConfigMap 中的 key 來控制 IP 分配的範圍。可以指定 CIDR 區段或 IP 範圍,並將其設定為全域(整個叢集)或特定 Namespace 使用。
若要允許 kube-vip 在任何 Namespace 中為類型為 LoadBalancer 的 Service 分配 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 35.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=LoadBalancer5.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 2m11s5.4. 查詢 kube-vip 為服務選舉的 leader 節點
kubectl get lease -n kube-system plndr-svcs-lock執行結果 :
NAME HOLDER AGE
plndr-svcs-lock k3s-kubevip-a3 5h17m5.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 forever6. 清除環境
# 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