自建的 Kubernetes 集群暴露让外网访问,目前只能使用 NodePort 或 Ingress 等的方法进行服务暴露。NodePort 缺点是每个暴露的服务需要占用所有节点的某个端口。Ingress 是一个不错的解方法。
metalLB就是让自建的 Kubernetes 集群也能使用 LoadBalancer 类型的 Service

MetalLB

项目地址:https://github.com/danderson/metallb

MetalLB 概念

MetalLB 会在 Kubernetes 内运行,监控服务对象的变化,一旦监测到有新的 LoadBalancer 服务运行,并且没有可申请的负载均衡器之后,就会完成地址分配和外部声明两部分的工作。

地址分配

在云厂商提供的 Kubernetes 集群中,Service 声明使用 LoadBalancer时,云平台会自动分配一个负载均衡器的IP地址给你,应用可以通过这个地址来访问。

使用 MetalLB 时,MetalLB 会自己为用户的 LoadBalancer 类型 Service 分配 IP 地址,当然该 IP 地址不是凭空产生的,需要用户在配置中提供一个 IP 地址池,Metallb 将会在其中选取地址分配给服务。

外部声明

一旦 MetalLB 为服务分配了IP地址,它需要对外宣告此 IP 地址,并让外部主机可以路由到此 IP。
外部声明有两种模式:

Layer 2 模式

BGP 模式

1、Layer 2 模式

Layer 2 模式下,每个 Service 会有集群中的一个 Node 来负责。服务的入口流量全部经由单个节点,然后该节点的 Kube-Proxy 会把流量再转发给服务的 Pods。也就是说,该模式下 MetalLB 并没有真正提供负载均衡器。尽管如此,MetalLB 提供了故障转移功能,如果持有 IP 的节点出现故障,则默认 10 秒后即发生故障转移,IP 会被分配给其它健康的节点。

Layer 2 模式 优点 与 缺点:

优点:

是它的通用性:它可以在任何以太网网络上运行,不需要特殊的硬件。

缺点:

Layer 2 模式下存在单节点瓶颈,服务所有流量都经过一个Node节点。这意味着服务的入口带宽被限制为单个节点的带宽。
由于 Layer 2 模式需要 ARP/NDP 客户端配合,当故障转移发生时,MetalLB 会发送 ARP 包来宣告 MAC 地址和 IP 映射关系的变化,地址分配略为繁琐。

2、BGP 模式

BGP 模式下,集群中所有node都会跟上联路由器建立BGP连接,并且会告知路由器应该如何转发service的流量。

BGP 模式 优点 与 缺点:

优点:

BGP模式下才是一个真正的 LoadBalancer,通过BGP协议正确分布流量,不再需要一个Leader节点。

缺点:

不能优雅处理故障转移,当持有服务的节点宕掉后,所有活动连接的客户端将收到 Connection reset by peer。
需要上层路由器支持BGP。而且因为BGP单session的限制,如果Calico也是使用的BGP模式,就会有冲突从而导致metallb无法正常工作。

MetalLB 部署要求

MetalLB 需要以下环境才能运行:

Kubernetes 1.13.0 版本或更高版本的集群。
Kubernetes 集群网络组件需要支持 MetalLB 服务,具体参考: https://metallb.universe.tf/installation/network-addons/
MetalLB 需要能分配IPv4地址。
根据操作模式的不同,可能需要一个或多个能够使用BGP的路由器。

MetalLB 目前支持网络插件范围:

网络插件 兼容性
Calico 部分支持(有附加条件)
Canal 支持
Cilium 支持
Flannel 支持
Kube-router 部分支持(有附加条件)
Romana 部分支持(有附加条件)
Weave Net 部分支持(有附加条件)

MetalLB 部署

注意

如果环境是 Kubernetes v1.14.2+ 使用 IPVS模式,必须启用ARP模式。
编辑集群中kube-proxy配置

kubectl edit configmap -n kube-system kube-proxy

  1. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  2. kind: KubeProxyConfiguration
  3. mode: "ipvs"
  4. ipvs:
  5. strictARP: true

使用 YAML 文件部署

  1. # 安装目前最新版本 v0.9.3
  2. # 创建 namespaces
  3. echo "151.101.76.133 raw.githubusercontent.com" >> /etc/hosts
  4. kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
  5. #首次安装需要设置 memberlist secret
  6. kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
  7. #上传镜像到所有节点 外网地址下载很慢需提前准备好镜像
  8. metallb/controller:v0.9.3
  9. metallb/speaker:v0.9.3
  10. # 部署
  11. kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
  12. # 查看
  13. kubectl get svc,pod -n metallb-system
  14. NAME READY STATUS RESTARTS AGE
  15. pod/controller-57f648cb96-gcnv4 1/1 Running 0 65m
  16. pod/speaker-c7mww 1/1 Running 0 62m
  17. pod/speaker-ddfg6 1/1 Running 0 63m

部署完,YAML 文件中主要包含以下一些组件:
metallb-system/controller:负责IP地址的分配,以及service和endpoint的监听
metallb-system/speaker:负责保证service地址可达,例如Layer 2模式下,speaker会负责ARP请求应答
Controller 和 Speaker 的 Service Accounts,以及组件需要运行的 RBAC 权限。
注意,部署后,还需要根据具体的地址通告方式,配置 configmap metallb-system/config。controller 会读取该configmap,并reload配置。

配置 MetalLB

