vcluster是一个开源的k8s多租户实现方式

多租户本质上就是SAAS,关于k8s的多租户,如果要实现到阿里云、腾讯云的规模,需要为每一个租户创建一个单独的k8s集群,那么需要考虑的不仅仅是k8s的问题,还要考虑如何调度整个k8s集群的问题,就是要在k8s之上再创建一层调度。

最后的效果可能是在一台云主机上,运行着多个不同k8s集群的master或者workload节点,而这一层负责调度k8s的集群的可能会是openstack、或者mesos,也或者是一个可以称之为主导的k8s集群,这种类似套娃的实现,所涉及到的资源分配、网络隔离、存储等等方面的复杂度并不是一般的团队能够应付的,也没有公开出来的可供参考的可行方案,可能这就是各个云平台的技术核心吧。

然而并没有那么多云平台等着我们去建设,而k8s的优越性对于各个领域的公司的数字化建设都是极具诱惑力的。从k8s 1.23版本开始,k8s支持的最大节点数为 5000个, Pod 总数最大 150000个, 容器总数最大300000个,有多少希望部署k8s集群的公司会达到这样的规模,即使达到了,再创建一个k8s集群就可以了,因此,更多的时候,我们要面对的是k8s集群的多租户实现。

k8s本身的namesapce以及rbac机制并不能很好的解决多租户问题,因为做不到绝对的隔离,租户多的时候,各种交叉的配置估计也绝对称得上反人类,而vcluster的出现就是为了解决单个k8s集群的多租户实现方式。

关于vcluster具体的实现方式,可以访问它的官方网站:点击这里

本文旨在从实际操作的角度出发,为大家提供一个可行性的思路,下图是vcluster的基本架构图,姑且当做分割线

这个图中的syncer是vcluster的核心,是建立在以k3s作为控制平面基础之上的,而最新的v0.5.0-beta.0可以以k8s作为控制平面的,在这张图上并没有相应的更新,由于在k3s上碰到的种种不如意,最后使用了以k8s作为控制平面,具体原因可以参考:issues

首先下载最新的v0.5.0-beta.0,下载之后直接重命名成vcluster,并将其放到/usr/local/bin目录下就可以了,改一下可执行权限,因为vcluster会使用helm,需安装helm, 下载url:release

按照官方文档的说法,现在就可以创建一个virtual cluster了,然而你需要直接先创建一个PV,因为会用到,而官方文档并没有告诉我们:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-vcluster-2
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /srv/nfs/kubedata/vcluster
    server: 10.xxx.xxx.xxx

PV创建之后,还需要编辑一个yaml的配置文件,以便使创建的virtual cluster可见所有的宿主k8s集群的节点,否则只能看到部分节点

这部分设置参考官网:
https://www.vcluster.com/docs/architecture/nodes#node-syncing-modes
编辑 vcluster-value.yaml如下
rbac:
  clusterRole:
    create: true
syncer:
  extraArgs: ["--fake-nodes=false", "--sync-all-nodes"]suo


做完以上这些步骤之后,就可以开始创建virtual cluster了:

vcluster create my-vcluster -n my-vcluster --distro k8s --kubernetes-version v1.20.13 -f vcluster-value.yaml

在这个创建命令行里使用了k8s版本v1.20.13,但是实际创建过程中,却是用了v1.20.12版本,没错,开源项目总会有惊喜,以后看源码的时候再找找原因吧;至于k8s镜像的pull,可以从阿里的镜像库pull再改成标准标签,这里不做说明。

最终创建的效果如下图:

可以看到,vcluster创建了一个命名空间my-vcluster,该命名空间下存在coredns、api、controller和etcd的pod,这些就是对应标准k8s集群的coredns、apiserver、controller-manager和etcd组件。

一个Virtual Cluster已经创建完成,接下来,我们将从以下四个方面观测vcluster的实现方式:

  • 应用部署

  • 外部组件

  • 网络连接

  • 存储


