使用 kube-vip 搭建高可用 Kubernetes 集群
kube-vip 可以在你的控制平面節(jié)點(diǎn)上提供一個(gè) Kubernetes 原生的 HA 負(fù)載均衡,我們不需要再在外部設(shè)置 HAProxy 和 Keepalived 來實(shí)現(xiàn)集群的高可用了。
kube-vip 是一個(gè)為 Kubernetes 集群內(nèi)部和外部提供高可用和負(fù)載均衡的開源項(xiàng)目,在 Vmware 的 Tanzu 項(xiàng)目中已經(jīng)使用 kube-vip 替換了用于 vSphere 部署的 HAProxy 負(fù)載均衡器,本文我們將先來了解 kube-vip 如何用于 Kubernetes 控制平面的高可用和負(fù)載均衡功能。
特點(diǎn)
Kube-Vip 最初是為 Kubernetes 控制平面提供 HA 解決方案而創(chuàng)建的,隨著時(shí)間的推移,它已經(jīng)發(fā)展為將相同的功能合并到 Kubernetes 的 LoadBalancer 類型的 Service 中了。
- VIP 地址可以是 IPv4 或 IPv6
- 帶有 ARP(第2層)或 BGP(第3層)的控制平面
- 使用領(lǐng)導(dǎo)選舉或 raft 控制平面
- 帶有 kubeadm(靜態(tài) Pod)的控制平面 HA
- 帶有 K3s/和其他(DaemonSets)的控制平面 HA
- 使用 ARP 領(lǐng)導(dǎo)者選舉的 Service LoadBalancer(第 2 層)
- 通過 BGP 使用多個(gè)節(jié)點(diǎn)的 Service LoadBalancer
- 每個(gè)命名空間或全局的 Service LoadBalancer 地址池
- Service LoadBalancer 地址通過 UPNP 暴露給網(wǎng)關(guān)
HAProxy 和 kube-vip 的 HA 集群
在以前我們?cè)谒接协h(huán)境下創(chuàng)建 Kubernetes 集群時(shí),我們需要準(zhǔn)備一個(gè)硬件/軟件的負(fù)載均衡器來創(chuàng)建多控制面集群,更多的情況下我們會(huì)選擇使用 HAProxy + Keepalived 來實(shí)現(xiàn)這個(gè)功能。一般情況下我們創(chuàng)建2個(gè)負(fù)載均衡器的虛擬機(jī),然后分配一個(gè) VIP,然后使用 VIP 為負(fù)載均衡器提供服務(wù),通過 VIP 將流量重定向到后端的某個(gè) Kubernetes 控制器平面節(jié)點(diǎn)上。
接下來我們?cè)賮砜纯慈绻覀兪褂?kube-vip 的話會(huì)怎樣呢?
kube-vip 可以通過靜態(tài) pod 運(yùn)行在控制平面節(jié)點(diǎn)上,這些 pod 通過ARP 對(duì)話來識(shí)別每個(gè)節(jié)點(diǎn)上的其他主機(jī),所以需要在 hosts 文件中設(shè)置每個(gè)節(jié)點(diǎn)的 IP 地址,我們可以選擇 BGP 或 ARP 來設(shè)置負(fù)載平衡器,這與 Metal LB 比較類似。這里我們沒有 BGP 服務(wù),只是想快速測試一下,所以這里我們使用 ARP 與靜態(tài) pod 的方式。
kube-vip 架構(gòu)
kube-vip 有許多功能設(shè)計(jì)選擇提供高可用性或網(wǎng)絡(luò)功能,作為VIP/負(fù)載平衡解決方案的一部分。
Cluster
kube-vip 建立了一個(gè)多節(jié)點(diǎn)或多模塊的集群來提供高可用性。在 ARP 模式下,會(huì)選出一個(gè)領(lǐng)導(dǎo)者,這個(gè)節(jié)點(diǎn)將繼承虛擬 IP 并成為集群內(nèi)負(fù)載均衡的領(lǐng)導(dǎo)者,而在 BGP 模式下,所有節(jié)點(diǎn)都會(huì)通知 VIP 地址。
當(dāng)使用 ARP 或 layer2 時(shí),它將使用領(lǐng)導(dǎo)者選舉,當(dāng)然也可以使用 raft 集群技術(shù),但這種方法在很大程度上已經(jīng)被領(lǐng)導(dǎo)者選舉所取代,特別是在集群中運(yùn)行時(shí)。
虛擬IP
集群中的領(lǐng)導(dǎo)者將分配 vip,并將其綁定到配置中聲明的選定接口上。當(dāng)領(lǐng)導(dǎo)者改變時(shí),它將首先撤銷 vip,或者在失敗的情況下,vip 將直接由下一個(gè)當(dāng)選的領(lǐng)導(dǎo)者分配。
當(dāng) vip 從一個(gè)主機(jī)移動(dòng)到另一個(gè)主機(jī)時(shí),任何使用 vip 的主機(jī)將保留以前的 vip <-> MAC 地址映射,直到 ARP 過期(通常是30秒)并檢索到一個(gè)新的 vip <-> MAC 映射,這可以通過使用無償?shù)?ARP 廣播來優(yōu)化。
ARP
kube-vip可以被配置為廣播一個(gè)無償?shù)?arp(可選),通常會(huì)立即通知所有本地主機(jī) vip <-> MAC 地址映射已經(jīng)改變。
下面我們可以看到,當(dāng) ARP 廣播被接收時(shí),故障轉(zhuǎn)移通常在幾秒鐘內(nèi)完成。
- 64 bytes from 192.168.0.75: icmp_seq=146 ttl=64 time=0.258 ms
- 64 bytes from 192.168.0.75: icmp_seq=147 ttl=64 time=0.240 ms
- 92 bytes from 192.168.0.70: Redirect Host(New addr: 192.168.0.75)
- Vr HL TOS Len ID Flg off TTL Pro cks Src Dst
- 4 5 00 0054 bc98 0 0000 3f 01 3d16 192.168.0.95 192.168.0.75
- Request timeout for icmp_seq 148
- 92 bytes from 192.168.0.70: Redirect Host(New addr: 192.168.0.75)
- Vr HL TOS Len ID Flg off TTL Pro cks Src Dst
- 4 5 00 0054 75ff 0 0000 3f 01 83af 192.168.0.95 192.168.0.75
- Request timeout for icmp_seq 149
- 92 bytes from 192.168.0.70: Redirect Host(New addr: 192.168.0.75)
- Vr HL TOS Len ID Flg off TTL Pro cks Src Dst
- 4 5 00 0054 2890 0 0000 3f 01 d11e 192.168.0.95 192.168.0.75
- Request timeout for icmp_seq 150
- 64 bytes from 192.168.0.75: icmp_seq=151 ttl=64 time=0.245 ms
使用 kube-vip
接下來我們來使用 kube-vip 搭建一個(gè)高可用的 Kubernetes 集群。先準(zhǔn)備6個(gè)節(jié)點(diǎn):
- 3個(gè)控制平面節(jié)點(diǎn)
- 3個(gè) worker 節(jié)點(diǎn)
首先在宿主機(jī)上面安裝相關(guān)依賴,包括 kubeadm、kubelet、kubectl 以及一個(gè)容器運(yùn)行時(shí),這里我們使用的是 containerd。
獲取 kube-vip 的 docker 鏡像,并在 /etc/kuberentes/manifests 中設(shè)置靜態(tài) pod 的 yaml 資源清單文件,這樣 Kubernetes 就會(huì)自動(dòng)在每個(gè)控制平面節(jié)點(diǎn)上部署 kube-vip 的 pod 了。
- # 設(shè)置VIP地址
- export VIP=192.168.0.100
- export INTERFACE=eth0
- ctr image pull docker.io/plndr/kube-vip:0.3.1
- ctr run --rm --net-host docker.io/plndr/kube-vip:0.3.1 vip \
- /kube-vip manifest pod \
- --interface $INTERFACE \
- --vip $VIP \
- --controlplane \
- --services \
- --arp \
- --leaderElection | tee /etc/kubernetes/manifests/kube-vip.yaml
接下來就可以配置 kubeadm 了,如下所示:
- cat > ~/init_kubelet.yaml <<EOF
- apiVersion: kubeadm.k8s.io/v1beta2
- kind: InitConfiguration
- bootstrapTokens:
- - token: "9a08jv.c0izixklcxtmnze7"
- description: "kubeadm bootstrap token"
- ttl: "24h"
- nodeRegistration:
- criSocket: "/var/run/containerd/containerd.sock"
- ---
- apiVersion: kubeadm.k8s.io/v1beta2
- kind: ClusterConfiguration
- controlPlaneEndpoint: "192.168.0.100:6443"
- ---
- apiVersion: kubelet.config.k8s.io/v1beta1
- kind: KubeletConfiguration
- cgroupDriver: "systemd"
- protectKernelDefaults: true
- EOF
- kubeadm init --config init_kubelet.yaml --upload-certs
然后安裝 CNI,比如我們選擇使用 Cilium。
- curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
- helm repo add cilium https://helm.cilium.io/
- helm install cilium cilium/cilium --version 1.9.4 \
- --namespace kube-system
在第一個(gè)控制平面節(jié)點(diǎn)準(zhǔn)備好后,讓其他節(jié)點(diǎn)加入你的集群。對(duì)于其他控制平面節(jié)點(diǎn),運(yùn)行如下命令:
- kubeadm join 192.168.0.100:6443 --token hash.hash\
- --discovery-token-ca-cert-hash sha256:hash \
- --control-plane --certificate-key key
對(duì)于工作節(jié)點(diǎn),運(yùn)行類似命令:
- kubeadm join 192.168.0.100:6443 --token hash.hash\
- --discovery-token-ca-cert-hash sha256:hash
正常執(zhí)行完成后集群就可以啟動(dòng)起來了:
- # kubectl get node -o wide
- NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
- k8s-master-0 Ready control-plane,master 121m v1.20.2 192.168.0.201 <none> Ubuntu 20.04.2 LTS 5.4.0-45-generic containerd://1.4.3
- k8s-master-1 Ready control-plane,master 114m v1.20.2 192.168.0.202 <none> Ubuntu 20.04.2 LTS 5.4.0-45-generic containerd://1.4.3
- k8s-master-2 Ready control-plane,master 113m v1.20.2 192.168.0.203 <none> Ubuntu 20.04.2 LTS 5.4.0-45-generic containerd://1.4.3
- k8s-worker-0 Ready <none> 114m v1.20.2 192.168.0.204 <none> Ubuntu 20.04.2 LTS 5.4.0-45-generic containerd://1.4.3
- k8s-worker-1 Ready <none> 114m v1.20.2 192.168.0.205 <none> Ubuntu 20.04.2 LTS 5.4.0-45-generic containerd://1.4.3
- k8s-worker-2 Ready <none> 112m v1.20.2 192.168.0.206 <none> Ubuntu 20.04.2 LTS 5.4.0-45-generic containerd://1.4.3
現(xiàn)在可以看到我們的控制面的端點(diǎn)是 192.168.0.100,沒有其他額外的節(jié)點(diǎn),是不是非常方便。
參考文檔:https://inductor.medium.com/say-good-bye-to-haproxy-and-keepalived-with-kube-vip-on-your-ha-k8s-control-plane-bb7237eca9fc