伴随着k8s的大量使用,无论是基于应用隔离或者高可用,容灾的需要还是运维管理的需求,很多企业都会部署多个K8S集群。 这就会导致有些应用依赖于其它k8s集群的微服务,需要从一个集群里的pod访问另外一个集群里的pod或者service。
为了解决跨集群服务调用的问题,市场上出现了很多开源项目来解决这个问题。有机遇CNI的,有与CNI无关的机遇集群间的,有基于service mesh的。本文重点介绍一个在集群间建立的开源方案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 文档
跨集群建立3层的网络连接,连接通道可以是加密的或者不加密的。
跨集群的服务发现
subctl, 命令行工具用来简化部署和管理
支持CIDRs重叠的的集群间通讯,两个集群的pod地址,service地址段重复。
Submariner 将连接的集群之间的网络扁平化,并实现集群间Pod和服务的IP可达性。 Submariner 还通过 Lighthouse 提供服务发现功能。
Gateway Engine: 网关引擎组件部署在每个参与的集群中,负责建立到其他集群的安全隧道。为了保持高可用,可以部署多个网关引擎,算法保证有一个active,其它的standby。 网关引擎是一个pluggable的架构,其负责建立隧道的组件可以替换
活动的网关引擎组向broker提供cluster 和endpoint的信息
Route Agent: 将跨集群流量从节点路由到活动的网关引擎(Gateway Engine)。
Broker: 促进网关引擎之间的元数据交换,使它们能够相互发现。
Service Discovery: 提供跨集群服务的发现。Lighthouse 项目为 Submariner 连接的 Kubernetes 集群提供 DNS 发现。 Lighthouse 实现了 Kubernetes Multi-Cluster Service APIs。
Globalnet Controller: 处理具有重叠 CIDR 的集群的互连。(可选)
Broker 是一个 API,所有参与的集群都可以访问,其中两个对象通过 .submariner.io 中的 CRD 进行交换:
Broker 必须部署在一个 Kubernetes 集群上,该集群的 API server必须可以被其它Kubernetes集群访问。 它可以是专用集群,也可以是连接的集群之一。
Broker本质上是一个CRD集合,保存在集群的ETCD数据库里。Broker也定义了一个ServiceAccount和相关的BRAC组件,让submariner组件安全的访问broker API。 Broker没有pod或者service
Broker的可用性不会影响数据链路的通信,控制平面组件将无法向其他集群发布新信息或更新信息,也无法从其他集群了解新信息或更新信息。在broker恢复后,控制平面的组件会自动重新同步和更新数据。
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.
Submariner是开源软件项目,在企业内使用开源软件时,服务是一个关键,有保障的服务才会提供及时的安全补丁,遇到问题时有专家提供服务。 红帽积极参与Submariner社区的开发,并在多集群管理组件ACM - Advanced Cluster Management for Kubenets提供 Submariner Addon来帮助使用 OCP - Openshift Container Platform的用户来实现跨集群的通讯。
在ACM里包含了Submariner的operator,安装配置过程非常简单,下面以我在AWS上的两个集群为例介绍我的测试过程。
使用OCP4.9, ACM2.4
两个集群:
oc get network cluster -o json -o jsonpath='clusterNetwork: {.spec.clusterNetwork[*].cidr}{"\n"}serviceNetwork: {.spec.serviceNetwork[*]}{"\n"}'
clusterNetwork: 10.128.0.0/14
serviceNetwork: 172.30.0.0/16
clusterNetwork: 10.132.0.0/14
serviceNetwork: 172.31.0.0/16
clusterset创建成功后,broker安装在ACM 的hub cluster中 。
选择在哪些集群上安装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
本节未做细致整理,大致的步骤和命令供参考,详细步骤参见官方文档
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
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
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
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
oc -n local-cluster get managedclusteraddons submariner -o yaml
oc -n managed-cluster get managedclusteraddons submariner -o yaml
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 将它们导出到每个被管集群。
登录到Red Hat Advanced Cluster Management hub集群的控制台,导航到Manage applications在Application页面,点击 Create application
输入应用的名称和namespace的名称
选择 Git repository
选择应用的git url, 比如 https://github.com/aarenw/acm-demo-app
guestbook web前端部署到local-cluster,在这里选择 Deploy on local cluster ,其余保持默认值
部署成功可以看到cluster resource的状态全部时绿色,资源的拓扑图可以看到每个资源的状态和属性。 注意可能存在一下问题:
查看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
....
$ 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 6379/TCP 24m app=redis,role=leader,tier=backend
$ 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 6379/TCP 17m app=redis,role=follower,tier=backend
默认情况下,Submariner 的一个限制是它不处理跨集群重叠的 CIDR(ServiceCIDR 和 ClusterCIDR)。每个集群必须使用不同的 CIDR。 为了支持连接集群中的重叠 CIDR,Submariner 有一个名为 Global Private Network 的组件 Globalnet ( globalnet)。这个 Globalnet 是一个虚拟网络,专门解决具有重叠CIDR的多集群通讯的问题。每个集群在部署时都从这个虚拟全局专用网络中获得一个子网,配置为新的集群参数GlobalCIDR(例如 242.0.0.0/8)。用户还可以手动为每个集群指定 GlobalCIDR加入Broker。
默认每个集群分配8个IP,用于在访问外部集群时使用,用资源 ClusterGlobalEgressIP 表示,数量可以配置。
通过创建 GlobalEgressIP 可以为namespace创建访问外部cluster时使用的EgressIP,它可以被namespace的所有pod或者自定pod使用。 GlobalEgressIP优先于ClusterGlobalEgressIP
导出ClusterIP类型的服务会自动从全局 CIDR 分配一个全局 IP 用于作为被访问的入口IP。对于Headless service,每个后备 pod 都分配有一个用于入口和出口的全局 IP。但是,如果后备 pod 与一个GlobalEgressIP 匹配,则其分配的 IP 将用于出口。
路由和 iptable 规则配置为使用相应的全局 IP 进行入口和出口。所有地址转换都发生在集群的活动的Gateway节点上。
通过测试发现通过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 文档