在单个 Kubernetes 集群上托管大量用户一直是个难题,其中有个难点:不同的组织会以不同的方式使用 Kubernetes,这样就没有一种可以适合所有人的租户模式。不过,Kubernetes 提供了创建不同多租户模式的基础模块,例如 RBAC 和 网络策略,我们可以以此构建适合自己的租户解决方案,以适应不同的多租户集群。

命名空间

基础模块中重要的就是命名空间(namespace),它构成了 Kubernetes 控制平面安全性和共享策略的基础。例如,默认情况下,RBAC、网络策略和资源配额(ResourceQuota)都适用于命名空间,另外诸如 Secret、服务账户(ServiceAccount)以及 Ingress 之类的对象都可以在任何一个命名空间中自由使用,并且能与其他命名空间完全隔离。

命名空间有两个关键属性,使其成为了策略执行的理想选择:

首先,它可以用来代表所有权。大多数的 Kubernetes 对象都在命名空间中,因此如果我们使用命名空间来表示所有权,就可以控制命名空间中所有对象。

其次,命名空间的创建和使用需要授权,只有超级管理员才能创建命名空间,其他用户必须要有对应权限才能使用这些命名空间,例如在命名空间中创建、查看或修改对象。这样,我们可以建立合适的安全策略,防止非特权用户创建资源对象。

命名空间的限制

但在实际应用中,这样的命名空间其实不太灵活,常常无法满足一些常见的需求。例如,当一个团队拥有很多个包含不同 secret 和资源配额的微服务时,在理想情况下,我们应该将这些微服务放在不同的命名空间中,以进行相互隔离,但这样会带来了两个问题:

首先,命名空间没有通用的所有权概念,即使它们都属于同一团队,这意味着,当团队控制多个命名空间时,Kubernetes 不仅没有命名空间共同所有者的记录,而且无法统一应用策略到这些命名空间。

其次,创建命名空间需要很高的特权,但我们不太可能允许开发团队的任何成员都可以创建命名空间。这意味着,每当团队需要新的命名空间时,他们都必须向集群管理员提出申请要求。这对于小型团队来说完全没有问题,但随着团队的发展和不断扩大,这有可能会影响团队效率。

分层命名空间

分层命名空间(Hierarchical Namespaces)是 Kubernetes 多租户工作组(Working Group for Multi-Tenancy,wg-multitenancy)提出的一种概念,可以帮助解决上述两个问题。分层命名空间最简单的一种形式是一个常规的 Kubernetes 命名空间,它包含一个小的自定义资源,该资源标识了一个单一可选的父命名空间。这样就建立了跨命名空间的所有权的概念,而不仅局限在命名空间之内。这种所有权概念可带来另外两种类型的行为:

  • 策略继承:如果一个命名空间是另一个命名空间的子代,那么 RBAC RoleBindings 等策略可以从父代命名空间空间复制到子代命名空间。
  • 创建继承:通常情况下,我们需要管理员特权才能创建命名空间,但是分层命名空间里有一种新选择:subnamespaces,我们可以使用父代命名空间中的部分权限来对子代命名空间进行操作。

这就为我们的开发团队解决了上面两个问题。集群管理员可以创建一个“根”命名空间以及所有相关的必要策略,然后将创建子命名空间的权限委派给团队的成员,这样团队成员就可以在不违反策略的情况下创建供自己使用的子命名空间。

分层命名空间的使用

分层命名空间由 Kubernetes 分层命名空间控制器(Hierarchical Namespace Controller,HNC)控制。HNC包含两个组件:

  • 控制器:该控制器在集群上运行,以管理子命名空间、传播策略对象、确保层级结构合法并管理扩展点。
  • kubectl 插件:该插件叫 kubectl-hns,可以使用户与控制器进行交互。

下面让我们来看看 HNC 如何使用。假设我们没有命名空间创建特权,但可以查看命名空间 team-a 并能在其中创建子命名空间。我们可以使用 kuberctl 插件:

$ kubectl hns create svc1-team-a -n team-a

这将创建一个名为 svc1-team-a 的子命名空间。这里要注意,由于子命名空间也只是常规的 Kubernetes 命名空间,因此所有子命名空间的名称也必须是唯一的。

我们可以通过树视图来查看这些命名空间的结构:

$ kubectl hns tree team-a
# Output:
team-a
└── svc1-team-a

另外,如果父命名空间中有任何策略,这些策略也会继承到子命名空间中。例如,假设 team-a 有一个名为 sres 的 RBAC RoleBinding,那么它也会出现在子命名空间中:

$ kubectl describe rolebinding sres -n svc1-team-a
# Output:
Name: sres
Labels: hnc.x-k8s.io/inheritedFrom=team-a # inserted by HNC
Annotations: <none>
Role:
Kind: ClusterRole
Name: admin
Subjects: ...

最后,HNC 还会为这些命名空间添加标签,这些标签包含了层级结构的相关信息,我们可以使用这些信息来设置其他策略。例如,我们可以创建以下网络策略:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: allow-team-a
namespace: team-a
spec:
ingress:
- from:
- namespaceSelector:
matchExpressions:
- key: 'team-a.tree.hnc.x-k8s.io/depth' # Label created by HNC
operator: Exists

该策略将被继承到 team-a 的所有子命名空间,另外还会允许所有这些子命名空间之间的 ingerss 流量。不过,这些“tree”标签只能由 HNC 创建,这样可以保证能反映最新的层级结构。

HNC v0.5.1 地址:https://github.com/kubernetes-sigs/multi-tenancy/releases/tag/hnc-v0.5.1

原文链接:https://kubernetes.io/blog/2020/08/14/introducing-hierarchical-namespaces/