本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
DIY:使用 Kubernetes 创建自己的云(第 3 部分)
本文进入了最有趣的阶段,深入探讨了在 Kubernetes 中运行 Kubernetes。重点介绍了 Kamaji 和 Cluster API 等技术,以及它们与 KubeVirt 的集成。
之前的讨论已经涵盖了在裸金属上准备 Kubernetes 以及如何将 Kubernetes 转变为虚拟机管理系统。本文是该系列的最后一篇,解释了如何利用以上所有内容,构建一个功能齐全的托管 Kubernetes,并只需单击一下即可运行虚拟 Kubernetes 集群。
首先,让我们深入了解 Cluster API。
Cluster API
Cluster API 是 Kubernetes 的一个扩展,它允许将 Kubernetes 集群作为另一个 Kubernetes 集群中的自定义资源进行管理。
Cluster API 的主要目标是为描述 Kubernetes 集群的基本实体并管理其生命周期提供统一的接口。这使得创建、更新和删除集群的过程自动化,从而简化了扩展和基础设施管理。
在 Cluster API 的上下文中,有两个术语:管理集群 和 租户集群。
- 管理集群 是一个用于部署和管理其他集群的 Kubernetes 集群。该集群包含所有必要的 Cluster API 组件,并负责描述、创建和更新租户集群。它通常仅用于此目的。
- 租户集群 是用户集群或使用 Cluster API 部署的集群。它们通过在管理集群中描述相关资源来创建。然后,最终用户使用它们来部署应用程序和服务。
重要的是要理解,物理上,租户集群不一定必须与管理集群运行在相同的基础设施上;更常见的情况是,它们运行在其他地方。
一张展示管理 Kubernetes 集群和租户 Kubernetes 集群使用 Cluster API 进行交互的图表
为了运行,Cluster API 利用了 提供者(providers) 的概念,这些提供者是负责被创建集群特定组件的独立控制器。在 Cluster API 中,有几种类型的提供者。主要的有:
- 基础设施提供者,负责提供计算基础设施,例如虚拟机或物理服务器。
- 控制平面提供者,它提供 Kubernetes 控制平面,即 kube-apiserver、kube-scheduler 和 kube-controller-manager 组件。
- 引导提供者,用于为正在创建的虚拟机和服务器生成 cloud-init 配置。
要开始使用,你需要安装 Cluster API 本身以及每种类型的一个提供者。你可以在项目的文档中找到支持的提供者的完整列表。
对于安装,您可以使用 clusterctl
实用程序,或者使用Cluster API Operator 作为更具声明性的方法。
选择提供者
基础设施提供者
要使用 KubeVirt 运行 Kubernetes 集群,必须安装KubeVirt 基础设施提供者。它能够在运行 Cluster API 的同一管理集群中部署工作节点的虚拟机。
控制平面提供者
Kamaji 项目为在管理集群内以容器形式运行租户集群的 Kubernetes 控制平面提供了一个现成的解决方案。这种方法有几个显著的优点:
- 成本效益:在容器中运行控制平面避免了为每个集群使用单独的控制平面节点,从而显著降低了基础设施成本。
- 稳定性:通过消除复杂的多层部署方案来简化架构。不再是依次启动虚拟机然后在其中安装 etcd 和 Kubernetes 组件,而是一个简单的控制平面,它被部署并作为 Kubernetes 内部的常规应用程序运行,并由一个 operator 管理。
- 安全性:集群的控制平面对最终用户是隐藏的,减少了其组件被攻破的可能性,并且还消除了用户对集群证书库的访问。这种对用户不可见的控制平面组织方式常被云提供商使用。
引导程序提供者
使用 Kubeadm 作为引导提供者,这是 Cluster API 中准备集群的标准方法。该提供者是作为 Cluster API 本身的一部分开发的。它只需要一个已安装 kubelet 和 kubeadm 的准备好的系统镜像,并允许生成 cloud-init 和 ignition 格式的配置。
值得注意的是,Talos Linux 也支持通过 Cluster API 进行配置,并为此提供了提供者。虽然之前的文章讨论了使用 Talos Linux 在裸金属节点上设置管理集群,但对于配置租户集群,Kamaji+Kubeadm 的方法具有更多优势。它便于在容器中部署 Kubernetes 控制平面,从而无需为控制平面实例设置单独的虚拟机。这简化了管理并降低了成本。
工作原理
Cluster API 中的主要对象是 Cluster 资源,它充当所有其他资源的父级。通常,此资源引用另外两个资源:一个描述控制平面的资源和一个描述基础设施的资源,每个资源都由一个单独的提供者管理。
与 Cluster 不同,这两个资源没有标准化,它们的类型(kind)取决于您正在使用的具体提供者。
一张图表,展示了 Cluster 资源与其在 Cluster API 中链接的资源之间的关系。
在 Cluster API 中,还有一个名为 MachineDeployment 的资源,它描述了一组节点,无论是物理服务器还是虚拟机。这个资源的功能类似于标准的 Kubernetes 资源,如 Deployment、ReplicaSet 和 Pod,提供了一种声明式描述一组节点和自动扩展的机制。
换句话说,MachineDeployment 资源允许您声明式地描述集群的节点,根据指定的参数和请求的副本数量自动化它们的创建、删除和更新。
一张展示 MachineDeployment 资源及其在 Cluster API 中的子资源关系的图表
为了创建机器,MachineDeployment 引用一个用于生成机器本身的模板和一个用于生成其 cloud-init 配置的模板。
一张图表,展示了 MachineDeployment 资源与它在 Cluster API 中链接的资源之间的关系。
要使用 Cluster API 部署一个新的 Kubernetes 集群,您需要准备以下一组资源:
- 一个通用的 Cluster 资源
- 一个 KamajiControlPlane 资源,负责由 Kamaji 操作的控制平面
- 一个 KubevirtCluster 资源,描述 KubeVirt 中的集群配置
- 一个 KubevirtMachineTemplate 资源,负责虚拟机模板
- 一个 KubeadmConfigTemplate 资源,负责生成令牌和 cloud-init
- 至少一个 MachineDeployment 来创建一些工作节点
完善集群
在大多数情况下,这已经足够了,但根据所使用的提供商,您可能还需要其他资源。您可以在 Kamaji 项目文档中找到为每种提供商创建的资源示例。
在这个阶段,你已经有了一个准备好的租户 Kubernetes 集群,但到目前为止,它只包含 API 工作节点和一些核心插件,这些插件是任何 Kubernetes 集群安装中标准包含的:kube-proxy 和 CoreDNS。为了完全集成,你还需要安装几个组件。
要安装额外的组件,您可以使用一个单独的 Cluster API Add-on Provider for Helm,或者使用在之前文章中讨论过的同一个 FluxCD。
在使用 FluxCD 创建资源时,可以通过引用由 Cluster API 生成的 kubeconfig 来指定目标集群。然后,安装将直接在其中执行。因此,FluxCD 成为一个通用工具,用于管理管理集群和用户租户集群中的资源。
一张图表,展示了 fluxcd 的交互方案,它可以在管理 Kubernetes 集群和租户 Kubernetes 集群中安装组件。
这里讨论的是哪些组件?通常,这套组件包括以下内容:
CNI 插件
为了确保租户 Kubernetes 集群中 Pod 之间的通信,有必要部署一个 CNI 插件。这个插件创建了一个虚拟网络,允许 Pod 之间相互交互,并且传统上作为 Daemonset 部署在集群的工作节点上。你可以选择并安装任何你认为合适的 CNI 插件。
一张图表,在一个嵌套的 Kubernetes 集群方案中,展示了一个安装在租户 Kubernetes 集群内部的 CNI 插件。
云控制器管理器(Cloud Controller Manager)
Cloud Controller Manager (CCM) 的主要任务是将 Kubernetes 与云基础设施提供商的环境集成起来(在您的情况下,是管理所有租户 Kubernetes 工作节点的管理 Kubernetes 集群)。以下是它执行的一些任务:
- 当创建类型为 LoadBalancer 的服务时,CCM 会启动创建云负载均衡器的过程,该负载均衡器将流量引导至您的 Kubernetes 集群。
- 如果从云基础设施中移除了一个节点,CCM 会确保它也从您的集群中移除,从而维护集群的当前状态。
- 使用 CCM 时,节点被添加到集群时会带有一个特殊的污点 `node.cloudprovider.kubernetes.io/uninitialized`,这允许在必要时处理额外的业务逻辑。成功初始化后,此污点将从节点上移除。
根据云提供商的不同,CCM 可以在租户集群内部或外部运行。
KubeVirt 云提供商设计为安装在外部的父管理集群中。因此,在租户集群中创建类型为 LoadBalancer 的服务会触发在父集群中创建 LoadBalancer 服务,这些服务将流量引导至租户集群。
一张图表,在一个嵌套 Kubernetes 集群的方案中,展示了一个安装在租户 Kubernetes 集群外部的 Cloud Controller Manager,以及它管理的从父 Kubernetes 集群到子 Kubernetes 集群的服务映射。
CSI 驱动程序
容器存储接口(Container Storage Interface,CSI)在与 Kubernetes 中的存储交互方面分为两个主要部分:
- csi-controller:该组件负责与云提供商的 API 交互,以创建、删除、附加、分离和调整卷的大小。
- csi-node:这个组件在每个节点上运行,并根据 kubelet 的请求,帮助将卷挂载到 Pod 上。
在使用 KubeVirt CSI 驱动的背景下,出现了一个独特的机会。由于 KubeVirt 中的虚拟机运行在管理 Kubernetes 集群内部,那里有功能齐全的 Kubernetes API 可用,这为在用户租户集群外部运行 csi-controller 开辟了道路。这种方法在 KubeVirt 社区中很受欢迎,并提供了几个关键优势:
- 安全性:此方法向最终用户隐藏了内部云 API,仅通过 Kubernetes 接口提供对资源的访问。因此,它降低了从用户集群直接访问管理集群的风险。
- 简单与便利:用户无需在他们的集群中管理额外的控制器,从而简化了架构并减轻了管理负担。
然而,CSI-node 必须运行在租户集群内部,因为它直接与每个节点上的 kubelet 交互。这个组件负责将卷挂载和卸载到 Pod 中,需要与直接发生在集群节点上的进程紧密集成。
KubeVirt CSI 驱动程序充当订购卷的代理。当在租户集群内部创建一个 PVC 时,会在管理集群中创建一个 PVC,然后将创建的 PV 连接到虚拟机。
一张图表,展示了在一个嵌套 Kubernetes 集群方案中,CSI 插件组件同时安装在租户 Kubernetes 集群内外的情况,以及它管理的从父 Kubernetes 集群到子 Kubernetes 集群的持久卷映射。
集群自动扩缩器
Cluster Autoscaler 是一个多功能的组件,可以与各种云 API 配合工作,其与 Cluster-API 的集成只是可用功能之一。为了正确配置,它需要访问两个集群:租户集群,用于跟踪 Pod 并确定是否需要添加新节点;以及管理 Kubernetes 集群,它在此处与 MachineDeployment 资源交互并调整副本数量。
虽然 Cluster Autoscaler 通常在租户 Kubernetes 集群内部运行,但在这种情况下,建议出于前面描述的相同原因将其安装在外部。这种方法更易于维护,也更安全,因为它阻止了租户集群的用户访问管理集群的管理 API。
一张图表,在一个嵌套的 Kubernetes 集群方案中,展示了一个安装在租户 Kubernetes 集群外部的 Cluster Autoscaler。
Konnectivity
我还想提及另一个额外的组件——Konnectivity。你很可能稍后会需要它来让你的租户 Kubernetes 集群中的 Webhook 和 API 聚合层正常工作。这个主题在我的一篇之前的文章中有详细介绍。
与上面介绍的组件不同,Kamaji 允许您轻松启用 Konnectivity,并将其作为租户集群的核心组件之一进行管理,与 kube-proxy 和 CoreDNS 并列。
结论
现在你拥有一个功能齐全的 Kubernetes 集群,具备动态扩展、自动配置卷和负载均衡器的能力。
接下来,您可能会考虑从您的租户集群收集指标和日志,但这超出了本文的范围。
当然,部署 Kubernetes 集群所需的所有组件都可以打包到一个 Helm Chart 中,并作为一个统一的应用程序进行部署。这正是我们在我们的开放 PaaS 平台 Cozystack 上组织一键部署托管 Kubernetes 集群的方式,您可以在那里免费试用文章中描述的所有技术。