冒死分析:不能访问自己的SLB是一个坏设计

1 开始

2023年6月,我们与一位客户展开合作,帮助客户做容器化转型,客户拥有10多家已经完成容器化改造的 ISV(Independent Software Vendor),客户需要一个PaaS 容器云平台,对供应商软件的资源配额、服务暴露、存储、配置等进行统一管理。

于是我们基于 Kubernetes 1.21.0,交付了一款容器云管理平台的 PaaS 产品,在部署过程中,遇到了一个关于 slb 的问题,并调研了4种解决方案,最终解决。

1.1 我们的负载均衡设计

image-20230924121943195.png

1.2 slb 实际的表现

  • 从集群中,除 3台 master 之外的任意 worker 节点,telnet master slb,永远是通的
  • 从3台 master 机器上 telnet master slb,时通时不通,故障率约为30%,这样的故障率在生产环境,是必须要解决的
  • harbor slb 和 ingress slb,也是同样的现象,slbA 的后端服务器(backend server),去访问 slbA ,访问结果是时通时不通;后端服务器之外的机器,去访问 slbA,永远是通的

image-20231008201051860.png

image-20231008201337037.png

1.3 产品运维人员给出的解释

当检测到是自己发出的请求后,slb 直接把这个包给丢了

image-20230924124232930.png
官方文档的解释:

image-20230927105632773.png

2 如何解决?

2.1 解决方案一 - ha + keepalive

由于供应商众多,且对高可用要求很高,所以在整个架构上,再加2台4C8G的机器,部署一套 HaProxy + KeepAlive,这也是 kubernetes 标准部署推荐的负载均衡方案,用以代替原来的 SLB。

但这样的一个缺点是,在一个集群里,用了两种 slb 产品,对一个强迫症患者而言,架构上有东拼西揍的混乱感。

image-20230926161031647.png

2.2 解决方案二 - localhost

最简单且改动最小的方案,只有master-slb下的3台机器,访问方式从 master-slb-ip,改成 localhost 本地访问,其他 worker 节点,依然保持走 master-slb-ip。

image-20230924124655865.png

2.3 解决方案三 - k8s 集群本身的负载均衡

在 default 命名空间下,有一个名为 kubernetes 的 svc,这是kubernetes 自带的master节点的服务暴露方式,describe 其service,可以看到endpoint的值是集群3台master的nodeIP,通过轮询的策略,将流量分发到不同的 master 节点。

使用 kubernetes 内部svc的方式,相比上面 localhost 方案,可用性更高;且通过 svc 访问服务,比直接通过 localhost 访问,在使用规范上,更符合 kubernetes 的设计理念。

[root@172 ~]# k describe svc kubernetes -n default
Name:              kubernetes
Namespace:         default
Labels:            component=apiserver
                   provider=kubernetes
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                11.3.0.1
IPs:               11.3.0.1
Port:              https  443/TCP
TargetPort:        6443/TCP
Endpoints:         master1nodeIP:6443,master2nodeIP:6443,master3nodeIP:6443
Session Affinity:  None
Events:            <none>

image-20230924130120812.png

2.4 解决方案四 - 四层变7层(已夭折)

在原来的架构里,我们申请的slb端口是 TCP 协议,期间查询到负载均衡协议变更为 HTTPS 后,可以实现slb backend server 访问 slb 本身,于是我们考虑先将 SLB 四层 TCP 协议变更为七层HTTPS协议,这样对我们的架构改动性最小。

但在变更后发现,SLB 变更为 HTTPS 协议后,其后端协议只支持 HTTP,而我们6443的 Apiserver 服务是一个https服务,协议无法兼容,于是出现了下面的报错,这个方案夭折。

image-20230926174004389.png

image-20230926173837888.png

3 为什么是个坏设计?

slb 下面的机器,访问自己上层的slb,这是一个十分常见且合理的场景,

一些硬件负载均衡设备,如 A5,F10是支持这样访问的,部分云厂商的负载均衡产品也支持;但部分云厂商负载均衡产品不支持这样的场景。

因为无法看到slb产品内部的实现,只能猜测slb产品经理可能是出于性能优化/安全的考虑,从设计上阻止了这类场景,

对于kubernetes集群,面对这样的现状,我们是可以进行改造,集群内部流量走 svc,外部流量走slb,来规避slb的这个缺陷,

但是对于未完成容器化改造,需要ECS 虚拟机部署的ISV,在同样的需求和现状下,他们唯一的方式就是弃用slb了。

这是一个坏设计