博客园出海记-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 使用

接着,更新 cilium 的部署,开启 cilium 与负载均衡相关的特性

shell 复制代码
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个配置选项

shell 复制代码
--set l2announcements.enabled=true
--set l2NeighDiscovery.enabled=true
--set externalIPs.enabled=true

部署完成后,检查一下配置是否生效

shell 复制代码
~ # 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 内容如下

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

shell 复制代码
# kubectl apply -f lb-service.yaml    
service/kube-api created

查看部署情况

shell 复制代码
~ # 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 内容如下

yaml 复制代码
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
  name: api-server
  annotations:
spec:
  blocks:
  - cidr: "172.21.48.240/28"

下单部署

shell 复制代码
~ # kubectl apply -f lb-ip-pool.yaml
ciliumloadbalancerippool.cilium.io/api-server created

查看部署结果

shell 复制代码
~ # 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 端口

shell 复制代码
~ # 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 内容如下

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

下单部署

shell 复制代码
~ # kubectl apply -f lb-l2-announcements.yaml 
ciliuml2announcementpolicy.cilium.io/control-plane created

查看部署情况

shell 复制代码
# kubectl get CiliumL2AnnouncementPolicy                                                          
NAME            AGE
control-plane   29s

查看 lease 是否生效

shell 复制代码
~ # kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-kube-system-kube-api   kube-cp-03 

查看 l2-announce 是否正常

shell 复制代码
~ # 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>
shell 复制代码
~# 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 端口

shell 复制代码
~ # telnet 172.21.48.240 6443
Trying 172.21.48.240...

连不上,L2 Announcements 没起作用。

用 arping 命令测试一下 APR 解析是否正常

shell 复制代码
~ # 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,竟然也是这个地址!原来,阿里云 VPC 中的任何内网 IP 都会被解析到它。这时才意识到,问题应该与身在云上有关。

后来在网上找到一篇博文 阿里云ecs服务器arp映射表与实际mac地址不一致的疑问 终于真相大白:

发工单后回答说:

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

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

参考:

相关推荐
小马过河R1 天前
K8s引入Service Mesh原因及Istio入门
云原生·容器·kubernetes·k8s·istio·service_mesh
Gogo8162 天前
k8s 跟 nacos 关于服务注册以及服务发现
java·nacos·k8s
@ chen13 天前
kubectl常用命令
云原生·kubernetes·k8s
博客园团队14 天前
博客园出海记-组装集装箱:自建 Kubernetes 集群
k8s·出海记
虚伪的空想家14 天前
K8S的dashboard部署与访问
云原生·容器·kubernetes·k8s·web·dashboard
一个向上的运维者14 天前
详细解读k8s的kind中service与pod的区别
容器·k8s
感哥15 天前
Kubernetes Pod
k8s
Elastic 中国社区官方博客16 天前
使用 cloud-native Elasticsearch 与 ECK 运行
大数据·数据库·elasticsearch·搜索引擎·kubernetes·k8s·全文检索
岚天start17 天前
K8s Ingress Annotations参数使用指南
nginx·kubernetes·k8s·ingress·ingress控制器