k8s virtual cluster 虚拟集群多租户解决方案
Kubernetes多租户vcluster解决方案云平台 容器化 k8s 多租户
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的多租户实现方式,虽然过程中仍有不稳定的状况,但是其设计思想是值得学习的,并且该项目是开源项目,我们甚至可以自己动手去参与其整个开发过程。
感谢您的阅读,如发现文章中的错漏,敬请留言指正!
敬请关注公众号 西友优云
更多推荐
所有评论(0)