应用部署

官方文档的架构图可以看出,vcluster使用了独立的控制平面,而调度器使用了宿主集群的调度器,下面我们通过应用部署验证一下。

既然vcluster是作为解决多租户问题而存在的,那么假设我们就是其中一个租户,就在刚才,k8s的管理员为我们创建了一个虚拟集群(virtual cluster),并且管理员通过下面的命令创建了一个使我们可以管理自己的虚拟集群的接口:

$ vcluster connect my-vcluster -n my-vcluster
[done] √ Virtual cluster kube config written to: ./kubeconfig.yaml. You can access the cluster via `kubectl --kubeconfig ./kubeconfig.yaml get namespaces`
[info]   Starting port-forwarding at 8443:8443
Forwarding from 127.0.0.1:8443 -> 8443
Forwarding from [::1]:8443 -> 8443

这条命令开启了一个8443端口,并且生成了一个kubeconfig.yaml的配置文件, 管理员把这个文件发给我们,我们就可以使用这个配置文件连接自己的虚拟集群了

我们看下集群里有多少个命名空间:

$ export KUBECONFIG=kubeconfig.yaml
$ kubectl get namespaces
NAME              STATUS   AGE
default           Active   11h
kube-node-lease   Active   11h
kube-public       Active   11h
kube-system       Active   11h

而宿主k8s管理员看到的命名空间是:

$ kubectl get namespaces
NAME                   STATUS   AGE
cert-manager           Active   26d
default                Active   385d
hands-on               Active   25d
host-namespace-1       Active   19h
ingress-nginx          Active   381d
istio                  Active   302d
istio-system           Active   302d
kube-node-lease        Active   385d
kube-public            Active   385d
kube-system            Active   385d
kubernetes-dashboard   Active   321d
metallb-system         Active   381d
my-vcluster            Active   18h #vcluster创建出的命名空间,作为一个虚拟集群
ns001                  Active   381d

可见由于virtual cluster使用了自己的controller manager,控制的资源对象与宿主k8s集群区分开来了,并且在宿主k8s集群内创建了一个以虚拟集群名字命名的命名空间,这个命名空间即作为一个虚拟集群

现在我们在自己的虚拟集群中创建自己的应用:

$ kubectl create namespace demo-nginx
$ kubectl create deployment nginx-deployment -n demo-nginx --image=nginx
​
$ kubectl get namespace
NAME              STATUS   AGE
default           Active   14h
demo-nginx        Active   96s #这里创建出了新的命名空间
kube-node-lease   Active   14h
kube-public       Active   14h
kube-system       Active   14h
​
$ kubectl -n demo-nginx get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE     IP               NODE        NOMINATED NODE   READINESS GATES
nginx-deployment-84cd76b964-jvwgp   1/1     Running   0          2m56s   10.244.166.223   centos111   <none>           <none>

我们把副本数扩展到8个,看下调度情况


$ kubectl scale deploy nginx-deployment --replicas=8 -n demo-nginx
deployment.apps/nginx-deployment scaled
​
$ kubectl -n demo-nginx get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE     IP               NODE        NOMINATED NODE   READINESS GATES
nginx-deployment-84cd76b964-bf4dj   1/1     Running   0          93s     10.244.192.55    centos113   <none>           <none>
nginx-deployment-84cd76b964-fdp45   1/1     Running   0          93s     10.244.192.23    centos113   <none>           <none>
nginx-deployment-84cd76b964-hln5r   1/1     Running   0          93s     10.244.192.50    centos113   <none>           <none>
nginx-deployment-84cd76b964-jvwgp   1/1     Running   0          7m23s   10.244.166.223   centos111   <none>           <none>
nginx-deployment-84cd76b964-nwchk   1/1     Running   0          93s     10.244.192.54    centos113   <none>           <none>
nginx-deployment-84cd76b964-pvrj6   1/1     Running   0          93s     10.244.166.194   centos111   <none>           <none>
nginx-deployment-84cd76b964-vgb8f   1/1     Running   0          93s     10.244.166.201   centos111   <none>           <none>
nginx-deployment-84cd76b964-vqn76   1/1     Running   0          93s     10.244.166.240   centos111   <none>           <none>

