k8s系列06-负载均衡器之MatelLB

本文详细介绍了如何在k8s集群上部署MetalLB,涵盖Layer2模式和BGP模式的配置与实践。MetalLB作为一个开源负载均衡器,提供地址分配和外部公告功能,允许在非云环境中创建服务类型为LoadBalancer的服务。文章探讨了两种模式的工作原理、系统要求、网络配置以及优缺点,帮助读者理解如何在不同场景下选择和使用MetalLB。
摘要由CSDN通过智能技术生成

本文主要在k8s原生集群上部署v0.12.1版本的MetalLB作为k8s的LoadBalancer,主要涉及MetalLBLayer2模式BGP模式两种部署方案。由于BGP的相关原理和配置比较复杂,这里仅涉及简单的BGP配置。

文中使用的k8s集群是在CentOS7系统上基于dockerflannel组件部署v1.23.6版本,此前写的一些关于k8s基础知识和集群搭建的一些方案,有需要的同学可以看一下。

1、工作原理

1.1 简介

在开始之前,我们需要了解一下MetalLB的工作原理。

MetalLB hooks into your Kubernetes cluster, and provides a network load-balancer implementation. In short, it allows you to create Kubernetes services of type LoadBalancer in clusters that don’t run on a cloud provider, and thus cannot simply hook into paid products to provide load balancers.

It has two features that work together to provide this service: address allocation, and external announcement.

MetalLB是 Kubernetes 集群中关于LoadBalancer的一个具体实现,主要用于暴露k8s集群的服务到集群外部访问。MetalLB可以让我们在k8s集群中创建服务类型为LoadBalancer的服务,并且无需依赖云厂商提供的LoadBalancer

它具有两个共同提供此服务的工作负载(workload):地址分配(address allocation)和外部公告(external announcement);对应的就是在k8s中部署的controllerspeaker

1.2 address allocation

地址分配(address allocation)这个功能比较好理解,首先我们需要给MetalLB分配一段IP,接着它会根据k8s的service中的相关配置来给LoadBalancer的服务分配IP,从官网文档中我们可以得知LoadBalancer的IP可以手动指定,也可以让MetalLB自动分配;同时还可以在MetalLB的configmap中配置多个IP段,并且单独设置每个IP段是否开启自动分配。

地址分配(address allocation)主要就是由作为deployment部署的controller来实现,它负责监听集群中的service状态并且分配IP

1.3 external announcement

外部公告(external announcement)的主要功能就是要把服务类型为LoadBalancer的服务的EXTERNAL-IP公布到网络中去,确保客户端能够正常访问到这个IP。MetalLB对此的实现方式主要有三种:ARP/NDP和BGP;其中ARP/NDP分别对应IPv4/IPv6协议的Layer2模式,BGP路由协议则是对应BGP模式。外部公告(external announcement)主要就是由作为daemonset部署的speaker来实现,它负责在网络中发布ARP/NDP报文或者是和BGP路由器建立连接并发布BGP报文。

1.4 关于网络

不管是Layer2模式还是BGP模式,两者都不使用Linux的网络栈,也就是说我们没办法使用诸如ip命令之类的操作准确的查看VIP所在的节点和相应的路由,相对应的是在每个节点上面都能看到一个kube-ipvs0网卡接口上面的IP。同时,两种模式都只是负责把VIP的请求引到对应的节点上面,之后的请求怎么到达pod,按什么规则轮询等都是由kube-proxy实现的。

两种不同的模式各有优缺点和局限性,我们先把两者都部署起来再进行分析。

2、准备工作

2.1 系统要求

在开始部署MetalLB之前,我们需要确定部署环境能够满足最低要求:

  • 一个k8s集群,要求版本不低于1.13.0,且没有负载均衡器相关插件
  • k8s集群上的CNI组件和MetalLB兼容
  • 预留一段IPv4地址给MetalLB作为LoadBalance的VIP使用
  • 如果使用的是MetalLB的BGP模式,还需要路由器支持BGP协议
  • 如果使用的是MetalLB的Layer2模式,因为使用了memberlist算法来实现选主,因此需要确保各个k8s节点之间的7946端口可达(包括TCP和UDP协议),当然也可以根据自己的需求配置为其他端口

