Kubernetes LoadBalancer-MetaILB

Metallb 提供了自行搭建 Kubernetes 集群负载均衡的功能,类似于公有云一样来体验负载均衡。

Metallb 提供两种模式:BGP、L2。两种模式各有优缺点,本文主要讲解 L2 模式。

一、部署MetaILB

https://metallb.universe.tf/
https://metallb.universe.tf/installation/

#1、修改 kube-proxy 配置文件 为 ipvs模式
#strictARP: true
[root@k8s-master01 kubeadm_yaml]# kubectl get cm -n kube-system
NAME                                                   DATA   AGE
calico-config                                          4      31h
coredns                                                1      32h
extension-apiserver-authentication                     6      32h
kube-apiserver-legacy-service-account-token-tracking   1      32h
kube-proxy                                             2      32h
kube-root-ca.crt                                       1      32h
kubeadm-config                                         1      32h
kubelet-config                                         1      32h
[root@k8s-master01 kubeadm_yaml]# kubectl edit cm kube-proxy -n kube-system
    ipvs:
      excludeCIDRs: null
      minSyncPeriod: 0s
      scheduler: ""
      strictARP: true #此为true

#2、Installation By Manifest
#版本为v0.13.12
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml
kubectl apply -f https://mirror.ghproxy.com/https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml

#3、检查是否创建了新的ns 和 pod
[root@k8s-master01 kubeadm_yaml]# kubectl get ns
NAME              STATUS   AGE
default           Active   32h
kube-node-lease   Active   32h
kube-public       Active   32h
kube-system       Active   32h
metallb-system    Active   48s #新创建
[root@k8s-master01 kubeadm_yaml]# kubectl get pods -n metallb-system
NAME                          READY   STATUS    RESTARTS   AGE
controller-786f9df989-4gzvz   1/1     Running   0          65s
speaker-kcgqx                 1/1     Running   0          65s
speaker-vfxct                 1/1     Running   0          65s
speaker-xr285                 1/1     Running   0          65s

#部署成功后,集群中在 metallb-system namespace 下会存在名为 Controller 的 deployment 和 Speaker 的 daemonSet
#• metallb-system/controller deployment,该 Controller 用于 watch 集群中使用 LoadBalancer 类型的 service 并为其分配 EXTERNAL-IP
#• metallb-system/speaker daemonset,该组件在每个节点都会运行,使用 hostNetwork 网络模式,可以保证上面分配的 IP 可访问。
[root@k8s-master01 auth]# kubectl get pods speaker-kcgqx -o jsonpath={.status.hostIP} -n metallb-system
192.168.40.112
[root@k8s-master01 auth]# kubectl get pods speaker-vfxct -o jsonpath={.status.hostIP} -n metallb-system
192.168.40.111
[root@k8s-master01 auth]# kubectl get pods speaker-xr285 -o jsonpath={.status.hostIP} -n metallb-system
192.168.40.101

#会添加 多个crd
[root@k8s-master01 kubeadm_yaml]# kubectl api-versions
admissionregistration.k8s.io/v1
apiextensions.k8s.io/v1
apiregistration.k8s.io/v1
apps/v1
authentication.k8s.io/v1
authorization.k8s.io/v1
autoscaling/v1
autoscaling/v2
batch/v1
certificates.k8s.io/v1
coordination.k8s.io/v1
crd.projectcalico.org/v1
discovery.k8s.io/v1
events.k8s.io/v1
flowcontrol.apiserver.k8s.io/v1beta2
flowcontrol.apiserver.k8s.io/v1beta3
metallb.io/v1alpha1 #
metallb.io/v1beta1  #
metallb.io/v1beta2  #
networking.k8s.io/v1
node.k8s.io/v1
policy/v1
rbac.authorization.k8s.io/v1
scheduling.k8s.io/v1
storage.k8s.io/v1
v1
[root@k8s-master01 kubeadm_yaml]# kubectl api-resources --api-group=metallb.io
NAME                SHORTNAMES   APIVERSION           NAMESPACED   KIND
addresspools                     metallb.io/v1beta1   true         AddressPool
bfdprofiles                      metallb.io/v1beta1   true         BFDProfile
bgpadvertisements                metallb.io/v1beta1   true         BGPAdvertisement
bgppeers                         metallb.io/v1beta2   true         BGPPeer
communities                      metallb.io/v1beta1   true         Community
ipaddresspools                   metallb.io/v1beta1   true         IPAddressPool
l2advertisements                 metallb.io/v1beta1   true         L2Advertisement

