Kubernetes学习(capsule实现多租户模型)
capsule做为kubernetes的多租户模型的另一实现, 与hnc有异曲同工之处,由于Capsule的声明性,以及所有的配置都可存储在Git中,因此Capsule原生具备GitOps特征
架构
在单个集群中,Capsule Controller将多个命名空间聚合在一个名为Tenant的轻量级抽象中,基本上是一组Kubernetes命名空间。在每个租户中,用户可以自由地创建他们的名称空间并共享所有分配的资源。
另一方面,capsule 策略引擎保持不同租户彼此隔离。网络和安全策略、资源配额、限制范围、RBAC以及在租户级别定义的其他策略会自动由租户中的所有命名空间继承。然后,用户可以自由地自主地操作他们的租户,而不需要集群管理员的干预
租户
在Capsule中,租户是一种抽象,用于在集群管理员定义的一组边界内将多个命名空间分组到单个实体中。然后将租户分配给称为租户所有者的用户或用户组
只有集群管理员才有权限操作Tenant对象
1 | apiVersion: capsule.clastix.io/v1beta2 |
声明了一个oil的租户, 它的租户管理员是User为alice的人或者是sa, 具有相应权限的sa也可以是租户管理员
每个租户都有一个委派的用户或一组用户作为租户管理员。在capsule中这被称为租户所有者。其他用户可以在租户内部使用由租户所有者直接分配的不同级别的权限和授权进行操作。
Capsule不关心集群中使用的身份验证策略,并且支持所有Kubernetes身份验证方法。使用Capsule的唯一要求是将租户用户分配到由--capsule-user-group
选项定义的组(该参数很重要,可以传入多个值),默认安装时这个参数的值为capsule.clastix.io。
具体的身份验证策略详见下文
一般在集群管理员创建完租户后, 会将相应的集群认证文件(常见的如kubeconfig文件,相当还可以是token等)发送给owners列表中的用户, 他们便可使用以下命令查看是否有创建namespace的权限
1 | # 具有创建ns的权限 |
Owner的用户可以创建/删除ns,但无法获取除ns之个的集群级别的资源权限, 相关的权限都限制在它创建的ns中
默认情况下,租户所有者将通过RoleBinding API被授予两个ClusterRole资源:
- Kubernetes admin(可以理解为是限制在namespace中的admin而不是真的集群admin),由它授权大多数命名空间范围内的资源
- 由Capsule创建名为capsule-namespace-deleter的权限,允许删除创建的命名空间,所以Owner有删除ns的权限
要注意的一点是,由于Owner支持serviceaccount,需要确保不能使用groupsystem:serviceaccounts或groupsystem:serviceaccounts:{capsule-namespace}作为Capsule组,否则您将在Capsule控制器中出现短路,因为Capsule本身由serviceaccount控制
属性
Tenant
1 | apiVersion: capsule.clastix.io/v1beta2 |
tenant支持的属性非常多,这里不一一列出,可以看出,实现这些属性的机制是通过了策略引擎的方式
GlobalTenantResource
GlobalTenantResource被设计用于在选定的tenant对象中共享资源
1 | apiVersion: capsule.clastix.io/v1beta2 |
由于GlobalTenantResource是群集范围内的资源,因此只有群集管理员才能操作该对象, 如果想在某一tenant中的所有ns共享资源,又如何实现呢?
TenantResource
TenantResource则是用于实现上述目的,tenant的Owner用户可以直接操作该对象
1 | apiVersion: capsule.clastix.io/v1beta2 |
有了这些属性,Tenant几乎可以实现任何的多租户需求
参考官网
身份验证
X509
官方给出的case是基于x509证书的方式实现的Owner身份认证,主要是hack/create-user.sh脚本,这里讲一下流程
首先通过openssl创建证书: key及csr
1 | openssl genrsa -out ${USER}-${TENANT}.key 2048 |
重点关注-subj参数, CN表示common Name, USER是脚本传入的用户,即tenant的Owner中指定的用户, MERGED_GROUPS为默认的capsule.clastix.io,然后生成CertificateSigningRequest对象提交给集群,集群自动approve及签发相关证书并生成USER的kubeconfig文件.由于CN中已经传入了USER, 这个USER也属于capsule.clastix.io组, 正好与–capsule-user-group参数capsule.clastix.io(默认值)一致,因此在创建完成后, USER使用kubeconfig文件即可拥有Tenant Owner权限,整个流程跟正常搭建kubernetes证书的生成是一个道理。
但是生产中不可能一个一个手工去创建用户证书, 前面说过,Capsule不关心集群中使用的身份验证策略,并且支持所有Kubernetes身份验证方法
所以只要是支持了kubernetes的身份验证策略的系统或者是平台,都可以对接上capsule, 以下再以rancher为例进行说明
对接Rancher
rancher做为一款常用的集群管理平台, 支持对接多种账号体系,如AD/LDAP, 这里以LDAP为例(rancher如何配置AD不属于本篇内容,不再赘述),
这里简单说一下原理: 当使用LDAP账号登陆rancher时,rancher会从LDAP中验证是否为合法用户,如果是则将该用户保存在自身的CRD对象中,同时生成该用户的唯一ID,同时这个用户会有一个用户组,也由rancher生成。
rancher在与kubernetes集群交互时,使用kubernetes提供的user impersontion
机制进行通信,将ldap中的用户信息(比如cn=zsk)封装成自身的用户机制(id=u-32ipck5o7k)去与kubernetes交互
上述机制很重要,所以这种情况下,在指定Tenant的Owener时 User应该是LDAP中的用户吗?答案是否定的,而应该要是rancher中的用户
rancher中的User对象信息如下:
1 | apiVersion: management.cattle.io/v3 |
所以capsule正确的设置userGroups的配置文件应该是, 与–capsule-user-group参数是等价的:
1 | apiVersion: capsule.clastix.io/v1beta2 |
openldap_group是rancher基于ldap机制时写入到kubernetes中用户对应的group信息,这个参数就是我们需要的. 在集群中查找某个用户的信息可使用以下方法:
1 | kubectl get clusterrole |
要注意的是,查询用户信息需要在rancher自身的集群中查询,而不是在rancher托管的集群
相关issue:
GitOps
capsule的所有属性都可以定义在git中, 那么与gitops的衔接可谓是非常便捷, 如flux, argoCD等gitops工具, 这一块不再赘述
可参考官网的详细步聚flux2-capsule
capsule-proxy
这里也简单提一下capsule-proxy项目,从上面的实践可以看出,虽然capsule通过webhook机制可以将权限限定在特定的namespaces中,但还是会存在下列一类的问题
1 | $ kubectl get namespaces |
原因是RBAC操作仅在Cluster-Scope中可用,并且不授予没有适当权限的用户。
为了解决这类问题,capsule引入了自己的解决方案, 即capsule-proxy.capsule-proxy实现了一个简单的反向代理,它只拦截对API服务器的特定请求,
然后capsule完成请求的转换、响应、返回数据,这样就可解决上述问题
相关的issue:
问题
当Tenant Owner用户使用命令创建ns时:
kubectl create ns zsk
, 提示以下错误:1
Error from server (You do not have any Tenant assigned: please, reach out to the system administrators): admission webhook "owner.namespace.capsule.clastix.io" denied the request: You do not have any Tenant assigned: please, reach out to the system administrators
原因: 出现这个报错的原因是在tenant中的owners.name设置成了ldap中的cn(zsk), 会与CapsuleConfiguration中的userGroups对应不上, 导致通过userinfo无法找到绑定的tenant.
解决: 修改为rancher中的用户组即可(openldap_group://CN=zsk,OU=yyy,DC=zzz,DC=com),参考对接Rancher一节如何解决Tenant下ns的resourcequota自定义问题:
解决: 由于需要为该租户下的每个ns设定resourcequota, tenant对象支持设置resourcequota会统一下发到所有ns, 但考虑到后续业务上会出现ns的resourcequota的大小不一, 因此resourcequota不能设置在tenant上,
可以通过tenantresource这个对象将resourcequota进行传播,同时使用namespaceselector来指定传播到ns范围,
tenant在创建ns时支持给ns添加label或者是annotation,因此可以与tenantresource中的namespaceselector对应起来,这样处理后就能实现tenant下的ns可以自定义resourcequota
不是必须: 这里最好有一个单独的ns(如ns-robot)专门用于传播对象,这样可以放心使用namespaceSelector选择ns而不用当心会重复选择到tenantresource所在的ns.如果没有namespaceSelector则默认是该租户下的所有ns
当resourcequota期间变小后, 不会对现在ns里的pod产生影响,但会出现已经使用的quota超过了ns的resourcequota的现象例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22apiVersion: capsule.clastix.io/v1beta2
kind: TenantResource
metadata:
name: tnt-default-rq
namespace: notebook
spec:
resyncPeriod: 120s
resources:
- namespaceSelector:
matchLabels:
tenant-rq.k2.com/use-default: enable
rawItems:
- apiVersion: v1
kind: ResourceQuota
metadata:
name: tnt-default-rq-notebook
spec:
hard:
limits.cpu: "120"
limits.memory: "928Gi"
requests.cpu: "120"
requests.memory: "928Gi"