配置 MetalLB 为 Layer 2模式 (使用 yaml 文件部署)

  1. vim MetalLB-Layer2-Configmap.yaml
  2. kind: ConfigMap
  3. apiVersion: v1
  4. metadata:
  5. name: config
  6. namespace: metallb-system
  7. data:
  8. config: |
  9. address-pools:
  10. - name: default
  11. protocol: layer2
  12. addresses:
  13. - 192.168.0.100-192.168.0.200 ##使用合法的可访问的地址

上面例子,将配置一个由 MetalLB 二层模式控制的 service 外部 IP 段为 192.168.0.100 - 192.168.0.200。
注意:IP段根据自己实际情况来设置

部署 configmap

kubectl apply -f MetalLB-Layer2-Configmap.yaml

创建内部负载均衡器

若要创建内部负载均衡器,请使用服务类型 LoadBalancer 和 azure-load-balancer-internal 注释创建名为 internal-lb.yaml 的服务清单,如以下示例所示:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: internal-app
  5. annotations:
  6. service.beta.kubernetes.io/azure-load-balancer-internal: "true"
  7. spec:
  8. type: LoadBalancer
  9. ports:
  10. - port: 80
  11. selector:
  12. app: internal-app

kubectl apply -f internal-lb.yaml

内部负载均衡器的 IP 地址显示在“EXTERNAL-IP”列中。 在此上下文中,External 是指负载均衡器的外部接口,不是指收到公共的外部 IP 地址。 可能需要一两分钟,IP 地址才会从 更改为实际的内部 IP 地址,如以下示例所示:

  1. kubectl get service internal-app
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. internal-app LoadBalancer 10.0.248.59 192.168.0.100 80:30555/TCP 2m

指定 IP 地址
若要对内部负载均衡器使用特定的 IP 地址,请将 loadBalancerIP 属性添加到负载均衡器 YAML 清单**。 在此方案中,指定的 IP 地址必须位于 AKS 群集所在的子网,并且必须尚未分配给某个资源。 例如,不应使用为 Kubernetes 子网指定的范围内的 IP 地址。

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: internal-app
  5. annotations:
  6. service.beta.kubernetes.io/azure-load-balancer-internal: "true"
  7. spec:
  8. type: LoadBalancer
  9. loadBalancerIP: 10.240.0.25
  10. ports:
  11. - port: 80
  12. selector:
  13. app: internal-app

在部署后查看服务详细信息时,“EXTERNAL-IP”列中的 IP 地址反映了指定的 IP 地址**:

  1. kubectl get service internal-app
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. internal-app LoadBalancer 10.0.184.168 10.240.0.25 80:30225/TCP 4m

测试
下面我们创建一个服务类型为 LoadBalancer 的 Nginx 服务 Demo 来演示

  1. vim demo1.deploy.yml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: demo1
  6. namespace: default
  7. labels:
  8. app: demo1
  9. spec:
  10. type: LoadBalancer
  11. ports:
  12. - port: 80
  13. targetPort: http
  14. protocol: TCP
  15. name: http
  16. selector:
  17. app: demo1
  18. ---
  19. apiVersion: apps/v1
  20. kind: Deployment
  21. metadata:
  22. name: demo1-deployment
  23. namespace: default
  24. labels:
  25. app: demo1
  26. spec:
  27. replicas: 2
  28. selector:
  29. matchLabels:
  30. app: demo1
  31. template:
  32. metadata:
  33. labels:
  34. app: demo1
  35. spec:
  36. containers:
  37. - name: demo1
  38. image: mritd/demo
  39. ports:
  40. - name: http
  41. containerPort: 80
  42. protocol: TCP

从输出结果,可以看到 LoadBalancer 类型的服务,并且分配外部 IP 地址是地址池中的第一个 IP 192.168.0.100。
直接访问下 LoadBalancer IP 192.168.0.100,下面访问成功。

  1. # 部署
  2. kubectl apply -f demo1.deploy.yml
  3. # 查看
  4. kubectl get svc,pod -n default
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  6. service/demo1 LoadBalancer 10.10.241.163 192.168.0.100 80:39916/TCP 34s
  7. NAME READY STATUS RESTARTS AGE
  8. pod/demo1-deployment-64f769965b-m28cn 1/1 Running 0 34s
  9. pod/demo1-deployment-64f769965b-wp2gg 1/1 Running 0 34s

配置 MetalLB 为 BGP 模式
配置 BGP 模式,需要先准备好下面4条信息:
MetalLB 应该连接的路由器IP地址
路由器的 AS 号
MetalLB 应该使用的 AS 号
以 CIDR 前缀表示的IP地址范围

下面简单介绍下 BGP 配置:

前面已经安装了 MetalLB 的 Controller 和 Speaker,所使用的是 Layer 2 模式。这里只需要把 Configmap 中 Config 改为 BGP 模式 配置就行。
假设 MetalLB 提供范围 192.168.10.0/24 和 AS 号 65009,并将其连接到 192.168.0.10 的 AS 号为 65000 的路由器,具体配置如下:

  1. vim MetalLB-BGP-Configmap.yaml
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. namespace: metallb-system
  6. name: config
  7. data:
  8. config: |
  9. peers:
  10. - peer-address: 192.168.0.10
  11. peer-asn: 65000
  12. my-asn: 65009
  13. address-pools:
  14. - name: default
  15. protocol: bgp
  16. addresses:
  17. - 192.168.10.0/24

如果集群环境支持 BGP,推荐使用 BGP 模式。