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

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

继续我们关于如何仅使用 Kubernetes 生态系统构建自己的云的系列文章。在上一篇文章中,我们解释了如何准备一个基于 Talos Linux 和 Flux CD 的基础 Kubernetes 发行版。在本文中,我们将向您展示 Kubernetes 中的几种不同的虚拟化技术,并准备好在 Kubernetes 中运行虚拟机所需的一切,主要是存储和网络。

我们将讨论 KubeVirt、LINSTOR 和 Kube-OVN 等技术。

但首先,让我们解释一下为什么需要虚拟机,以及为什么不能只使用 docker 容器来构建云?原因在于容器提供的隔离级别不够充分。尽管情况每年都在改善,但我们仍然经常遇到允许逃逸容器沙箱并在系统中提升权限的漏洞。

另一方面,Kubernetes 最初并非设计为一个多租户系统,这意味着基本的使用模式是为每个独立项目和开发团队创建单独的 Kubernetes 集群。

虚拟机是云环境中隔离租户的主要手段。在虚拟机中,用户可以以管理员权限执行代码和程序,但这不会影响其他租户或环境本身。换句话说,虚拟机可以实现硬多租户隔离,并在租户之间互不信任的环境中运行。

Kubernetes 中的虚拟化技术

有几种不同的技术将虚拟化引入 Kubernetes 世界:KubeVirtKata Containers 是最受欢迎的。但你应该知道它们的工作方式不同。

Kata Containers 实现了 CRI(容器运行时接口),并通过在虚拟机中运行标准容器来提供额外的隔离级别。但它们在同一个 Kubernetes 集群中工作。

A diagram showing how container isolation is ensured by running containers in virtual machines with Kata Containers

Kata Containers 通过在虚拟机中运行容器来确保容器隔离的示意图

KubeVirt 允许使用 Kubernetes API 运行传统的虚拟机。KubeVirt 虚拟机作为常规的 linux 进程运行在容器中。换句话说,在 KubeVirt 中,容器被用作运行虚拟机 (QEMU) 进程的沙箱。这可以从下图清晰地看出,通过观察 KubeVirt 中如何实现虚拟机的实时迁移。当需要迁移时,虚拟机从一个容器移动到另一个容器。

A diagram showing live migration of a virtual machine from one container to another in KubeVirt

KubeVirt 中虚拟机从一个容器实时迁移到另一个容器的示意图

还有一个替代项目 - Virtink,它使用 Cloud-Hypervisor 实现轻量级虚拟化,并且最初专注于使用 Cluster API 运行虚拟 Kubernetes 集群。

考虑到我们的目标,我们决定使用 KubeVirt,因为它是该领域最受欢迎的项目。此外,我们在 KubeVirt 方面拥有丰富的专业知识,并且已经做出了很多贡献。

KubeVirt 易于安装,并且可以使用 containerDisk 功能开箱即用地运行虚拟机 - 这允许您直接从容器镜像仓库将 VM 镜像存储和分发为 OCI 镜像。使用 containerDisk 的虚拟机非常适合创建 Kubernetes 工作节点和其他不需要状态持久化的虚拟机。

为了管理持久数据,KubeVirt 提供了一个单独的工具,Containerized Data Importer (CDI)。它允许克隆 PVC 并使用基础镜像中的数据填充它们。如果您想自动为虚拟机供应持久卷,CDI 是必需的,并且 KubeVirt CSI Driver 也需要 CDI,KubeVirt CSI Driver 用于处理来自租户 Kubernetes 集群的持久卷声明。

但首先,您必须决定在哪里以及如何存储这些数据。

Kubernetes 虚拟机的存储

随着 CSI(容器存储接口)的引入,与 Kubernetes 集成的各种技术变得可用。事实上,KubeVirt 充分利用了 CSI 接口,使得为虚拟化选择存储与为 Kubernetes 本身选择存储紧密契合。但是,有一些需要考虑的细微之处。与通常使用标准文件系统的容器不同,块设备对于虚拟机来说效率更高。