2.2 cni插件的兼容性

MetalLB官方给出了对主流的一些CNI的兼容情况,考虑到MetalLB主要还是利用了k8s自带的kube-proxy组件做流量转发,因此对大多数的CNI兼容情况都相当不错。

CNI 兼容性 主要问题
Calico Mostly (see known issues) 主要在于BGP模式的兼容性,但是社区也提供了解决方案
Canal Yes -
Cilium Yes -
Flannel Yes -
Kube-ovn Yes -
Kube-router Mostly (see known issues) 无法支持 builtin external BGP peering mode
Weave Net Mostly (see known issues) externalTrafficPolicy: Local支持情况视版本而定

从兼容性上面我们不难看出,大多数情况是没问题的,出现兼容性问题的主要原因就是和BGP有冲突。实际上BGP相关的兼容性问题几乎存在于每个开源的k8s负载均衡器上面。

2.3 云厂商的兼容性

MetalLB官方给出的列表中,我们可以看到对大多数云厂商的兼容性都很差,原因也很简单,大多数的云环境上面都没办法运行BGP协议,而通用性更高的layer2模式则因为各个云厂商的网络环境不同而没办法确定是否能够兼容

The short version is: cloud providers expose proprietary APIs instead of standard protocols to control their network layer, and MetalLB doesn’t work with those APIs.

当然如果使用了云厂商的服务,最好的方案是直接使用云厂商提供的LoadBalance服务。

3、Layer2 mode

3.1 部署环境

本次MetalLB的部署环境为基于dockerflannel部署的1.23.6版本的k8s集群

IP Hostname
10.31.8.1 tiny-flannel-master-8-1.k8s.tcinternal
10.31.8.11 tiny-flannel-worker-8-11.k8s.tcinternal
10.31.8.12 tiny-flannel-worker-8-12.k8s.tcinternal
10.8.64.0/18 podSubnet
10.8.0.0/18 serviceSubnet
10.31.8.100-10.31.8.200 MetalLB IPpool

3.2 配置ARP参数

部署Layer2模式需要把k8s集群中的ipvs配置打开strictARP,开启之后k8s集群中的kube-proxy会停止响应kube-ipvs0网卡之外的其他网卡的arp请求,而由MetalLB接手处理。

strict ARP开启之后相当于把 将 arp_ignore 设置为 1 并将 arp_announce 设置为 2 启用严格的 ARP,这个原理和LVS中的DR模式对RS的配置一样,可以参考之前的文章中的解释

strict ARP configure arp_ignore and arp_announce to avoid answering ARP queries from kube-ipvs0 interface

# 查看kube-proxy中的strictARP配置
$ kubectl get configmap -n kube-system kube-proxy -o yaml | grep strictARP
      strictARP: false

# 手动修改strictARP配置为true
$ kubectl edit configmap -n kube-system kube-proxy
configmap/kube-proxy edited

# 使用命令直接修改并对比不同
$ kubectl get configmap kube-proxy -n kube-system -o yaml | sed -e "s/strictARP: false/strictARP: true/" | kubectl diff -f - -n kube-system

# 确认无误后使用命令直接修改并生效
$ kubectl get configmap kube-proxy -n kube-system -o yaml | sed -e "s/strictARP: false/strictARP: true/" | kubectl apply -f - -n kube-system

# 重启kube-proxy确保配置生效
$ kubectl rollout restart ds kube-proxy -n kube-system

# 确认配置生效
$ kubectl get configmap -n kube-system kube-proxy -o yaml | grep strictARP
      strictARP: true

3.3 部署MetalLB

MetalLB的部署也十分简单,官方提供了manifest文件部署(yaml部署),helm3部署Kustomize部署三种方式,这里我们还是使用manifest文件部署。

