kubernetes - 局域网可用的负载均衡器-MetalLB
在 Kubernetes 中,对于 LoadBalancer 类型的 Service,k8s 并没有为裸机集群实现负载均衡器,因此我们只有在以下 IaaS 平台(GCP, AWS, Azure)上才能使用 LoadBalancer 类型的 service。
因此裸机集群只能使用 NodePort 或者 externalIPs service 来对面暴露服务,然而这两种方式和 LoadBalancer service 相比都有很大的缺点,而 MetalLB 的出现就是为了解决这个问题。 也就是说在裸机的 K8s 集群无法使用 LoadBancer 类型的 Service。否则,您会发现 LoadBancer 的 Service 一直处于 Pending 状态,而 MetalLB 的出现就是为了解决这个问题。
MetalLB 是一款开源软件,它采用标准的路由协议(ARP 或 BGP)实现了裸机 K8s 集群的负载均衡功能。 https://metallb.io/installation/
L2&BGP 模式说明
MetalLB L2 模式
L2 模式下,Metallb
将会通过 ARP(for ipv4)、NDP(for ipv6)宣告 LoadBalancerIP
的地址。
在 Metallb
< v0.13.2
之前,只能通过 configMap
来配置 Metallb
。
在 v0.13.2
之后,通过 CRD 资源的方式配置 Metallb
,另外 configMap
的方式已被弃用。
在 Layer2
模式下,当创建服务时,Metallb
(speaker
组件)会为这个服务选举出集群中某个节点,作为这个服务对外暴露的主机。
当对 Service 的 externalIP
发出请求时,此节点会代替这个 externalIP
回复 arp
请求。
所以对 Service 发出的请求,会首先到达集群中这个节点,然后再经过这个节点上的 kube-proxy
组件,最后将流量导向这个 service 某个具体的端点 (endpoint)。
服务选举节点的逻辑主要有三点:
- 首先过滤掉未 ready 的节点以及 endpoint 未 ready 所在的节点
- 如果该服务的 endpoint 分布在同一个节点,那么筛选此节点作为该服务 IP 的
arp
响应者 - 如果该服务的 endpoint 分布在不同的节点,那么通过
sha256
计算节点 + # + externalIP
后,按照字典顺序取第一个
这样,MetalLB 就会为每个 Service 选择一个节点作为暴露的主机。
metallb
会将这单个 Service 的流量,全部导向某个节点,所以这个节点可能会成为限制性能的瓶颈。
Service 的带宽限制也会取决于单个节点的带宽,这也是使用 ARP 或 NDP 最主要的限制。
此外,当此节点发生故障时,MetalLB 需要为服务重新选择一个新的节点。
然后 Metallb
会给客户端发送一个"免费"的 arp
,告知客户端需要更新他们的 Mac 地址缓存。
在客户端更新缓存前,流量仍会转发到故障节点。因此从某种程度来看:故障转移的时间,依赖于客户端更新 Mac 地址缓存的速度。
使用
-
创建 IP 池
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: demo-pool namespace: metallb-system labels: ipaddresspool: demo spec: addresses: - 192.168.10.0/24 - 192.168.9.1-192.168.9.5 - fc00:f853:0ccd:e799::/124 autoAssign: true avoidBuggyIPs: false
-
addresses
:IP 地址列表,每一个列表成员可以是一个 CIDR,可以是一个地址范围(如 192.168.9.1 - 192.168.9.5),也可以是不同ipFamily
、Metallb
会从其中分配 IP 给LoadBalancer
服务 -
autoAssign
:是否自动分配 IP 地址,默认为 true。在某些情况下(IP 地址不足或公有 IP),不希望池中的 IP 被轻易地分配,可设置为 false。
可以通过在 service 中设置 annotations:metallb.universe.tf/address-pool: pool-name
。或者在spec.LoadBalancerIP
字段设置 IP(注意这种方式已被k8s标记为遗弃)。 -
avoidBuggyIPs
:是否避免使用池中以.0
或.255
地址,默认为 false。 -
配置
LoadBalancerIP
通告规则 (L2)通过
L2Advertisement
绑定地址池,这样告诉Metallb
这些地址应该由ARP
或NDP
通告出去。apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: demo namespace: metallb-system spec: ipAddressPools: - demo-pool ipAddressPoolSelectors: - matchLabels: ipaddresspool: demo nodeSelectors: - matchLabels: kubernetes.io/hostname: kind-control-plane
-
ipAddressPools
:可选,通过 name 筛选地址池,如ipAddressPools
和ipAddressPoolSelectors
同时未指定,则作用于所有地址池。 -
ipAddressPoolSelectors
:可选,通过 labels 筛选地址池,如ipAddressPools
和ipAddressPoolSelectors
同时未指定,则作用于所有地址池。 -
nodeSelectors
:可选,用于筛选哪些节点作为loadBalancerIP
的下一跳,默认所有节点。 -
创建
LoadBalancer Service
apiVersion: v1 kind: Service metadata: name: metallb1-cluster labels: name: metallb #annotations: #metallb.universe.tf/address-pool: lan spec: type: LoadBalancer allocateLoadBalancerNodePorts: false ports: - port: 18081 targetPort: 8080 protocol: TCP selector: app: metallb-cluster
只需要指定
spec.type=LoadBalancer
,这样Metallb
就会自然接管此Service
的生命周期。!!! note
如果想让 Service 从指定的地址池中分配地址,通过 `annotations: metallb.universe.tf/address-pool: <pool-name>` 指定。或者通过 `service.spec.loadBalancerIP` 字段指定 IP(需要保证存在于一个池中,不推荐这种方式)。 如果存在多种负载均衡器,可通过 `service.spec.loadBalancerClass` 字段指定。在部署 `Metallb` 时,可通过 `--lb-class` flag 进行配置。
负载均衡性
-
当
Service.spec.externalTrafficPolicy=cluster
这种模式下,具有良好的负载均衡性,但流量可能经历多跳,这会隐藏客户端源 IP。
______________________________________________________________________________ | -> kube-proxy(SNAT) -> pod A | | | | client -> loadBalancerIP:port -> | -> node A(Leader) -> | | | | | -> kube-proxy(SNAT) -> node B -> kube-proxy -> pod B | ------------------------------------------------------------------------------
-
当
Service.spec.externalTrafficPolicy=local
这种模式下,会保留客户端源IP,但负载均衡性较差,流量会一直到某一个后端Pod。
__________________________________________________________________________________________ | -> kube-proxy -> pod A (后端Pod在本节点) | | | | client -> loadBalancerIP:port -> | -> node A(Leader) -> | | | | | -> kube-proxy -> node B -> kube-proxy -> pod B (后端Pod在不同节点) | ------------------------------------------------------------------------——————————————————
MetalLB BGP 模式(L3)
Layer2
模式局限在一个二层网络中,流向 Service 的流量都会先转发到某一个特定的节点,这并不算真正意义上的负载均衡。
BGP 模式不局限于一个二层网络,集群中每个节点都会跟 BGP Router 建立 BGP 会话,宣告 Service 的 ExternalIP
的下一跳为集群节点本身。
这样外部流量就可以通过 BGP Router 接入到集群内部,BGP Router 每次接收到目的是 LoadBalancer
IP 地址的新流量时,它都会创建一个到节点的新连接。
但选择哪一个节点,每个路由器厂商都有一个特定的算法来实现。所以从这个角度来看,这具有良好的负载均衡性。
使用
-
创建 IP 池
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: bgp-pool namespace: metallb-system labels: ipaddresspool: demo spec: addresses: - 192.168.10.0/24 autoAssign: true avoidBuggyIPs: false
-
配置
LoadBalancerIP
通告规则 (L3)!!! note
BGP 模式需要硬件支持运行 BGP 协议。若无,可使用如 `frr`、`bird` 等软件代替。
推荐使用
frr
进行安装:# ubuntu apt install frr # centos yum install frr
frr
配置BGP
:router bgp 7675 # bgp as number bgp router-id 172.16.1.1 # route-id 常常是接口IP no bgp ebgp-requires-policy # 关闭 ebpf filter !!! neighbor 172.16.1.11 remote-as 7776 # 配置 ebgp -> neighbor 1, 172.16.1.11 为集群一节点 neighbor 172.16.1.11 description master1 # description neighbor 172.16.2.21 remote-as 7776 # 节点 2 neighbor 172.16.2.21 description woker1
Metallb
配置: -
配置
BGPAdvertisement
此 CRD 主要用于指定需要通过 BGP 宣告的地址池,同 L2 模式,可通过池名称或者
labelSelector
筛选。同时可配置BGP一些属性:apiVersion: metallb.io/v1beta1 kind: BGPAdvertisement metadata: name: local namespace: metallb-system spec: ipAddressPools: - bgp-pool aggregationLength: 32
aggregationLength
:路由后缀聚合长度,默认为 32,意味这 BGP 通告的路由的掩码为 32,值调小可聚合路由条数aggregationLengthV6
:同上,用于 ipv6,默认为 128ipAddressPools
:[]string,选择需要 BGP 通告的地址池ipAddressPoolSelectors
:通过 label 筛选地址池nodeSelectors
:通过 node label 筛选loadBalancerIP
的下一跳节点,默认为全部节点peers
:[]string,BGPPeer
对象的名称,用于声明此BGPAdvertisement
作用于哪些 BGP 会话communities
:参考 BGP communities,可以直接配置,也可以指定 communities CRD 的名称
-
配置 BGP Peer
BGP Peer 用于配置 BGP 会话的配置,包括对端 BGP AS 及 IP 等。
apiVersion: metallb.io/v1beta2 kind: BGPPeer metadata: name: test namespace: metallb-system spec: myASN: 7776 peerASN: 7675 peerAddress: 172.16.1.1 routerID: 172.16.1.11
myASN
:本端 ASN,范围为1-64511(public AS)
、64512-65535(private AS)
peerASN
:对端 ASN,范围同上。如果二者相等,则为iBGP
;否则为eBGP
peerAddress
:对端路由器 IP 地址sourceAddress
:指定本段建立 BGP 会话的地址,默认从本节点网卡自动选择nodeSelectors
:根据 node label 指定哪些节点需要跟 BGP Router 建立会话
-
创建
LoadBalancer
类型的 Service$ kubectl get svc | grep LoadBalancer metallb-demo LoadBalancer 172.31.63.207 10.254.254.1 18081:30531/TCP 3h38m
验证
在 BGP Router 上可以看到通过 BGP 学习到的路由:
$ vtysh
Hello, this is FRRouting (version 8.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.
router# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
f - OpenFabric,
> - selected route, * - FIB route, q - queued, r - rejected, b - backup
t - trapped, o - offload failure
K>* 0.0.0.0/0 [0/100] via 10.0.2.2, eth0, src 10.0.2.15, 03:52:17
C>* 10.0.2.0/24 [0/100] is directly connected, eth0, 03:52:17
K>* 10.0.2.2/32 [0/100] is directly connected, eth0, 03:52:17
B>* 10.254.254.1/32 [20/0] via 172.16.1.11, eth1, weight 1, 03:32:16
* via 172.16.2.21, eth2, weight 1, 03:32:16
C>* 172.16.1.0/24 is directly connected, eth1, 03:52:17
可以看到通往 LoadBalancerIP
的下一跳分别是集群节点 1 和节点 2,在 BGP Router 执行连通性测试:
root@router:~# curl 10.254.254.1:18081
{"pod_name":"metallb-demo","pod_ip":"172.20.166.20","host_name":"worker1","client_ip":"172.20.161.0"}
FRR Mode
目前 Metallb
BGP 模式有两种 Backend 实现:Native BGP
和 FRR BGP
。
FRR BGP
目前是实验阶段,对比 Native BGP
,FRR BGP
主要有以下几个优点:
BFD
协议支持(提高故障反应能力,缩短故障时间)- 支持
IPV6 BGP
- 支持
ECMP
实战演练
此处L2模式举例,我的网络插件使用的是kube-router
, 默认启用了ARP
,所以直接安装:
[root@master-01 metallb]# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
[root@master-01 metallb]# kubectl get pod -A | grep metallb
metallb-system controller-6dd967fdc7-9lq2p 1/1 Running 0 11h
metallb-system speaker-kjm8c 1/1 Running 0 11h
metallb-system speaker-knv64 1/1 Running 0 11h
metallb-system speaker-rcxhc 1/1 Running 0 11h
L2 模式测试
[root@master-01 metallb]# cat IPAddressPool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: pool
namespace: metallb-system
spec:
addresses:
# 可分配的 IP 地址,可以指定多个,包括 ipv4、ipv6,我的路由器网段就是下面的网段
- 192.168.1.240-192.168.1.250
[root@master-01 metallb]# cat L2Advertisement.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
spec:
ipAddressPools:
- pool #上一步创建的 ip 地址池,通过名字进行关联
[root@master-01 metallb]# kubectl create -f IPAddressPool.yaml
[root@master-01 metallb]# kubectl create -f L2Advertisement.yaml
Nginx 部署测试
[root@master-01 metallb]# cat nginx-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: docker.io/nginx:latest
ports:
- containerPort: 80
[root@master-01 metallb]# cat nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
selector:
app: nginx
ports:
- name: nginx-port
protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
[root@master-01 metallb]# kubectl create -f nginx-deployment.yml
[root@master-01 metallb]# kubectl create -f nginx-svc.yaml
[root@master-01 metallb]# kubectl get deployments.apps nginx-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 10h
[root@master-01 metallb]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 11h
nginx LoadBalancer 10.68.74.145 192.168.1.240 80:31054/TCP 10h
[root@master-01 metallb]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-755fbfbc49-gljgb 1/1 Running 0 10h
nginx-deployment-755fbfbc49-l2928 1/1 Running 0 10h
nginx-deployment-755fbfbc49-lvbsp 1/1 Running 0 10h
[root@master-01 metallb]# ip addr show ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:80:af:bc brd ff:ff:ff:ff:ff:ff
altname enp2s1
inet 192.168.1.56/24 brd 192.168.1.255 scope global dynamic noprefixroute ens33
valid_lft 45382sec preferred_lft 45382sec
inet6 fe80::20c:29ff:fe80:afbc/64 scope link noprefixroute
valid_lft forever preferred_lft forever
[root@master-01 metallb]# curl -I 192.168.1.240
HTTP/1.1 200 OK
Server: nginx/1.27.2
Date: Sun, 13 Oct 2024 20:34:22 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 02 Oct 2024 15:13:19 GMT
Connection: keep-alive
ETag: "66fd630f-267"
Accept-Ranges: bytes
本文来自博客园,作者:Jas0n0ss,转载请注明原文链接:https://www.cnblogs.com/Jas0n0ss/p/18463354