本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

DIY:使用 Kubernetes 创建自己的云(第 2 部分)

继续我们关于如何仅使用 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 功能开箱即用地运行虚拟机 - 这允许您直接从容器镜像仓库中以 OCI 镜像的形式存储和分发虚拟机镜像。带有 containerDisk 的虚拟机非常适合创建 Kubernetes工作节点和其他不需要状态持久化的虚拟机。

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

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

Kubernetes 虚拟机的存储

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

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

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

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

A diagram showing external data storage communication with the compute nodes

一张图表,显示外部数据存储与计算节点的通信

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

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(开放虚拟网络),最初是为 OpenStack 开发的,它为 Kubernetes 中的虚拟机提供了一个完整的网络解决方案,具有用于管理 IP 和 MAC 地址的自定义资源,支持在节点之间保留 IP 地址的实时迁移,并能够创建 VPC 以实现租户之间的物理网络分离。

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

服务网络

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

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

一张图表,显示服务网络(服务网络插件)在 Kubernetes 网络方案中的作用

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

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

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

外部流量负载均衡器

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

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

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

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

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

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

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

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

结论

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

下一篇文章中,我将详细介绍如何在此基础上,只需点击一个按钮即可实现功能齐全的 Kubernetes 集群的配置。