本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
DIY:使用 Kubernetes 创建自己的云(第 1 部分)
在 Ænix,我们对 Kubernetes 怀有深厚的感情,并梦想着所有现代技术都将很快开始利用其卓越的模式。
你是否曾想过构建自己的云?我敢打赌你想过。但只使用现代技术和方法,不离开舒适的 Kubernetes 生态系统,这可能吗?我们在开发 Cozystack 过程中的经验要求我们深入研究这个问题。
你可能会争辩说,Kubernetes 并非为此目的而设计,为什么不直接使用 OpenStack 来管理裸金属服务器,并在其中按预期运行 Kubernetes 呢?但这样做,你只是将责任从自己手中转移到了 OpenStack 管理员手中。这至少会给你的生态系统增加另一个庞大而复杂的系统。
为什么要将事情复杂化呢?毕竟,Kubernetes 此时已经拥有运行租户 Kubernetes 集群所需的一切。
我想与你分享我们基于 Kubernetes 开发云平台的经验,重点介绍我们自己使用的、并且相信值得你关注的开源项目。
在本系列文章中,我将讲述我们如何仅使用开源技术从裸金属准备托管 Kubernetes 的故事。从数据中心准备的基础层面开始,运行虚拟机、隔离网络、设置容错存储,到制备功能齐全的 Kubernetes 集群,包括动态卷制备、负载均衡器和自动扩缩。
以此文为始,我将开启一个由几部分组成的系列文章。
- 第 1 部分:为你的云奠定基础。在裸金属上准备和运营 Kubernetes 期间面临的挑战,以及一个现成的基础设施制备方案。
- 第 2 部分:网络、存储和虚拟化。如何将 Kubernetes 转变为启动虚拟机的工具,以及为此需要什么。
- 第 3 部分:Cluster API 以及如何通过按下一个按钮开始制备 Kubernetes 集群。自动扩缩、动态卷制备和负载均衡器是如何工作的。
我将尝试尽可能独立地描述各种技术,但同时,我也会分享我们的经验以及我们为何选择某种解决方案。
首先,让我们了解 Kubernetes 的主要优势以及它如何改变了使用云资源的方式。
重要的是要理解,在云上和在裸金属上使用 Kubernetes 是不同的。
云中的 Kubernetes
当你在云中操作 Kubernetes 时,你无需担心持久卷、云负载均衡器或制备节点的过程。所有这些都由你的云提供商处理,他们以 Kubernetes 对象的形式接受你的请求。换句话说,服务器端对你完全隐藏,你也不想知道云提供商具体是如何实现的,因为这不在你的责任范围之内。
展示云 Kubernetes 的图表,其中负载均衡和存储在集群外部完成
Kubernetes 提供了方便的抽象,这些抽象在任何地方都以相同的方式工作,允许你在任何云中的任何 Kubernetes 上部署你的应用程序。
在云中,你通常有几个独立的实体:Kubernetes 控制平面、虚拟机、持久卷和负载均衡器。使用这些实体,你可以创建高度动态的环境。
多亏了 Kubernetes,虚拟机现在只被看作是利用云资源的实用实体。你不再在虚拟机内部存储数据。你可以在任何时候删除所有虚拟机并重新创建它们,而不会破坏你的应用程序。Kubernetes 控制平面将继续保存关于你的集群中应该运行什么的信息。负载均衡器将继续将流量发送到你的工作负载,只需更改端点以将流量发送到新节点。而你的数据将安全地存储在云提供的外部持久卷中。
这种方法是在云中使用 Kubernetes 的基础。其原因显而易见:系统越简单,就越稳定,为了这种简单性,你选择在云中购买 Kubernetes。
裸金属上的 Kubernetes
在云中使用 Kubernetes 确实简单方便,但对于裸金属安装来说却并非如此。在裸金属世界中,Kubernetes 反而变得难以承受的复杂。首先,因为整个网络、后端存储、云负载均衡器等通常不是在集群外部运行,而是在集群内部运行。结果是这样的系统更新和维护起来要困难得多。
展示裸金属 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 方法做同样的事情。
本质上,你所需要做的就是在容器内运行临时的 DHCP 和 PXE 服务器。然后你的节点将从你的镜像启动,你可以使用一个简单的 Debian 风格的脚本来帮助你引导你的节点。
那个 `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 集群。敬请关注,会很有趣的!