前言
k8s默认不提供LoadBalancer实现,一般是云厂商的k8s集群会提供这样的功能,让我们的服务暴露到某个外部ip地址上。个人自建集群的话可以使用MetalLB来实现外部ip地址的分配。
正文
安装
根据文档来安装还是挺简单的。
1 | kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.7/config/manifests/metallb-native.yaml |
一行就搞定了。这里会在每个节点上启动speaker,再单独起一个controller。
- The
metallb-system/controller
deployment. This is the cluster-wide controller that handles IP address assignments.- The
metallb-system/speaker
daemonset. This is the component that speaks the protocol(s) of your choice to make the services reachable.
简单来说controller是分配地址,而speaker是让外部访问能到达指定容器的。
配置
接着配置L2模式,这种模式配置起来是比较简单的,它的原理是响应ARP请求,将节点的MAC地址返回给客户端,由此实现响应。
Layer 2 mode is the simplest to configure: in many cases, you don’t need any protocol-specific configuration, only IP addresses.
Layer 2 mode does not require the IPs to be bound to the network interfaces of your worker nodes. It works by responding to ARP requests on your local network directly, to give the machine’s MAC address to clients.
先配置ip池
1 | apiVersion: metallb.io/v1beta1 |
然后配置L2Advertisement,如果没有配置spec.ipAddressPools的话,那么这个L2Advertisement则绑定所有IPAddressPool。
1 | apiVersion: metallb.io/v1beta1 |
至于BGP模式,使用上会相对复杂,不理解网络的人可能会有点懵。死去的计算机网络在攻击我,让我想起了网工的那段学习。BGP是一个路由协议,用于发现邻近路由,通过手动维护IP路由表或前缀表,相连AS之间相互通信,从而实现路由网络。
在使用metallb之前,由于我们使用k3s搭建集群,k3s默认提供了LoadBalancer实现,参见。但是这种实现它是创建了新的pod,需要额外的资源。说实话,为啥中文文档没翻译全?我看的也是云里雾里的。
The ServiceLB controller watches Kubernetes Services with the
spec.type
field set toLoadBalancer
.For each LoadBalancer Service, a DaemonSet is created in the
kube-system
namespace. This DaemonSet in turn creates Pods with asvc-
prefix, on each node. These Pods use iptables to forward traffic from the Pod’s NodePort, to the Service’s ClusterIP address and port.If the ServiceLB Pod runs on a node that has an external IP configured, the node’s external IP is populated into the Service’s
status.loadBalancer.ingress
address list. Otherwise, the node’s internal IP is used.
看了下网络上的其他解析,其实ServiceLB他的就是在pod所在节点增加iptables规则,将指定端口的流量转发给service的clusterIP,它只能把节点ip当作对外ip。这和我们预期:提供一个非节点ip自动绑定该地址不符。
所以说了这么多,我们就是要默认禁用掉ServiceLB。
1 | vim /etc/systemd/system/multi-user.target.wants/k3s.service |
1 | systemctl daemon-reload |
重启下k3s后,可以发现svclb-*容器就不见了。
测试
老朋友nginx
1 | apiVersion: apps/v1 |
查看service分配的地址
1 | [root@worker1 metalLB]# kubectl get svc |
这个EXTERNAL-IP就是metallb为我们分配的地址。访问也是正常的。
至此,我们便实现了本地集群分配ip地址的功能。
但是问题出现了,当重复尝试定义metadata.annotations.metallb.universe.tf/loadBalancerIPs: 192.168.28.211
时,同一个ip地址没法分配给多个服务,即使两个svc是不同的端口。
这时候我们定义svc就需要指定相应的annotations.
By default, Services do not share IP addresses. If you have a need to colocate services on a single IP, you can enable selective IP sharing by adding the
metallb.universe.tf/allow-shared-ip
annotation to services.
改造下刚才的yaml
1 | apiVersion: v1 |
这个yaml中,我们指定了svc的loadBalancerIP,并且将这个ip设置为可共享的。
另一个svc也是一样
1 | apiVersion: v1 |
再次查看svc可以发现两个svc分配的ip都是192.168.28.211。
总结
到这里我们整个metalLB的接入就完成了,这个也是上一家公司采用的方案。整体搭建下来感觉还是比较简单的,注意点就是地址池定义需要在节点子网里,否则外部根本没法访问,这其中涉及到网络相关的知识。例如我虚拟机子网是192.168.28.0/24,那么地址池必须在这个范围里面,否则路由规则都没有。除非我在本地添加路由
1 | route add 192.168.27.0 mask 255.255.255.0 192.168.28.1 |
那么我就可以访问到非虚拟机子网的地址。原理就是路由到网关然后192.168.28.1网关做了ARP,metalLB响应了ARP请求。那么我们就能访问到了。
所以网络规划前期就要做好,如果节点子网是/16的话,那么再分配/24网络的话会更加方便,因为上层帮你做了路由,应该也能采用BGP模式,不过我就不尝试了。