二、创建地址池:IPAddressPool资源示例

#地址池可以支持 IP 网段,也支持 IP 组

vim metallb-ipaddresspool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: localip-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.40.51-192.168.40.80
  autoAssign: true
  avoidBuggyIPs: true
  
[root@k8s-master01 metalLB]# kubectl apply -f metallb-ipaddresspool.yaml
ipaddresspool.metallb.io/localip-pool created
[root@k8s-master01 metalLB]# kubectl get ipaddresspool -n metallb-system
NAME           AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
localip-pool   true          true              ["192.168.40.51-192.168.40.80"]

三、创建二层公告机制

#确认网卡名字 ens33
[root@k8s-master01 metalLB]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:2e:b7:51 brd ff:ff:ff:ff:ff:ff
    inet 192.168.40.101/24 brd 192.168.40.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::a8eb:930a:dda4:4259/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:82:c1:6f:3d brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
4: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 1e:cb:63:82:01:1d brd ff:ff:ff:ff:ff:ff
5: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
    link/ether a2:dd:f4:a1:55:18 brd ff:ff:ff:ff:ff:ff
    inet 10.96.0.1/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.96.0.10/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.97.5.90/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.101.98.91/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
6: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
    inet 172.16.32.128/32 scope global tunl0
       valid_lft forever preferred_lft forever
7: calia0b4ef9fe3f@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::ecee:eeff:feee:eeee/64 scope link
       valid_lft forever preferred_lft forever

#网卡改为自己名字
vim metallb-l2advertisement.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: localip-pool-l2a
  namespace: metallb-system
spec:
  ipAddressPools:
  - localip-pool
  interfaces:
  - ens33
  
[root@k8s-master01 metalLB]# kubectl apply -f metallb-l2advertisement.yaml
l2advertisement.metallb.io/localip-pool-l2a created
You have new mail in /var/spool/mail/root
[root@k8s-master01 metalLB]# kubectl get l2advertisement -n metallb-system
NAME               IPADDRESSPOOLS     IPADDRESSPOOL SELECTORS   INTERFACES
localip-pool-l2a   ["localip-pool"]                             ["ens33"]

四、测试实验LoadBalancer

#创建一个3个副本的pod
[root@k8s-master01 metalLB]# vim nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx-demo
  name: nginx-demo
spec:
  replicas: 3
  containers:
  - image: nginx
    name: nginx-demo

[root@k8s-master01 metalLB]# kubectl apply -f nginx.yaml

#创建一个LoadBalancer Service
[root@k8s-master01 metalLB]# cat nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-loadbalancer-server
spec:
  type: LoadBalancer
  selector:
    app: nginx-test
  ports:
    - protocol: TCP
      port: 80
      
[root@k8s-master01 metalLB]# kubectl apply -f nginx-service.yaml

#192.168.40.51 是从地址池分配而来的第一个IP
[root@k8s-master01 metalLB]# kubectl get svc
NAME                        TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
kubernetes                  ClusterIP      10.96.0.1       <none>          443/TCP        33h
nginx-loadbalancer-server   LoadBalancer   10.110.35.224   192.168.40.51   80:31238/TCP   8s
nginx-service               NodePort       10.97.5.90      <none>          80:31009/TCP   45m

#直接通过VIP访问 或者 节点IP:Nodeport
[root@k8s-master01 metalLB]# curl 192.168.40.51
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

#节点IP:Nodeport
[root@k8s-master01 auth]# curl 192.168.40.101:31238
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
可以发现使用 VIP 和 NodePort 最终都是负载到多个 Nginx 实例上,那岂不是 NodePort 更方便?
假如某个 Node 运行异常,节点 IP 无法使用,那么客户端就需要切换 Node IP 访问,显然没有高可用;
对于使用 VIP 访问,MetalLB 自带高可用属性,无需担心节点异常等异常场景,下文会讲解高可用是如何实现的。

原理

#参考
#https://mp.weixin.qq.com/s/x3FKy-ssA1157FJrQc8vvA
#通过一系列的实验和原理说明,网络中一个 IP 没有绑定到某个设备,只要该 IP 在网络层是可到达,
#那么只要能够回复该 IP 的 ARP 请求就能实现通信。MetalLB L2 模式也是如此。
[root@k8s-master01 auth]# kubectl get node
NAME           STATUS   ROLES           AGE    VERSION
k8s-master01   Ready    control-plane   5d2h   v1.28.2
k8s-node01     Ready    work            5d2h   v1.28.2
k8s-node02     Ready    work            5d2h   v1.28.2

