Featured image of post 使用vCluster解决Kubernetes多租户挑战

使用vCluster解决Kubernetes多租户挑战

本文深入探讨了Kubernetes多租户环境中的挑战,介绍了如何使用vCluster虚拟集群技术解决命名空间级别的隔离限制,并通过实际演示展示了vCluster与Falco、Kyverno等安全工具的集成方式。

理解多租户

当为客户构建内部开发者平台(IDP)时,Kubernetes通常作为该平台的强大核心。这是因为其技术能力和不断扩展的生态系统。一个常见的IDP用例是支持多个租户(例如多个应用程序或软件工程团队)的软件开发生命周期(SDLC)。采用Kubernetes有助于在这些不同租户之间共享资源,虽然这有助于优化成本和强制执行标准,但也带来了工作负载彼此隔离的挑战。这被称为多租户,对于IDP,我们必须确保这种隔离是公平和安全的。

使用原生Kubernetes功能实现多租户

幸运的是,Kubernetes提供了一些开箱即用的功能来支持多租户设置中的隔离。这种隔离可以在控制平面和数据平面进行。让我们简要描述这些功能。

控制平面隔离

  • 命名空间用于将租户的不同资源分组到逻辑单元中。虽然这允许您有效地应用例如安全策略,但它本身不提供任何隔离。
  • **基于角色的访问控制(RBAC)**在执行授权方面起着关键作用。这有助于限制对每个单独租户授予或拒绝的API资源访问,并且可以与命名空间结合使用。
  • 资源配额可用于为单个命名空间设置资源消耗限制。这不仅限于计算资源如CPU和内存,还可以定义为例如存储资源或对象计数。

数据平面隔离

  • 网络策略用于限制出口和入口流量,因为默认情况下允许所有网络通信。这有助于强制执行租户之间的网络隔离。
  • 存储隔离可以通过动态卷供应实现。
  • 节点隔离通过为各个租户提供专用节点支持,并且仅允许通过使用例如污点和容忍度来允许租户特定的工作负载。

虽然这些功能为多租户提供了坚实的基础,但在成熟的IDP设置中通常不够。特别是命名空间的概念正在成为控制平面级别隔离的限制因素。考虑一个团队(我们将单个团队视为单个租户)需要部署特定自定义资源定义(CRD)的情况,该CRD是运行仅其应用程序所需的某个工具所必需的。由于CRD必须在集群范围部署,它们不是命名空间的。因此,团队无法自行部署CRD(因为根据隔离要求,他们的访问权限仅限于其命名空间)。这意味着他们将联系平台工程团队请求支持,这基本上留给平台工程师这些选项:

  • 拒绝请求并可能失去该团队作为平台用户,这可能也会导致平台作为产品的负面声誉。
  • 给团队更多权利,以便他们可以部署集群范围的资源,这与租户隔离的想法相矛盾。
  • 为团队部署CRD,这也意味着负责管理租户资源,这与IDP理念相矛盾,并且可能很快成为不可管理的负担(另一方面,如果多个团队需要CRD,因此它可以成为平台产品,则此选项可能是正确的选择)。
  • 为该团队创建一个专用集群,他们获得完全访问权限,这会显著增加成本、操作负担和配置蔓延。

在所描述的用例中,这些选项对于平台用户或平台工程师来说都不是真正有吸引力的。我们如何解决这个困境?进入vCluster!

vCluster

vCluster是LoftLabs的一个工具,允许我们在物理主机集群上启动虚拟集群。这些虚拟集群是完全功能的Kubernetes集群,并提供API服务器端点。因此,它们是支持多租户设置中隔离的有吸引力的提议。这是如何工作的?

概念

从物理主机集群的角度来看,vCluster只是运行在命名空间中的应用程序。此应用程序由两个重要组件组成。

首先有虚拟控制平面,包括您在每个常规集群中也找到的组件:

  • Kubernetes API服务器处理虚拟集群内的所有API请求。
  • 控制器管理器负责确保一致的资源状态。
  • 数据存储存储虚拟集群中资源的状态。
  • 调度程序是可选的,可以用于代替默认主机调度程序。

其次有同步器。此组件负责将资源从虚拟集群同步到运行vCluster的主机命名空间。这是必要的,因为vCluster本身没有可以调度工作负载的网络或节点。默认情况下,只有低级资源如Pods、ConfigMaps、Secrets和Services被同步到主机。

以下图表取自官方文档,描述了上述架构。如果您对更多细节感兴趣,强烈建议阅读文档。

