TL;DR
网络方面的常识又多又杂,很多又是零碎内核的局部。本来本人不是做网络方面的,零碎内核常识也单薄。但恰好是这些生疏的内容满满的引诱,加上当初的工作跟网络关联更多了,逮住机会就学习下。
这篇以 Kubernetes LoadBalancer 为终点,应用 MetalLB 去实现集群的负载均衡器,在探索其工作原理的同时理解一些网络的常识。
因为 MetalLB 的内容有点多,一步步来,明天这篇仅介绍其中简略又容易了解的局部,不出意外还会有下篇(太简单,等我搞明确先 :D)。
LoadBalancer 类型 Service
因为 Kubernets 中 Pod 的 IP 地址不固定,重启后 IP 会发生变化,无奈作为通信的地址。Kubernets 提供了 Service 来解决这个问题,对外裸露。
Kubernetes 为一组 Pod 提供雷同的 DNS 名和虚构 IP,同时还提供了负载平衡的能力。这里 Pod 的分组通过给 Pod 打标签(Label)来实现,定义 Service 时会申明标签选择器(selector)将 Service 与 这组 Pod 关联起来。
依据应用场景的不同,Service 又分为 4 种类型:ClusterIP、NodePort、LoadBalancer 和 ExternalName,默认是 ClusterIP。这里不一一具体介绍,有趣味的查看 Service 官网文档。
除了明天的配角 LoadBalancer 外,其余 3 种都是比拟罕用的类型。LoadBalancer 官网的解释是:
应用云提供商的负载均衡器向内部裸露服务。内部负载均衡器能够将流量路由到主动创立的 NodePort 服务和 ClusterIP 服务上。
看到“云提供商提供”几个字时往往望而生畏,有时又须要 LoadBalancer 对外裸露服务做些验证工作(尽管除了 7 层的 Ingress 以外,还能够应用 NodePort 类型的 Service),而 Kubernetes 官网并没有提供实现。比方上面要介绍的 MetalLB 就是个不错的抉择。
MetalLB 介绍
MetalLB 是裸机 Kubernetes 集群的负载均衡器实现,应用规范路由协定。
留神: MetalLB 目前还是 beta 阶段。
前文提到 Kubernetes 官网并没有提供 LoadBalancer 的实现。各家云厂商有提供实现,但如果不是运行在这些云环境上,创立的 LoadBalancer Service 会始终处于 Pending 状态(见下文 Demo 局部)。
MetalLB 提供了两个性能:
- 地址调配:当创立 LoadBalancer Service 时,MetalLB 会为其调配 IP 地址。这个 IP 地址是从 <u>事后配置的 IP 地址库</u> 获取的。同样,当 Service 删除后,已调配的 IP 地址会从新回到地址库。
- 对外播送:调配了 IP 地址之后,须要让集群外的网络晓得这个地址的存在。MetalLB 应用了规范路由协定实现:ARP、NDP 或者 BGP。
播送的形式有两种,第一种是 Layer 2 模式,应用 ARP(ipv4)/NDP(ipv6)协定;第二种是 BPG。
<u> 明天次要介绍简略的 Layer 2 模式 </u>,顾名思义是 OSI 二层的实现。
具体实现原理,看完 Demo 再做剖析,等不及的同学请间接跳到最初。
运行时
MetalLB 运行时有两种工作负载:
- Controler:Deployment,用于监听 Service 的变更,调配 / 回收 IP 地址。
- Speaker:DaemonSet,对外播送 Service 的 IP 地址。
Demo
装置之前介绍下网络环境,Kubernetes 应用 K8s 装置在 Proxmox 的虚拟机上。
装置 K3s
装置 K3s,这里须要通过 --disable servicelb
禁用 k3s 默认的 servicelb。
参考 K3s 文档,默认状况下 K3s 应用 Traefik ingress 控制器 和 Klipper Service 负载均衡器来对外裸露服务。
curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config
创立工作负载
应用 nginx 镜像,创立两个工作负载:
kubectl create deploy nginx --image nginx:latest --port 80 -n default
kubectl create deploy nginx2 --image nginx:latest --port 80 -n default
同时为两个 Deployment 创立 Service,这里类型抉择 LoadBalancer:
kubectl expose deployment nginx --name nginx-lb --port 8080 --target-port 80 --type LoadBalancer -n default
kubectl expose deployment nginx2 --name nginx2-lb --port 8080 --target-port 80 --type LoadBalancer -n default
查看 Service 发现状态都是 Pending 的,这是因为装置 K3s 的时候咱们禁用了 LoadBalancer 的实现:
kubectl get svc -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 14m
nginx-lb LoadBalancer 10.43.108.233 <pending> 8080:31655/TCP 35s
nginx2-lb LoadBalancer 10.43.26.30 <pending> 8080:31274/TCP 16s
这时就须要 MetalLB 退场了。
装置 MetalLB
应用官网提供 manifest 来装置,目前最新的版本是 0.12.1
。此外,还能够其余装置形式供选择,比方 Helm、Kustomize 或者 MetalLB Operator。
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml
kubectl get po -n metallb-system
NAME READY STATUS RESTARTS AGE
speaker-98t5t 1/1 Running 0 22s
controller-66445f859d-gt9tn 1/1 Running 0 22s
此时再查看 LoadBalancer Service 的状态依然是 Pending 的,嗯?因为,MetalLB 要为 Service 调配 IP 地址,但 IP 地址不是凭空来的,而是须要事后提供一个地址库。
这里咱们应用 Layer 2 模式,通过 Configmap 为其提供一个 IP 段:
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.1.30-192.168.1.49
此时再查看 Service 的状态,能够看到 MetalLB 为两个 Service 调配了 IP 地址 192.168.1.30
、192.168.1.31
:
kubectl get svc -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 28m
nginx-lb LoadBalancer 10.43.201.249 192.168.1.30 8080:30089/TCP 14m
nginx2-lb LoadBalancer 10.43.152.236 192.168.1.31 8080:31878/TCP 14m
能够申请测试下:
curl -I 192.168.1.30:8080
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Wed, 02 Mar 2022 15:31:15 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 25 Jan 2022 15:03:52 GMT
Connection: keep-alive
ETag: "61f01158-267"
Accept-Ranges: bytes
curl -I 192.168.1.31:8080
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Wed, 02 Mar 2022 15:31:18 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 25 Jan 2022 15:03:52 GMT
Connection: keep-alive
ETag: "61f01158-267"
Accept-Ranges: bytes
macOS 本地应用 arp -a
查看 ARP 表能够找到这两个 IP 及 mac 地址,能够看出两个 IP 都绑定在同一个网卡上,此外还有虚拟机的 IP 地址。也就是说 3 个 IP 绑定在该虚拟机的 en0 上:
而去虚拟机(节点)查看网卡(这里只能看到零碎绑定的 IP):
Layer 2 工作原理
Layer 2 中的 Speaker 工作负载是 DeamonSet 类型,在每台节点上都调度一个 Pod。首先,几个 Pod 会先进行选举,选举出 Leader。Leader 获取所有 LoadBalancer 类型的 Service,将已调配的 IP 地址绑定到以后主机到网卡上。也就是说,所有 LoadBalancer 类型的 Service 的 IP 同一时间都是绑定在同一台节点的网卡上。
当内部主机有申请要发往集群内的某个 Service,须要先确定指标主机网卡的 mac 地址(至于为什么,参考维基百科)。这是通过发送 ARP 申请,Leader 节点的会以其 mac 地址作为响应。内部主机会在本地 ARP 表中缓存下来,下次会间接从 ARP 表中获取。
申请达到节点后,节点再通过 kube-proxy 将申请负载平衡指标 Pod。所以说,如果 Service 是多 Pod 这里有可能会再跳去另一台主机。
优缺点
长处很显著,实现起来简略(绝对于另一种 BGP 模式下路由器要反对 BPG)。就像笔者的环境一样,只有保障 IP 地址库与集群是同一个网段即可。
当然毛病更加显著了,Leader 节点的带宽会成为瓶颈;与此同时,可用性欠佳,故障转移须要 10 秒钟的工夫(每个 speaker 过程有个 10s 的循环)。
参考
- 地址解析协定
-
MetalLB 概念
文章对立公布在公众号
云原生指北