大多数的官方教程为了简化部署的步骤,都是写着直接用kubectl命令部署一个yaml的url,这样子的好处是部署简单快捷,但是坏处就是本地自己没有存档,不方便修改等操作,因此我个人更倾向于把yaml文件下载到本地保存再进行部署。

# 下载v0.12.1的两个部署文件
$ wget https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml
$ wget https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml

# 如果使用frr来进行BGP路由管理,则下载这两个部署文件
$ wget https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml
$ wget https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb-frr.yaml

下载官方提供的yaml文件之后,我们再提前准备好configmap配置,github上面有提供一个参考文件,layer2模式需要的配置并不多,这里我们只做最基础的一些参数配置定义即可:

  • protocol这一项我们配置为layer2
  • addresses这里我们可以使用CIDR来批量配置(198.51.100.0/24),也可以指定首尾IP来配置(192.168.0.150-192.168.0.200),这里我们指定一段和k8s节点在同一个子网的IP
$ cat > configmap-metallb.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 10.31.8.100-10.31.8.200
EOF

接下来就可以开始进行部署,整体可以分为三步:

  1. 部署namespace
  2. 部署deploymentdaemonset
  3. 配置configmap
# 创建namespace
$ kubectl apply -f namespace.yaml
namespace/metallb-system created
$ kubectl get ns
NAME              STATUS   AGE
default           Active   8d
kube-node-lease   Active   8d
kube-public       Active   8d
kube-system       Active   8d
metallb-system    Active   8s
nginx-quic        Active   8d

# 部署deployment和daemonset,以及相关所需的其他资源
$ kubectl apply -f metallb.yaml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/controller created
podsecuritypolicy.policy/speaker created
serviceaccount/controller created
serviceaccount/speaker created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
role.rbac.authorization.k8s.io/config-watcher created
role.rbac.authorization.k8s.io/pod-lister created
role.rbac.authorization.k8s.io/controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/config-watcher created
rolebinding.rbac.authorization.k8s.io/pod-lister created
rolebinding.rbac.authorization.k8s.io/controller created
daemonset.apps/speaker created
deployment.apps/controller created

# 这里主要就是部署了controller这个deployment来检查service的状态
$ kubectl get deploy -n metallb-system
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
controller   1/1     1            1           86s
# speaker则是使用ds部署到每个节点上面用来协商VIP、收发ARP、NDP等数据包
$ kubectl get ds -n metallb-system
NAME      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
speaker   3         3         3       3            3           kubernetes.io/os=linux   64s
$ kubectl get pod -n metallb-system -o wide
NAME                         READY   STATUS    RESTARTS   AGE    IP           NODE                                      NOMINATED NODE   READINESS GATES
controller-57fd9c5bb-svtjw   1/1     Running   0          117s   10.8.65.4    tiny-flannel-worker-8-11.k8s.tcinternal   <none>           <none>
speaker-bf79q                1/1     Running   0          117s   10.31.8.11   tiny-flannel-worker-8-11.k8s.tcinternal   <none>           <none>
speaker-fl5l8                1/1     Running   0          117s   10.31.8.12   tiny-flannel-worker-8-12.k8s.tcinternal   <none>           <none>
speaker-nw2fm                1/1     Running   0          117s   10.31.8.1    tiny-flannel-master-8-1.k8s.tcinternal    <none>           <none>

      
$ kubectl apply -f configmap-layer2.yaml
configmap/config created

3.4 部署测试服务

我们还是自定义一个服务来进行测试,测试镜像使用nginx,默认情况下会返回请求客户端的IP和端口

$ cat > nginx-quic-lb.yaml <<EOF
apiVersion: v1
kind: Namespace
metadata:
  name: nginx-quic

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-lb
  namespace: nginx-quic
spec:
  selector:
    matchLabels:
      app: nginx-lb
  replicas: 4
  template:
    metadata:
      labels:
        app: nginx-lb
    spec:
      containers:
      - name: nginx-lb
        image: tinychen777/nginx-quic:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-lb-service
  namespace: nginx-quic