[root@k8s-master01 auth]# kubectl get svc
NAME                        TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
kubernetes                  ClusterIP      10.96.0.1       <none>          443/TCP        5d2h
nginx-loadbalancer-server   LoadBalancer   10.110.35.224   192.168.40.51   80:31238/TCP   3d17h

#MetalLB Speaker 在选举完成后,会向对应 Service 发送一个 Event,表明被哪个节点宣告了。
#通过以下命令可以知道该 VIP 会被 k8s-master01 上的 Speaker 响应。
[root@k8s-master01 metalLB]# kubectl describe svc nginx-loadbalancer-server
Name:                     nginx-loadbalancer-server
Namespace:                default
Labels:                   <none>
Annotations:              metallb.universe.tf/ip-allocated-from-pool: localip-pool
Selector:                 app=nginx-test
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.110.35.224
IPs:                      10.110.35.224
LoadBalancer Ingress:     192.168.40.51
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  31238/TCP
Endpoints:                172.16.85.217:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason        Age                   From             Message
  ----    ------        ----                  ----             -------
  Normal  nodeAssigned  107s (x27 over 2d5h)  metallb-speaker  announcing from node "k8s-master01" with protocol "layer2"

#[00:0C:29:2E:B7:51] 是 k8s-master01的MAC地址
[root@k8s-node01 ~]# arping -c 1 -I ens33 192.168.40.51
ARPING 192.168.40.51 from 192.168.40.111 ens33
Unicast reply from 192.168.40.51 [00:0C:29:2E:B7:51]  0.852ms
Sent 1 probes (1 broadcast(s))

 工作过程

MetalLB 分为两部分组成,分别是 Controller 和 Speaker。两者的分工如下所示:
    • Controller 负责监听 Service 变化,依据对应的 IP 池进行 IP 分配。
    • Speaker 负责监听 Service 的变化,并依据具体的协议发起相应的广播或应答、节点的 Leader 选举。

所以说 MetalLB 的 Controller 和 Speaker 并没有直接通讯,而是依赖于集群中的 LoadBalancer Service 间接协作
下面针对集群中新增一个 LoadBalancer Service 阐述 MetalLB 的工作过程:

 

• Controller Watch 到集群中新增了一个 LoadBalancer Service,从 IpPoolAddress 中获取一个 没有被使用的 IP,并至该 Service 的 status.loadBalancer
• Kube-proxy Watch 到 Service 的更新操作,在集群每个节点上创建一条 IPVS 规则,该规则就是将上述 MetalLB 分配的 IP 负载到该 Service 的 Endpoint
• Speaker 是 DaemonSet 类型的工作负载,所以每个 K8S 节点都有一个实例。上述 Controller 和 Kube-proxy 工作的同时,Speaker 也同时 Watch Service 的新增。首先根据 memberlist[3] 选举出一个 Leader (这里就回答了第三个问题),
  会不断监听该 VIP 的 ARP 请求,然后把当前 Spaeker 所在的 Node 主机网卡 MAC 地址回复给对端。
• Speaker 会向 Service 发送一条 event 记录,表明该 Service 的 LoadBalancer 被该节点承载流量。
根据上面的描述,就是对于一个 LoadBalancer Service 最终只有一个节点承载流量,当前节点出现故障会立即进行选主,
新的 Leader Speaker 会承载 ARP 请求,显然这种高可用的方式不是很纯粹。
这里说的并不是所有的 Service 的流量都被某一个节点承载,只是当前 Service 的生命周期的流量都在一个节点上。
因为每个 Service 都会通过 memberlist[4] 选举一个 Leader。
这时候通过 192.168.40.51 去访问 nginx 服务时,整个数据流是什么样的?

 

• 向 192.168.40.51 发起请求时,数据包到数据链路层发送 ARP 广播请求,询问该广播域谁有  192.168.40.51  的 MAC 地址
• 这时候 Speaker Leader 会回复该 ARP 请求,将当前节点的网卡 MAC 地址回复给对端
• 客户端拿到了 MAC 地址后数据包继续封装发送成功到达对端,也就是 Speaker Leader 节点
• 根据上面介绍 Kube-proxy 会在 MetalLB 分配 IP 后在每个节点创建一条 IPVS 转发规则,将请求流量负载到后端每个实例。
  所以流量到达 Speaker Leader 节点后会被负载到后端多个 nginx 实例上

 

posted @ 2024-01-06 20:31  しみずよしだ  阅读(178)  评论(0)    收藏  举报