那么您可以用虚拟集群做什么,它如何帮助多租户?团队可以请求虚拟集群,例如通过自助服务IDP产品。IDP在专用于团队的主机命名空间中启动虚拟集群,并将所需的连接细节(例如通过kubeconfig)提供回请求团队。然后团队可以使用虚拟集群部署例如所需的CRD以及他们的应用程序。他们还可以使用虚拟集群创建尽可能多的命名空间。从主机的角度来看,所有虚拟集群工作负载仍然限制在指定的主机命名空间。通过这种方式,vCluster解决了团队的命名空间级别多租户限制,同时从IDP的角度维护它。让我们基于免费的vCluster核心产品启动一个虚拟集群来看它在行动中。

动手实践

先决条件

  • 访问运行中的Kubernetes集群,该集群将作为主机(对于此演示,使用带有Kubernetes v1.32的本地colima集群)。
  • kubectl与主机和虚拟集群交互。
  • vCluster CLI安装虚拟集群(注意:也支持其他选项如Helm、Terraform、ArgoCD和Cluster API;此演示使用vCluster 0.24.1)。
  • 可选:由于您需要定期在主机和虚拟集群之间切换上下文,您可能想考虑安装kubectx。

部署虚拟集群

通过访问Kubernetes集群,我们使用vCluster CLI启动虚拟集群:

运行vcluster create tenant-a-vcluster --namespace tenant-a创建此虚拟集群。这自动将条目添加到您的kubeconfig并将当前上下文更改为虚拟集群。通过运行kubectl get namespace,您应该看到以下输出,看起来与您在每个物理集群中找到的内容相当:

1
2
3
4
5
NAME              STATUS   AGE
default           Active   1m05s
kube-node-lease   Active   1m05s
kube-public       Active   1m05s
kube-system       Active   1m05s

在您的主机集群中,您将看到名为tenant-a的新命名空间,包括两个pod:

  • vCluster pod,包括控制平面和同步器容器
  • core-dns确保pod和服务可以通过主机名在虚拟集群中定位彼此。此core-dns pod从虚拟集群同步到主机集群。

部署工作负载

作为下一步,我们部署一些示例工作负载,看看在虚拟和主机集群中发生了什么:

  • 在虚拟集群中,通过运行kubectl run nginx --image=nginx在默认命名空间中启动简单的nginx pod
  • 在主机集群中,您将看到同步器将nginx pod放置在虚拟集群的tenant-a命名空间中,并带有调整的名称,包括pod、命名空间和虚拟集群:nginx-x-default-x-tenant-a-vcluster
  • 由于nginx pod在主机上运行,如果没有限制如网络策略,它可以被主机上运行的其他工作负载访问,就像其他常规工作负载一样。要测试这一点,您可以例如获取主机集群中nginx pod的IP:NGINX_IP=$(kubectl get pod nginx-x-default-x-tenant-a-vcluster -n tenant-a --template '{{.status.podIP}}')

然后运行: kubectl run temp-pod --rm -it --restart=Never --image=curlimages/curl -- curl $NGINX_IP 这应该输出一些来自nginx的html,表明请求成功。

部署CRD

正如我们所说,部署非命名空间资源如CRD在多租户场景中提出挑战,并且vCluster可以通过允许租户自己管理CRD来帮助解决这个问题,我们还需要测试这一点。

将以下CRD部署到虚拟集群: kubectl apply -f https://raw.githubusercontent.com/Liquid-Reply/vcluster-idp-tools/refs/heads/main/crds/crontabs.yaml

然后将相应的自定义资源(CR)部署到虚拟集群: kubectl apply -f https://raw.githubusercontent.com/Liquid-Reply/vcluster-idp-tools/refs/heads/main/resources/crontab.yaml

您将看到成功消息,对象已创建:crontab.stable.example.com/my-new-cron-object created

尝试将相同的CR(不是CRD)部署到主机集群。您将看到错误消息,指示相应的CRD未安装,因为它仅在虚拟集群中可用。

动手实践总结

从这个非常基本的动手实践中,我们可以看到启动虚拟集群的简单性,这些集群看起来和行为像常规集群。部署到虚拟集群的工作负载同步到主机集群,在那里它表现得像常规集群工作负载。由于虚拟集群带来自己的API服务器和数据存储,我们可以部署例如仅在那里可用而不在主机上的CRD。控制平面级别的这种隔离在多租户环境中是非常有价值的资产,并且可以解决我们最初描述的挑战。

与主机应用程序的交互

