DIY:使用 Kubernetes 创建您自己的云(第二部分)
继续我们关于如何仅使用 Kubernetes 生态系统构建自己的云的系列文章。在前一篇文章中,我们解释了如何基于 Talos Linux 和 Flux CD 准备基本的 Kubernetes 发行版。在本文中,我们将向您展示 Kubernetes 中的一些不同的虚拟化技术,并准备好在 Kubernetes 中运行虚拟机所需的一切,主要是存储和网络。
我们将讨论诸如 KubeVirt、LINSTOR 和 Kube-OVN 等技术。
但首先,让我们解释一下为什么需要虚拟机,以及为什么不能只使用 Docker 容器来构建云?原因是容器没有提供足够的隔离级别。尽管情况逐年改善,但我们经常遇到允许逃脱容器沙箱并提升系统权限的漏洞。
另一方面,Kubernetes 最初并非设计为多租户系统,这意味着基本的使用模式是为每个独立的项目和开发团队创建一个单独的 Kubernetes 集群。
虚拟机是在云环境中将租户彼此隔离的主要手段。在虚拟机中,用户可以执行具有管理权限的代码和程序,但这不会影响其他租户或环境本身。换句话说,虚拟机可以实现硬多租户隔离,并在租户之间互不信任的环境中运行。
Kubernetes 中的虚拟化技术
有几种不同的技术可以将虚拟化引入 Kubernetes 世界:KubeVirt 和 Kata Containers 是最受欢迎的。但您应该知道它们的工作方式不同。
Kata Containers 实现了 CRI(容器运行时接口),并通过在虚拟机中运行标准容器,为标准容器提供了额外的隔离级别。但它们在同一个 Kubernetes 集群中工作。
KubeVirt 允许使用 Kubernetes API 运行传统的虚拟机。KubeVirt 虚拟机作为容器中的常规 Linux 进程运行。换句话说,在 KubeVirt 中,容器被用作运行虚拟机(QEMU)进程的沙箱。这可以从下图清楚地看出,通过查看 KubeVirt 中如何实现虚拟机的实时迁移。当需要迁移时,虚拟机从一个容器移动到另一个容器。
还有一个替代项目 - Virtink,它使用 Cloud-Hypervisor 实现轻量级虚拟化,最初专注于使用 Cluster API 运行虚拟 Kubernetes 集群。
考虑到我们的目标,我们决定使用 KubeVirt 作为该领域最受欢迎的项目。此外,我们拥有丰富的专业知识,并且已经为 KubeVirt 做出了很多贡献。
KubeVirt 易于安装,并且允许您使用 containerDisk 功能开箱即用运行虚拟机 - 这允许您直接从容器镜像注册表存储和分发 VM 镜像作为 OCI 镜像。具有 containerDisk 的虚拟机非常适合创建 Kubernetes 工作节点和其他不需要状态持久性的 VM。
为了管理持久数据,KubeVirt 提供了一个单独的工具,容器化数据导入器 (CDI)。它允许克隆 PVC,并用来自基本镜像的数据填充它们。如果您想为虚拟机自动配置持久卷,则 CDI 是必要的,并且它也是 KubeVirt CSI 驱动程序所必需的,该驱动程序用于处理来自租户 Kubernetes 集群的持久卷声明。
但首先,您必须决定在哪里以及如何存储这些数据。
Kubernetes 虚拟机的存储
随着 CSI(容器存储接口)的引入,可以集成 Kubernetes 的技术范围变得广泛。事实上,KubeVirt 充分利用了 CSI 接口,使虚拟化的存储选择与 Kubernetes 本身的存储选择紧密结合。但是,有一些细微差别需要考虑。与通常使用标准文件系统的容器不同,块设备对于虚拟机更有效。
尽管 Kubernetes 中的 CSI 接口允许请求两种类型的卷:文件系统和块设备,但重要的是要验证您的存储后端是否支持这一点。
为虚拟机使用块设备消除了对诸如文件系统之类的额外抽象层的需求,这使其性能更高,并且在大多数情况下可以使用 ReadWriteMany 模式。此模式允许多个节点同时访问卷,这是在 KubeVirt 中启用虚拟机实时迁移的关键功能。
存储系统可以是外部的或内部的(在超融合基础设施的情况下)。在许多情况下,使用外部存储会使整个系统更加稳定,因为您的数据与计算节点分开存储。
外部存储解决方案在企业系统中通常很受欢迎,因为此类存储通常由外部供应商提供,负责其运营。与 Kubernetes 的集成仅涉及安装在集群中的一个小组件 - CSI 驱动程序。此驱动程序负责在此存储中配置卷并将它们连接到 Kubernetes 运行的 pod。但是,此类存储解决方案也可以使用纯开源技术来实现。一种流行的解决方案是由 democratic-csi 驱动的 TrueNAS。
另一方面,超融合系统通常使用本地存储(当您不需要复制时)和软件定义存储来实现,这些存储通常直接安装在 Kubernetes 中,例如 Rook/Ceph、OpenEBS、Longhorn、LINSTOR 等。
超融合系统有其优点。例如,数据局部性:当您的数据存储在本地时,访问此类数据的速度更快。但是,存在一些缺点,例如这种系统通常更难管理和维护。
在 Ænix,我们希望提供一种开箱即用的解决方案,该解决方案无需购买和设置额外的外部存储即可使用,并且在速度和资源利用率方面都是最佳的。LINSTOR 成为该解决方案。经过时间考验且在行业中流行的技术(例如 LVM 和 ZFS 作为后端)使我们确信数据安全存储。基于 DRBD 的复制速度非常快,并且消耗少量的计算资源。
为了在 Kubernetes 中安装 LINSTOR,有 Piraeus 项目,它已经提供了现成的块存储,可与 KubeVirt 一起使用。
Kubernetes 虚拟机的网络
尽管接口相似 - CNI,但 Kubernetes 中的网络架构实际上更加复杂,通常由许多彼此没有直接连接的独立组件组成。事实上,您可以将 Kubernetes 网络分为四个层次,如下所述。
节点网络(数据中心网络)
节点彼此互连的网络。此网络通常不由 Kubernetes 管理,但它很重要,因为没有它,任何东西都无法工作。在实践中,裸机基础设施通常具有多个此类网络,例如,一个用于节点到节点的通信,第二个用于存储复制,第三个用于外部访问等。
配置节点之间的物理网络交互超出了本文的范围,因为在大多数情况下,Kubernetes 利用的是现有网络基础设施。
Pod 网络
这是您的 CNI 插件提供的网络。CNI 插件的任务是确保集群中所有容器和节点之间的透明连接。大多数 CNI 插件实现了一个扁平网络,从中分配单独的 IP 地址块以在每个节点上使用。
在实践中,您的集群可以有多个由 Multus 管理的 CNI 插件。这种方法通常用于基于 KubeVirt 的虚拟化解决方案 - Rancher 和 OpenShift。主要的 CNI 插件用于与 Kubernetes 服务集成,而其他 CNI 插件用于实现专用网络 (VPC) 以及与数据中心物理网络的集成。
可以使用默认 CNI 插件来连接网桥或物理接口。此外,还有一些专门的插件,例如 macvtap-cni,旨在提供更高的性能。
在 Kubernetes 中运行虚拟机时要记住的一个额外方面是需要 IPAM(IP 地址管理),特别是对于 Multus 提供的辅助接口。这通常由您的基础设施中运行的 DHCP 服务器管理。此外,虚拟机的 MAC 地址分配可以由 Kubemacpool 管理。
尽管在我们的平台中,我们决定另辟蹊径并完全依赖 Kube-OVN。此 CNI 插件基于最初为 OpenStack 开发的 OVN(开放虚拟网络),它为 Kubernetes 中的虚拟机提供了完整的网络解决方案,具有用于管理 IP 和 MAC 地址的自定义资源,支持在节点之间保留 IP 地址的实时迁移,并允许为租户之间的物理网络隔离创建 VPC。
在 Kube-OVN 中,您可以为整个命名空间分配单独的子网,或者使用 Multus 将其作为额外的网络接口连接。
服务网络
除了 CNI 插件外,Kubernetes 还有一个服务网络,主要用于服务发现。与传统虚拟机相反,Kubernetes 最初设计为使用随机地址运行 pod。服务网络提供了一个方便的抽象(稳定的 IP 地址和 DNS 名称),它将始终将流量定向到正确的 pod。尽管虚拟机的 IP 通常是静态的,但在云中,此方法也常用于虚拟机。
Kubernetes 中服务网络的实现由服务网络插件处理。标准实现被称为 kube-proxy,它被用于大多数集群。但如今,此功能可能由 CNI 插件提供。最先进的实现由 Cilium 项目提供,该项目可以以 kube-proxy 替换模式运行。
Cilium 基于 eBPF 技术,该技术允许高效地卸载 Linux 网络堆栈,从而与基于 iptables 的传统方法相比,提高了性能和安全性。
在实践中,Cilium 和 Kube-OVN 可以轻松地 集成,以提供一个统一的解决方案,为虚拟机提供无缝的多租户网络,以及高级网络策略和组合的服务网络功能。
外部流量负载均衡器
在此阶段,您已经拥有在 Kubernetes 中运行虚拟机所需的一切。但实际上还有一件事。您仍然需要从集群外部访问您的服务,而外部负载均衡器将帮助您组织此操作。
对于裸机 Kubernetes 集群,有几种可用的负载均衡器:MetalLB、kube-vip、LoxiLB,此外,Cilium 和 Kube-OVN 也提供了内置实现。
外部负载均衡器的作用是提供一个外部可用的稳定地址,并将外部流量导向服务网络。服务网络插件将像往常一样将其导向您的 Pod 和虚拟机。
在大多数情况下,在裸机上设置负载均衡器是通过在集群内的节点上创建浮动 IP 地址,并使用 ARP/NDP 或 BGP 协议在外部公布它来实现的。
在探索了各种选项之后,我们认为 MetalLB 是最简单且最可靠的解决方案,尽管我们没有严格强制只使用它。
另一个好处是,在 L2 模式下,MetalLB 的 speakers 通过使用 memberlist 协议执行活跃度检查来持续检查其邻居的状态。这使得故障转移可以独立于 Kubernetes 控制平面工作。
结论
这总结了我们对 Kubernetes 中虚拟化、存储和网络的概述。这里提到的技术在 Cozystack 平台上可用并已预配置,您可以在该平台上无限制地尝试它们。
在下一篇文章中,我将详细介绍如何在此基础上,只需单击一个按钮即可实现功能齐全的 Kubernetes 集群的配置。