spec:
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  selector:
    app: nginx-lb
  ports:
  - protocol: TCP
    port: 80 # match for service access port
    targetPort: 80 # match for pod access port
  type: LoadBalancer
  loadBalancerIP: 10.31.8.100
EOF

注意上面的配置中我们把service配置中的type字段指定为LoadBalancer,并且指定了loadBalancerIP10.31.8.100

注意:并非所有的LoadBalancer都允许设置 loadBalancerIP

如果LoadBalancer支持该字段,那么将根据用户设置的 loadBalancerIP 来创建负载均衡器。

如果没有设置 loadBalancerIP 字段,将会给负载均衡器指派一个临时 IP。

如果设置了 loadBalancerIP,但LoadBalancer并不支持这种特性,那么设置的 loadBalancerIP 值将会被忽略掉。

# 创建一个测试服务检查效果
$ kubectl apply -f nginx-quic-lb.yaml
namespace/nginx-quic created
deployment.apps/nginx-lb created
service/nginx-lb-service created

查看服务状态,这时候TYPE已经变成LoadBalancerEXTERNAL-IP显示为我们定义的10.31.8.100

# 查看服务状态,这时候TYPE已经变成LoadBalancer
$ kubectl get svc -n nginx-quic
NAME               TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
nginx-lb-service   LoadBalancer   10.8.32.221   10.31.8.100   80:30181/TCP   25h

此时我们再去查看k8s机器中的nginx-lb-service状态,可以看到ClusetIPLoadBalancer-VIPnodeport的相关信息以及流量策略TrafficPolicy等配置

$ kubectl get svc -n nginx-quic nginx-lb-service -o yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {
   "apiVersion":"v1","kind":"Service","metadata":{
   "annotations":{
   },"name":"nginx-lb-service","namespace":"nginx-quic"},"spec":{
   "externalTrafficPolicy":"Cluster","internalTrafficPolicy":"Cluster","loadBalancerIP":"10.31.8.100","ports":[{
   "port":80,"protocol":"TCP","targetPort":80}],"selector":{
   "app":"nginx-lb"},"type":"LoadBalancer"}
Kubernetes (k8s) 是一个开源的容编排平台,用于自动化容化应用程序的部署、扩展和管理。在 k8s 中,负载均衡是一个核心功能,用于处理多个节点间的流量分发,确保服务的高可用性和性能。以下是 Kubernetes 中实现负载均衡的基本方法: 1. **内置服务(Service)**: 使用 k8s 的 `Service` 对象,它可以将流量路由到后端的 pod。服务支持多种类型的负载均衡,如: - **ClusterIP**: 内部 IP,仅对集群内部可见,适合不需要外部访问的应用场景。 - **NodePort**: 配置端口映射,允许从外部直接访问服务。 - **LoadBalancer** 或 **ExternalName**: 如果集群支持云提供商的负载均衡,可以创建外部负载均衡来处理流量。 2. **Ingress**: 一个高级别资源,用于定义网络规则并可以关联到多个服务。Ingress 提供了更灵活的配置选项,如路径、SSL/TLS终止等,并可以使用第三方控制(如 NGINX Ingress Controller)来实现更复杂的负载均衡策略。 3. **Envoy**: 在 Istio 或其他类似的服务网格解决方案中,Envoy 作为一个高性能的边缘代理被用作负载均衡,它能提供细粒度的路由控制和可扩展性。 4. **自定义负载均衡**: 用户可以通过部署自己的部署或使用 Helm charts 安装第三方负载均衡,如 HAProxy、Traefik 等。 相关问题--: 1. Kubernetes 的哪些服务类型支持负载均衡? 2. 如何通过 Ingress 实现更复杂的路由策略? 3. Envoy 在 Kubernetes 中的角色是什么? 4. Kubernetes 负载均衡如何考虑高可用性?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值