前言

伴随着k8s的大量使用,无论是基于应用隔离或者高可用,容灾的需要还是运维管理的需求,很多企业都会部署多个K8S集群。 这就会导致有些应用依赖于其它k8s集群的微服务,需要从一个集群里的pod访问另外一个集群里的pod或者service。
为了解决跨集群服务调用的问题,市场上出现了很多开源项目来解决这个问题。有机遇CNI的,有与CNI无关的机遇集群间vpn的,有基于service mesh的。本文重点介绍一个在集群间建立vpn的开源方案Submariner。
Submariner 支持Pod 和服务在不同 Kubernetes 集群中的之间直接通讯,无论是在本地还是在公有云中。 Submariner 是完全开源的,设计旨在与网络插件 (CNI) 无关。 Submariner 是一个基于代理的集中式架构,该代理收集有关集群配置的信息并发送回参数以[供使用]。
本文的参考资料连接
Submariner开源项目官网
Demo App
Blog: Connecting managed clusters with Submariner in Red Hat Advanced Cluster Management for Kubernetes
Redhat Submariner 文档


一、Submariner功能

跨集群建立3层的网络连接,连接通道可以是加密的或者不加密的。
跨集群的服务发现
subctl, 命令行工具用来简化部署和管理
支持CIDRs重叠的的集群间通讯,两个集群的pod地址,service地址段重复。

二、架构

Submariner 将连接的集群之间的网络扁平化,并实现集群间Pod和服务的IP可达性。 Submariner 还通过 Lighthouse 提供服务发现功能。
架构图

2.1​ Submariner重要组件

  • Gateway Engine: 网关引擎组件部署在每个参与的集群中,负责建立到其他集群的安全隧道。为了保持高可用,可以部署多个网关引擎,算法保证有一个active,其它的standby。 网关引擎是一个pluggable的架构,其负责建立隧道的组件可以替换

    • IPsec , 通过 Libreswan 实现. 默认插件.
    • WireGuard (via the wgctrl library).
    • 不加密隧道, implementation using VXLAN.

    活动的网关引擎组向broker提供cluster 和endpoint的信息

  • Route Agent: 将跨集群流量从节点路由到活动的网关引擎(Gateway Engine)。

  • Broker: 促进网关引擎之间的元数据交换,使它们能够相互发现。

  • Service Discovery: 提供跨集群服务的发现。Lighthouse 项目为 Submariner 连接的 Kubernetes 集群提供 DNS 发现。 Lighthouse 实现了 Kubernetes Multi-Cluster Service APIs。

  • Globalnet Controller: 处理具有重叠 CIDR 的集群的互连。(可选)

2.2 Broker

Broker 是一个 API,所有参与的集群都可以访问,其中两个对象通过 .submariner.io 中的 CRD 进行交换:

  • Cluster:参与集群的静态信息,比如pod和service的 CIDR。
  • Endpoint:包含集群中活动网管引擎的相关信息,比如IP。

Broker 必须部署在一个 Kubernetes 集群上,该集群的 API server必须可以被其它Kubernetes集群访问。 它可以是专用集群,也可以是连接的集群之一。
Broker本质上是一个CRD集合,保存在集群的ETCD数据库里。Broker也定义了一个ServiceAccount和相关的BRAC组件,让submariner组件安全的访问broker API。 Broker没有pod或者service
Broker的可用性不会影响数据链路的通信,控制平面组件将无法向其他集群发布新信息或更新信息,也无法从其他集群了解新信息或更新信息。在broker恢复后,控制平面的组件会自动重新同步和更新数据。

2.3 服务发现

Lighthouse DNS维护一个额外的DNSserver负责域clusterset.local的解析。k8s 的coredns会把clusterset.local后缀的域名请求forward到lighthouse DNS, Lighthouse DNS会利用缓存中的ServiceImport resources来解析,如果记录存在就返回记录,不存在就返回NXDOMAIN。
When a single Service is deployed to multiple clusters, Lighthouse DNS server prefers the local cluster first before routing the traffic to other remote clusters in a round-robin fashion.

