本文发布已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否已过时。

自己动手:使用 Kubernetes 创建自己的云 (第一部分)

在 Ænix,我们对 Kubernetes 情有独钟,并梦想所有现代技术都能很快开始利用其卓越的模式。

你是否曾想过构建自己的云?我敢打赌你肯定想过。但是,是否有可能仅使用现代技术和方法来做到这一点,而又不离开舒适的 Kubernetes 生态系统呢?我们在开发 Cozystack 的经验促使我们对此进行了深入研究。

你可能会争辩说 Kubernetes 不适用于此目的,为什么不直接使用 OpenStack 处理裸金属服务器,然后按预期在其内部运行 Kubernetes。但这样做,你只是将责任从自己手中转移到了 OpenStack 管理员手中。这会给你的生态系统增加至少一个庞大而复杂的系统。

为什么要复杂化呢?毕竟,Kubernetes 目前已经具备运行租户 Kubernetes 集群所需的一切。

我想与你分享我们开发基于 Kubernetes 的云平台的经验,重点介绍我们自己使用并认为值得你关注的开源项目。

在本系列文章中,我将讲述我们如何仅使用开源技术从裸金属准备托管 Kubernetes 的故事。从数据中心准备的基础级别开始,运行虚拟机,隔离网络,设置容错存储,一直到供应具有动态卷供应、负载均衡器和自动伸缩功能的完整 Kubernetes 集群。

通过本文,我将开始一个由几个部分组成的系列文章

  • 第一部分:为你的云准备基础工作。在裸金属上准备和运行 Kubernetes 时面临的挑战,以及供应基础设施的现成方案。
  • 第二部分:网络、存储和虚拟化。如何将 Kubernetes 变成启动虚拟机的工具,以及为此所需的一切。
  • 第三部分:Cluster API 以及如何一键启动 Kubernetes 集群的供应。自动伸缩的工作原理、卷的动态供应以及负载均衡器。

我将尽可能独立地描述各种技术,同时也会分享我们的经验以及我们为什么选择某种解决方案。

首先,让我们了解 Kubernetes 的主要优势以及它如何改变了使用云资源的方法。

重要的是要理解,在云中和在裸金属上使用 Kubernetes 是不同的。

云中的 Kubernetes

当你在云中操作 Kubernetes 时,你无需担心持久卷、云负载均衡器或节点供应过程。所有这些都由你的云供应商处理,他们接受你以 Kubernetes 对象形式提出的请求。换句话说,服务器端对你来说是完全隐藏的,而且你也不必真正了解云供应商具体如何实现这些功能,因为这不在你的职责范围内。

A diagram showing cloud Kubernetes, with load balancing and storage done outside the cluster

一张图显示云中的 Kubernetes,负载均衡和存储都在集群外部完成

Kubernetes 提供了方便的抽象,它们在任何地方都以相同的方式工作,让你可以在任何云中的任何 Kubernetes 上部署你的应用。

在云中,你通常会拥有几个独立的实体:Kubernetes 控制平面、虚拟机、持久卷和负载均衡器作为不同的实体。使用这些实体,你可以创建高度动态的环境。

感谢 Kubernetes,虚拟机现在仅被视为利用云资源的实用实体。你不再将数据存储在虚拟机内部。你可以随时删除所有虚拟机并重新创建它们,而不会中断你的应用。Kubernetes 控制平面将继续保留有关集群中应运行内容的信心。负载均衡器将持续将流量发送到你的工作负载,只需更改终端节点即可将流量发送到新节点。你的数据将安全地存储在云提供的外部持久卷中。

这种方法在使用云中的 Kubernetes 时至关重要。原因显而易见:系统越简单,就越稳定,而为了这种简单性,你会在云中购买 Kubernetes 服务。

裸金属上的 Kubernetes

在云中使用 Kubernetes 确实非常简单便捷,但对于裸金属安装来说就不是这样了。在裸金属世界中,Kubernetes 反而变得极其复杂。首先,因为整个网络、后端存储、云均衡器等通常不是在集群外部运行,而是在集群内部运行。因此,这样的系统更新和维护起来要困难得多。

A diagram showing bare metal Kubernetes, with load balancing and storage done inside the cluster

一张图显示裸金属上的 Kubernetes,负载均衡和存储都在集群内部完成