到目前为止,很明显vCluster非常适合想要管理非命名空间资源的平台用户,但平台工程团队呢?通常此团队部署应用程序堆栈,帮助他们和用户确保安全、成本效益和合规性等。此堆栈中的工具是否仍然按预期为虚拟集群工作,尽管它们部署在主机集群上?作为示例,我们将看看Falco(在运行时检测安全威胁)和Kyverno(定义作为集群工作负载防护栏的策略),并看看它们如何与虚拟集群工作负载交互。

Falco

由于Falco是平台堆栈的一部分,我们首先需要在主机集群上安装它(参见官方文档):

1
2
3
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install --replace falco --namespace falco --create-namespace --set tty=true falcosecurity/falco

(演示使用图表版本4.21.3)

这在falco命名空间中部署Falco作为DaemonSet。根据主机集群中的节点数量,如果安装成功,您应该看到相应的pod运行: kubectl get pods -n falco

Falco正在跟踪可疑活动,如在容器中生成shell或打开敏感文件。要测试功能性和与虚拟集群工作负载的交互,我们将重用作为演示一部分部署的nginx pod:

  1. 观察主机集群中的Falco日志: kubectl logs ds/falco -n falco -f

  2. 提取nginx pod中etc/shadow文件的内容(此文件被认为是敏感的,因为它包含密码信息): kubectl exec -it nginx -- cat etc/shadow

  3. 在Falco日志中,您现在应该看到该记录的可疑活动的警告: Warning Sensitive file opened for reading by non-trusted program ...

由于vCluster将工作负载从虚拟集群同步到主机,Falco可以检测这种活动,因为它作为常规主机工作负载出现在Falco面前。这对平台用户和平台工程师都是好消息,因为这意味着在使用vCluster时不需要重新配置Falco。

Kyverno

就像Falco一样,我们首先需要在主机集群上安装Kyverno(参见官方文档):

1
2
3
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno --create-namespace

(演示使用图表版本3.4.1)

成功安装后,Kyverno在kyverno命名空间中运行: kubectl get pods -n kyverno

验证规则

要测试Kyverno策略是否正确应用于虚拟集群工作负载以及违规是否向租户浮现,我们将部署示例策略。此策略包括一个规则,验证pod是否标有app.kubernetes.io/name

kubectl apply -f https://raw.githubusercontent.com/Liquid-Reply/vcluster-idp-tools/refs/heads/main/kyverno-policies/require-labels.yaml

您可以通过检查主机和虚拟集群的事件来检查策略是否正确应用于工作负载:

  • 对于主机集群上的虚拟集群命名空间执行:kubectl events -n tenant-a | grep PolicyViolation

由于该命名空间中的工作负载都没有所需的标签,我们看到许多验证错误,正如我们从Kyverno期望的那样: policy require-labels/check-for-labels fail: validation error: The label app.kubernetes.io/name is required.

  • 现在对于虚拟集群运行: kubectl events | grep PolicyViolation

对于虚拟集群中的演示nginx pod,您将看到与主机集群中同步的nginx pod相同的验证错误消息。这表明由主机集群上的平台团队管理的策略的违规透明地浮现在虚拟集群的用户面前。这工作的原因是事件从主机同步到虚拟集群,但同样重要的是要记住这个过程是异步的,意味着事件将首先出现在主机上,然后在虚拟集群中。

上面部署的策略设置为审计,这意味着将有关于策略违规的警告,但工作负载不会被阻止部署。让我们看看如果我们在强制模式下部署相同的策略会发生什么:

  1. 在主机集群中部署新策略: kubectl apply -f https://raw.githubusercontent.com/Liquid-Reply/vcluster-idp-tools/refs/heads/main/kyverno-policies/require-labels-enforce.yaml

  2. 在虚拟集群中部署一些没有所需标签的示例工作负载: kubectl run nginx-enforce --image=nginx

  3. 查看pod kubectl describe pod nginx-enforce

您将看到pod在虚拟集群中卡在挂起状态。这表明清单已保存到vCluster数据存储,但它保持在挂起状态,因为它无法同步到主机集群,因为那里的策略强制执行app.kubernetes.io/name标签对工作负载的要求。

如果您尝试直接在主主机集群上部署相同的工作负载,Kyverno将立即阻止部署并显示错误消息。与虚拟集群相比,主机集群的数据存储中不会保存任何内容。

变异规则