三、术语和概念

  • ClusterSet 一组两个或多个高度互信的集群,它们之间共享服务。在集群集中,相同名称的所有命名空间都被认为是相同的命名空间。
  • ServiceExport(CRD) - 用于指定要在集群集中公开服务。如果多个集群从同一个命名空间导出一个同名服务,它们将被识别为一个逻辑服务。
    • ServiceExports 必须由用户在每个集群中以及服务所在的命名空间内显式创建,以表明该服务应该对集群集中的其他集群可见和可发现。ServiceExport可以手动或通过命令subctl export创建对象。
    • 当一个服务被导出时,它就可以被访问为..svc.clusterset.local.
    • 对于无头服务,单个 Pod 可以作为....svc.clusterset.local. 必须是有效的DNS-1123 标签
  • ServiceImport(CRD) - 每个集群中的多集群服务的表示。由 Lighthouse 在内部创建和使用,不需要任何用户操作。

四、测试Submariner安装

Submariner是开源软件项目,在企业内使用开源软件时,服务是一个关键,有保障的服务才会提供及时的安全补丁,遇到问题时有专家提供服务。 红帽积极参与Submariner社区的开发,并在多集群管理组件ACM - Advanced Cluster Management for Kubenets提供 Submariner Addon来帮助使用 OCP - Openshift Container Platform的用户来实现跨集群的通讯。
在这里插入图片描述
在ACM里包含了Submariner的operator,安装配置过程非常简单,下面以我在AWS上的两个集群为例介绍我的测试过程。

4.1 测试环境

使用OCP4.9, ACM2.4
两个集群:

  • local-cluster (ACM hub集群本身也被作为被管集群)
  • managed-cluster
    在这里插入图片描述
    ACM2.4的Submariner还不包含Globalnet组件,需要在即将发布2.5里包含。因此需要连个集群的clusterNetwork和serviceNetwork上不要重叠。
    在对应的集群用下面的命令可以查看当前集群的clusterNetwork,serviceNetwork 的CIDR
oc get network cluster -o json -o jsonpath='clusterNetwork:  {.spec.clusterNetwork[*].cidr}{"\n"}serviceNetwork:  {.spec.serviceNetwork[*]}{"\n"}'

local cluster

clusterNetwork: 10.128.0.0/14
serviceNetwork: 172.30.0.0/16

managed cluster

clusterNetwork: 10.132.0.0/14
serviceNetwork: 172.31.0.0/16

4.2 在ACM控制台创建cluterset

  1. 在ACM控制台 Infrastructure -> Clusters-> Cluster Sets 点击 Create
    clusterset按钮
    在这里插入图片描述

  2. 输入clusterset名称
    在这里插入图片描述

  3. 添加集群到clusterset
    在这里插入图片描述
    在这里插入图片描述

clusterset创建成功后,broker安装在ACM 的hub cluster中 。

4.3 安装Submariner add-ons

  1. 点击刚创建的clusterset,在Submariner add-ons里安装
    在这里插入图片描述

  2. 选择在哪些集群上安装addon,在这些集群中会安装broker以外的其它组件,这些组件安装在这些集群的submariner-operator namespace中
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    安装完add-on后,operator会自动选择Gateway的节点,可用一下命令在各cluster里查看组件状态和Gateway信息。

oc -n submariner-operator get pods

oc get node --selector=submariner.io/gateway=true -o wide

oc -n submariner-operator describe Gateway

4.4 用命令行CLI的方式安装

本节未做细致整理,大致的步骤和命令供参考,详细步骤参见官方文档

  1. 在hub server上创建 ManagedClusterSet
cat << EOF | kubectl apply -f -
apiVersion: cluster.open-cluster-management.io/v1beta1
kind: ManagedClusterSet
metadata:
  name: submarinerset
EOF

会创建submarinerset-broker的namespace,并部署broker
oc get crds -n submarinerset-broker |grep -iE ‘submariner|multicluster.x-k8s.io’

查看哪些cluster加入到broker
oc get clusters.submariner.io -n submarinerset-broker

  1. 在每一个被管集群的namespace里创建 SubmarinerConfig资源来准备sumariner集群环境
    For local-cluster:
