Kubernetes 多集群服务 API
由于各种原因,采用 Kubernetes 的企业内部存在着几个、几十甚至上百个集群。比如出于研发流程上的考虑,不同环境下都存在独立的集群;监管层面的考虑,就地存储的用户数据需要搭配应用集群;单个集群的容量限制,无法满足业务体量;可用性要求的多云、多地、多中心;Kubernetes 原地升级成本大进而考虑新建集群,等等各种原因。然而,Kubernetes 设计之初并没有考虑多集群。
这些集群彼此之间看似独立,但又有着千丝万缕的联系。比如高可用性的多集群,实现了集群级的灾备,但集群中的服务如何实现跨集群的故障迁移?
我们先看下集群内的应用如何对集群外提供服务。由于 Kubernetes 网络隔离特性,存在着天然的网络边界,需要借助某些方案(如 Ingress、NodePort)来将服务暴露到集群外。虽然解决了连通性的问题,但是服务的注册和发现还无法解决。
k8s-svc-exposing
通常我们将 Service 作为 Kubernetes 平台的服务注册和发现,今天要介绍的 Multi-Cluster Service(多集群服务 API,简称 MCS API) 则可以看成是 跨 Kubernetes 集群的服务注册发现。
MCS 介绍
MCS API 来自 Kubernetes Enhancement Proposal KEP-1645: Multi-Cluster Services API[1] 目前还处于提案阶段。
MCS 的目标是既然多集群不可避免,那就定义一套 API 来实现服务的跨集群注册和发现,并且这套 API 足够轻量级,做到能够像访问本地服务一样访问其他集群的服务。
注意 MCS API 只提供 API 和实现的设计指导,不提供具体的实现。
术语
- clusterset:集群集,集合内的集群间共享服务。
- mcs-controller:多集群服务控制器,在多集群间完成服务的注册和发现。
- cluster name:集群的唯一标识,用以标识注册的服务来自哪个集群。
CRD
ServiceExport
定义对集群集中其他集群暴露的服务,与Service
一样必须创建在与负载相同的命名空间下,且与Service
同名。ServiceImport
定义了导入的其他集群的Service
。
type ServiceExport struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Status ServiceExportStatus `json:"status,omitempty"`
}
type ServiceImport struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ServiceImportSpec `json:"spec,omitempty"`
Status ServiceImportStatus `json:"status,omitempty"`
}
type ServiceImportSpec struct {
Ports []ServicePort `json:"ports"`
IPs []string `json:"ips,omitempty"`
Type ServiceImportType `json:"type"`
SessionAffinity corev1.ServiceAffinity `json:"sessionAffinity"`
SessionAffinityConfig *corev1.SessionAffinityConfig `json:"sessionAffinityConfig"`
}
跨集群服务注册
如果多个集群都以同样的名字和命名空间对外声明服务,这些服务将被视作单一的组合服务。例如 1 个集群集下有 4 个集群,其中 1 个集群在命名空间 bar
下有个名为 foo
的 Service
。其他集群要想使用该集群的 Service
,该集群需要对外声明该 Service
是可以跨集群提供服务,完成多集群服务的注册。
mcs-service
多集群服务的注册通过在 Service
的同命名空间下创建同名的 ServiceExport
资源来完成。ServiceExport
可以手动创建,也可以实现支持服务的自动注册:根据标识将集群内或者指定命名空间下的服务自动注册。
更多的设计细节,可以参考 Service Exporting 设计细节[2]。
跨集群服务发现
在 ServiceExport
资源创建完成后,该服务会被集群集下的其他集群“发现”:同名的命名空间下出现同名的 ServiceImport
资源,并且该 ServiceImport
与服务注册的 Endpoint
相关联。
mcs-exported
同样,如果一个服务下线或者停止跨集群服务,删除 ServiceExport
资源后,其他集群中的 ServiceImport
也会相应地被销毁。ServiceImport
的管理,由 MCS 实现中的 mcs-controller 来完成。
值得注意的是,关于 ServiceImport
的使用,该标准中并未界定具体的实现细节。由于 Pod 在 Kubernetes 中是非永久性资源,我们访问应用的时候通常使用 Service
。多集群服务的理想情况,是在使用 Service
的时候能自动使用其他集群的 Endpoints
,做到对应用的无感知。
一种方案是修改 Service API
的实现(比如 kube-proxy
) 进行来使用 ServiceImport
。
另一种方案则是不需要修改现在 Service API
的实现,由 mcs-controller 来创建影子服务,该服务与新创建的 EndpointSlice
关联,后者聚合了该跨服务的所有 Endpoints
。一个 ServiceImport
下可能有来自多个集群的 EndpointSlice
,每个 EndpointSlice
上有各自集群的标识。
mcs-endpoints
在官方的设计细节中建议了 ClusterIP
和 DNS 的方案并提供了细节,但不是唯一的实现方案。
总结
MCS API 希望在 API 的层面提供多集群服务的定义,但由于其实现的复杂性,该提案的进展缓慢。从 2020.2 提出方案,到 2020.8 提供 API 的 alpha 实现之后没有多少进展。
即便如此,多集群服务的注册和发现机制可以给后面的实现带来一定的参考价值。