你可以自己判断:在云中,更新一个节点时,你通常会删除虚拟机(甚至使用 kubectl delete node),然后让你的节点管理工具基于不变的镜像创建一个新节点。新节点将加入集群,并像一个节点一样“正常工作”;这遵循了 Kubernetes 世界中非常简单且常用的模式。许多集群每隔几分钟就会订购新的虚拟机,仅仅因为他们可以使用更便宜的 Spot 实例。然而,当你有一台物理服务器时,你不能简单地删除并重新创建它,首先因为它经常运行一些集群服务、存储数据,而且其更新过程要复杂得多。

解决这个问题有不同的方法,包括 kubeadm、kubespray 和 k3s 所做的原地更新,以及通过 Cluster API 和 Metal3 实现物理节点供应的全面自动化。

我喜欢 Talos Linux 提供的混合方法,其中你的整个系统都在一个配置文件中描述。此文件的大多数参数可以在不重新启动或重新创建节点的情况下应用,包括 Kubernetes 控制平面组件的版本。然而,它仍然保持了 Kubernetes 最大化的声明式特性。这种方法最大限度地减少了更新裸金属节点时对集群服务的不必要影响。在大多数情况下,你无需在次要更新时迁移虚拟机和重建集群文件系统。

为未来的云准备基础

那么,假设你决定构建自己的云。要从头开始,你需要一个基础层。你不仅需要考虑如何在服务器上安装 Kubernetes,还需要考虑如何更新和维护它。考虑一下,你将不得不考虑更新内核、安装必要的模块以及软件包和安全补丁等事情。现在,你必须考虑比使用云中现成 Kubernetes 时无需担心的事情多得多。

当然,你可以使用标准的发行版,如 Ubuntu 或 Debian,或者你可以考虑专门的发行版,如 Flatcar Container Linux、Fedora Core 和 Talos Linux。每个发行版都有其优缺点。

我们呢?在 Ænix,我们使用相当多特定的内核模块,如 ZFS、DRBD 和 OpenvSwitch,因此我们决定预先构建一个包含所有必要模块的系统镜像。在这种情况下,Talos Linux 对我们来说最方便。例如,这样的配置就足以构建一个包含所有必要内核模块的系统镜像

arch: amd64
platform: metal
secureboot: false
version: v1.6.4
input:
  kernel:
    path: /usr/install/amd64/vmlinuz
  initramfs:
    path: /usr/install/amd64/initramfs.xz
  baseInstaller:
    imageRef: ghcr.io/siderolabs/installer:v1.6.4
  systemExtensions:
    - imageRef: ghcr.io/siderolabs/amd-ucode:20240115
    - imageRef: ghcr.io/siderolabs/amdgpu-firmware:20240115
    - imageRef: ghcr.io/siderolabs/bnx2-bnx2x:20240115
    - imageRef: ghcr.io/siderolabs/i915-ucode:20240115
    - imageRef: ghcr.io/siderolabs/intel-ice-firmware:20240115
    - imageRef: ghcr.io/siderolabs/intel-ucode:20231114
    - imageRef: ghcr.io/siderolabs/qlogic-firmware:20240115
    - imageRef: ghcr.io/siderolabs/drbd:9.2.6-v1.6.4
    - imageRef: ghcr.io/siderolabs/zfs:2.1.14-v1.6.4
output:
  kind: installer
  outFormat: raw

然后我们使用 docker 命令行工具构建一个操作系统镜像

cat config.yaml | docker run --rm -i -v /dev:/dev --privileged "ghcr.io/siderolabs/imager:v1.6.4" - 

结果,我们得到了一个包含我们所需一切的 Docker 容器镜像,我们可以用它在服务器上安装 Talos Linux。你也可以这样做;这个镜像将包含所有必要的固件和内核模块。

但问题来了,你如何将刚形成的镜像分发给你的节点呢?

我考虑 PXE 引导的想法已经有一段时间了。例如,我大约两年前写过一篇文章Kubefarm 项目就是完全使用这种方法构建的。但不幸的是,它确实无法帮助你部署第一个将托管其他集群的父集群。所以现在我们准备了一个解决方案,可以使用 PXE 方法来做这件事。

本质上,你所需要做的就是在容器内运行临时的 DHCPPXE 服务器。然后你的节点将从你的镜像启动,你可以使用一个简单的类似 Debian 的脚本来帮助你引导你的节点。

asciicast

那个 talos-bootstrap 脚本的源码可以在 GitHub 上找到。