除了Kyverno策略中验证资源的规则外,还有另一种重要类型的规则。这些是变异规则,可用于修改资源,我们也要测试它们的行为。为此,我们将使用具有以下变异的规则的策略:

  • 添加标签foo: bar
  • 添加注释foo: bar
  • 如果不存在,添加cpu和内存的资源请求

您可以在此处查看策略: https://github.com/Liquid-Reply/vcluster-idp-tools/blob/main/kyverno-policies/add-labels-annotations-resources.yaml

  1. 在主机集群中安装策略: kubectl apply -f https://raw.githubusercontent.com/Liquid-Reply/vcluster-idp-tools/refs/heads/main/kyverno-policies/add-labels-annotations-resources.yaml

  2. 删除上面的强制策略,以便不阻止新工作负载部署: kubectl delete -f https://raw.githubusercontent.com/Liquid-Reply/vcluster-idp-tools/refs/heads/main/kyverno-policies/require-labels-enforce.yaml

  3. 由于变异仅应用于新创建的pod,我们将在虚拟集群中部署另一个nginx pod: kubectl run nginx-mutate --image=nginx

要查看变异是否正确应用,您可以查看pod: kubectl describe pod nginx-mutate

您将看到foo: bar标签和注释都已应用于虚拟集群中的pod,但资源未设置。相比之下,同步的主机pod不仅设置了标签和注释,而且资源已根据策略进行调整 - 这是您期望的,因为Kyverno在主机上运行。原因是,就像事件(如前所述)一样,标签和注释从主机同步到虚拟集群,但这不适用于所有对象属性。事实上,大多数pod规范不会被同步回来。官方文档中更详细地描述了这种双向同步,并概述了正在同步的内容。

影响

仅从功能角度查看pod,可能仅使用主机集群的Kyverno就可以了,因为策略将应用于主机上同步的pod。这些同步的pod是在底层节点上运行的实际工作负载,因此它们将遵守定义的策略。此解决方案的缺点是,使用虚拟集群的租户没有洞察应用于其工作负载的所有更改,因为它们不会从主机同步回来。这可能是平台不良开发人员体验的重要来源。

更重要的是要记住,Kyverno策略只能应用于实际从vCluster同步到主机的对象(默认情况下只有pod、secret、configmap和service正在同步)。例如,vCluster中的Deployment资源将不会同步到主机。后果是,针对Deployment的主机Kyverno策略(例如确保定义最小副本数)将不会生效。

好消息是有一些方法可以改进这一点:

  • 另外在虚拟集群中安装Kyverno和策略:这使得所有变异和验证对使用虚拟集群的团队透明。团队现在也可以部署自己的策略并使用所有可用的Kyverno功能。缺点是额外的Kyverno安装需要资源和维护工作。
  • 使用Kyverno集成,这是vCluster企业功能:这有助于在虚拟集群内强制执行策略,仅使用单个主机Kyverno安装。但这里一个值得注意的限制是,您只能使用Kyverno的资源库功能查找主机集群上的其他资源。
  • 目前vCluster的功能实际上可以通过插件扩展以执行自定义逻辑。但重要的是要记住,这种自定义逻辑必须被开发和维护,这可能是显著的工作。此外,此功能的广泛采用似乎有限,因此它可能在未来不会收到进一步更新。

总结

我们探讨了vCluster如何解决内部开发者平台(IDP)在控制平面级别的Kubernetes多租户限制。虽然Kubernetes通过命名空间、RBAC和其他功能提供基线隔离,但当团队需要部署集群范围的资源如CRD时,这些变得不足。

vCluster通过在主机集群内创建虚拟Kubernetes集群来解决这个问题,允许团队在其隔离环境中具有完全管理控制,同时从平台的角度维护适当的约束。

我们演示了工作负载如何在虚拟和主机集群之间同步,并检查了与Falco和Kyverno的交互和兼容性。虽然存在一些同步挑战,特别是与Kyverno,但我们了解到实际工作负载在主机集群上运行,因此由主机应用程序正确管理,这对平台工程师和用户同样是重要方面。

展望

虽然我们专注于改进上述控制平面隔离,但我们没有讨论如何增强数据平面隔离。不久前,LoftLabs宣布了vNode来解决 exactly 这个挑战。看看这个工具如何适应成熟的多租户解决方案以及vCluster和vNode将如何协同工作将是很有趣的。作为下一步,我们Liquid Reply将测试此组合以评估可能性。如果您想更深入地讨论vCluster,请随时联系我们。

参考

  • Falco
  • Kubernetes多租户
  • Kyverno
  • vCluster
  • vNode
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计