博客園出海記-K8S集群優(yōu)化:一次命中注定的失敗

在上一篇出海記博文提到,本想試試 Cilium 的一個高科技,卻試驗失敗。
這個高科技就是,Cilium 可以實現(xiàn)一個“魔法”,給 Kubernetes LoadBalancer Service 分配一個虛擬 IP,當內網中的機器訪問這個 IP 時,Cilium 會選舉1臺節(jié)點服務器,填上它的 MAC 地址,瞞天過海地與請求方進行通信。如果用上這個科技,就可以用 Kubernetes Service 取代阿里云負載均衡作為 control-plane 的負載均衡,省錢又環(huán)保。
上次試驗失敗后,心里有些不甘心,又繼續(xù)折騰。本想著成功后能發(fā)篇博文分享勝利經驗,沒想到現(xiàn)實很骨感——失敗其實是注定的,因為忽略了一個關鍵點——云上的網絡是一個虛擬世界,基于 ARP 協(xié)議的巧妙玩法,在現(xiàn)實(虛擬現(xiàn)實)面前行不通。
于是,這篇原本打算分享勝利經驗的出海記博文被迫降級為分享失敗經驗。
以下是試驗 Cilium L2 Announcements 失敗過程的分享。
首先,在阿里云專有網絡 VPC 中預留 172.21.48.240/28 網段給 Cilium LoadBalancer 使用