尽管 Kubernetes 中的 CSI 接口允许请求两种类型的卷:文件系统和块设备,但重要的是要验证您的存储后端是否支持此功能。

为虚拟机使用块设备消除了对附加抽象层(如文件系统)的需求,这使其性能更高,并且在大多数情况下支持使用 ReadWriteMany 模式。此模式允许从多个节点并发访问卷,这是在 KubeVirt 中实现虚拟机实时迁移的关键功能。

存储系统可以是外部的或内部的(在超融合基础设施的情况下)。在许多情况下,使用外部存储可以使整个系统更加稳定,因为您的数据与计算节点分开存储。

A diagram showing external data storage communication with the compute nodes

显示外部数据存储与计算节点通信的示意图

外部存储解决方案在企业系统中通常很受欢迎,因为此类存储通常由外部供应商提供,并负责其运维。与 Kubernetes 的集成仅涉及安装在集群中的一个小型组件 - CSI driver。该驱动程序负责在该存储中供应卷,并将其连接到 Kubernetes 运行的 Pod。但是,此类存储解决方案也可以使用纯开源技术实现。其中一种流行的解决方案是 TrueNAS,由 democratic-csi 驱动程序提供支持。

A diagram showing local data storage running on the compute nodes

显示在计算节点上运行的本地数据存储的示意图

另一方面,超融合系统通常使用本地存储(当您不需要复制时)和软件定义存储实现,这些存储通常直接安装在 Kubernetes 中,例如 Rook/CephOpenEBSLonghornLINSTOR 等。

A diagram showing clustered data storage running on the compute nodes

显示在计算节点上运行的集群数据存储的示意图

超融合系统有其优点。例如,数据局部性:当您的数据本地存储时,访问这些数据会更快。但也有缺点,因为这样的系统通常更难管理和维护。

在 Ænix,我们希望提供一个开箱即用的解决方案,无需购买和设置额外的外部存储即可使用,并且在速度和资源利用方面都达到最佳。LINSTOR 成为了这个解决方案。LVM 和 ZFS 等久经考验且行业流行的技术作为后端,确保数据安全存储。基于 DRBD 的复制速度极快,并且消耗很少的计算资源。

为了在 Kubernetes 中安装 LINSTOR,有一个 Piraeus 项目,它已经提供了现成的块存储,可与 KubeVirt 一起使用。

Kubernetes 虚拟机的网络

尽管具有相似的接口 - CNI,但 Kubernetes 中的网络架构实际上更复杂,通常由许多相互之间没有直接连接的独立组件组成。实际上,您可以将 Kubernetes 网络划分为四个层次,如下所述。

节点网络(数据中心网络)

节点之间相互连接的网络。这个网络通常不是由 Kubernetes 管理的,但它非常重要,因为没有它,一切都将无法工作。在实践中,裸金属基础设施通常有不止一个这样的网络,例如一个用于节点间通信,第二个用于存储复制,第三个用于外部访问等。

A diagram showing the role of the node network (data center network) on the Kubernetes networking scheme

显示节点网络(数据中心网络)在 Kubernetes 网络架构中的作用的示意图

配置节点之间的物理网络交互超出了本文的范围,因为在大多数情况下,Kubernetes 利用的是现有网络基础设施。

Pod 网络

这是由您的 CNI 插件提供的网络。CNI 插件的任务是确保集群中所有容器和节点之间的透明连接。大多数 CNI 插件实现了一个平面网络,并从中为每个节点分配单独的 IP 地址块。

A diagram showing the role of the pod network (CNI-plugin) on the Kubernetes network scheme

显示 Pod 网络(CNI 插件)在 Kubernetes 网络架构中的作用的示意图

在实践中,您的集群可以有多个由 Multus 管理的 CNI 插件。这种方法常用于基于 KubeVirt 的虚拟化解决方案中,例如 RancherOpenShift。主要 CNI 插件用于与 Kubernetes 服务集成,而额外的 CNI 插件用于实现私有网络 (VPC) 并与您的数据中心物理网络集成。