cat << EOF | oc apply -f -
apiVersion: submarineraddon.open-cluster-management.io/v1alpha1
kind: SubmarinerConfig
metadata:
  name: submariner
  namespace: local-cluster
spec: {}  
EOF

For managed-cluster:

cat << EOF | oc apply -f -
apiVersion: submarineraddon.open-cluster-management.io/v1alpha1
kind: SubmarinerConfig
metadata:
  name: submariner
  namespace: managed-cluster
spec: {}
EOF
  1. 把被管集群集群加入到managedclusterset
oc label managedclusters local-cluster "cluster.open-cluster-management.io/clusterset=submarinerset" --overwrite
oc label managedclusters managed-cluster "cluster.open-cluster-management.io/clusterset=submarinerset" --overwrite
  1. 部署submariner到被管节点
cat << EOF | oc apply -f -
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: ManagedClusterAddOn
metadata:
     name: submariner
     namespace: local-cluster
spec:
     installNamespace: submariner-operator
EOF
cat << EOF | oc apply -f -
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: ManagedClusterAddOn
metadata:
     name: submariner
     namespace: managed-cluster
spec:
     installNamespace: submariner-operator
EOF
  1. 检查addon的部署状态
 oc -n local-cluster get managedclusteraddons submariner -o yaml
 oc -n managed-cluster get managedclusteraddons submariner -o yaml
  • 在local-cluster集群上
oc -n submariner-operator get pods

oc get node --selector=submariner.io/gateway=true -o wide

oc -n submariner-operator describe Gateway

五、部署测试应用验证跨集群通讯

我用一个客户留言簿示例程序来验证被管集群间的通讯。
我在测试过程中fork了这个项目放在自己的项目里这样方便修改。
此示例中的应用程序包含guestbook Web前端redis-leader service用于存储redis-follower service存储备份。新的留言guestbook写入redis leader数据库,redis-follower从redis-leader同步数据,guestbook写完数据后从redis-follower读历史数据显示在页面上。
在这里插入图片描述

在此示例中,我们使用 Red Hat Advanced Cluster Management 将guestbook前端和redis-leader服务部署到local-cluster. 然后我们将redis-follower服务部署到managed-cluster. 为了使这个应用程序能够工作,redis-leader服务和redis-follower服务应该能够相互访问,所以我们使用serviceexports.multicluster.x-k8s.io API 将它们导出到每个被管集群。

  1. 登录到Red Hat Advanced Cluster Management hub集群的控制台,导航到Manage applications在Application页面,点击 Create application

  2. 输入应用的名称和namespace的名称

  3. 选择 Git repository

  4. 选择应用的git url, 比如 https://github.com/aarenw/acm-demo-app

  5. Branch 选择main path 选择 guestbook
    在这里插入图片描述

  6. guestbook web前端部署到local-cluster,在这里选择 Deploy on local cluster ,其余保持默认值
    在这里插入图片描述

  7. 部署成功可以看到cluster resource的状态全部时绿色,资源的拓扑图可以看到每个资源的状态和属性。 注意可能存在一下问题:

  • 镜像库用的是gcr.io ,如果不能直接访问,需要下载后推送到你的集群可以访问的镜像库
  • pod使用了80端口,可能导致部署不成功,可以给所在namespace的default serviceaccount 添加scc来解决
  • 记得fork这个git修改route.yaml里的your-ocp-cluster-name 和your-ocp-cluster-domain , 否则route不可访问

在这里插入图片描述

在这里插入图片描述

  1. 重复2~7, 部署redis-leader, 在第5步里path选择 redis-leader
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 重复2~7, 部署redis-follower 到managed-cluster, 第5步里选择redis-follower, 第6步选择 Deploy application resources only on clusters matching specified labels , Label输入name , Value 输入managed-cluster
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 访问guestbook web 前端route的url,可以发现应用以及能够正常访问
    在这里插入图片描述

  2. 查看guestbook的deployment 文件, pod 通过环境变量访问redis-local 和redis-follower, 无论本地还是远程都是访问serviceexports的域名

