MetalLB + OpenShift

#背景
在K8S/OpenShift中,如果要向集群外部暴露应用的服务,目前使用的方法有:NodePort、HostPort、Route、LoadBalancer、HostNetwork。

  • Route:为最常用的方法,但是一般只支持7层负载(默认),使用一个外部LB来负载多个Route实例,对外的访问的IP为该LB的IP。
  • NodePort:在私有集群中也常用来暴露服务,但是它必须使用30000以上的端口,数量有限,不便于管理,而且对于一些特殊的服务,如DNS,必须使用小端口号,那么使用NodePort则无法满足。
  • HostNetwork:对于集群中的一些特殊服务使用该方式,它将容器与宿主机的网络绑定,如OpenShift中的Router服务就是使用HostNetwork与Router节点绑定。它的缺点是pod必须与主机绑定,同时每个Node上只能运行一个Pod实例,因为端口无法被复用。
  • HostPort:与HostNetwork类似,将Pod指定的端口与Node对应的端口绑定。
  • LoadBalancer:一般只在公有云平台使用,可以使Service获得与主机同一平面的IP,方便对服务进行控制与对外输出。缺点是依赖于公有云平台。

从上可看出对于HTTP服务使用Route可以很方便地对外提供服务,而对于TCP/UDP服务比较好的方式是使用LoadBalancer(当然HTTP服务也方便使用该方式),但是它依赖于云平台,有没有一种方式能够帮助集群在非IaaS平台上使用LoadBalancer方式呢?答案是有的,那就是Google的项目MetalLB。

什么是MetalLB

MetalLB的官方地址:https://metallb.universe.tf/
MetalLB是使用标准路由协议的裸机Kubernetes/OpenShift集群的软负载均衡器,可以在物理机环境下实现对Service服务分配IP。

MetalLB支持两种申明模式:

  1. Layer 2模式:ARP/NDP

Layer 2模式下,每个service会有集群中的一个node来负责。当服务客户端发起ARP解析的时候,对应的node会响应该ARP请求,之后,该service的流量都会指向该node(看上去该node上有多个地址)。

Layer 2模式并不是真正的负载均衡,因为流量都会先经过1个node后,再通过kube-proxy转给多个end points。如果该node故障,MetalLB会迁移 IP到另一个node,并重新发送免费ARP告知客户端迁移。现代操作系统基本都能正确处理免费ARP,因此failover不会产生太大问题。

Layer 2模式更为通用,不需要用户有额外的设备;但由于Layer 2模式使用ARP/ND,地址池分配需要跟客户端在同一子网,地址分配略为繁琐。同时Layer 2模式所有流量会进入到一个Node,该Node的带宽可能会成为一个网络瓶颈。

  1. BGP模式。

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

BGP模式是真正的LoadBalancer。

MetalLB由两个组件组成:

  1. controller,负责IP地址的分配,以及service和endpoint的监听。
  2. speaker,负责保证service地址可达,例如Layer 2模式下,speaker会负责ARP请求应答。

部署MetalLB

Kubernetes

  1. 安装metalLB相关的应用
    1
    # kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.8.1/manifests/metallb.yaml
  2. 创建configmap配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: ConfigMap
    metadata:
    namespace: metallb-system
    name: config
    data:
    config: |
    address-pools:
    - name: default
    protocol: layer2
    addresses:
    - 192.168.1.240-192.168.1.250
    EOF

OpenShift

  1. 关闭OpenShift自带的LoadBalancer功能,这一步很重要,否则会跟
    删除/etc/origin/master/master-config.yaml中的externalIPNetworkCIDRs设置
    1
    2
    3
    networkConfig:
    externalIPNetworkCIDRs:
    - 0.0.0.0/0
  2. 下载MetalLB安装文件
    1
    # wget https://raw.githubusercontent.com/google/metallb/v0.8.1/manifests/metallb.yaml
  3. 删除将文件中DaemonSet资源中的配置spec.template.spec.securityContext.runAsUser
  4. 添加权限
    1
    # oc adm policy add-scc-to-user privileged -n metallb-system -z speaker
  5. 运行安装
    1
    # oc apply -f metallb.yaml
  6. 创建configmap配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: ConfigMap
    metadata:
    namespace: metallb-system
    name: config
    data:
    config: |
    address-pools:
    - name: default
    protocol: layer2
    addresses:
    - 192.168.1.240-192.168.1.250
    EOF

测试

  1. 对已有的一个应用创建新LoadBalancer类型的svc
    1
    [root@master1 ~]# oc expose dc/flask-app --name=metallb-app --type=LoadBalancer --port=5000
  2. 查看当前svc的状态
    1
    2
    3
    4
    [root@master1 ~]# oc get svc
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    flask-app ClusterIP 172.30.193.89 <none> 5000/TCP 35d
    metallb-app LoadBalancer 172.30.7.121 192.168.1.240,172.29.114.1 5000:32276/TCP 6m
    其中service flask-app为之前创建的ClusterIP类型;service metallb-app为新建的LoadBalancer类型的服务,它带有一个由MetalLB控制器分配的一个IP 192.168.1.240(另外还有一个OpenShift分配的一个额外IP 172.29.114.1,该IP可忽略)。
  3. 使用openshift集群外部的机器访问该应用
    1
    2
    [root@i-avler8qs ~]# curl http://192.168.1.240:5000
    Hello world v2

总结

在没有使用了LoadBalancer(如MetalLB)前,OpenShift上的Service像是待在这里的宠物,外部访问它非常受限;使用了LoadBalancer后,Service主动向外面世界敞开了怀抱,每个Service都绑定有一个独立的IP,方便外部应用访问,同时也方便使用传统的防火墙方式进行控制访问。
LoadBalancer扩大了对OpenShift/Kubernetes集群使用的想象空间,而MetalLB无疑是性价比最高的方式。
#参考文章
MetalLB - 贫苦 K8S 用户的负载均衡支持
为裸金属K8S集群提供外部负载均衡器
OpenShift上安装MetalLB