默认 CNI 插件可用于连接网桥或物理接口。此外,还有一些专用插件,例如 macvtap-cni,它们旨在提供更高的性能。

在 Kubernetes 中运行虚拟机时需要记住的另一个方面是对 IPAM(IP 地址管理)的需求,特别是对于 Multus 提供的辅助接口。这通常由您的基础设施中的 DHCP 服务器来管理。此外,虚拟机的 MAC 地址分配可以通过 Kubemacpool 进行管理。

然而,在我们的平台中,我们决定另辟蹊径,完全依赖于 Kube-OVN。这个 CNI 插件基于 OVN(Open Virtual Network),OVN 最初是为 OpenStack 开发的,它为 Kubernetes 中的虚拟机提供了完整的网络解决方案,具有用于管理 IP 和 MAC 地址的自定义资源,支持在节点之间保留 IP 地址的实时迁移,并允许创建 VPC 以实现租户之间的物理网络隔离。

在 Kube-OVN 中,您可以为整个命名空间分配单独的子网,或者使用 Multus 将它们作为附加的网络接口连接。

Services 网络

除了 CNI 插件,Kubernetes 还有一个 services 网络,这主要用于服务发现。与传统的虚拟机不同,Kubernetes 最初设计用于运行具有随机地址的 Pod。而 services 网络提供了一个方便的抽象(稳定的 IP 地址和 DNS 名称),它将始终把流量导向正确的 Pod。尽管云中的虚拟机 IP 通常是静态的,但同样的方法也常用于它们。

A diagram showing the role of the services network (services network plugin) on the Kubernetes network scheme

显示 services 网络(services 网络插件)在 Kubernetes 网络方案中作用的图示

Kubernetes 中 services 网络的实现由 services 网络插件处理,标准实现称为 kube-proxy,并在大多数集群中使用。但如今,此功能也可能作为 CNI 插件的一部分提供。最先进的实现由 Cilium 项目提供,它可以以 kube-proxy 替换模式运行。

Cilium 基于 eBPF 技术,该技术允许高效地卸载 Linux 网络堆栈,从而与基于 iptables 的传统方法相比,提高了性能和安全性。

实际上,Cilium 和 Kube-OVN 可以轻松地集成,以提供统一的解决方案,为虚拟机提供无缝的多租户网络,以及高级网络策略和组合的 services 网络功能。

外部流量负载均衡器

在这个阶段,您已经拥有在 Kubernetes 中运行虚拟机所需的一切。但实际上还有一件事。您仍然需要从集群外部访问您的服务,而外部负载均衡器将帮助您组织这一点。

对于裸金属 Kubernetes 集群,有多种负载均衡器可用:MetalLBkube-vipLoxiLB,此外 CiliumKube-OVN 也提供了内置实现。

外部负载均衡器的作用是提供一个外部可用的稳定地址,并将外部流量导向 services 网络。services 网络插件将像往常一样将其导向您的 Pod 和虚拟机。

The role of the external load balancer on the Kubernetes network scheme

显示外部负载均衡器在 Kubernetes 网络方案中作用的图示

在大多数情况下,在裸金属上设置负载均衡器是通过在集群内的节点上创建浮动 IP 地址,并使用 ARP/NDP 或 BGP 协议在外部宣告来实现的。

在探索了各种选项后,我们认为 MetalLB 是最简单、最可靠的解决方案,尽管我们并未严格强制只使用它。

另一个好处是,在 L2 模式下,MetalLB speakers 通过使用成员列表协议执行活性检查来持续检查其邻居的状态。这使得故障转移独立于 Kubernetes 控制平面工作。

结论

这总结了我们对 Kubernetes 中的虚拟化、存储和网络的概述。这里提到的技术已在 Cozystack 平台中可用并已预先配置,您可以在其中无限制地试用它们。

下一篇文章中,我将详细介绍在此基础上,如何实现一键式部署功能齐全的 Kubernetes 集群。