这个脚本允许你在五分钟内将 Kubernetes 部署到裸金属上,并获得访问它的 kubeconfig。然而,许多未解决的问题仍然摆在眼前。

交付系统组件

在这个阶段,你已经有了一个能够运行各种工作负载的 Kubernetes 集群。然而,它还没有完全发挥作用。换句话说,你需要设置网络和存储,以及安装必要的集群扩展,比如用于运行虚拟机的 KubeVirt,还有监控栈和其他系统范围的组件。

传统上,这是通过将 Helm Charts 安装到你的集群中来解决的。你可以通过在本地运行 helm install 命令来做到这一点,但当你想要跟踪更新,并且如果你有多个集群并希望保持它们一致时,这种方法就会变得不便。实际上,有很多声明式的方法可以做到这一点。为了解决这个问题,我建议使用最佳的 GitOps 实践。我指的是 ArgoCD 和 FluxCD 等工具。

虽然 ArgoCD 凭借其图形界面和中心控制平面更适合开发目的,但 FluxCD 则更适合创建 Kubernetes 发行版。使用 FluxCD,你可以指定应启动哪些 Chart 以及使用哪些参数,并描述依赖关系。然后,FluxCD 将为你处理一切。

建议在你的新创建的集群中执行 FluxCD 的一次性安装,并为其提供配置。这将安装所有必要组件,使集群达到预期状态。

通过在新集群中进行一次性 FluxCD 安装并进行相应配置,你可以使其自动部署所有必要组件。这将使你的集群能够自行升级到所需状态。例如,安装我们的平台后,你会看到以下预配置的包含系统组件的 Helm Charts

NAMESPACE                        NAME                        AGE    READY   STATUS
cozy-cert-manager                cert-manager                4m1s   True    Release reconciliation succeeded
cozy-cert-manager                cert-manager-issuers        4m1s   True    Release reconciliation succeeded
cozy-cilium                      cilium                      4m1s   True    Release reconciliation succeeded
cozy-cluster-api                 capi-operator               4m1s   True    Release reconciliation succeeded
cozy-cluster-api                 capi-providers              4m1s   True    Release reconciliation succeeded
cozy-dashboard                   dashboard                   4m1s   True    Release reconciliation succeeded
cozy-fluxcd                      cozy-fluxcd                 4m1s   True    Release reconciliation succeeded
cozy-grafana-operator            grafana-operator            4m1s   True    Release reconciliation succeeded
cozy-kamaji                      kamaji                      4m1s   True    Release reconciliation succeeded
cozy-kubeovn                     kubeovn                     4m1s   True    Release reconciliation succeeded
cozy-kubevirt-cdi                kubevirt-cdi                4m1s   True    Release reconciliation succeeded
cozy-kubevirt-cdi                kubevirt-cdi-operator       4m1s   True    Release reconciliation succeeded
cozy-kubevirt                    kubevirt                    4m1s   True    Release reconciliation succeeded
cozy-kubevirt                    kubevirt-operator           4m1s   True    Release reconciliation succeeded
cozy-linstor                     linstor                     4m1s   True    Release reconciliation succeeded
cozy-linstor                     piraeus-operator            4m1s   True    Release reconciliation succeeded
cozy-mariadb-operator            mariadb-operator            4m1s   True    Release reconciliation succeeded
cozy-metallb                     metallb                     4m1s   True    Release reconciliation succeeded
cozy-monitoring                  monitoring                  4m1s   True    Release reconciliation succeeded
cozy-postgres-operator           postgres-operator           4m1s   True    Release reconciliation succeeded
cozy-rabbitmq-operator           rabbitmq-operator           4m1s   True    Release reconciliation succeeded
cozy-redis-operator              redis-operator              4m1s   True    Release reconciliation succeeded
cozy-telepresence                telepresence                4m1s   True    Release reconciliation succeeded
cozy-victoria-metrics-operator   victoria-metrics-operator   4m1s   True    Release reconciliation succeeded

总结

结果,你获得了一个高度可重复的环境,你可以将其提供给任何人,并且知道它的运行完全符合预期。这实际上就是Cozystack 项目所做的,你可以完全免费试用。

在接下来的文章中,我将讨论如何准备 Kubernetes 以运行虚拟机以及如何通过单击按钮运行 Kubernetes 集群。敬请期待,这会很有趣!