一文了解字节跳动 KubeZoo 的核心理念 —— 协议转换

319次阅读  |  发布于2年以前

本文是 KubeZoo 系列技术文章的第二篇,将重点介绍 KubeZoo 的核心理念和关键实现 —— 协议转换。

https://github.com/kubewharf/kubezoo

理念简介

从租户的视角来看,无论是 namespace scope 还是 cluster scope 的资源,用户都拥有完整和独立的视角,比如租户 A 拥有名字为 foo 的 namespace,租户 B 也可以拥有名字为 foo 的 namespace,而且这两个 namespace 只是名字相同,其资源和权限实际上是互相隔离的。

但是从 Kubernetes 的视角来看,其对 namespace 的资源要求在 namespace 内是命名唯一的,对于 cluster scope 的资源,其要求在全局上也是命名唯一的。

当所有的租户都共享同一个 Kubernetes 时,为了解决多个租户具备完整独立视角和 Kubernetes 要求在 namespace/scope cluster 不同层次命名的唯一性的矛盾,我们提出了协议转换的理念,以此解决命名唯一性的问题。

如上图所示,由于每个租户的名字/ID 是全局唯一的,因此如果在相关的资源名字上增加租户的标志,也就间接保证了在实际的 Kubernetes 控制面上保证了资源全局的唯一性;从租户的视角来看,如果我们对租户呈现其资源时,将相关的前缀剥离,即保证了租户的真实呈现视角和隔离性。

以 namespace 视角为例,比如租户 tenant2 有 default 和 prod 两个 namespace,其在上游的真实 namespace 则是加上了租户的前缀,故为 tenant2-default 和 tenant2-prod。

因此 KubeZoo 在租户和上游的 Kubernetes 集群充当了中间的转换适配层,接受租户的请求和上游 Kubernetes 的返回,并根据不同类型的资源进行不同的处理。

核心实现

KubeZoo 基于“协议转换”核心理念实现控制面多租户功能,通过在资源的 name/namespace 等字段上增加租户的唯一标志,从而解决不同租户的同名资源在同一个上游物理的 K8s 冲突问题,该思想和 Linux 内存管理有如出一辙之处。

K8s 原生 API 大致可以被划分为如下 4 类:

通过对上述 API 采取对应的协议转换方案,最终为绝大部分的 API 提供多租户能力。经分析检验, KubeZoo 的一致性测试通过率高达 86%。

Namespace Scope 资源

K8s 大概有 40 多种 namespace scope 的资源,比如 deployment / statefulset / pod / configmap 等。通过在每个资源的 namespace 字段关联租户信息,从而实现 namespace scope 资源的多租户能力。

对于租户不同类型的请求,KubeZoo 首先从请求的证书中解析租户信息,之后具体转换如下:

对于 POST / GET / PUT 操作的协议转换过程,所依赖的 ownerReference 资源名字也需要做相关处理,对于 Cluster Scope 的资源名字,其处理逻辑请见下节“Cluster Scope Resource”;对于 Custom Resouce 的资源名字,其处理逻辑等请见下文“Custom Resource”。

ownerReference 的命名转换是普遍适用的场景,不仅适用于本节 namespace scope resource,还包括 cluster scope resource 和 custom resource 等,后文不再累述。

Cluster Scope 资源

K8s 大概有 20 多种 cluster scope 的资源,比如 pv / namespace / storageclass 等等。通过在 name 关联租户信息,从而实现 cluster scope 资源的多租户能力。

对于租户不同类型的请求,KubeZoo 首先从请求的证书中解析租户信息,之后具体转换如下:

对于 node 类型的资源,由于 KubeZoo 方案是倾向由弹性容器提供数据面多租户的能力,因此上游集群不会纳管实际的计算节点,故实现上采取屏蔽 node 类型的资源的做法。

Custom 资源

Custom Resource Definition(CRD)是一种特殊的 cluster scope 资源,由于其 name 由 group + plural 组成,我们选择在 group 前缀关联租户信息。除此处细节差异外,其它的逻辑则和上述 “cluster scope resource” 基本保持一致。详情如下图所示:

Custom Resource(CR)可以分为 namespace scope 和 cluster scope,我们以 namespace scope 的 CR 的请求为例,KubeZoo 从证书解析租户信息后,具体的转换逻辑如下:

对于 cluster scope 的 CR,结合上述逻辑易于推导,此处不再详述。

Cross Reference 资源

需要注意的是,同一租户的不同类型的资源 Spec 中的字段可能会存在资源引用的依赖情况,pvc 的 spec.volumeName 为对应的 pv,大体上可以将引用分为两类:

以 pvc 具体 case 为例,租户视角的 pvc:

1 apiVd: PersistentVolumeClaim
2 metadata:
3   name: mypersion: vl
4 kinvc
5   namespace: default
6   ......
7 spec:
8    volumeName: pv-cb23c200-f249-11ea-9039-3497f65a8415
9    ......

上游 K8s 视角的 pvc:

1 apiVersion: vl
2 kind: PersistentVolumeClaim
3 metadata:
4   name:mypvc
5   namespace: foofoo-default
6   ......
7 spec:
8   volumeName: foofoo-pv-cb23c200-f249-11ea-9039-3497f65a8415
9   ......

常见涉及 namespace 和 cluster 之间交叉依赖的资源有如下 10 种:

clusterrole / clusterrolebinding / endpoint / endpointslice / pv / pvc / role / rolebinding / tokenreview / volumeattachment。

Non-resource 资源

除了上述核心资源以外,K8s 还有一部分 Non-resource API,主要包括监控、日志、Doc 等。

对于 readyz / healthz / livez / version 等类型近 60 个 GET 类型的 API,这些 API 主要用于表示 master / etcd / poststarthook 各类 controller 的健康状况、版本信息等。因此对于此类 API,KubeZoo 只需要将请求转发至下游 K8s 即可。

对于 /openapi/v2 API,KubeZoo 首先需要从上游 K8s 获取其支持的 openapi 资源,并从相关的资源过滤出租户的 CRD,然后合并原生 API 资源,最终返回给租户。

对于 /metrics API,这些 metrics 信息无法从租户粒度进行区分,且对于托管版本的 Serverless K8s 用户作用相对有限,因此暂不考虑支持详细的租户级别的 metrics。

对于 /logs API,由于其仅简单展示访问的审计信息,因此暂未支持详细的租户级别的 logs。从实现的角度可以在 KubeZoo 侧提供记录和审计能力,并将审计信息存储到 KubeZoo 的存储中。

总结

本文详细介绍了 KubeZoo 的核心理念和详细设计,从而保证了资源隔离的视图和逻辑的准确性;由于不同类型的资源存在一些差异,因此针对 namespace scope、cluster scope、custom 和 non-resource 等类型的资源分别进行了处理,既提供了隔离能力,又保证了准确性。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8