上面的操作中,我们创建了命名空间,并且部署和扩展了一个nginx应用,扩展的pod被宿主k8s集群的调度器调度到各个节点上,下面我看下宿主k8s集群管理员所看到的情况:

$ kubectl -n my-vcluster get po
NAME                                                           READY   STATUS    RESTARTS   AGE
coredns-5b9d5f9f77-wqm6q-x-kube-system-x-my-vcluster           1/1     Running   1          14h
my-vcluster-679bcc4c-fs5j7                                     1/1     Running   12         17h
my-vcluster-api-74c7bb77c8-57ssl                               1/1     Running   11         17h
my-vcluster-controller-f594cd9d8-7wsx2                         1/1     Running   5          17h
my-vcluster-etcd-0                                             1/1     Running   3          17h
nginx-deployment-84cd76b964-bf4dj-x-demo-nginx-x-my-vcluster   1/1     Running   0          11m
nginx-deployment-84cd76b964-fdp45-x-demo-nginx-x-my-vcluster   1/1     Running   0          11m
nginx-deployment-84cd76b964-hln5r-x-demo-nginx-x-my-vcluster   1/1     Running   0          11m
nginx-deployment-84cd76b964-jvwgp-x-demo-nginx-x-my-vcluster   1/1     Running   0          17m
nginx-deployment-84cd76b964-nwchk-x-demo-nginx-x-my-vcluster   1/1     Running   0          11m
nginx-deployment-84cd76b964-pvrj6-x-demo-nginx-x-my-vcluster   1/1     Running   0          11m
nginx-deployment-84cd76b964-vgb8f-x-demo-nginx-x-my-vcluster   1/1     Running   0          11m
nginx-deployment-84cd76b964-vqn76-x-demo-nginx-x-my-vcluster   1/1     Running   0          11m

在虚拟空间创建的应用,在宿主k8s集群中被展示在一个平面内,虚拟集群的命名空间作为一个命名规则被放置在资源对象名称的末尾“demo-nginx-x-my-vcluster”。因此,对于各个租户,vcluster使用了独立的控制平面,可以看作是一种硬隔离;而对于宿主k8s管理员,看到只是命名空间的隔离,而这又不是简单的命名空间隔离:

$ kubectl -n my-vcluster get deploy
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
my-vcluster              1/1     1            1           17h
my-vcluster-api          1/1     1            1           17h
my-vcluster-controller   1/1     1            1           17h

宿主k8s管理员看不到租户创建的deploy对象,只能看到创建出来的pod,因此也不能对资源对象进行扩缩容等操作。

至此,我们了解了vcluster在应用部署方面的实现方式,总体感觉是很好的,租户独立的控制平面在相当程度上做到了硬隔离,为多租户实现奠定了很好的基础。
 


外部组件

租户使用的虚拟集群中,没有安装metrics-server组件,而宿主k8s集群中已经安装了:

虚拟集群

$ kubectl top node
error: Metrics API not available

宿主k8s

$ kubectl top node
NAME        CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
centos111   516m         25%    4655Mi          39%
centos112   225m         11%    1149Mi          31%
centos113   346m         17%    2544Mi          32%

如果租户也需要看到资源使用情况,那么我们来看下租户自己安装metrics-server组件的效果:

$ kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
service/metrics-server created
deployment.apps/metrics-server created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created

安装成功后,看下虚拟集群的资源使用:

$ kubectl top node
NAME        CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
centos111   512m         640%   4776Mi          55%
centos112   251m         40%    1151Mi          38%
centos113   360m         28%    2572Mi          38%

宿主k8s集群的资源使用:

