DIY:使用 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 世界中一个非常简单且常用的模式。许多集群每隔几分钟就会订购新的虚拟机,仅仅因为它们可以使用更便宜的竞价实例。但是,当您拥有物理服务器时,您不能仅仅删除并重新创建它,首先是因为它通常运行一些集群服务,存储数据,并且其更新过程要复杂得多。

有不同的方法来解决这个问题,从 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,您可以指定应启动哪些具有哪些参数的图表,并描述依赖项。然后,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 集群。敬请期待,这会很有趣!