在私有Kubernetes集群中实现服务的负载均衡
2020-02-03李翔
李翔
(中远海运科技股份有限公司 上海市 200135)
以Docker 为基础的Kubernetes 容器集群近几年发展迅猛,它极大地简化了容器集群的创建、集成、部署、运维流程,从而推动了容器技术在云端的大规模使用[1],越来越多的企业开始采用Kubernetes 集群来承载业务系统和运维系统。对于在公有云(如AWS 、Google Cloud、阿里云等)中部署的Kubernetes 集群,负载均衡由云服务商提供,可以与公有云的内部网络实现完整集成,在集群内部可以获取负载均衡地址,并进行管理。
对于部署于企业内部机房的私有Kubernetes 集群,负载均衡就成为了一个难题。由于企业的内部机房往往建设较早,缺乏相关的集成管理接口,从集群原生角度较难实现完整集成。通常的做法是将Kubernetes 集群中的服务以NodePort 方式进行发布,在集群外部通过负载均衡的软硬件(如F5、HAProxy 等)进行负载均衡。此种方式虽然能实现负载均衡,但还存在一系列的问题,例如:
(1)负载均衡IP 的管理独立于Kubernetes 集群之外,为了保证负载均衡的正常工作,需要购买专门的硬件(F5 等)或部署专门的软件集群(HAProxy、Nginx 等);
(2)使用NodePort 方式提供服务,相较Service/LoadBalance方式,因增加了数据传输链路的复杂度,传输性能会略有下降。有文献指出,性能损失约7%[1]。
由开源社区实现的MetalLB,以原生方式在集群中实现了负载均衡IP 的动态管理和流量分发,为私有化集群的数据交换提供了一种新的方案。
1 Kubernetes集群网络
1.1 Kubernetes网络模型
Docker 使用了一个比较简单的网络模型,即内部的网桥加内部的保留 IP,容器与外部网络的数据交互通过网桥(docker0)来完成。在Kubernetes 集群中,采用了被称为CNI(Container Network Interface)的网络标准来完成通信。集群管理员通过采用不同的CNI插件(如Calico、Flannel、Weave 等)来完成网络的构建。
在单一主机中,Pod之间使用docker0网桥来直接进行网络交互,但在跨主机,以及集群内外部之间的数据交互,主要有两种解决方案:
(1)通过修改系统底层的网络配置,加入容器网络IP 地址等,此方案通常需要与SDN(Software Define Networking)结合,以实现网络的动态配置;
(2)不修改底层的网络配置,复用原有的网络平面,采用如下两种策略实现网络互通:
在“一带一路”倡议助推我国图书出版业产业持续升级、合作模式不断创新,带动外国作家创作与讲述中国故事与中国主题并传播中国文化的过程中,外国作家及作品对“一带一路”文化进行现代阐释的积极意义不言而喻。
图1:Kubernetes 集群网络简图
1.修改主机路由:将容器网络加入主机路由表,通过路由转发实现三层互通。支持此方案的网络插件有Calico、Flannel 等;
2.采用隧道传输:对容器数据包进行封装,使用主机的网络传输至目标主机,目标主机收到数据包后进行拆包,再传递给对应的容器。常见的隧道协议有ipip、VxLAN 等,支持此方案的网络插件有Flannel 等。
1.2 Service与kube-proxy
Service 从本质上来说,是一种内部的负载均衡实现。它拥有一个虚拟IP(Cluster IP),以及一个用于集群内部访问的域名。Service 代理了功能相同的一系列Pod 组成的后端的访问,所有到达Service 的请求会按照负载均衡策略分发至不同的Pod。当Pod产生异常或有新Pod 上线时,Service 也能动态调整分发策略。
依据不同的类型,Service 可以提供不同的网络服务。常见的类型有:
(1)ClusterIP:此方式用于集群的内部访问,这也是Service的默认类型;
(2)NodePort:将服务与主机端口进行绑定,通过主机IP+端口的方式,可在集群与相应的Pod 进行交互,这也是Kubernetes集群向外部提供服务的常见方法;
(3)LoadBalancer:负载均衡服务,此服务也用于向集群外部提供服务,但需要与主机所在的底层网络进行交互,实现二层/三层数据交换。
Kube-Proxy 组件是实现整套Service 机制的核心,此组件通过Kubernetes 集群的ApiServer 对集群中的Pod/Service 进行监控,通过监控相关的定义、配置等实现Service 的功能。
图2:Service
2 私有Kubernetes集群中的负载均衡
2.1 MetalLB的工作原理
由章节1 可知,若需要集群外部通过负载均衡IP 来访问Service,需要解决两个问题:
(1)外部请求如何到达集群主机节点;
(2)数据包到达主机节点后,如何正确交付给对应的Service。
在基于IP 的以太网中,数据包的分发主要有两种策略:
(1)对于同一子网的数据包,操作系统会查找自身的ARP/NDP 表,将IP 地址转换成MAC 地址后进行二层交换;
(2)对于不同子网的数据包,操作系统会查找自身的路由表,选择合适的路由将数据包发送给对应的网关,实现三层交换。
为此,只需将负载均衡地址采用ARP 或者路由,实现二层/三层的绑定后,集群内外的数据包交互即可顺利进行。
对于问题2,由章节1.2 中的介绍可知,Service 是由kubeproxy 来实现的,数据包到达集群节点主机后,只需交付给kubeproxy,通过端口号正确识别并转发至对应的服务,即可实现与内部访问集群相同的效果。
MetalLB 组件通过在各集群节点中使用DaemonSet 部署Speaker 组件,通过发送ARP/NDP 广播包(二层)或与核心路由器进行BGP 配对(三层),实现负载均衡IP 绑定,完成集群内外的数据交换。
2.2 基于ARP的负载均衡
在ARP 模式下,MetalLB 组件会选择一台集群节点(物理机或虚拟机)作为外部网络的应答节点,通过向主机网络发送ARP/NDP 广播包,实现负载均衡IP 与MAC 的绑定。到达此主机的数据包会进入kube-proxy,再分发到相应的Pod。
在ARP 模式下,所有的外部请求均只到达一个主机节点,交付kube-proxy 后再进行集群内的分发,此工作模式与keepalived 类似。若此应答节点出现故障,MetalLB 会重新选择一个应答主机节点,在新应答节点中继续发送ARP/NDP 广播,实现IP 地址的切换。
由于此模式基于网络二层交换,对硬件设备没有任何的附加需求。但是,负载均衡的IP 地址需要与集群主机的底层网络处于相同子网中。
2.3 基于BGP的负载均衡
在BGP 模式下,MetalLB 将自身虚拟为多个路由器(每台集群节点为一台路由),与集群节点底层网络中的核心路由器之间使用BGP 协议进行配对。通过广播自身的负载均衡IP,在核心路由器中建立相应的动态路由。
当核心路由启用多路径支持时,此模式可以实现“真正”的负载均衡,即集群外的数据包由核心路由分发至不同的集群节点,交由kube-proxy 后再分发至相应Pod。
由于基于BGP 的路由分发是无状态的,一个Session 中的多个数据包可能会被路由至不同的集群节点。所幸的是,Kubernetes 的Service 提供了sessionAffinity 的设置,对于有session stick 的场景,可以通过启用sessionAffinity 来保证来自相同用户的数据包到达同一个Pod。
BGP 的路由分发是基于对协议、原地址、目标地址等参数的hash 来进行路由选择。当后端的集群节点发生故障而下线时,整个集群的活跃连接可能会出现“闪断”的情况,即,客户端将收到“Connection reset by peer”的错误提示。为此,一方面可以考虑在核心路由器中启用ECMP hash 算法来降低影响,另一方面在应用中增加出错重连机制,保证数据的可靠交换。
BGP 模式工作基于IP 交换,工作在网络第三层,故负载均衡的IP 可以与集群节点的IP 处于不同子网内。但是,此模式需要底层网络的路由设备支持BGP 协议,在网络硬件设备上有一定的限制。
2.4 组件部署与应用
参照官方的说明文档,在内部的Kubernetes 集群中部署了MetalLB 组件。考虑部署的复杂性以及对网络基础设施的要求,采用了基于ARP 的二层均衡方案。
在私有Kuberntes 集群中部署MetalLB,仅需通过kubectl,部署相关的yaml 文件即可。在部署之前,需配置负载均衡地址池。此外,对于1.14.2 版本以上启用了IPVS 的Kubernetes 集群,还需要启用kube-proxy 的严格APR 模式。
MetalLB 地址池配置文件内容如下:
使用如下语句在Kubernetes 集群中部署MetalLB:
观察到相关组件均正常运行后,即可对Service 进行调整,将ServiceType 更改为“LoadBalancer”方式,结果如图2 所示。
可以看到,在外部Endpoint 中已经获取到了指定负载均衡IP地址池范围内的IP 地址,并已正常显示在Service 中。通过访问相应的IP 地址,服务联通正常。
3 结语
本文介绍了Kubernetes 集群的网络基本架构知识,对开源组件MetalLB 的工作原理、不同的负载均衡模式进行了说明,最后在公司内部的私有化集群中以ARP 模式部署了MetalLB 组件,实现了集群对负载均衡的支持。
从MetalLB 的工作原理分析,其本质仍然为一个“外部”的负载均衡实现,一个与Kubernetes 集成的“HAProxy”负载集群。通过与Kubernetes API 交互,MetalLB 能够实时获取Service 相关配置信息,动态配置网络发现和转发,同时反馈Load Balancer 的状态。从网络性能角度考虑,因工作原理仍然是将相关数据包交付给Kube-proxy,与NodePort 方式无异。但从管理的便捷性和集成度上说,采用MetalLB 的负载均衡方式实现了基于集群的统一管理,大大减轻了管理人员的工作量。
截止目前,MetalLB 组件已在私有集群中稳定运行了一年多,各项性能指标均达到了预期要求。