....
        env:
        - name: GET_HOSTS_FROM
          value: env
        - name: REDIS_LEADER_SERVICE_HOST
          value: redis-leader.guestbook.svc.clusterset.local
        - name: REDIS_FOLLOWER_SERVICE_HOST
          value: redis-follower.guestbook.svc.clusterset.local
....
  1. 在两个cluster上分别查看serviceexports, serviceimport以及service,可以发现在cluster里可以看到本地或者远程的导出的服务,所有serviceimport都指向service的IP,也就是说无论这个service在本地还是远程都一个通过serviceimport访问,这样简化了应用部署,提高部署灵活性。
  • 在local-cluster
$ oc get serviceexports  -n guestbook
NAME           AGE
redis-leader   16m

$ oc get serviceimport -n submariner-operator 
NAME                                       TYPE           IP                  AGE
redis-follower-guestbook-managed-cluster   ClusterSetIP   ["172.31.221.63"]   9m16s
redis-leader-guestbook-local-cluster       ClusterSetIP   ["172.30.56.27"]    19m

$ oc get service redis-leader   -n guestbook -o wide
NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE   SELECTOR
redis-leader   ClusterIP   172.30.56.27   <none>        6379/TCP   24m   app=redis,role=leader,tier=backend
  • on managed-cluster
$ oc get serviceexports  -n guestbook
NAME             AGE
redis-follower   16m

$oc get serviceimport -n submariner-operator
NAME                                       TYPE           IP                  AGE
redis-follower-guestbook-managed-cluster   ClusterSetIP   ["172.31.221.63"]   16m
redis-leader-guestbook-local-cluster       ClusterSetIP   ["172.30.56.27"]    26m

$ oc get service   -n guestbook -o wide
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE   SELECTOR
redis-follower   ClusterIP   172.31.221.63   <none>        6379/TCP   17m   app=redis,role=follower,tier=backend

六、后续

6.1 ACM 2.5 2022/6/9 正式GA,可以支持GlobalNet

6.2 GlobalNet

默认情况下,Submariner 的一个限制是它不处理跨集群重叠的 CIDR(ServiceCIDR 和 ClusterCIDR)。每个集群必须使用不同的 CIDR。 为了支持连接集群中的重叠 CIDR,Submariner 有一个名为 Global Private Network 的组件 Globalnet ( globalnet)。这个 Globalnet 是一个虚拟网络,专门解决具有重叠CIDR的多集群通讯的问题。每个集群在部署时都从这个虚拟全局专用网络中获得一个子网,配置为新的集群参数GlobalCIDR(例如 242.0.0.0/8)。用户还可以手动为每个集群指定 GlobalCIDR加入Broker。

集群范围的全局出口IP

默认每个集群分配8个IP,用于在访问外部集群时使用,用资源 ClusterGlobalEgressIP 表示,数量可以配置。

Namespace范围的全局出口IP

通过创建 GlobalEgressIP 可以为namespace创建访问外部cluster时使用的EgressIP,它可以被namespace的所有pod或者自定pod使用。 GlobalEgressIP优先于ClusterGlobalEgressIP

Service 的全局入口 IP

导出ClusterIP类型的服务会自动从全局 CIDR 分配一个全局 IP 用于作为被访问的入口IP。对于Headless service,每个后备 pod 都分配有一个用于入口和出口的全局 IP。但是,如果后备 pod 与一个GlobalEgressIP 匹配,则其分配的 IP 将用于出口。

路由和 iptable 规则配置为使用相应的全局 IP 进行入口和出口。所有地址转换都发生在集群的活动的Gateway节点上。
Globalnet
Globalnet


总结

通过测试发现通过Red Hat ACM多集群管理组件,不仅可以轻松管理多集群的生命周期,应用部署,通过Submariner addon更是轻松提供了跨集群的安全应用访问,简单易用,期待ACM2.5版本的Submariner功能正式GA,并提供Globalnet功能。


本文的参考资料连接:
Submariner开源项目官网
Demo App
Blog: Connecting managed clusters with Submariner in Red Hat Advanced Cluster Management for Kubernetes
Redhat Submariner 文档

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