k8s virtual cluster 方案 - vCluster
术语
- host cluster:virtual cluster 中的宿主 k8s 集群,承载了所有的计算资源。也会被叫做 super cluster。
- virtual cluster:virtual cluster 中的租户 k8s 集群,通常简写 vc。也会被叫做 tenant cluster。
- vCluster:k8s virtual cluster 的实现之一,即本文中要介绍的方案。
项目简介
k8s 的多租功能
k8s 自身在多租的能力上支持较差,提供了 namespace 级别的隔离,不同的租户使用不同的 namespace,但该隔离功能较弱。很多组件部署在同一个 workload 会存在诸多问题:
- 使用全局对象存在冲突,比如 CRD。
- 存在诸多安全性问题,比如多个租户之间的 pod 完全可以互访,没有任何隔离机制。
- 不同组件对于 k8s 的版本不统一。
为了解决多租的问题,最简单的思路就是使用多 k8s 集群,业界的 KubeFed v2、karmada、clusternet、OCM 等均为多 k8s 集群的实现。但多 k8s 集群因为存在独立的控制面和计算资源,存在资源消耗过多的问题。
还有一个中间思路为仅做 k8s 的控制面隔离,计算资源仍然共享,即 pod 也可以解决很多的多租隔离问题。k8s 的控制面隔离又存在两个主要方案:
- 独立的 kube-apiserver 和 etcd、kube-controller-manager, kube-scheduler 共享 host cluster。该方案中有独立的 kube-apiserver 组件,这里的 etcd 可以被 sqllite、mysql 等存储取代。该方案统称为 virtual cluster,简称为 vc。
- 独立的 proxy apiserver,kube-apiserver、kube-controller-manager、kube-scheduler 共享 host cluster。访问 k8s 的请求先到 proxy apiserver,proxy apiserver 转发到 host cluster 的 kube-apiserver。可以在 proxy apiserver 中提供独立的 RBAC 机制,实现一定程度的隔离。该方案在开源中未看到具体的实现。
vCluster 介绍
vCluster 为 virtual cluster 的开源实现之一,由 Loft Labs 提供,Github Star 3.7K,代码行数 4 万行。除了开源版本外,还提供了商业版本 vCluster PRO。
vCluster 设计原则:
- 最小化资源占用。在实现上使用了单 pod 的 k3s 作为 k8s 的控制面。
- 复用 host cluster 的计算、存储和网络资源。
- 降低对 host cluster 的请求。
- 简单灵活。
- 不需要 host cluster 的管理员权限。
- vCluster 多个 namespace 下的对象映射到 host cluster 的同一个 namespace 下。同时也可以支持 vCluster 的一个 namespace 对应 host cluster 的一个 namespace。
- 易清理。
Getting Started
k8s 集群准备
k8s 集群这里使用了 kind 方案,kind 配置 kind.conf 如下:
1 | kind: Cluster |
执行 kind create cluster --config kind.conf
即可创建 k8s 集群,包含了一个 control-plane 节点,三个 worker 节点。
安装 vcluster
vCluster 提供了使用 vcluster cli、helm 和 kubectl 三种安装方式,使用 vcluster cli 最为简单,其底层同样采用 helm chart 的方式部署,下面采用 vcluster cli 的方式进行安装。
安装 vcluster cli 工具:
1 | curl -L -o vcluster "https://github.com/loft-sh/vcluster/releases/latest/download/vcluster-darwin-arm64" && sudo install -c -m 0755 vcluster /usr/local/bin && rm -f vcluster |
或者执行 brew install vcluster
安装 vcluster 命令行工具。
执行命令 vcluster create my-vcluster
创建 virtual cluster。会在 host cluster 上创建 namespace vcluster-my-vcluster
,该 namespace 下创建如下对象:
1 | $ kubectl get all -n vcluster-my-vcluster |
可以看到在该 namespace 下创建了 coredns 和 StatefulSet my-vcluster。每个租户有独立的 coredns 组件,用来做域名解析。my-vcluster 为 vCluster 的管控面组件,包括了 k8s controller plane 和 syncer 组件。
执行 vcluster connect my-vcluster
后会在本地启动代理,并自动切换本地的 kubeconfig context,将 context 切换到 virtual cluster。执行 kubectl 命令即可连接到对应的 k8s 集群。virtual cluster 集群中的信息如下:
1 | $ kubectl get all -A |
在 virtual cluster 可以看到仅包含了 coredns 组件。
在 virtual cluster 和在 host cluster 中的 node 信息:
1 | $ kubectl get node -o wide |
可以看到在 virtual cluster 和 host cluster 中的 node 名字相同,这是因为 node 在 vCluster 中并没有做隔离,而是从 host cluster 中做了同步。但 virtual cluster 中的 node 节点仅包含 pod 在 host cluster 中已经使用的 node 节点,未使用的节点并不会在 virtual cluster 上。
同时可以看到 vc 和 host cluster 中的 node ip 地址并不相同,vc 中的 node ip 地址跟 host cluster 中的对应 service clusterip 相同,在 host cluster 中的对应 service 名字为 $vClusterName-node-$hostClusterNodeName
。
1 | $ kubectl get svc --context kind-vcluster -n vcluster-my-vcluster my-vcluster-node-vcluster-worker3 |
在 virtual cluster 中创建 k8s 对象:
1 | # 在 virtual cluster 创建 namespace |
在 host cluster 中看到如下对象:
1 | # 在 host cluster 上并没有对应的 namespace demo-nginx |
可以看到仅 pod 在 host cluster 中同步存在,而 namespace、deployment 这些对象仅存在于 virtual cluster 中。在 virtual cluster 中创建的多个不同 namespace pod 仅会存在于 host cluster 的同一个 namespace 下。
验证 service
在 virtual cluster 中创建 service 对象:
1 | apiVersion: v1 |
在 virtual cluster 中包含如下的 Service 对象:
1 | $ kubectl get svc -n demo-nginx |
在 host cluster 中会同步创建如下的 Service 对象,内容如下:
1 | apiVersion: v1 |
可以看到 host cluster 中的 service 的 ClusterIP 跟 virutal cluster 一致,但 spec.selector 字段已经被 syncer 修改,以便可以匹配到正确的 pod。
验证 Ingress
默认情况下 Ingress 不会同步到 host cluster,需要通过开关的方式启动。创建文件 values.yaml,内容如下:
1 | sync: |
执行 vcluster create my-vcluster --upgrade -f values.yaml
即可修改现在 vcluster 集群配置。
在 virtual cluster 中创建如下的 Ingress 对象:
1 | apiVersion: networking.k8s.io/v1 |
查看 host cluster 中的 Ingress 信息如下:
1 | $ kubectl get ingress --context kind-vcluster -n vcluster-my-vcluster -o yaml nginx-x-demo-nginx-x-my-vcluster |
可以看到 Ingress 中对应的 Service 名字已经修改了 host cluster 中对应的 Service 名字。
销毁
在使用完成后执行如下命令即可销毁 virtual cluster:
1 | 切换本地的 context |
架构
组件
整个架构中,有两大核心组件:k8s Control Plane 和 syncer。其中 StatefulSet my-vcluster 中容器 syncer,默认情况下在该容器中同时启动了 k3s 容器作为 vCluster 控制平面和 vCluster 的 syncer 进程。
1 | kubectl exec -it --context kind-vcluster -n vcluster-my-vcluster my-vcluster-0 -- ps -ef |
controller plane
控制平面默认使用 k3s,存储使用 sqllite,也可以使用 etcd、mysql、postgresql。k8s 发行版也可以使用 k0s、Vanilla(标准 k8s)、第三方镜像等。控制平面由如下几个组件组成:
- k8s apiserver。
- 数据存储,比如 sqllite、etcd 等。
- kube-controller-manager
- kube-scheduler:可选组件,默认使用 host cluster 调度器。
在 Pro 版本中,允许控制面跟 pod 部署在不同的 host cluster。
syncer
virtual cluster 中并不包含实际的计算、存储和网络资源,syncer 的职责为将对象从 virtual cluster 同步到 host cluster,也有少部分对象需要从 host cluster 同步到 virtual cluster。
vCluster 将 k8s 对象划分为 low level 和 high level,其中 high level 的对象仅存在于 virtual cluster 中,比如 Deployment、CRD 等对象。low level 的对象会通过 syncer 模块同步到 host cluster 上,包括 Pod、ConfigMap、Secret 等。low level 的对象在 virutal cluster 为多个 namespace,但均会映射到 host cluster 的一个 namespace 下。另外,vCluster 也支持将 virtual cluster 的多个 namespace 映射到 host cluster 的多个 namespace,该特性目前处于 alpha 状态。
vCluster 可以通过配置的方式来定制资源的同步,更复杂的同步规则提供了插件机制实现。
vCluster 默认支持的同步资源列表:https://www.vcluster.com/docs/syncer/core_resources。
已经创建完成的 syncer 配置,可以通过 vcluster create my-vcluster --upgrade -f values.yaml
的方式修改,该命令会调用 helm update,helm update 命令最终会修改 StatefulSet syncer 的配置,并触发 pod 的重启。
k8s node 同步
支持多种 node 的同步行为,通过修改 syncer 的启动参数:
- Fake Node:默认行为。根据 pod 中的 spec.nodeName 创建 Fake Node。Fake Node 为 syncer 服务自动创建。如果没有 pod 调度到 Fake Node 上,则 Fake Node 会自动删除。
- Real Node:根据 pod 中的 spec.nodeName 创建 Real Node,Real Node 的信息从 host cluster 同步。如果没有 pod 调度到 Real Node 上,则 Real Node 会自动删除。
- Real Node All:同步 host cluster 的所有 node 到 virtual cluster。如果要使用 DaemonSet,需要使用该模式。
- Real Nodes Label Selector:仅同步 label selector 匹配的 host node 到 virtual cluster 中。
- Real Nodes + Label Selector:仅同步包含在 pod spec.nodeName 且 Label selector 可以选中的 host cluster node 到 virtual cluster 中。
pod 调度
默认情况下,virtual cluster 中的 pod 调度会使用 host cluster 的调度,但存在如下的问题:
- 在 virtual cluster node 上的 label 对于 pod 调度不会生效。
- drait、trait 命令对于 virtual cluster 上的 pod 没有影响。
- virtual cluster 中使用自定义调度器不生效。
基于上述限制,vCluster 支持如下两种方案:
- 支持在 virtual cluster 中使用独立的调度器。可以给 virtual cluster 上的 node 增加标签、污点等信息,pod 的调度在 virtual cluster 中的调度器实现,syncer 组件仅将已经调度完成的 pod 同步到 host cluster。
- 仍然复用 host cluster 调度器,但做了部分功能的增强:在 syncer 服务中指定仅同步部分 host node 到 virtual cluster 中,这样 pod 就仅会调度到 host cluster 的特定 node 上。
网络
virutal cluster 中无独立的 pod 网络和 service 网络,完全复用 host cluster 的网络。
Service 网络
- 会从 virtual cluster 同步到 host cluster,两者的 clusterip 一致。
- 允许将一些 host cluster 中的 service 同步到 virtual cluster 中,同时指定service 的名字。
- 允许将 virtual cluster 中的 service 同步到 host cluster 中,同时指定service 的名字。
Ingress 网络
允许将 virtual cluster 中的 Ingress 同步到 host cluster,以便复用 host cluster 中的 Ingress Controller。
DNS 解析
在 virtual cluster 中部署了单独的 coredns 组件,默认情况下,在 vritual cluster 中的域名仅能解析内部的域名,不能解析 host cluster 上的域名。可以通过开关的方式,将 virtual cluster 中的域名解析转发到 host cluster 的 coredns。
在 PRO 版本中,coredns 组件可以集成到 syncer 组件内部,以便节省资源。
NetworkPolicy
默认情况下,vcluster 中会忽略 virtual cluster 中的 NetworkPolicy 资源。可以通过开关的方式打开该配置,即可将 NetworkPolicy 规则同步到 host cluster。
存储
默认情况下,host StorageClass 不会同步到 vc,可以通过开关的方式打开同步。
默认情况下,pv 不会从 vc 同步到 host cluster,可以通过开关的方式打开。
可观测性
monitoring
metrics-server 用来监控 k8s 的 Deployment、StatefulSet 等对象,metrics-server 可以复用 host cluster 中的,但需要启用 metrics server proxy 功能。也可以在 vc 中单独部署一套 metrics server。
在 vc 集群中,由于每个 k8s node 的 ip 地址为 host cluster 中的 service clusterip,在 vc 中网络是可达的,可以获取到对应的监控信息。
logging
需要用到Hostpath Mapper组件,该组件为 DaemonSet 的形式。后续即可以部署 loki 等组件。
安全
隔离模式
在启动的时候指定--isolate
,在该模式下对 workload 做了多种限制。
- 对 vcluster pod 的 Pod Security 做限制,不符合规范的 pod 不会同步到 host cluster。
- 可以对 vc 中 pod 的总资源量做限制。
- 在 host cluster 上通过 NetworkPolicy 做隔离。
virtual cluster 集群的创建
目前仅能通过 vcluster cli、helm 的方式来创建,底层均为 helm chart 的方式来管理,缺少服务化功能。
virtual cluster 集群对外暴露方法
获取 kubeconfig
vcluster connect 命令
该命令可以修改本地的 kubeconfig 文件,并将 context 切换为 virtual cluster context。默认为 virutual cluster 的管理员权限,可以指定使用特定的 ServiceAccount。
host cluster secret 中获取到 kubeconfig
在 host cluster 中,在 vc 的 namespace 下,存在一个以 vc-
开头的 Secret,该 Secret 中保存了 kubeconfig 完整信息。
vc 集群中的 apiserver 的暴露地址
可以在 syncer 启动的时候指定获取的 kubeconfig 中的 endpoint 地址。endpoint 地址即为 vc 集群中的 kube-apiserver 的地址,该 kube-apiserver 的地址可以通过 host cluster 中的 Ingress、LoadBalancer Service、NodePort Service 等方式对外暴露。
高可用设计
control plane 高可用
k3s 可以支持高可用架构,在创建 vc 的时候通过指定的副本的方式来设置高可用。其他的 k8s 发行版同样类似的实现。
备份与恢复
vCluster 本身并没有提供对于 vc 集群的数据备份与恢复功能,可以通过通用的 velero 方式实现备份与恢复功能。
总结
未做网络隔离,容器网络和 service 网络仍然在同一个平面,要想相互隔离,必须使用 NetworkPolicy。
其他
- 获取 helm chart 到本地
helm repo add lofts https://charts.loft.sh/
helm fetch lofts/vcluster
``