Kubernetes 负载均衡器 MetalLB
基本情况介绍
一、service
Kubernetes中一个应用服务会有一个或多个实例,每个实例(Pod)的IP地址由网络插件动态随机分配(Pod重启后IP地址会改变)。为屏蔽这些后端实例的动态变化和对多实例的负载均衡,引入了 Service这个资源对象。
type 类型
根据创建 Service 的 type 类型不同,主要分为几下几种:
ClusterIP:通过为 Kubernetes 的 Service 分配一个集群内部可访问的固定虚拟IP(Cluster IP),实现集群内的访问;
NodePort:将 service 的 port 映射到集群内每个节点的相同一个端口,实现通过 nodeIP:nodePort 从集群外访问服务;
LoadBalance:向所使用的公有云申请一个负载均衡器(负载均衡器后端映射到各节点的 nodePort),实现从集群外通过 LB 访问服务;
Port
Service 中主要涉及三种 Port:
port 表示 service 暴露在 clusterIP 上的端口,clusterIP:Port 是提供给集群内部访问 kubernetes 服务的入口;
NodePort:提供给从集群外部访问 kubernetes 服务的入口;
TargetPort:容器port,targetPort 是 pod 上的端口,从 port 和 nodePort 上到来的数据最终经过 kube-proxy 流入到后端 pod 的 targetPort 上进入容器。
port 和 nodePort 都是 service 的端口,前者暴露给从集群内访问服务,后者暴露给从集群外访问服务。从这两个端口到来的数据都需要经过反向代理 kube-proxy 流入后端具体 pod 的 targetPort,从而进入到 pod 上的容器内。
IP
使用 Service 服务会涉及到几种 IP:
Cluster IP:虚拟地址,由 kube-proxy 使用 iptables 规则重新定向到其本地端口,再均衡到后端Pod。当 kube-proxy 发现一个新的 service 后,它会在本地节点打开一个任意端口,创建相应的iptables 规则,重定向服务的 clusterIP 和 port 到这个新建的端口,开始接受到达这个服务的连接。
Pod IP:每个 Pod 启动时,会自动创建一个镜像为 gcr.io/google_containers/pause 的容器,Pod内部其他容器的网络模式使用container模式,指定为 pause 容器的ID(network_mode: “container:pause 容器ID”),使得 Pod 内所有容器共享 pause 容器的网络,与外部的通信经由此容器代理,pause容器的 IP 也可以称为Pod IP。
Node IP: 将服务作为一个应用程序内部的层次,使的服务可以从集群外部访问,指定 service 的spec.type=NodePort,通过 nodeip:nodeport 从集群外访问服务。
工作方式
定义服务的时候通过 selector 指定服务对应的 pods,根据 pods 的地址创建出 endpoints 作为服务后端;Endpoints Controller 会 watch Service 以及 pod 的变化,维护对应的 Endpoint 信息。kube-proxy根据 Service 和 Endpoint 来维护本地的路由规则。当 Endpoint 发生变化,即 Service 以及关联的pod发生变化,kube-proxy 都会在每个节点上更新 iptables,实现一层负载均衡。
MetalLB 基本介绍
官方文档地址:https://metallb.universe.tf/
该项目发布于 2017 年底,当前处于 Beta 阶段。
MetalLB完全支持的网络插件Canal、Cilium、Flannel、Kube-ovn等。如果kube-proxy运行在IPVS模式先,需要设置strictARP: true
(后面会详细阐述)
Kubernetes不提供网络负载均衡器的实现(LoadBalancer类型的服务)用于裸机集群。Kubernetes附带的Network LB的实现都是调用各种IaaS平台(GCP,AWS,Azure等)的粘合代码。如果您未在受支持的IaaS平台(GCP,AWS,Azure等)上运行,则LoadBalancers在创建时将无限期保持“待处理”状态。
裸机集群运营商只剩下两个较小的工具,即“ NodePort”和“ externalIPs”服务,可将用户流量引入其集群。这两个选项在生产用途上都有很大的缺点,这使裸金属集群成为Kubernetes生态系统中的二等公民。
MetalLB旨在通过提供与标准网络设备集成的Network LB实现来解决这种不平衡问题,从而使裸机群集上的外部服务也尽可能“正常运行”。
简单来说:在裸机集群上(不是公有云厂商的主机,比如阿里云,腾讯云)部署k8s后,外部网络访问集群里的服务,通过ingress-nginx的形式,但是这个ingress-nginx的服务service端口类型对外暴露只能使用“ NodePort”和“ externalIPs”服务,不能使用LoadBalancer的,使用MetalLB则可以解决这个问题,使ingress-nginx的service端口类型对外暴露使用LoadBalancer的形式(不用使用云厂商提供的负载均衡器)
MetalLB是 Kubernetes 集群中关于LoadBalancer的一个具体实现,主要用于暴露k8s集群的服务到集群外部访问。由两个共同提供此服务的工作负载(workload):地址分配和外部公告;对应的就是在 k8s中部署的 controller 和 speaker。
Metallb 会在 Kubernetes 内运行,监控服务对象的变化,一旦察觉有新的 LoadBalancer 服务运行,并且没有可申请的负载均衡器之后,就会完成两部分的工作:
1.地址分配,用户需要在配置中提供一个地址池,Metallb 将会在其中选取地址分配给服务。
2.地址广播,根据不同配置,Metallb 会以二层(ARP/NDP)或者 BGP 的方式进行地址的广播。
地址分配(address allocation)
需要给 MetalLB 分配一段 IP,接着它会根据 service 中的相关配置来给LoadBalancer的服务分配IP,LoadBalancer的IP可以手动指定,也可以让MetalLB自动分配;同时还可以在 MetalLB 的configmap中配置多个 IP 段,并且单独设置每个 IP 段是否开启自动分配。
地址分配(address allocation)主要就是由作为 deployment 部署的 controller 来实现,它负责监听集群中的 service 状态并且分配 IP。
外部公告(external announcement)
外部公告的主要功能就是要把服务类型为LoadBalancer的服务的IP公布到网络中去,确保客户端能够正常访问到这个 IP 。MetalLB 对此的实现方式主要有三种:ARP/NDP和BGP;其中 ARP/NDP 分别对应IPv4/IPv6 协议的 Layer2 模式,BGP路由协议则是对应 BGP 模式。
外部公告主要就是由作为daemonset部署的speaker来实现,它负责在网络中发布 ARP/NDP 报文或者是和 BGP 路由器建立连接并发布 BGP 报文。
工作原理
Metallb 包含两个组件,Controller 和 Speaker,Controller 为 Deployment 部署方式,而 Speaker 则采用 Daemonset 方式部署到集群内部各个Node节点。
具体的工作原理如下图所示,Controller 负责监听 Service 变化,当 Service 配置为 LoadBalancer 模式时,从 IP 池分配给到相应的 IP 地址并对该 IP 的生命周期进行管理。Speaker 则会依据选择的协议进行相应的广播或应答,实现 IP 地址的通信响应。当业务流量通过 TCP/UDP 协议到达指定的 Node 时,由Node 上面运行的 Kube-Proxy 组件对流量进行处理,并分发到对应服务的 Pod 上面。
安装
安装之前的准备检查工作
如果您在IPVS模式下使用kube-proxy,则从Kubernetes v1.14.2开始,您必须启用严格的ARP模式。请注意,如果您将kube-router用作服务代理,则不需要此设置,因为默认情况下它启用了严格的arp。
您可以通过在当前集群中编辑kube-proxy配置来实现:
# kubectl edit configmap -n kube-system kube-proxy
并修改如下图内容
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs" # 这个在安装时默认选择了ipvs
ipvs:
strictARP: true # 修改这个
更新kube-proxy pod
# kubectl get pod -n kube-system |grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'
另外一种修改生效方式:
# see what changes would be made, returns nonzero returncode if different
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl diff -f - -n kube-system
# actually apply the changes, returns nonzero returncode on errors only
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl apply -f - -n kube-system
安装
# mkdir metallb && cd cd metallb/
# wget https://github.com/metallb/metallb/blob/main/config/manifests/metallb-native.yaml
# vim metallb-native.yaml #查看里面需要的镜像
提前将镜像下载好,上传至私有仓库 (quay.io是Red Hat运营的镜像库,虽然没有被墙,但下载还是比较慢。可以从这个里面找到使用镜像的最新版本,然后再使用自己的github代理拉取,推送到自己的dockerhub仓库里)
(下载quay.io的镜像,可以参考这个文章:https://www.cnblogs.com/hahaha111122222/p/17097890.html)
使用私库里的镜像,需要修改metallb-native.yaml文件中使用的镜像名
Metallb 安装,会生成自己的命名空间以及 RBAC 配置。
# kubectl apply -f metallb-native.yaml
# kubectl -n metallb-system get all
创建密钥,否则会出现报错 ===》 这一步没看懂要干啥,可以暂不操作的
# kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
# kubectl -n metallb-system get secrets
配置
接下来我们要生成一个 Configmap 文件,为 Metallb 设置网址范围以及协议相关的选择和配置,这里以一个简单的二层配置为例:
# vim config.yml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 172.25.1.100-172.25.1.200
# kubectl apply -f config.yml
注意:这里的 IP 地址范围需要跟集群实际情况相对应。(跟集群主机节点的私网IP对应,可以理解成同网段尚未使用的IP所在网段范围)
注意: 新版本使用的配置文件内容有变化,网址:https://metallb.universe.tf/configuration/_advanced_l2_configuration/
测试
我们创建一个svc进行测试
# vim nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
#externalIPs:
#- 172.25.2.100
#clusterIP: None
#type: NodePort
type: LoadBalancer #指定一个 LoadBalancer 类型的 Service
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: myapp:v1
可以看到我们部署的nginx-svc已经分配到了一个地址池中的ip
集群内部访问
集群外部访问
metallb跟ingress结合起来使用
对于ingress的yaml文件,可以复制过来进行修改,需要修改网络模式
去除之前做的节点绑定,不监听主机的端口
从DeamonSet 类型修改成 Deployment类型
生效并查看
# kubectl apply -f deploy.yaml
# kubectl -n ingress-nginx get all
此时访问路径为: user -> vip(metallb) -> ingress-nginx -> svc -> pod
示例
# cat nginx-svc.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: myapp:v1
# cat ingress-demo.yml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
# tls:
# - hosts:
# - www1.westos.org
# secretName: tls-secret
rules:
- host: www1.westos.org
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
# kubectl apply -f nginx-svc.yml
# kubectl apply -f ingress-demo.yml
# kubectl get ingress
# kubectl describe ingress nginx-test
# kubectl get svc nginx-svc
这里需要给这个ip做好地址解析,然后在外部访问域名查看效果。
calico网络插件
calico简介:
flannel实现的是网络通信,calico的特性是在pod之间的隔离。
通过BGP路由,但大规模端点的拓扑计算和收敛往往需要一定的时间和计算资源。纯三层的转发,中间没有任何的NAT和overlay,转发效率最好。
Calico 仅依赖三层路由可达。Calico 较少的依赖性使它能适配所有 VM、Container、白盒或者混合环境场景。
calico网络架构
Felix:监听ECTD中心的存储获取事件,用户创建pod后,Felix负责将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。同样如果用户制定了隔离策略,Felix同样会将该策略创建到ACL中,以实现隔离。
BIRD:一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,路由的时候到这里来。
IPIP工作模式:适用于互相访问的pod不在同一个网段中,跨网段访问的场景。
BGP工作模式:适用于互相访问的pod在同一个网段,适用于大型网络。