博客园出海记-K8S集群优化:一次命中注定的失败

博客园出海记-K8S集群优化:一次命中注定的失败

在上一篇出海记博文提到,本想试试 Cilium 的一个高科技,却试验失败。

这个高科技就是,Cilium 可以实现一个“魔法”,给 Kubernetes LoadBalancer Service 分配一个虚拟 IP,当内网中的机器访问这个 IP 时,Cilium 会选举1台节点服务器,填上它的 MAC 地址,瞒天过海地与请求方进行通信。如果用上这个科技,就可以用 Kubernetes Service 取代阿里云负载均衡作为 control-plane 的负载均衡,省钱又环保。

上次试验失败后,心里有些不甘心,又继续折腾。本想着成功后能发篇博文分享胜利经验,没想到现实很骨感——失败其实是注定的,因为忽略了一个关键点——云上的网络是一个虚拟世界,基于 ARP 协议的巧妙玩法,在现实(虚拟现实)面前行不通。

于是,这篇原本打算分享胜利经验的出海记博文被迫降级为分享失败经验。

以下是试验 Cilium L2 Announcements 失败过程的分享。

首先,在阿里云专有网络 VPC 中预留 172.21.48.240/28 网段给 Cilium LoadBalancer 使用

博客园出海记-K8S集群优化:一次命中注定的失败

接着,更新 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 

都生效了。

接下来,部署用于转发请求到 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 监听于 6443 端口,将转发请求给 control-plane 的 api-server,看上去是一个称职的 control-plane 负载均衡,但它现在只是一个摆设,中看不中用,因为只有 CLUSTER-IP,没有 EXTERNAL-IP,只能在 k8s 集群内部访问,而要作为 control-plane 负载均衡,需要能通过节点服务器所在的网络访问。

再接下来,部署 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 

这时你会发现 kube-api service 的 EXTERNAL-IP 被分配了一个节点所在网络(阿里云VPC)的 IP 地址,这个 IP 是通过部署清单中的 io.cilium/lb-ipam-ips: 172.21.48.240 指定的,如果没有指定,会从 CiliumLoadBalancerIPPool cidr 指定的网段中分配一个。

我们试试在其中一台节点服务器上访问这个 IP 与 6443 端口

~ # curl -k https://172.21.48.240:6443/livez ok 

竟然可以访问了,我们只是部署了 CiliumLoadBalancerIPPool,其他什么也没做,是 cilium 根据我下的菜单(清单文件),知道我的口味,把一切都包办了。

现在,k8s 集群中的任一节点都可以通过 172.21.48.240 访问负载均衡,不管是 control-plane 还是 worker 节点,

但仅仅这样是不够的,比如向集群加入新节点或者节点服务器重启时, 怎么访问这台负载均衡?

我们还需要再下一个菜单,开启 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地址不一致的疑问 终于真相大白:

发工单后回答说:
ecs底层是虚拟化的网络,不是传统的物理网络。表项的学习是通过arp代理实现,为了避免大量ARP学习影响组件性能,所以看到的都是同一个MAC ee:ff:ff:ff:ff:ff ,是正常的现象。

原来,阿里云 VPC 的虚拟网络并未完全遵循标准的 ARP 协议,Cilium 巧妙的 L2 Announcements 方案如同“无根之木”,在云平台这个虚拟世界的“物理定律”下,其失效是必然的。所以,这次失败是命中注定的。

参考:

发表评论

评论已关闭。

相关文章