本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
介绍分层名字空间
在单个 Kubernetes 集群上安全托管大量用户一直是一项棘手的任务。其中一个关键原因在于不同的组织以不同的方式使用 Kubernetes,因此没有一种租户模型可能适合所有人。相反,Kubernetes 为您提供了构建块来创建您自己的租户解决方案,例如基于角色的访问控制(RBAC)和网络策略(NetworkPolicies);这些构建块越好,安全构建多租户集群就越容易。
用于租户的命名空间
到目前为止,最重要的构建块是命名空间,它构成了几乎所有 Kubernetes 控制平面安全和共享策略的支柱。例如,RBAC、NetworkPolicies 和 ResourceQuotas 默认都尊重命名空间,并且 Secrets、ServiceAccounts 和 Ingresses 等对象可以在任何一个命名空间内自由使用,但与其他命名空间完全隔离。
命名空间具有两个关键特性,使其成为策略执行的理想选择。首先,它们可以用于**表示所有权**。大多数 Kubernetes 对象**必须**位于命名空间中,因此如果您使用命名空间来表示所有权,您总能确定存在一个所有者。
其次,命名空间具有**授权创建和使用**。只有高权限用户才能创建命名空间,其他用户需要明确的权限才能使用这些命名空间——即,在这些命名空间中创建、查看或修改对象。这使得它们能够在非特权用户创建 Pod 和 Service 等“常规”对象之前,仔细创建并附带适当的策略。
命名空间的限制
然而,在实践中,命名空间不够灵活,无法满足一些常见用例。例如,假设一个团队拥有多个微服务,这些微服务具有不同的 Secret 和配额。理想情况下,他们应该将这些服务放置在不同的命名空间中,以便彼此隔离,但这带来了两个问题。
首先,这些命名空间没有共同的所有权概念,即使它们都由同一个团队拥有。这意味着如果团队控制多个命名空间,Kubernetes 不仅没有记录它们的共同所有者,而且命名空间范围的策略也无法统一应用于它们。
其次,团队通常在能够自主操作时表现最佳,但由于命名空间创建是高度特权的,因此开发团队的任何成员都不太可能被允许创建命名空间。这意味着每当团队想要一个新的命名空间时,他们必须向集群管理员提交工单。虽然这对于小型组织来说可能是可以接受的,但随着组织的增长,这会产生不必要的麻烦。
引入分层命名空间
分层命名空间是 Kubernetes 多租户工作组(wg-multitenancy)开发的一个新概念,旨在解决这些问题。最简单的形式是,分层命名空间是一个常规的 Kubernetes 命名空间,其中包含一个小的自定义资源,用于标识一个可选的单一父命名空间。这建立了**跨**命名空间而非仅**在**命名空间内部的所有权概念。
这个所有权概念支持另外两种行为:
- **策略继承:**如果一个命名空间是另一个命名空间的子级,则 RBAC RoleBindings 等策略对象会从父级**复制到子级**。
- **委派创建:**通常您需要集群级权限才能创建命名空间,但分层命名空间增加了一种替代方案:**子命名空间**,只需父命名空间中的有限权限即可操作。
这解决了我们开发团队的两个问题。集群管理员可以为团队创建一个“根”命名空间,以及所有必要的策略,然后将创建子命名空间的权限委托给该团队的成员。然后,这些团队成员可以创建子命名空间供自己使用,而不会违反集群管理员施加的策略。
实践分层命名空间
分层命名空间由一个名为**分层命名空间控制器**(Hierarchical Namespace Controller,或称**HNC**)的 Kubernetes 扩展提供。HNC 包含两个组件:
- **管理器**在您的集群上运行,管理子命名空间,传播策略对象,确保您的层级结构合法并管理扩展点。
- **kubectl 插件**,称为 `kubectl-hns`,让用户可以轻松与管理器交互。
两者都可以从我们的仓库发布页面轻松安装。
让我们看看 HNC 的实际运行。想象一下我没有命名空间创建权限,但我可以查看命名空间 `team-a` 并在其中创建子命名空间1。使用该插件,我现在可以说:
$ kubectl hns create svc1-team-a -n team-a
这会创建一个名为 `svc1-team-a` 的子命名空间。请注意,由于子命名空间只是普通的 Kubernetes 命名空间,所有子命名空间名称仍然必须是唯一的。
我可以通过请求树状视图来查看这些命名空间的结构
$ kubectl hns tree team-a
# Output:
team-a
└── svc1-team-a
如果父命名空间中有任何策略,这些策略现在也会出现在子命名空间中2。例如,假设 `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` 的所有后代,并且**还**将允许所有这些命名空间之间的入站流量。“tree”标签只能由 HNC 应用,并保证反映最新的层级结构。
您可以从用户指南中了解 HNC 的所有功能。
后续步骤与参与方式
如果您认为分层命名空间适用于您的组织,HNC v0.5.1 已在 GitHub 上发布。我们很想知道您的想法,您用它来解决什么问题,以及您最希望添加哪些功能。与所有早期软件一样,您在使用 HNC 生产环境时应谨慎,但我们获得的反馈越多,我们就能越早推动 HNC 1.0 的发布。
我们也欢迎更多贡献者,无论是修复或报告 bug,还是帮助原型设计新功能,例如例外、改进的监控、分层资源配额或细粒度配置。
请通过我们的仓库、邮件列表或在 Slack 上与我们联系——我们期待您的来信!
Adrian Ludwin 是一名软件工程师,也是分层命名空间控制器的技术负责人。
注 1:实际上,您在父命名空间中创建了一个名为“子命名空间锚点”的小对象,然后 HNC 会为您创建子命名空间。
注 2:默认情况下,只传播 RBAC Role 和 RoleBinding,但您可以配置 HNC 以传播任何命名空间的 Kubernetes 对象。