接著,更新 cilium 的部署,開啟 cilium 與負載均衡相關的特性
cilium upgrade --version 1.18.1 \
--namespace kube-system \
--set bpf.masquerade=true \
--set kubeProxyReplacement=true \
--set l2announcements.enabled=true \
--set l2NeighDiscovery.enabled=true \
--set externalIPs.enabled=true
相比之前的 cilium 部署,多了3個配置選項
--set l2announcements.enabled=true
--set l2NeighDiscovery.enabled=true
--set externalIPs.enabled=true
部署完成后,檢查一下配置是否生效
~ # cilium config view | grep enable-l2
enable-l2-announcements true
enable-l2-neigh-discovery true
~ # kubectl -n kube-system exec ds/cilium -- cilium-dbg status --verbose | grep externalIPs 2 ?
- externalIPs: Enabled
都生效了。
接下來,部署用于轉發(fā)請求到 api-server 的 LoadBalancer Service,清單文件 lb-service.yaml 內容如下
apiVersion: v1
kind: Service
metadata:
annotations:
io.cilium/lb-ipam-ips: 172.21.48.240
labels:
component: kube-apiserver
tier: control-plane
name: kube-api
namespace: kube-system
spec:
selector:
component: kube-apiserver
tier: control-plane
ports:
- name: https
port: 6443
protocol: TCP
targetPort: 6443
type: LoadBalancer
下單部署這個 service
# kubectl apply -f lb-service.yaml
service/kube-api created
查看部署情況
~ # kubectl get svc --field-selector spec.type=LoadBalancer -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-api LoadBalancer 10.107.192.67 <pending> 6443:31920/TCP 31s
這個 kube-api service 監(jiān)聽于 6443 端口,將轉發(fā)請求給 control-plane 的 api-server,看上去是一個稱職的 control-plane 負載均衡,但它現(xiàn)在只是一個擺設,中看不中用,因為只有 CLUSTER-IP,沒有 EXTERNAL-IP,只能在 k8s 集群內部訪問,而要作為 control-plane 負載均衡,需要能通過節(jié)點服務器所在的網絡訪問。
再接下來,部署 CiliumLoadBalancerIPPool,清單中 cidr 對應的是阿里云 VPC 預留的網段,清單文件 lb-ip-pool.yaml 內容如下
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: api-server
annotations:
spec:
blocks:
- cidr: "172.21.48.240/28"
下單部署
~ # kubectl apply -f lb-ip-pool.yaml
ciliumloadbalancerippool.cilium.io/api-server created
查看部署結果
~ # kubectl get CiliumLoadBalancerIPPool
NAME DISABLED CONFLICTING IPS AVAILABLE AGE
api-server false False 16 77s
~ # kubectl get svc --field-selector spec.type=LoadBalancer -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-api LoadBalancer 10.107.192.67 172.21.48.240 6443:31920/TCP 17m
這時你會發(fā)現(xiàn) kube-api service 的 EXTERNAL-IP 被分配了一個節(jié)點所在網絡(阿里云VPC)的 IP 地址,這個 IP 是通過部署清單中的 io.cilium/lb-ipam-ips: 172.21.48.240 指定的,如果沒有指定,會從 CiliumLoadBalancerIPPool cidr 指定的網段中分配一個。
我們試試在其中一臺節(jié)點服務器上訪問這個 IP 與 6443 端口
~ # curl -k https://172.21.48.240:6443/livez
ok
竟然可以訪問了,我們只是部署了 CiliumLoadBalancerIPPool,其他什么也沒做,是 cilium 根據(jù)我下的菜單(清單文件),知道我的口味,把一切都包辦了。
現(xiàn)在,k8s 集群中的任一節(jié)點都可以通過 172.21.48.240 訪問負載均衡,不管是 control-plane 還是 worker 節(jié)點,
但僅僅這樣是不夠的,比如向集群加入新節(jié)點或者節(jié)點服務器重啟時, 怎么訪問這臺負載均衡?
我們還需要再下一個菜單,開啟 cilium 的魔法能力 —— L2 Announcements,讓同一個內網中沒有加入集群的服務器也能訪問負載均衡,清單文件 lb-l2-announcements.yaml 內容如下
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: control-plane
spec:
serviceSelector:
matchLabels:
component: kube-apiserver
tier: control-plane
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
loadBalancerIPs: true
externalIPs: true
注:serviceSelector 需要對應之前部署的 LoadBalancer Service
下單部署
~ # kubectl apply -f lb-l2-announcements.yaml
ciliuml2announcementpolicy.cilium.io/control-plane created
查看部署情況
# kubectl get CiliumL2AnnouncementPolicy
NAME AGE
control-plane 29s
查看 lease 是否生效
~ # kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-kube-system-kube-api kube-cp-03
查看 l2-announce 是否正常
~ # kubectl -n kube-system get pod -l 'app.kubernetes.io/name=cilium-agent' -o wide | grep kube-cp-03 1 ?
cilium-sgjv9 1/1 Running 8 (21m ago) 14d 172.21.49.58 kube-cp-03 <none> <none>
~# kubectl -n kube-system exec pod/cilium-sgjv9 -- cilium-dbg shell -- db/show l2-announce
IP NetworkInterface
172.21.48.240 eth0
確認 L2 Announcements 已部署成功。
找一臺內網中沒有加入 k8s 集群的服務器測試一下,看是否可以訪問負載均衡的 6443 端口
~ # telnet 172.21.48.240 6443
Trying 172.21.48.240...
連不上,L2 Announcements 沒起作用。
用 arping 命令測試一下 APR 解析是否正常
~ # arping 172.21.48.240
ARPING 172.21.48.240
58 bytes from ee:ff:ff:ff:ff:ff (172.21.48.240): index=0 time=59.783 usec
58 bytes from ee:ff:ff:ff:ff:ff (172.21.48.240): index=1 time=69.028 usec
58 bytes from ee:ff:ff:ff:ff:ff (172.21.48.240): index=2 time=88.491 usec
ARP 解析雖然成功了,但卻是一個很奇怪的 MAC 地址 ee:ff:ff:ff:ff:ff。一開始以為是 cilium-agent 在搞鬼,但后來試著 arping 一個根本不存在的內網 IP,竟然也是這個 MAC 地址!原來,阿里云 VPC 中的任何內網 IP 都是這樣解析。這時才意識到,問題應該與身在云上有關。
后來在網上找到一篇博文 阿里云ecs服務器arp映射表與實際mac地址不一致的疑問 終于真相大白:
發(fā)工單后回答說:
ecs底層是虛擬化的網絡,不是傳統(tǒng)的物理網絡。表項的學習是通過arp代理實現(xiàn),為了避免大量ARP學習影響組件性能,所以看到的都是同一個MAC ee:ff:ff:ff:ff:ff ,是正常的現(xiàn)象。
原來,阿里云 VPC 的虛擬網絡并未完全遵循標準的 ARP 協(xié)議,Cilium 巧妙的 L2 Announcements 方案如同“無根之木”,在云平臺這個虛擬世界的“物理定律”下,其失效是必然的。所以,這次失敗是命中注定的。
參考:
浙公網安備 33010602011771號