0x01 前言
在使用Rancher那么久发现有个问题,Rancher默认情况下只有通过nginx实现的七层负载均衡,缺少很重要的四层负载均衡,这让我们这些裸金属集群的用户很是头疼。当然,资金充足的话可以购置物理负载均衡设备挂在Rancher的worker节点前,但可惜的是没钱。
在公有云环境中一般有负载均衡的服务,这是很成熟的一套解决方案,可以检测后端也就是worker的状态从而实现高可用,四层和七层都是没问题的。
至于裸金属环境就比较尴尬,没有预设一套可靠的四层解决方案,其实七层也是不太可靠,虽然在Rancher内部可以实现高可用,但在外部却没法受益。
0x02 需求
首先来一张简单的架构图:
如果使用ingress实现的七层负载均衡,则用户只能通过某个worker节点访问,如果使用的是TCP或UDP协议,还可以使用master作为入口。但因为没有负载均衡的关系,当一直用着的入口挂掉之后,虽然整套集群不会蹦,但是也无法接入相关服务,直至人为介入修正。
正如上图中所示,如果我通过DNS将某个域名解析到某个节点或多个节点,如果节点挂了,我必须人为调整DNS或者通过API实现动态DNS,但这势必造成业务流量的长时间中断。
最好的解决方案是在外部挂一套负载均衡,但真的没钱买,只好在内部实现,而这个功能得靠MetalLB。
这个东西很简单,就是实现四层负载均衡和IP分配工作的,和虚IP类似,但一个IP只能给一个namespace占用,不知道是不是我使用的方法不对,但这问题不大,IP段够多。
先说明大致的工作流程。首先需要在system项目中建立起一个namespace,然后运行相关pod。其中有一个主配置文件,大致如下:
apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | address-pools: - name: ngx-ip-pool protocol: layer2 addresses: - 10.1.3.220-10.1.3.224
这是配置映射,大致内容如下:
- name:IP池的名称,建议按需进行调整IP池的数量,方便进行网络隔离
- protocol:上面用的是二层协议,它还支持BGP协议,但我没有相关设备和环境进行测试
- addresses:虚拟IP的IP池,可以用上面的格式,也可以用CIDR格式
完成部署后还需要在对应的namespace中添加一个四层负载均衡器,配置文件大致如下:
apiVersion: v1 kind: Service metadata: name: ngx-metallb-220to224 annotations: metallb.universe.tf/address-pool: ngx-ip-pool spec: ports: - name: ingress port: 80 protocol: TCP targetPort: 80 selector: l4slb: ngx-metallb-220to224 type: LoadBalancer
可以从type中看到这是一个负载均衡器,它拥有一个注释:
- metallb.universe.tf/address-pool: ngx-ip-pool
指定它使用名为ngx-ip-pool的IP池,也就是上面创建的IP池;暴露TCP 80端口,后端的端口也是80;最后还有一个selector:
- l4slb: ngx-metallb-220to224
最后拿着这个selector到需要暴露端口的pod里添加到标签即可。最终实现的就是可以通过IP池中的某个IP访问目标pod的TCP 80端口,如果需要监听443,则增加一个端口即可。
但这里有个需要注意的点:在同一个IP下不能有同样的端口,因为无法在同一个IP监听同样的协议和端口。还有一个小技巧,可以用它为ingress实现四层负载均衡,监听TCP 80和TCP 443即可实现七层高可用。
因为是虚拟IP,涉及到ARP响应,请留意交换机和路由器上的安全配置。
0x03 部署
部署流程非常简单,全程使用kubectl即可,首先来到官网查看手册:
首先来到Rancher集群,打开kubectl:
紧接着执行命令创建namespace:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
继续执行以下命令创建对应的pod:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
最后创建secret,注意!这只是第一次部署时需要执行:
此时来到system项目中并没有发现刚才创建的namespace,因为没有指定默认的项目,得先来到命名空间页面中修改所属的项目:
将其修改为System并保存即可:
最后回到system项目中即可发现对应的namespace和pod:
0x04 配置
接下来进行配置,首先阅读手册:
主要还是前面讲到的两个配置文件,首先配置IP池。首先来到配置映射页面,单击导入YAML按钮:
当然,你还可以使用kubectl进行导入:
完成后可以点击进入检查:
紧接着部署LoadBalancer,先来到负载均衡的页面,然后单击导入YAML按钮:
填入配置信息并选择ingress作为目标namespace:
完成后可以进入该四层负载均衡检查相关配置:
完成后来到ingress pod所在的位置:
将上面配置的selector key和value加入标签即可:
如何获取IP?回到四层负载均衡器并查看YAML:
0x05 测试
从上面的步骤已经知道IP是10.1.3.220,因为后端是ingress,也就是一个nginx,而它还监听了TCP 80端口,所以可以直接访问:
如果返回404就说明已经完成了部署,而且工作正常。因为没有指定hostname的情况下请求ingress端口是返回404的。接下来部署一个nginx进行测试:
可以看到我在集群内部映射了一个端口,将容器的TCP 80映射到TCP 10080,这是为了实现TCP连接测试所用的。pod成功启动后来到负载均衡器中添加一个七层负载均衡:
然后用curl测试,一切正常,返回的是默认的nginx页面代码:
然后用前面同样的方法在这个项目中建立一个负载均衡器,但这次用的配置文件有点不一样:
apiVersion: v1 kind: Service metadata: name: ngx-metallb-220to224 annotations: metallb.universe.tf/address-pool: ngx-ip-pool spec: ports: - name: nginx port: 10080 protocol: TCP targetPort: 80 selector: l4slb: ngx-metallb-220to224 type: LoadBalancer
用同样的方式获取IP,然后测试:
0x06 结语
总的来说,基本能满足需求。但因为以下两点而不太适合大规模部署使用:
- 容易导致虚IP所在的worker产生带宽压力
- 相同namespace下如果有多个端口需要映射出去,则pod的端口不能一样
第一点可以通过BGP来解决,但是BGP模式下当某个worker挂掉之后会导致TCP连接重置的情况发生,而且配置起来比较麻烦。
在实际使用的过程中还遇到可用IP不足的情况,这个可以在交换机上配置sub IP,将虚IP和业务IP段隔离,同时也方便做ACL。
总的来说,适合在测试环境中使用。