$ kubectl top node
NAME        CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
centos111   542m         27%    4776Mi          40%
centos112   210m         10%    1154Mi          31%
centos113   355m         17%    2567Mi          33%

数值是一样的,但是百分比不一样,master节点的cpu使用率在虚拟集群中显示为640%,而宿主集群只有27%,这应该跟虚拟集群的controller manager pod的cpu资源限制相关, 如果pod只使用了0.x的cpu,cpu的使用率当然会标高,但是内存的使用率却没有那么大差别。

vcluster虽然使用独立的控制平面,但是使用如metrics-server这类组件时,数据让人稍感迷惑,也许后续版本会考虑这方面的问题,毕竟现在才是0.5 beta版。


网络连接


官方文档的架构图可以看出,vcluster使用独立的域名解析,而网络层则使用宿主集群的网络层,这样设计也说得过去,毕竟从租户角度,根本看不到其他租户的资源对象,从而无法获知其他租户资源对象的网络信息。

如果非要追求网络层的硬隔离,势必要在各个虚拟集群中创建独立的网络层,租户之间的互通就变成另外的课题,而且虚拟集群的整个控制平面都是独立的情况下,网络的硬隔离变得没有必要,使用network policy进行软隔离就可基本解决问题了。

实际操作中,虚拟集群中的pod 通过IP地址可以连通宿主集群中的pod,反之亦然,而域名解析的验证就没有必要做了,在应用部署一节里,我们知道宿主集群连虚拟集群的命名空间都看不到,域名解析又怎么能成功。


存储


从官方架构图可以看出,vcluster隔离了pvc这一层面的资源对象,而pv则使用宿主集群的pv资源,我们来验证一下:

租户并不能在虚拟集群中看到pv信息,这没问题,有pv用就是了,没有的话pvc创建不成,自然要去找宿主集群管理员去创建,符合多租户思想:

$ kubectl get pv
No resources found

宿主集群pv资源

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                                 STORAGECLASS          REASON   AGE
pv-vcluster                                10Gi       RWO            Recycle          Bound       my-vcluster/data-my-vcluster-etcd-0                                  28h
pv-vcluster-2                              10Gi       RWO            Recycle          Available                                                                        16h

我们看到宿主集群中pv-vcluster已经被虚拟集群的etcd使用了,还有一个pv-vcluster-2,我们作为租户,在虚拟集群创建一个pvc看看:

$ cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-v1-0
  namespace: default
  labels:
    app: vcluster
    release: vcluster-1
  finalizers:
    - kubernetes.io/pvc-protection
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  volumeMode: Filesystem
  
$ kubectl apply -f pvc.yaml
  
$ kubectl get pvc
NAME        STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-v1-0   Bound    pv-vcluster-2   10Gi       RWO                           8m45s

可以看到,pvc直接绑定了,再看下宿主集群

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                           STORAGECLASS          REASON   AGE
pv-vcluster                                10Gi       RWO            Recycle          Bound    my-vcluster/data-my-vcluster-etcd-0                                            28h
pv-vcluster-2                              10Gi       RWO            Recycle          Bound    my-vcluster/data-v1-0-x-default-x-my-vcluster                                  16h
​
$ kubectl -n my-vcluster get pvc
NAME                                STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-my-vcluster-etcd-0             Bound    pv-vcluster     10Gi       RWO                           23h
data-v1-0-x-default-x-my-vcluster   Bound    pv-vcluster-2   10Gi       RWO                           11m

宿主集群的pv已经被绑定,并且可以看到虚拟集群创建的pvc,只是名称不一样。


总结:

通过应用部署、外部组件、网络连接和存储四个方面验证vcluster的多租户实现方式,虽然过程中仍有不稳定的状况,但是其设计思想是值得学习的,并且该项目是开源项目,我们甚至可以自己动手去参与其整个开发过程。

感谢您的阅读,如发现文章中的错漏,敬请留言指正!

敬请关注公众号 西友优云

Logo

开源、云原生的融合云平台

更多推荐