MicroK8S-IOT-边缘计算-全-

MicroK8S IOT 边缘计算(全)

原文:annas-archive.org/md5/897152be91ee8fde7d086e67e7455c67

译者:飞龙

协议:CC BY-NC-SA 4.0

序言

本书的创意来源于我的一位客户,他希望在其资源受限的边缘设备上实现一个最小化的容器编排引擎。部署完整的 Kubernetes 解决方案并不是最佳选择,但随后我发现了最小化的 Kubernetes 发行版,并经过多次实验后,选择了 MicroK8s 成功地为他们构建了多个边缘计算的用例和场景。

Canonical 的 MicroK8s Kubernetes 发行版小巧、轻量且完全符合标准。它是一个简约型的发行版,专注于性能和简易性。由于其小巧的体积,MicroK8s 可以轻松部署在物联网和边缘设备中。本书结束时,你将了解如何使用 MicroK8s 有效地实施以下边缘计算用例和场景:

  • 启动并运行你的 Kubernetes 集群

  • 启用核心 Kubernetes 插件,如域名系统DNS)和仪表盘

  • 在多节点 Kubernetes 集群上创建、扩展和执行滚动更新

  • 使用各种容器网络选项,如 Calico、Flannel 和 Cilium

  • 设置 MetalLB 和 Ingress 选项以实现负载均衡

  • 使用 OpenEBS 存储复制实现有状态应用

  • 配置 Kubeflow 并运行 AI/ML 用例

  • 配置与 Istio 和 Linkerd 的服务网格集成

  • 使用 Knative 和 OpenFaaS 运行无服务器应用

  • 配置日志记录和监控选项(Prometheus、Grafana、Elastic、Fluentd 和 Kibana)

  • 配置一个多节点、高可用性的 Kubernetes 集群

  • 配置 Kata 实现安全容器

  • 配置严格的隔离模式运行

根据Canonical 2022 年 Kubernetes 和云原生操作报告juju.is/cloud-native-kubernetes-usage-report-2022),48%的受访者表示,迁移到或使用 Kubernetes 和容器的最大障碍是缺乏内部能力和有限的人员。

如报告所示,存在技能缺口和知识空白,我相信本书将通过涵盖解决这些关键领域,帮助你在短时间内迅速掌握所需知识。

本书适合谁阅读

本书面向 DevOps 和云工程师、Kubernetes 站点可靠性工程师SREs)以及希望实现高效部署软件解决方案的应用开发人员。它对技术架构师和技术领导者也非常有用,尤其是那些希望采用云原生技术的人。对容器化应用设计与开发、虚拟机、网络、数据库以及编程有基本了解,将有助于你更好地从本书中受益。

本书内容

第一章Kubernetes 入门,介绍了 Kubernetes 及其各个组件,以及相关的抽象概念。

第二章介绍 MicroK8s,介绍了 MicroK8s 并展示了如何安装它,如何验证其安装状态,以及如何监控和管理 Kubernetes 集群。我们还将学习如何使用一些附加组件并部署一个示例应用程序。

第三章物联网和边缘计算基础,深入探讨了 Kubernetes、边缘计算和云计算如何协同工作,推动智能商业决策。本章概述了物联网IoT)、边缘计算及其相互关系,以及边缘计算的优势。

第四章为物联网和边缘计算处理 Kubernetes 平台,分析了 Kubernetes 如何为边缘计算提供有力的价值主张,以及展示如何将 Kubernetes 用于边缘工作负载的不同架构方法,同时支持满足企业应用需求的架构——低延迟、资源受限、数据隐私和带宽可扩展性。

第五章创建和实现更新的多节点 Raspberry Pi Kubernetes 集群,探讨了如何设置一个 MicroK8s Raspberry Pi 多节点集群,部署一个示例应用程序,并执行对已部署应用的滚动更新。我们还将了解如何扩展已部署的应用程序。我们还将讨论一些构建可扩展、安全和高度优化的 Kubernetes 集群模型的推荐实践。

第六章配置容器的连接性,探讨了在 Kubernetes 集群中如何处理网络连接。此外,我们将了解如何使用 Calico、Cilium 和 Flannel CNI 插件来为集群配置网络。我们还将讨论选择 CNI 服务时需要考虑的最重要因素。

第七章设置 MetalLB 和 Ingress 进行负载均衡,深入研究了如何使用 MetalLB 和 Ingress 技术将服务暴露到集群外部。

第八章监控基础设施和应用程序的健康状况,探讨了监控、日志记录和警报的各种选择,并提供了如何配置它们的详细步骤。我们还将了解需要关注的关键指标,以成功管理您的基础设施和应用程序。

第九章使用 Kubeflow 运行 AI/MLOps 工作负载,讲解了如何使用 Kubeflow ML 平台开发和部署一个示例 ML 模型。我们还将讨论一些在 Kubernetes 上运行 AI/ML 工作负载的最佳实践。

第十章使用 Knative 和 OpenFaaS 框架实现无服务器架构,审视了 MicroK8s 中包含的两个最流行的无服务器框架 Knative 和 OpenFaaS,这两个基于 Kubernetes 的平台用于开发、部署和管理现代无服务器工作负载。我们还将讨论开发和部署无服务器应用程序的最佳实践。

第十一章使用 OpenEBS 管理存储复制,介绍了如何使用 OpenEBS 实现存储复制,确保数据在多个节点之间同步。我们将详细介绍配置和实施一个使用 OpenEBS Jiva 存储引擎的 PostgreSQL 状态应用程序的步骤。我们还将探讨 Kubernetes 存储的最佳实践以及数据引擎的建议。

第十二章为横切关注点实现服务网格,带您完成部署 Istio 和 Linkerd 服务网格的步骤。您还将学习如何部署和运行示例应用程序,并了解如何配置和访问仪表盘。

第十三章通过 HA 集群进行组件故障恢复,向您介绍设置一个高可用集群的步骤,该集群能够承受组件故障并继续为工作负载提供服务。我们还将讨论在生产就绪集群上实施 Kubernetes 应用程序的一些最佳实践。

第十四章利用硬件虚拟化确保容器安全,探讨了如何使用 Kata Containers(一种安全的容器运行时)通过利用硬件虚拟化技术来提供更强的工作负载隔离。我们还讨论了在生产级集群上建立容器安全性的最佳实践。

第十五章为隔离容器实现严格的限制,向您展示如何安装带有严格限制选项的 MicroK8s snap,监控安装进度,并管理在 Ubuntu Core 上运行的 Kubernetes 集群。我们还将部署一个示例应用程序,并检查该应用程序是否能够在启用严格限制的 Kubernetes 集群上运行。

第十六章深入未来,探讨了 Kubernetes 和 MicroK8s 如何独特地为加速物联网(IoT)和边缘计算部署提供支持,以及塑造我们新未来的关键趋势。

关于 MicroK8s 的常见问题

为了充分利用本书

对基于容器的应用程序设计和开发、虚拟机、网络、数据库和编程的基本了解,将有助于您最大限度地利用本书 以下是构建 MicroK8s Kubernetes 集群的前提条件:

本书涵盖的软件/硬件 操作系统要求

|

  • 一张 microSD 卡(最低 4GB,推荐 8GB)

  • 一台带有 microSD 卡驱动器的计算机

  • 一台 Raspberry Pi 2、3 或 4(3 个或更多)

  • 一根 micro-USB 电源线(Pi 4 需要 USB-C)

  • 一个 Wi-Fi 网络或一根带有互联网连接的以太网线

  • (可选)一台带 HDMI 接口的显示器

  • (可选)适用于 Pi 2 和 3 的 HDMI 线和适用于 Pi 4 的 micro-HDMI 线

  • (可选)一台 USB 键盘

  • 一个 SSH 客户端,例如 PuTTY

  • 一个虚拟化管理程序,例如 Oracle VM VirtualBox 6.1,用于创建虚拟机

运行 Ubuntu 虚拟机的 Windows 或 Linux

如果您使用的是本书的数字版本,我们建议您自己输入代码,或从书中获取代码。这样做将帮助您避免因复制和粘贴代码而导致的潜在错误。

下载示例代码文件

您可以从 GitHub 下载本书的示例代码文件:github.com/PacktPublishing/IoT-Edge-Computing-with-MicroK8s。如果代码有更新,它将会在 GitHub 仓库中更新。

我们还有其他代码包,来自我们丰富的书籍和视频目录,您可以在github.com/PacktPublishing/查看。快来看看吧!

下载彩色图片

我们还提供了一份包含本书中使用的截图和图表的彩色图片的 PDF 文件。你可以在这里下载:packt.link/HprZX

使用的约定

本书中使用了多种文本约定。

文本中的代码:表示文本中的代码词语、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入以及 Twitter 用户名。例如:“要查看可用的已安装插件列表,请使用status命令。”

一块代码设置如下:

apiVersion: v1
kind: Service
metadata:
  name: metallb-load-balancer
spec:
  selector:
    app: whoami
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

所有的命令行输入或输出如下所示:

kubectl apply -f loadbalancer.yaml

粗体:表示新术语、重要词汇或屏幕上显示的词语。例如,菜单或对话框中的词语以粗体显示。举个例子:“在 Kubernetes 仪表盘中的命名空间下,导航到监控,然后点击服务。”

提示或重要说明

显示如下:

联系我们

我们欢迎读者提供反馈。

一般反馈:如果您对本书的任何内容有疑问,请通过电子邮件联系我们,邮箱地址是 customercare@packtpub.com,并在邮件主题中注明书名。

勘误表:虽然我们已尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在本书中发现错误,欢迎向我们报告。请访问www.packtpub.com/support/errata并填写表单。

盗版问题:如果您在互联网上发现我们任何形式的作品的非法副本,请您提供位置地址或网站名称。请通过发送链接到 copyright@packt.com 与我们联系。

如果你对成为作者感兴趣:如果你有专业知识,并且对撰写或贡献书籍感兴趣,请访问authors.packtpub.com

分享您的想法

一旦您阅读了使用 MicroK8s 进行 IoT 边缘计算,我们希望听到您的想法!请点击此处直达亚马逊评论页面来为本书分享您的反馈。

您的评论对我们和技术社区至关重要,并将帮助我们确保提供卓越的内容质量。

第一部分:Kubernetes 和 MicroK8s 的基础知识

在这部分中,您将介绍 MicroK8s 及其生态系统。您还将学习如何安装 MicroK8s Kubernetes 集群并使其运行起来。

本书的这部分包括以下章节:

  • 第一章开始学习 Kubernetes

  • 第二章介绍 MicroK8s

第一章:开始使用 Kubernetes

Kubernetes是一个开源容器编排引擎,自动化了容器应用程序的部署、扩展和管理。自 7 年前首次发布以来,它在短短的时间内取得了巨大的进展。它曾经需要与像 Cloud Foundry Diego、CoreOS 的 Fleet、Docker Swarm、Kontena、HashiCorp 的 Nomad、Apache Mesos、Rancher 的 Cattle、Amazon ECS 等容器编排引擎竞争并超越它们。如今,Kubernetes 正在一个完全不同的环境中运行。这表明,开发人员只需要掌握一个容器编排引擎,就能够应对 90%的容器相关工作。

Kubernetes 容器编排框架是一个适用于生产的开源平台,建立在谷歌 15 年以上运行生产工作负载的经验基础上,同时结合了社区贡献的最佳实践和理念。Kubernetes 将应用程序的容器划分为逻辑单元,以便更容易地进行管理和发现。容器(cgroups)自 2007 年初首次被包含在主线 Linux 内核中以来便存在。容器的小巧和可移植性使得它能够比虚拟机容纳更多的容器,降低了基础设施成本,并使得更多的程序能够更快地部署。然而,直到 Docker(2013 年)问世之前,由于可用性问题,它并未引起显著关注。

Docker 与标准虚拟化不同;它基于操作系统级虚拟化。容器不同于使用中介层(虚拟机监控器)在物理硬件上运行虚拟机的虚拟化,容器运行在操作系统内核之上的用户空间中。因此,容器非常轻量且快速。这可以从下图中看到:

图 1.1 – 虚拟机与容器

图 1.1 – 虚拟机与容器

Kubernetes 容器编排框架自动化了运行容器化工作负载和服务所需的大部分操作工作。这涵盖了资源配置、部署、扩展(上下)、网络、负载均衡以及软件团队必须执行的其他任务,以管理容器的生命周期。Kubernetes 为开发人员带来的一些关键好处如下:

  • 声明式应用拓扑:描述了每个服务的实现方式,以及它们对其他服务的依赖关系和资源需求。由于我们将这些数据以可执行格式呈现,我们可以在开发初期就测试应用的部署部分,并将其视为可编程的应用基础设施:

图 1.2 – 声明式应用拓扑

图 1.2 – 声明式应用拓扑

  • 声明式服务部署:一组容器的更新和回滚过程被封装,使其成为一个重复的自动化过程。

  • 动态部署应用程序:这允许根据应用程序需求、可用资源和管理策略,在集群上以可预测的顺序部署应用程序。

  • 灵活的调度器:在定义条件方面具有很大的灵活性,能够将 Pods 分配到满足这些条件的特定或一组工作节点。

  • 应用程序弹性:容器和管理平台通过多种方式帮助应用程序提高韧性,如下所示:

    • 资源消耗策略,如 CPU 和内存配额

    • 使用断路器、超时、重试等处理故障

    • 故障切换和服务发现

    • 自动扩展和自愈

  • 自服务环境:这些环境允许团队和个人实时从集群中创建隔离的环境,用于持续集成/持续交付(CI/CD)、实验和测试。

  • 服务发现、负载均衡和断路器:在不使用应用程序代理的情况下,服务可以发现并消费其他服务。这里列举的内容远不止这些。

本章将涵盖以下主要内容:

  • 容器的演变

  • Kubernetes 概述 – 理解 Kubernetes 组件

  • 理解 Pods

  • 理解部署(Deployments)

  • 理解 StatefulSets 和 DaemonSets

  • 理解作业和 CronJobs

  • 理解服务

容器的演变

容器技术是一种将应用程序打包的方式,使其能够在分离的依赖项中运行,计算机系统的隔离性已经彻底改变了今天的软件开发。在本节中,我们将探讨一些关键方面,包括这项技术的起源以及容器技术背后的背景:

图 1.3 – 容器技术简史

图 1.3 – 容器技术简史

早期的容器(带有 Unix 版本 7 的 chroot 系统),在 1970 年代开发,提供了一种隔离的环境,使服务和应用程序能够在不干扰其他进程的情况下运行,从而为程序、服务和其他进程的测试创建了沙箱。最初的概念是将容器的工作负载与生产系统的工作负载分离,使开发者能够在不干扰其他服务的情况下,在生产硬件上测试他们的应用程序和过程。随着时间的推移,容器在用户、数据、网络等方面的隔离能力不断提升。

随着 2000 年代初期 Free BSD Jails 的发布,容器技术终于得到了广泛关注。"Jails" 是计算机分区,可以在同一系统上有多个监狱/分区。这种监狱架构是在 2001 年通过 Linux VServer 开发的,它包括资源分区,并在 2005 年通过 OpenVZ 与 Linux 内核连接。监狱架构与边界分离技术合并,成为了 2004 年的 Solaris 容器。

容器技术在 2006 年引入控制组后得到了显著进展。控制组(或 cgroups)用于跟踪和隔离资源使用情况,如 CPU 和内存。它们很快被采纳,并在 2008 年的Linux 容器LXC)中得到了改进,这是当时最完整和稳定的容器技术版本,因为它在运行时不需要对 Linux 内核进行任何更改。许多新技术因为 LXC 的可靠性和稳定性而应运而生,最早的是 2011 年的 Warden,更重要的是 2013 年的 Docker。

自 2013 年以来,容器得到了广泛应用,因为许多 Linux 发行版发布了新的部署和管理工具。运行在 Linux 系统上的容器已经转变为操作系统级别的虚拟化解决方案,旨在为单个 Linux 主机提供多个隔离的 Linux 环境。Linux 容器无需拥有自己的客户操作系统;它们共享主机操作系统的内核。容器的启动速度远快于虚拟机,因为它们不需要专门的操作系统。

容器可以利用 Linux 内核技术,如命名空间、Apparmor、SELinux 配置文件、chroot 和 cgroups 来创建一个隔离的操作环境,同时 Linux 安全模块提供额外的保护,确保容器无法访问主机机器或内核。从 Linux 角度看,容器化通过允许容器在同一 CPU 架构下运行各种 Linux 发行版,提供了更大的灵活性。

Linux 容器为我们提供了一种基于各种 Linux 发行版构建容器镜像的方法,以及一个管理容器生命周期的 API。Linux 发行版还包括用于处理 API 的客户端工具,以及快照功能和支持将容器实例从一个容器主机迁移到另一个容器主机的功能。

然而,虽然在 Linux 平台上运行的容器拓宽了其适用性,但它们仍然面临一些基本的难题,包括统一管理、真正的可移植性、兼容性和扩展控制。

Apache Mesos、Google Borg 和 Facebook Tupperware 的出现,标志着容器编排和集群管理能力的显著进展,这些平台提供了不同程度的容器编排和集群管理功能,允许即时创建数百个容器,并提供支持自动故障转移等容器大规模管理所需的关键功能。然而,直到 Docker —— 容器的一种变体 —— 的出现,容器革命才真正开始。

由于 Docker 的流行,出现了多个管理平台,包括 Marathon、Kubernetes、Docker Swarm,以及更广泛的 DC/OS 环境,Mesosphere 基于 Mesos 构建了该环境来管理不仅是容器,还包括各种遗留应用程序和数据服务,例如使用 Java 编写的程序。尽管每个平台在编排和管理上的方法不同,但它们都有一个共同的目标:使容器在工作场所中更加主流。

容器技术的势头在 2017 年随着 Kubernetes 的发布而加速,Kubernetes 是一种高效的容器编排解决方案。Kubernetes 被 CNCF 采纳并获得 Docker 的支持后,成为行业标准。因此,结合 Kubernetes 和其他容器工具的使用成为了行业的标准做法。

随着 cgroups v2(Linux 版本 4.5)的发布,增加了多个新特性,包括无根容器、增强的管理功能,以及最重要的,cgroup 控制器的简化。

在过去几年中,容器的使用爆炸式增长(juju.is/cloud-native-kubernetes-usage-report-2021),不仅在新兴的 "云原生" 应用中,也在 IT 组织希望将现有遗留程序“容器化”以便更容易迁移到云端的场景中。随着云原生开发方法的成熟,容器现已成为应用交付的事实标准。

我们将在下一节中深入探讨 Kubernetes 组件。

Kubernetes 概述 – 理解 Kubernetes 组件

在本节中,我们将介绍 Kubernetes 系统的各个组件及其抽象。

下图展示了构建一个功能齐全的 Kubernetes 集群所需的各个组件:

图 1.4 – 一个 Kubernetes 系统及其抽象

图 1.4 – 一个 Kubernetes 系统及其抽象

让我们描述一下 Kubernetes 集群的组件:

  • 节点,即运行容器化工作单元的工作机器,构成了 Kubernetes 集群。每个集群至少有一个工作节点。

  • 有一个 API 层(Kubernetes API)可以与 Kubernetes 集群进行通信,用户可以通过一个名为 kubectl 的命令行界面访问该层。

在 Kubernetes 集群中有两种资源类型(如前面的图所示):

  • 控制平面,负责控制和管理集群

  • 节点,即运行应用程序的工作节点

所有在集群中的操作都由控制平面协调,包括应用程序调度、维持应用程序的预期状态、扩展应用程序以及部署新更新。

集群的节点可以是虚拟机VMs)或作为工作机器的物理计算机。Kubelet 是一个节点管理代理,它将每个节点连接到 Kubernetes 控制平面。容器管理工具,如 Docker,也应该在节点上存在。

控制平面执行命令以启动应用程序容器,每当需要在 Kubernetes 上启动应用程序时。容器由控制平面调度到集群的节点上运行。

节点通过控制平面提供的 Kubernetes API 连接到控制平面。Kubernetes API 允许最终用户直接与集群接口。主组件提供集群的控制平面功能。

API 服务器、控制器管理器和调度器是构成 Kubernetes 控制平面的三个进程。Kubernetes API 通过 API 服务器暴露,它是 Kubernetes 控制平面的前端。控制器管理器负责管理集群的控制器,这些控制器处理日常活动。调度器监视没有分配节点的新 pod,并为其分配一个节点。集群中的每个工作节点负责以下进程:

  • Kubelet:负责与 Kubernetes 主控平面进行所有通信。

  • kube-proxy:处理每个节点上的所有网络代理服务。

  • 容器运行时,例如 Docker。

控制平面组件负责做出全局集群决策(如应用程序调度),以及监控和响应集群事件。对于集群,有一个基于 Web 的 Kubernetes 仪表盘。这允许用户管理和调试基于集群的应用程序以及集群本身。Kubernetes 集群可以运行在各种平台上,包括你的笔记本电脑、云托管的虚拟机和裸机服务器。

MicroK8s 是一种简化的 Kubernetes 实现,它在本地工作站上构建一个 Kubernetes 集群,并在仅包含一个节点的小集群上部署所有 Kubernetes 服务。它可以用来在本地 Kubernetes 环境中进行实验。MicroK8s 兼容 Linux、macOS X、树莓派和 Windows,并可以用于本地 Kubernetes 设置的实验或边缘生产用例。启动、停止、查看状态和删除是 MicroK8s CLI 提供的基本引导程序,用于与集群进行交互。我们将在下一章学习如何安装 MicroK8s,检查安装状态,监控和控制 Kubernetes 集群,以及部署示例应用和附加组件。

除了图 1.4中列出的组件外,系统状态的其他对象也存在。以下是一些最基本的 Kubernetes 对象:

  • Pod

  • 部署(Deployments)

  • 有状态集(StatefulSets)和守护进程集(DaemonSets)

  • 作业(Jobs)和定时作业(CronJobs)

  • 服务(Services)

在 Kubernetes 系统中,Kubernetes 对象是持久性实体。这些实体由 Kubernetes 用来表示集群的状态。一旦创建,Kubernetes 将无限期地运行,以验证该对象是否存在。你只是通过构建一个对象来告诉 Kubernetes 框架你的集群工作负载应该是什么样子;这就是你集群的理想状态。无论是创建、更新还是删除 Kubernetes 对象,你都必须使用 Kubernetes API 与其交互。例如,当你使用 kubectl 命令行界面时,CLI 会处理所有 Kubernetes API 查询。你也可以通过使用任何客户端库,在你的应用程序中直接访问 Kubernetes API。以下图示展示了各种 Kubernetes 对象:

图 1.5 – Kubernetes 对象概述

图 1.5 – Kubernetes 对象概述

Kubernetes 提供了前述的一组对象(如 Pod、服务和控制器)来满足我们应用程序的需求并推动其架构。我们用来构建任何新服务的设计原则和设计模式,都是由这些新的原始对象和平台能力决定的。例如,部署对象是一个 Kubernetes 对象,可以表示在集群中运行的应用程序。当你构建部署时,可以指示在部署规范中应运行三个副本。Kubernetes 系统解析部署规范,并部署你所需应用的三个实例,根据需要调整其状态。如果这些实例中的任何一个因任何原因失败,Kubernetes 框架会通过纠正规范和状态之间的不一致来响应这一变化——在这种情况下,通过建立一个新的实例。

理解 Kubernetes 如何工作是至关重要的,但理解如何与 Kubernetes 进行通信同样重要。在下一节中,我们将讨论与 Kubernetes 集群交互的一些方式。

与 Kubernetes 集群交互

在本节中,我们将探讨与 Kubernetes 集群交互的不同方式。

Kubernetes 仪表盘是一个可以通过 Web 访问的用户界面。它可用于将容器化应用程序部署到 Kubernetes 集群,进行故障排除并控制集群资源。该仪表盘可用于多种用途,包括以下几项:

  • 所有节点和持久存储卷都列在 管理员 概览中,并附有每个节点的汇总指标。

  • 工作负载视图显示了按命名空间列出的所有正在运行的应用程序,以及当前 Pod 的内存利用率和在部署中当前就绪的 Pod 数量。

  • 发现视图显示了已公开并启用集群发现的服务列表。

  • 你可以通过 日志 视图深入查看属于同一个 Pod 的容器的日志。

  • 对于每个集群中的应用程序以及集群中运行的所有 Kubernetes 资源,存储视图会识别任何持久卷声明。

图 1.6 – Kubernetes 仪表盘

图 1.6 – Kubernetes 仪表盘

  • 借助 Kubernetes 命令行工具 kubectl,你可以对 Kubernetes 集群执行命令。kubectl 是一个命令行工具,用于部署应用程序、检查和管理集群资源以及查看日志。kubectl 可以安装在各种 Linux、macOS 和 Windows 平台上。

kubectl 的基本语法如下:

kubectl [command] [type] [name] [flags]

让我们更详细地查看一下commandtypenameflags

  • command:这定义了你希望在一个或多个资源上执行的操作,如creategetdeletedescribe

  • type:这定义了资源的类型,如 Pods 和 Jobs。

  • name:这定义了资源的名称。名称区分大小写。如果省略名称,则显示所有资源的详细信息;例如,kubectl get pods

  • flags:这定义了可选的标志。

在接下来的章节中,我们将更详细地了解这些 Kubernetes 对象。

理解 Pods

Pods 是在 Kubernetes 中构建和管理的最小可部署计算单元。它们由一个或多个共享存储和网络资源以及运行指令的容器组成。Pods 具有以下组件:

  • 一个专用的 IP 地址,使它们能够相互通信

  • 基于应用程序需求的持久存储卷

  • 决定容器如何运行的配置信息

以下图示展示了 Pod 的各个组件:

图 1.7 – Pod 的组件

图 1.7 – Pod 的组件

被称为工作负载资源的控制器创建 pod,并监督 pod 在集群中的部署、复制和健康状态。

最常见的控制器类型如下:

  • Jobs 适用于短生命周期的批处理任务,这些任务会执行直到完成。

  • 部署适用于无状态和持久化的应用程序,如 Web 服务器。

  • StatefulSets 适用于既有状态又持久化的应用程序,如数据库。

这些控制器使用来自 pod 模板的配置信息构建 pod,并通过在部署中指定的实例数创建副本,确保运行的 pod 满足 pod 模板中提供的部署规范。

如前所述,Kubectl 命令行界面包括各种命令,允许用户构建 pods、部署它们、检查正在运行的 pod 状态,并删除不再需要的 pod。

以下是最常用的关于 pods 的 kubectl 命令:

  • create 命令创建 pod:

    kubectl create -f FILENAME.
    

例如,kubectl create -f ./mypod.yaml 命令将从 mypod YAML 文件创建一个新的 pod。

  • get pod/pods 命令将显示一个或多个资源的信息。可以使用相应的标签选择器来过滤信息:

    kubectl get pod pod1
    
  • delete 命令删除 pod:

    kubectl delete -f FILENAME.
    

例如,kubectl delete -f ./mypod.yaml 命令将从集群中删除 mypod pod。

由此我们了解到,pod 是 Kubernetes 应用程序的最小单元,由一个或多个 Linux 容器组成。在下一部分中,我们将学习部署。

理解部署

部署允许你对 pods 和 ReplicaSets 进行声明式的变更。你可以为部署提供期望的状态,部署控制器将逐步将实际状态更改为期望状态。

部署可用于创建新的 ReplicaSets,或者用新的部署替换现有部署。当新版本准备好上线时,部署可以通过使用预定义规则轻松处理升级,且无需停机。以下图示展示了一个部署的示例:

图 1.8 – 部署

图 1.8 – 部署

以下是一个部署的示例。它创建了一个 ReplicaSet 来启动三个 nginx pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-sample-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1:21
        ports:
        - containerPort: 80

在上述示例中,发生了以下情况:

  • 创建了一个名为 nginx-sample-deployment 的部署,正如 metadata.name 字段所示。

  • 此部署的镜像由 Spec.containers.image 字段设置(nginx:latest)。

  • 部署创建了三个副本 pod,如 replicas 字段所示。

最常用的关于部署的 kubectl 命令如下:

  • apply 命令创建 pod:

    kubectl apply -f FILENAME.
    

例如,kubectl apply -f ./nginx-deployment.yaml 命令将从 nginx-deployment.yaml YAML 文件创建一个新的部署。

  • get deployments 命令检查部署的状态:

    kubectl get deployments 
    

这将产生以下输出:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-sample-deployment   3/3     0            0           1s

显示以下字段:

  • NAME 表示命名空间中部署的名称。

  • READY 显示有多少副本的应用程序可用。

  • UP-TO-DATE 显示已更新到达所需状态的副本数量。

  • AVAILABLE 显示可用副本的数量。

  • AGE 表示应用程序运行的时间长度。

  • describe deployments 命令显示部署的详细信息:

    kubectl describe deployments
    
  • delete 命令删除由 apply 命令创建的部署:

    kubectl delete -f FILENAME.
    

通过这一点,我们已经了解到,部署用于定义应用程序的生命周期,包括使用哪个容器镜像、应该有多少个 Pod,以及它们如何更新。在下一节中,我们将探讨 StatefulSets 和 DaemonSets。

理解 StatefulSets 和 DaemonSets

在本节中,我们将介绍两种在 Kubernetes 上部署应用程序的不同方法:使用 StatefulSets 和 DaemonSets。

StatefulSets

StatefulSet API 对象用于处理有状态的应用程序。像部署一样,StatefulSet 处理具有相同容器规范的 Pod。不同于部署,StatefulSet 为每个 Pod 保持持久的身份。虽然这些 Pod 是基于相同的规范生成的,但它们不能相互替换:每个 Pod 都有一个独特的身份,并且这个身份会在任何重新调度过程中保留。

以下示例演示了 StatefulSet 的组成部分:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx 
  serviceName: "nginx"
  replicas: 3 
  template:
    metadata:
      labels:
        app: nginx 
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www_volume
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www_volume
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 10Gi

在前面的示例中,我们有以下内容:

  • nginx 是一个无头服务,用于控制网络域。

  • web 是一个 StatefulSet,它的规范指示将在独立的 Pod 中启动三个来自 nginx 容器的副本。

  • volumeClaimTemplates 将使用由 PersistentVolume 提供者提供的 PersistentVolumes 来提供稳定的存储。

现在,让我们继续讨论 DaemonSets。

DaemonSets

DaemonSet 确保所有(或部分)节点上都有一个副本的 Pod 在运行。随着节点被添加到集群中,Pod 会被添加到这些节点上。随着节点从集群中移除,Pod 会被清理。当你删除一个 DaemonSet 时,它产生的 Pod 也会被删除。

以下是一些关于 DaemonSets 的示例用例:

  • 在每个节点上运行一个守护进程以进行集群存储,例如 glusterdceph

  • 在每个节点上运行一个守护进程以收集日志,例如 FluentdFluentBitlogstash

  • 在每个节点上运行一个守护进程以进行监控,例如 Prometheus Node Exporter、collectd 或 Datadog agent。

以下代码展示了一个正在运行 fluent-bit Docker 镜像的 DaemonSet:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: kube-system
  labels:
    k8s-app: fluent-bit
spec:
  selector:
    matchLabels:
      name: fluent-bit
  template:
    metadata:
      labels:
        name: fluent-bit
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:latest
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi

在前面的示例中,fluent-bit DaemonSet 的规范告诉 fluent-bit 在所有节点上运行。

关于 DaemonSets,最常用的 kubectl 命令如下:

  • createapply 命令创建 DaemonSet:

    kubectl apply -f FILENAME.
    

例如,kubectl apply -f ./daemonset-deployment.yaml 命令将从 daemonset-deployment.yaml YAML 文件创建一个新的 DaemonSet。

  • get daemonset 命令用于监控 DaemonSet 的状态:

    kubectl get daemonset 
    

这将产生以下输出:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
daemonset-deployment   3/3     0            0           1s

显示以下字段:

  • NAME 表示命名空间中 DaemonSets 的名称。

  • READY 显示应用程序可用的副本数量。

  • UP-TO-DATE 显示已更新的副本数,以实现所需状态。

  • AVAILABLE 显示应用程序可用的副本数量。

  • AGE 表示应用程序运行的时间长度。

  • describe daemonset 命令显示 DaemonSets 的详细信息:

    kubectl describe daemonset
    
  • delete 命令删除由 apply 命令创建的部署:

    kubectl delete <<daemonset>>
    

这样,我们已经学到,DaemonSet 确保所有或一组节点都运行一个 Pod 副本,而 StatefulSet 用于管理有状态应用程序。在下一节中,我们将学习作业和 CronJobs。

理解作业和 CronJobs

在本节中,我们将学习如何使用 Kubernetes 作业构建执行特定任务的临时 Pod。CronJobs 类似于作业,但它们根据设定的时间表运行任务。

作业

一个作业启动一个或多个 Pod,并继续尝试执行它们,直到特定数量的 Pod 成功。作业会跟踪成功完成的 Pod 数量。当满足成功完成的数量时,任务(即作业)完成。

当您删除作业时,它也会删除所有由其创建的 Pod。暂停作业会导致所有当前的 Pod 被删除,直到作业恢复。以下代码显示了一个每分钟运行的作业配置,并打印 example Job Pod is Running 作为其输出:

apiVersion: batch/v1
kind: Job
metadata:
  name: example-job
spec:
 template:
    spec:
      containers:
      - name: example-job
        image: busybox
        command: ['echo', 'echo example Job Pod is Running']
      restartPolicy: OnFailure
      backoffLimit: 4

关于作业的最常用 kubectl 命令如下:

  • createapply 命令创建 Pod:

    kubectl apply -f FILENAME.
    

例如,kubectl apply -f ./jobs-deployment.yaml 命令将从 jobs-deployment.yaml YAML 文件创建新的作业。

  • describe jobs 命令显示作业的详细信息:

    kubectl describe jobs <<job name>>
    

CronJob

CronJob 是一个定期创建的作业。它相当于 crontab(cron 表格)文件中的一行。它定期执行以 Cron 格式编写的作业。

CronJobs 用于自动化常见的过程,如备份和报告生成。您可以通过将每个作业设置为无限次重复(例如每天、每周或每月一次)来决定何时开始工作。

以下是一个 CronJob 示例,它每分钟打印 example-cronjob Pod is Running 输出:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: example-cronjob
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: example-cronjob
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo example-cronjob Pod is Running ; sleep 5
          restartPolicy: OnFailure

在这里,schedule: /1 * 表示在 Linux 系统中使用的是 crontab 语法。

作业和 CronJobs 是 Kubernetes 的关键组件,特别用于执行批处理过程和其他关键的临时任务。我们将在下一节中讨论服务抽象。

理解服务

在 Kubernetes 中,带有 app=exampleApp 标签的 9876

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: exampleApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9876

在前面的示例中,创建了一个名为 example-service 的新 Service 对象,将 TCP 端口 9876 路由到具有 app=exampleApp 标签的任何 pod。Kubernetes 会为该服务分配一个 IP 地址,供服务代理使用。简单来说,Kubernetes 服务将一组 pods 连接到一个抽象的服务名称和 IP 地址。服务提供了 pods 之间的发现和路由。例如,服务将应用程序的前端与其后端连接,这两个部分部署在不同的集群部署中。服务通过标签和选择器来匹配与其他应用程序的 pods。

Kubernetes 服务的核心属性如下:

  • 定位 pods 的标签选择器

  • 集群的 IP 地址和分配的端口号

  • 端口定义

  • (可选)将入站端口映射到 targetPort

Kubernetes 会自动分配一个集群 IP 地址,供服务代理路由流量使用。选择器的控制器将检查与定义的标签匹配的 pods。一些应用程序需要通过服务暴露多个端口。Kubernetes 通过使用多端口服务来简化这一点,用户可以在单个服务对象中定义多个端口。

在以下示例中,我们已将端口 80443 暴露到目标端口 80808090,以便使用 app=webserver-nginx-multiport-example 选择器将 HTTP 和 HTTPS 流量路由到任何底层的 pods:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: webserver-nginx-multiport-example
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8080
    - name: https
      protocol: TCP
      port: 443
      targetPort: 8090

也可以不使用选择器来定义服务;但必须显式地使用 endpoints 对象连接服务(IP 地址、端口等)。这是因为与选择器不同,Kubernetes 不知道服务应该连接到哪些 pods,因此不会自动构建 endpoint 对象。

没有选择器的服务的一些使用案例如下:

  • 连接到不同命名空间或集群中的另一个服务

  • 与外部服务的通信、数据迁移、测试服务、部署等

让我们创建一个包含三个副本的 Apache Web 服务器部署:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: apache-deployment
  labels:
    app: webserver
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      labels:
        app: webserver
    spec:
      containers: 
      - name: apache
        image: httpd:latest
        ports:
        - containerPort: 80

使用以下命令创建部署:

kubectl apply -f apache-deployment.yaml

以下是最常见的服务类型:

  • ClusterIP:这是默认类型,并通过集群的内部 IP 地址暴露服务。这些服务仅在集群内可访问。因此,用户需要实现端口转发或代理,以将 ClusterIP 暴露给更广泛的流量入口。

  • NodeIP:NodePort> 允许用户从外部与服务进行通信。

  • LoadBalancer:这是将集群暴露到更广泛互联网的首选解决方案。LoadBalancer 类型的服务会创建一个负载均衡器(负载均衡器的类型取决于云提供商),并将服务暴露到外部。它还会自动创建 ClusterIP 和 NodePort 服务,并相应地路由流量。

  • externalName ex.sampleapp.test.com 字段通过返回 CNAME 记录的值来实现。

总结

总结来说,Kubernetes 是一个容器编排系统,它维护一个高度可用的机器集群,这些机器作为一个整体协同工作。在本章中,我们发现 Kubernetes 支持多种抽象,使得容器化应用能够部署到集群中,而不受限于特定的机器。我们还了解了 pods 代表了集群中一组正在运行的容器。部署(Deployment)非常适合在集群上管理无状态的应用工作负载。StatefulSets 可以用于运行一个或多个连接的 pods 来管理有状态应用,而 DaemonSets 则指定 pods 并提供节点特定的功能。最后,jobs 和 CronJobs 负责批处理和其他关键的临时任务。简而言之,Kubernetes 是一个便携、可扩展、自愈的容器编排系统。

在下一章,我们将了解被称为 MicroK8s 的轻量级 Kubernetes 引擎,它可以运行在边缘计算、物联网和设备上。MicroK8s 同样非常适合离线原型设计、测试和开发。

第二章:介绍 MicroK8s

Kubernetes 是全球最流行的容器应用编排技术,能够自动化它们的部署和扩展,同时简化维护工作。Kubernetes 自身也有一套复杂性。那么,组织如何利用容器化来应对复杂性,同时避免增加 Kubernetes 的复杂性呢?

Canonical 的 MicroK8s 是一个强大的云原生计算基金会认证的 Kubernetes 发行版。以下是它成为强大企业计算平台的一些关键原因:

  • 以 snap 包的形式交付:这些是桌面、云甚至 物联网 (IoT) 设备的应用包,易于安装并通过自动更新进行安全保护,支持安装在任何支持 snap 的 Linux 发行版上。

  • 严格的隔离:确保与底层操作系统完全隔离,提供一个适合生产环境的高度安全的 Kubernetes 环境。

  • 生产级附加组件:例如 Istio、Knative、CoreDNS、Prometheus、Jaeger、Linkerd、Cilium 和 Helm 等附加组件可用。它们易于设置,只需要几行命令。为了更好的 人工智能 (AI) 和 机器学习 (ML) 能力,Kubeflow 也作为 MicroK8s 的附加组件提供。

MicroK8s 通过降低复杂性来加速 Kubernetes 部署。将设备视为分布式容器化程序,使开发人员能够专注于应用而非基础设施,从而使运维团队的工作变得更加轻松。

MicroK8s 使你能够将多个 Kubernetes 安装合并为一个集群,并在一个或多个节点上分配工作负载。在本章中,我们将介绍以下主要内容:

  • 介绍 MicroK8s Kubernetes

  • 快速安装

  • 部署示例应用

  • 启用附加组件

  • 启动/停止 MicroK8s

  • 配置 MicroK8s 使用本地镜像

  • 配置 MicroK8s 使用内置镜像仓库

  • 配置 MicroK8s 使用私有/公共镜像仓库

  • 配置 MicroK8s 服务

  • 排查应用和集群问题

介绍 MicroK8s Kubernetes

MicroK8s 是一个生产级的 Kubernetes 发行版,既强大又轻量,且可靠。它是面向企业的 Kubernetes 发行版,具有较小的内存和磁盘占用,同时预安装了如 Istio、Knative、Grafana、Cilium 等附加组件。无论你是在运行生产环境,还是刚刚开始使用 Kubernetes,MicroK8s 都能满足你的需求。

任何曾尝试使用 Kubernetes 的人都知道,要使其部署起来非常困难。市场上有其他极简主义解决方案,它们减少了部署时间和复杂性,但代价是缺少关键的可扩展性和附加组件。

MicroK8s 让你在 60 秒内就能开始使用 Kubernetes,所以你无需浪费太多时间来克服障碍。

一些关键功能如下:

  • 最小:对于笔记本电脑和工作站开发,开发者需要最小的 Kubernetes 解决方案。在 Ubuntu 上运行时,MicroK8s 是一个自包含的 Kubernetes 集群,可以与 Azure AKS、Amazon EKS 和 Google GKE 一起使用。

  • 简单:通过使用单一安装包,降低管理和操作成本。所有附加组件和依赖项都已包括在内。

  • 安全:所有安全漏洞的更新都可用,可以立即应用或根据你的维护周期进行调度。

  • 最新:MicroK8s 跟进上游 Kubernetes,发布 Beta、RC 和最终版本时与上游 Kubernetes 同步发布。

  • 完整:MicroK8s 已包含一组精选的清单,适用于常见的 Kubernetes 功能和服务:

a. 自动更新到最新的 Kubernetes 版本

b. 服务网格:Istio 和 Linkerd

c. 无服务器:Knative 和 OpenFaaS

d. 监控:Fluentd、Prometheus 和 Grafana,度量

e. 入口、DNS、仪表板和集群

f. 用于 AI/ML 的 GPU 绑定

g. Cilium、Helm 和 Kubeflow

现在我们知道了 MicroK8s 是什么,接下来我们来看看如何轻松开始使用它。

快速安装

MicroK8s 通过 snaps 部署。Snaps 是容器化的软件包(类似于 Docker),易于创建和安装;它们捆绑了依赖项,并且可以在所有主要 Linux 系统上无修改地运行。Snaps 会自动更新,且运行安全。同时,请记住,MicroK8s snap 会频繁更新,以跟上 Kubernetes 的发布。

在接下来的部分,我们将指导你完成一个最小安装,并在介绍过程中逐步进行。

技术要求

对于最小安装,您需要以下内容:

  • 你应该有一个 Linux 发行版,如 Ubuntu(20.04 LTS、18.04 LTS 或 16.04 LTS)环境来执行命令,或者任何支持 snapd 的其他操作系统。

  • 推荐 4 GB 内存和 20 GB 磁盘空间。

步骤 1 – 安装

在接下来的步骤中,我们将安装一个 MicroK8s 集群。我们将安装一组有限的组件,如 api-servercontroller-managerschedulerkubeletcnikube-proxy

可以使用以下命令安装 MicroK8s snap:

sudo snap install microk8s --classic

以下命令执行输出确认 MicroK8s 已成功安装:

图 2.1 – MicroK8s 安装

图 2.1 – MicroK8s 安装

当前,我使用的是托管在云端的 Ubuntu 虚拟机,但 MicroK8s 也可以安装在 Windows、macOS 和 Raspberry Pi 的 ARM 硬件上。对于其他平台,请参考以下链接:https//microk8s.io/docs/install-alternatives

重要提示

你还可以在安装 MicroK8s 时指定一个频道。指定的频道由两部分组成——track风险级别。例如,要安装带有stable风险级别的 MicroK8s v1.20,执行以下操作:

sudo snap install microk8s --classic --channel=1.20/stable

当 MicroK8s 团队确定一个版本(edgecandidate)已准备就绪时,你的集群将更新为stable风险级别,表示没有用户在更危险的分支上运行相同版本时发现任何错误。

为了访问 Kubernetes,MicroK8s 包含它自己的kubectl版本。它可用于执行监控和控制 Kubernetes 集群的命令。MicroK8s 添加了microk8s.kubectl命令,以避免与现有的kubectl发生冲突并覆盖任何现有的 Kubernetes 配置文件。如果你只使用 MicroK8s,可以考虑使用以下命令创建一个别名:

sudo snap alias microk8s.kubectl kubectl

以下命令执行输出确认别名已成功添加:

图 2.2 – kubectl – 添加别名

图 2.2 – kubectl – 添加别名

到此为止,你已经安装了 MicroK8s。在接下来的步骤中,我们将验证安装是否成功。

步骤 2 – 验证安装

接下来,使用以下命令检查新部署的节点是否处于Ready状态:

kubectl get nodes

以下是命令执行输出:

图 2.3 – 验证安装

图 2.3 – 验证安装

如果你看到图 2.3中显示的错误,说明 MicroK8s 没有足够的权限。MicroK8s 会创建一个组,以便更容易使用需要管理员权限的命令。为了通过将当前用户添加到该组来获取对.kube缓存目录的访问权限,请运行以下两个命令:

sudo usermod -a -G microk8s azureuser
sudo chown -f -R azureuser ~/.kube

以下是输出内容:

图 2.4 – 将用户添加到组中

图 2.4 – 将用户添加到组中

如果你仍然收到错误信息,说明 MicroK8s 仍在后台启动节点。等待几分钟后再试。如果安装成功,你应该看到以下输出:

图 2.5 – 验证安装

图 2.5 – 验证安装

你还可以使用kubectl describe命令来获取节点的详细信息,如下所示:

图 2.6 – 在节点上使用 describe 命令

图 2.6 – 在节点上使用 describe 命令

到此为止,你已经拥有了一个完全功能的 Kubernetes 集群。总结一下,我们已经安装了 MicroK8s 并验证了安装是否成功。接下来,我们将部署一个示例应用程序到 MicroK8s 集群上。

部署示例应用程序

我们将部署nginx Web 服务器示例应用程序。它是通过nginx Web 应用程序响应客户端请求的软件:

kubectl create deployment nginx --image=nginx

以下命令执行输出表明部署没有错误,在接下来的步骤中,我们可以验证 Pods 是否已创建:

图 2.7 – 创建部署

图 2.7 – 创建部署

检查pods状态,以验证应用是否已部署并正在运行:

kubectl get pods

以下命令执行输出表明 Pods 已创建并处于Running状态:

图 2.8 – 检查部署状态

图 2.8 – 检查部署状态

nginx应用已经成功部署,因此可以通过以下命令暴露它:

kubectl expose deployment nginx \
--port 80 \
--target-port 80 \
--type ClusterIP \
--selector=run=nginx \
--name nginx

以下命令执行输出表明nginx应用已成功暴露:

图 2.9 – 暴露部署

图 2.9 – 暴露部署

你应该会看到一个新的服务和分配的ClusterIP地址:

图 2.10 – 获取 svc 和 ClusterIP 地址

图 2.10 – 获取 svc 和 ClusterIP 地址

现在服务已暴露到外部,我们可以启动浏览器,并通过从本地机器指向外部 IP 来访问nginx应用:

图 2.11 – nginx 登录页面

图 2.11 – nginx 登录页面

恭喜!你现在已成功将nginx应用部署到一个完全功能的 Kubernetes 集群,并通过 MicroK8s 完成。这将帮助你理解 MicroK8s 如何在不到 60 秒的时间内让你快速启动。在下一节中,我们将学习附加组件以及如何启用它们。

启用附加组件

为了提供一个纯粹、轻量化的 Kubernetes 版本,MicroK8s 使用了最基本的组件。只需几次敲击键盘,附加组件,即预打包的组件,可以为你的 Kubernetes 集群提供从简单 DNS 控制到使用 Kubeflow 进行机器学习的附加功能。

首先,应该启用 DNS 附加组件,以促进服务之间的通信。存储附加组件为需要存储的程序提供主机上的目录空间。

使用以下命令可以轻松设置:

microk8s enable dns storage

命令执行输出如下:

图 2.12 – 启用 DNS 和存储附加组件

图 2.12 – 启用 DNS 和存储附加组件

有关可用的 MicroK8s 附加组件的完整列表,请参考附加组件完整列表部分。

启用附加组件后,使用以下命令检查是否可以启动所有附加服务的组件:

kubectl get all --all-namespaces

以下命令执行输出(在高亮部分)表明附加服务已经启动并且处于Running状态:

图 2.13 – 验证附加组件是否已启动

图 2.13 – 验证附加组件是否已启动

已启用的附加组件可以随时通过使用disable命令禁用:

microk8s disable dns

要检查可用和已安装插件的列表,请使用 status 命令,如下所示:

图 2.14 – 使用状态命令检查可用和已安装的插件列表

图 2.14 – 使用状态命令检查可用和已安装的插件列表

如果出现错误,MicroK8s 会为你提供故障排除工具,以检查出现了什么问题。在接下来的章节中,我们将学习如何使用故障排除工具。

插件完整列表

下表显示了撰写本文时的插件当前列表:

表 2.1 – MicroK8s 插件完整列表表 2.1 – MicroK8s 插件完整列表

表 2.1 – MicroK8s 插件完整列表

我们现在已经了解了什么是插件,并且启用了几个插件,比如 dnsstorage。我们也查看了插件的完整列表。在下一节中,我们将探讨如何启动/停止 MicroK8s 集群。

启动/停止 MicroK8s

MicroK8s 将无限期运行,除非你指示它 停止。使用这些简单的命令,你可以 停止并启动 MicroK8s。

要停止 MicroK8s 集群,请使用 microk8s stop 命令:

图 2.15 – 使用停止命令停止 MicroK8s 集群

图 2.15 – 使用停止命令停止 MicroK8s 集群

要启动 MicroK8s 集群,请使用 microk8s start 命令:

图 2.16 – 使用启动命令启动 MicroK8s 集群

图 2.16 – 使用启动命令启动 MicroK8s 集群

如果 MicroK8s 在运行中,它会在重启后自动重新启动。如果不希望发生这种情况,只需在关闭计算机之前运行microk8s stop命令。

我们已经了解了如何启动和停止 MicroK8s 集群。在接下来的章节中,我们将探讨如何配置 MicroK8s 使用本地镜像。

配置 MicroK8s 使用本地镜像

Kubernetes 编排框架使用容器镜像来管理容器化应用程序。这些镜像可以在本地文件系统中,也可以从远程仓库下载。最流行的容器工具是 Docker。下图介绍了 Docker 的作用:

图 2.17 – Docker 的作用

图 2.17 – Docker 的作用

假设我们已经在本地 Docker 镜像仓库中构建并提供了一个容器镜像。例如,在这里,我有一个 nginx1.21 镜像在本地 Docker 镜像仓库中:

图 2.18 – 来自本地 Docker 仓库的 Docker 镜像

图 2.18 – 来自本地 Docker 仓库的 Docker 镜像

nginx1.21本地镜像仅被 Docker 识别,MicroK8s Kubernetes 将不会意识到该镜像的存在。这是因为 MicroK8s Kubernetes 集群不包含您的本地 Docker 守护程序。我们可以通过以下命令将 Docker 镜像推送到 MicroK8s 镜像缓存中,从本地 Docker 守护程序导出它:

图 2.19 – 推送 Docker 镜像

图 2.19 – 推送 Docker 镜像

现在我们已将镜像导入到 MicroK8s 镜像缓存中,可以使用以下命令确认镜像是否存在:

microk8s ctr images ls

下面的命令执行输出显示我们的nginx1.21镜像在 MicroK8s 镜像缓存中可用:

图 2.20 – containerd 镜像列表

图 2.20 – containerd 镜像列表

现在我们有了镜像,可以使用microk8s kubectl apply -f <file>命令将其部署到 MicroK8s Kubernetes。

在这里,我创建了带有部署说明的nginx.local文件:

图 2.21 – 带有部署说明的 nginx.local 文件

图 2.21 – 带有部署说明的 nginx.local 文件

现在我们可以使用kubectl apply命令部署:

图 2.22 – 使用 apply 命令创建部署

图 2.22 – 使用 apply 命令创建部署

创建部署后,使用kubectl get deployment命令检查部署状态:

图 2.23 – 检查部署状态

图 2.23 – 检查部署状态

显示以下字段:

  • NAME表示命名空间中部署的名称。

  • READY表示应用程序的副本已准备就绪供用户使用。

  • UP-TO-DATE表示已更新以达到所需状态的副本数量。

  • AVAILABLE表示用户可用的副本数量。

  • AGE表示应用程序运行的时间。

Kubernetes 将会假装在docker.io的 Docker Hub 注册表中有一张镜像,它已经有了一个缓存副本。每当镜像发生变化时,可以重复此过程。

我们已经学习了如何使用本地构建的镜像而无需注册表。接下来,我们将学习如何使用 MicroK8s 内置的注册表来管理镜像。

配置 MicroK8s 以使用其内置注册表

通过减少上传和下载 Docker 镜像的时间,拥有私有 Docker 注册表可以帮助您提高生产效率。MicroK8s 附带的注册表托管在 Kubernetes 集群内,并作为本地主机端口32000上的NodePort服务访问。

重要提示

请注意,此注册表未经安全保护,需要额外步骤来限制外部访问(例如生产环境中)。

第一步是使用以下命令启用内建注册中心:

图 2.24 – 启用注册中心

图 2.24 – 启用注册中心

如你所见,注册中心插件已设置了一个 40 Gi 持久化存储卷声明用于存储镜像。请注意,存储插件也已启用,并与注册中心一同启用存储声明。

现在我们已设置好注册中心,下一步是标记镜像并将其推送到内建注册中心:

图 2.25 – 标记 Docker 镜像

图 2.25 – 标记 Docker 镜像

将标记后的镜像推送到内建镜像,如下所示:

图 2.26 – 推送标记后的镜像

图 2.26 – 推送标记后的镜像

重要提示

在某些 Docker 版本中,推送到此不安全的注册中心可能会失败,除非守护进程特别设置为信任它。

/etc/docker/daemon.json and restart the Docker daemon:

{

"insecure-registries" : ["localhost:32000"]

}

使用docker images命令检查镜像是否已标记:

图 2.27 – 检查图片是否被标记

图 2.27 – 检查图片是否被标记

现在我们有了镜像,可以使用kubectl apply -f <file>命令将其部署到 MicroK8s Kubernetes。

在这里,我已经创建了包含部署指令的nginx.builtin文件:

图 2.28 – 带有部署指令的 nginx.builtin 文件

图 2.28 – 带有部署指令的 nginx.builtin 文件

到此为止,我们已准备好使用kubectl apply命令进行部署:

图 2.29 – 使用 apply 命令创建部署

图 2.29 – 使用 apply 命令创建部署

创建部署时,使用kubectl get deployment命令检查部署状态:

图 2.30 – 使用 kubectl get deployment 命令检查部署状态

图 2.30 – 使用 kubectl get deployment 命令检查部署状态

Kubernetes 将从内建的注册中心拉取镜像。如果镜像有变更,构建推送镜像到内建注册中心可以重复进行,从而传播更新。如果你有专门的机器托管注册中心节点,你需要修改配置文件以指向该节点的 IP 地址。

在下一节中,我们将详细介绍如何配置 MicroK8s 从任何公共或私有注册中心拉取镜像的步骤。

配置 MicroK8s 使用私有/公共注册中心

MicroK8s 也可以从私有公共注册中心拉取镜像,但在能够拉取容器镜像之前,MicroK8s Kubernetes 必须知道注册中心的端点。

假设有一个10.131.231.155,我们构建的镜像需要使用IP 地址:端口/镜像:标签的注册中心端点语法进行标记,如下所示:

docker build . -t 10.131.231.155:32000/nginx1.21:registry

重要提示

/etc/docker/daemon.json and restart the Docker daemon:

{

"insecure-registries" : ["10.131.231.155:32000"]

}

要将其推送到 hub.docker.com,使用 docker login 命令登录并推送标记为 docker-hub-username/image-name:tag 的镜像:

docker tag 87a94228f133 10.131.231.155:32000/nginx1.21:registry

一旦镜像被标记,将带标记的镜像推送到私有或公共注册表:

docker push 10.131.231.155:32000/nginx1.21:registry

现在我们已经有了镜像,可以像之前一样使用kubectl apply -f <file>命令来部署。在这里,我创建了一个包含部署指令的文件:

图 2.31 – 包含指令的部署文件

图 2.31 – 包含指令的部署文件

创建部署后,使用kubectl get deployment命令检查部署状态。

重要提示

生产环境中,应该使用私有安全注册表,这种注册表更加安全,并限制特定用户/应用程序的访问。推荐的方法是通过 Docker 登录凭证创建一个密钥,并使用此密钥访问安全注册表。

总结一下,我们看了如何使用本地镜像或从公共或私有注册表获取的镜像来设置 MicroK8s。在下一节中,我们将学习如何配置 MicroK8s 的各种服务或组件。

配置 MicroK8s 服务

MicroK8s 由多个服务或组件构成,这些服务由多个系统守护进程管理。这些服务的配置文件存储在 $SNAP_DATA 目录下,通常指向 /var/snap/microk8s/current

要重新配置服务,我们需要编辑相应的文件,然后重新启动相应的守护进程。下表展示了 MicroK8s 将要运行的系统守护进程服务

表 2.2 – MicroK8s 系统守护进程服务列表表 2.2 – MicroK8s 系统守护进程服务列表

表 2.2 – MicroK8s 系统守护进程服务列表

在下一节中,我们将探讨如何排查应用程序和集群层级的问题。

故障排除应用程序和集群问题

需要特别注意,可能会出现问题;这可能是 Kubernetes 组件本身的问题,也可能是 MicroK8s 组件的问题。在本节中,我们将介绍一些常见问题以及帮助您诊断问题的工具。

应用层

本节帮助用户调试部署在 Kubernetes 上但未按预期运行的应用程序。

检查Pod是排查问题的第一步。通过以下命令,您可以查看 Pod 的当前状态及其历史事件列表:

kubectl describe pods ${POD_NAME}

在以下命令执行输出中,您可以看到 kubectl describe pod 命令获取了容器的详细信息及 Pod 的配置信息(标签、资源需求等),以及容器和 Pod 的状态信息(状态、就绪情况、重启次数、事件等):

图 2.32 – 使用 kubectl describe pods 命令进行故障排除

图 2.32 – 使用 kubectl describe pods 命令进行故障排除

下表总结了根据先前描述的命令中 Pod 状态可能出现的所有问题和解决方案:

表 2.3 – 检查 Pod 状态以查找可能的问题

表 2.3 – 检查 Pod 状态以查找可能的问题

我们已经了解了如何从 Pod 状态信息推断应用程序问题;接下来,我们将在集群级别进行查看。

集群级别

要获取有关 MicroK8s 集群整体健康状况的详细信息,可以运行以下命令:

microk8s kubectl cluster-info dump

默认情况下,此命令将所有集群信息输出,用于调试和诊断集群问题。它还会输出集群中所有 Pod 的日志,日志按命名空间和 Pod 名称分成不同的目录。

Kubernetes 还会在它管理的任何资源发生变化时生成事件。事件通常包括触发事件的实体、事件类型(如NormalWarningError等)以及原因。这些数据通常存储在etcd中,并在运行kubectl events命令时提供。

这些事件提供了洞察力,帮助了解在特定实体进入某一状态时,幕后发生了什么:

microk8s kubectl get events

MicroK8s 还内置了检查工具,可以生成关于 MicroK8s 子系统及其运行机器状态的综合报告。通过运行该工具,我们可以验证系统是否正常工作,并收集所有重要数据以供故障报告使用:

sudo microk8s inspect

运行此工具并收集数据需要管理员权限:

图 2.33 – MicroK8s 检查工具

图 2.33 – MicroK8s 检查工具

我们意识到问题可能会发生;应用程序、Kubernetes 组件或 MicroK8s 组件可能会出现问题。我们已经学会了如何诊断问题,并掌握了一些调试工具来帮助我们解决问题。

如果您无法解决问题,并且认为是由于 MicroK8s 中的 bug 引起的,请通过以下链接向项目仓库提交问题:github.com/ubuntu/microk8s/issues/

总结

总结来说,本章我们学习了如何安装 MicroK8s、检查安装进度,并且监控和控制 Kubernetes 集群。我们还学习了如何安装示例应用程序并使用一些附加组件。

我们还学习了如何使用 MicroK8s 与本地容器镜像以及从公共和私有注册中心获取的镜像。我们还研究了生成关于 MicroK8s 及其运行系统完整报告的检查工具,并了解了常见问题以帮助解决最常见的故障。

边缘计算的关键概念将在下一章介绍。我们还将探讨在开发边缘架构时需要注意的一些事项。

第二部分:Kubernetes 作为物联网和边缘计算的首选平台

数据量持续增长,尤其是在制造业、石油和天然气、能源和交通等正在经历快速数字化转型的行业中。需要在边缘管理这种数据爆炸以及许多相关挑战,包括系统的复杂性、数据隐私、延迟问题、低带宽连接性以及在云端或数据中心存储和处理数据的成本上升。在本部分中,我们将探讨 Kubernetes、边缘和云如何协作,推动智能商业决策。

本书的这一部分包括以下章节:

  • 第三章物联网和边缘计算基础

  • 第四章处理物联网和边缘计算中的 Kubernetes 平台

第三章:物联网和边缘计算的基本要素

数据量持续增长,尤其是在制造业、石油和天然气、能源和交通等正在快速进行数字化转型的行业。这种边缘数据爆炸需要被管理。管理过程中面临诸多挑战,包括系统复杂性、数据隐私、延迟问题、带宽不足和存储与处理数据的成本上升。

边缘计算通过将计算资源尽可能靠近数据源,减少设备和服务器之间的远程连接,从而改善数据的处理、传输和交付方式。对于制造、石油天然气、能源或交通等大型行业,工业边缘计算正被部署以实时分析和管理资产端的所有数据,进行实时分析或将聚合数据传输到云端进一步处理。

边缘计算有三个主要组件,如下所述:

  • 连接性:能够连接任何工业系统并收集和标准化数据以供立即使用

  • 智能:专注于在边缘进行数据处理和分析功能,以便在数据源处进行决策并提取洞察。

  • 编排:创建、部署、管理和更新边缘应用的能力

边缘计算使企业能够更高效地在数据源附近进行操作,并减少延迟。为了做出能够提升质量和流程的快速业务决策,边缘计算解锁了实时分析功能,例如库存使用情况、资产的运行时间和停机时间、产能利用率等。

边缘计算可以实现预测性/处方性维护、基于条件的监控、整体设备效能、视觉系统、质量改进等。边缘数据还可以支持更先进的应用场景,如人工智能AI)和机器学习ML)。智能边缘正在推动重大运营和流程改进。

企业已经开始采用现代边缘平台,通过统一解决方案推动这些举措,使边缘计算、边缘分析和边缘智能三方面得以实现。Gartner 的一份最新报告指出,预计到 2028 年,制造业和自然资源行业中部署的物联网IoT)终端将达到 19 亿台。

在本章及下一章中,我们将探讨 Kubernetes、边缘计算和云计算如何协同工作,推动智能商业决策。

本章将介绍以下主要内容:

  • 什么是物联网(IoT)?

  • 什么是边缘计算?

  • 物联网和边缘计算是如何相关的?

  • 边缘计算的好处

  • 要实现边缘计算、边缘分析和边缘智能,需要什么条件?

什么是物联网(IoT)?

根据Gartnerwww.gartner.com/en/information-technology/glossary/internet-of-things),物联网是由嵌入技术的物理物体组成的网络,这些物体可以与其内部状态或外部环境进行通信、感知或互动。简而言之,物联网指的是将世界上所有设备连接到互联网的过程。

物联网设备可以涵盖从日常物品,如灯泡,到医疗资产,如医疗设备、可穿戴设备、智能设备,甚至智能城市中的交通灯等各种设备。

以下是一些物联网应用案例:

  • 虚拟现实VR)和增强现实AR

  • 智能城市

  • 工业物联网

  • 自动驾驶汽车

  • 智能温控器

  • 智能家居

  • 智能手表

典型的物联网系统在反馈循环中不断发送、接收和分析数据。人类或人工智能和机器学习可以在接近实时或较长时间段内进行分析。Gartner 估计,到 2020 年,互联网连接的物体将达到 200 亿个。即使这些物体不是通用设备,而是如喷气发动机、联网汽车甚至咖啡机等产品,情况依然如此。

数据通过物联网边缘设备进行收集和处理。物联网边缘设备具有存储和计算能力,可以在毫秒级别做出低延迟的决策并分析数据;然而,并非所有边缘设备都是物联网设备。在接下来的部分,我们将更深入地探讨这一点。

企业可以采用物联网解决方案来评估物联网边缘设备生成的数据,并利用这些信息改善商业决策并开发新的商业应用。接下来的部分,我们将探讨一个典型的物联网解决方案设计,允许你集成和协同来自各种系统、设备、应用和人类交互的数据。

物联网解决方案的关键元素

由于缺乏接口和通信标准,物联网边缘设备与企业应用之间存在差距。物联网平台充当中间件,连接这两个端点,弥合这一差距。另一方面,现代物联网平台更进一步,包含了硬件和应用层的功能。因此,物联网平台的功能集可能包括边缘数据处理或复杂数据分析技术。

连接到互联网的设备——如智能手机、数字手表和电器——通过与物联网平台安全通信开始这一过程。平台收集并分析来自各种设备的数据,然后通过应用程序将最有价值的数据发送到设备。

支持重要通信技术的 IoT 边缘设备,包括 Wi-Fi、窄带物联网NB-IoT)、Sigfox,以及消息队列遥测传输MQTT)、受限应用协议CoAP)和安全超文本传输协议HTTPS)等协议,可以直接连接到 IoT 平台。由于并非总是如此,因此需要一个抽象层,提供所需的技术和程序来在多个协议和通信技术之间进行转换,从而调解数据流。

以下图所示展示了一种典型的 IoT 平台架构——它允许你与来自多个系统、设备、应用程序和人类交互的数据进行连接与协作:

图 3.1 – IoT 平台架构

图 3.1 – IoT 平台架构

下面是每一层架构的简要描述:

  • 基础设施层包含了使平台整体功能得以实现的组件。在这里,你会找到各种计算/存储/网络解决方案、容器管理选项、数据湖、内部平台消息传递以及监控和存储解决方案。

  • 集成层主要负责接收来自连接的边缘设备的数据,进行分析,将数据分发到业务应用程序,并管理设备。

  • 安全层负责确保数据的有效性、安全性和隐私性,通过执行数据保护和权限原则,并实施补救控制措施和行动来进行保障。

  • 应用层包含使用集成层基本 IoT 功能以满足业务目标的应用程序。这里是复杂数据分析、状态监控、改造/配置和预测性维护等应用程序的存放位置。

现在我们已经了解了 IoT 平台的基本架构,让我们来看看 IoT 面临的一些挑战。近年来,来自设备的传感器生成数据量激增,这一趋势预计将持续下去。由于数据在创建后很快就失去价值,通常在毫秒级,企业将数据转化为洞察并进而采取行动的速度被认为是至关重要的。因此,通过尽可能缩短数据生成和决策或行动之间的时间,可以提高企业的敏捷性。

然而,由于数据传输速度本质上受限于光速,延迟只有通过缩短数据必须传输的距离才能减少或完全消除。由于在仅有云计算的环境中,数据需要传播数百甚至数千英里,因此在延迟至关重要的情况下,边缘计算可以被用于解决这个问题。接下来我们将探讨边缘计算如何解决这一问题。

什么是边缘计算?

边缘计算是一种分布式计算概念,其中智能被集成到边缘设备(也称为边缘节点)中,使得数据可以在接近数据采集点的地方进行实时处理和分析。在采用边缘计算时,数据不需要传输到云端或集中式的数据处理系统。

根据Gartner的估算,未来最多 55%的物联网数据将能够在数据源头进行处理,无论是在设备本身,还是通过边缘计算。的确,可扩展性可能在这一变化中发挥关键作用,因为不断增加的数据需求将必然集中在延迟问题上,降低延迟可能显著提高反应速度,从而节省时间和金钱。

以下图示展示了一个典型的边缘计算架构:

图 3.2 – 边缘计算架构

图 3.2 – 边缘计算架构

由于数据处理接近数据聚合源,边缘计算消除了将数据传输到云端或本地数据中心进行处理和分析的需求。因此,网络和服务器的压力也大大减轻。边缘计算特别适用于物联网(IoT),尤其是工业物联网IIoT),因为它能够实时处理数据,并具有更快的响应时间。

除了为工业和制造组织提供更快的数字化转型,边缘计算技术还带来了更多的突破,例如人工智能(AI)和机器学习(ML)。

让我们来看一些边缘计算的具体应用场景:

  • 视觉推理:高分辨率摄像头通常安装在边缘设备上。它们能够从这些摄像头获取视频流,并在边缘设备上进行机器学习(ML)。这种推理可以仅仅检测视野中的人,或者进行更复杂的推理。此外,一些边缘应用还能够检测某人是否佩戴了正确的口罩,是否体温过高,或者是否保持适当的社交距离。

  • 异常检测:在许多工业工厂中,边缘设备发挥着重要作用。它们能够看到和听到人类无法察觉的事物,并能够推断出问题,甚至在许多情况下比最专业的人工操作员更为精准。例如,边缘计算设备可以精确监控电动机的功率使用情况,并迅速作出反应,无需人工干预。边缘设备还能够监听复杂机械的异常声音,并在出现问题时及时通知人工操作员。

  • 环境监测:边缘设备能够检测到诸如颗粒污染或有毒气体等危险情况,并能够在毫秒级的时间内迅速采取措施进行缓解或减少风险,同时联系相关部门。

  • 多接入边缘计算MEC):由于这些计算机地理位置接近用户,电信 MEC 设施中的计算机可以提供以前仅在大型企业数据中心或公共云中才能提供的服务,但延迟大大降低。因为这些 MEC 中的边缘设备类似于大型数据中心中的服务器类机器,所以它们能够处理任何数据中心的工作负载。

现在我们已经看到了一些边缘计算的应用场景,让我们在下一节中看看物联网与边缘计算之间的关系。

物联网和边缘计算是如何相关的?

物联网从处理能力靠近物理设备或数据源中获益。为了让物联网传感器和设备收集的数据能够更快地做出反应或消除问题,这些数据必须在边缘进行分析,而不是返回到中央站点。

通过为物联网设备的数据和计算需求提供本地处理和存储源,边缘计算减少了物联网设备与其连接的中央信息技术IT)网络之间的通信延迟。

如果没有边缘计算,物联网将依赖于云或数据中心的连接和计算服务。在物联网设备与云之间来回传输数据可能会延迟响应时间并降低操作效率。

边缘计算还解决了通过缓慢的蜂窝或卫星连接传输大量数据所需的网络带宽问题,同时也提供了在网络连接丢失时,系统能够离线工作的能力。

通过使用边缘计算,可以利用连接的物联网设备生成的大量数据。通过将分析算法和机器学习模型部署到边缘,可以在本地处理数据并用来做出快速决策。数据也可以在本地聚合,然后发送到中央位置进行处理或长期存储。

在云计算模型下,计算资源和服务通常集中在大型数据中心。云平台通常提供将物联网设备连接到互联网所需的网络基础设施。边缘设备需要网络访问以实现设备与中央数据库之间的双向通信。云平台通常用于提供网络连接。从边缘设备通过云传输数据到数据中心,或者边缘设备将其决策记录传回数据中心以供数据存储、处理或大数据分析,都是云平台通信能力的示例。

许多物联网和边缘计算的应用场景源于在实时情况下需要在本地处理数据的需求,其中将数据发送到数据中心进行处理会导致不可接受的延迟。以下是一些示例:

  • 物联网传感器在制造车间中不断生成数据流,这些数据可以用于防止设备故障并优化运营。根据某些估计,一家拥有 2000 台设备的现代化工厂每月可以生成约 2200 TB太字节)的数据。与将这些大量数据发送到远程数据中心相比,更快且成本更低的是将其处理得更靠近设备。然而,通过集中数据平台连接设备仍然是可取的。例如,设备可以接收标准化的软件升级,并传输经过筛选的数据,这些数据可以帮助改进其他工厂位置的操作。

  • 另一个常见的边缘计算例子是联网汽车。计算机安装在公交车和铁路上,用于追踪乘客流量和服务交付。借助车载技术,送货司机可以确定最有效的路线。在采用边缘计算策略时,每辆车都在与车队其他车辆相同的标准化平台上运行,从而提高了服务的可靠性,并在整个车队中保持数据安全。

  • 网络功能虚拟化NFV),即在网络边缘运行的虚拟计算机,正日益受到电信运营商的欢迎。这些虚拟机VMs)可以取代昂贵的专有硬件。通过边缘计算策略,供应商可以在成千上万的远程站点上保持软件的可靠运行,并确保统一的安全标准。在移动网络中,运行在接近终端用户位置的应用程序可以减少延迟,并允许服务提供商提供新的服务。

以下图示展示了工厂车间的传感器和设备如何与云和边缘进行交互:

图 3.3 – 与物联网(IoT)、边缘计算和云交互的典型制造车间架构

图 3.3 – 与物联网(IoT)、边缘计算和云交互的典型制造车间架构

以下是每一层的简要描述:

  • 设备层:此层表示主要设备的各个组成部分或实际设备本身,并与本地运营技术和物联网能力相连,以便快速交互。此层使用云训练的机器学习(ML)模型来执行机器学习评分或推断。同时,这里还存储着大量原始设备数据。

  • 边缘连接与工厂应用层:工厂应用层提供对工厂中所有设备或设备本身的可视化和控制,而边缘连接层则允许设备和工厂应用之间的通信。

  • 云托管企业层:云托管企业层提供一个投资组合视图,主要提供对多个工厂的视图和控制。业务分析和机器学习算法是该层的一部分,用于预测并通过训练的机器学习模型提供可操作的智能。这些模型可以在该层中重新训练,利用来自所有工厂设备组合的数据,然后将其发送回边缘,最终传递到每个设备的物联网软件,从而使操作更加智能化。

边缘计算的好处

总结来说,数据可以通过边缘计算在网络边缘进行分析、处理和传输。换句话说,数据在实时分析时,离存储地点很近,没有延迟。来自物联网设备的数据可以在网络边缘进行分析,然后再发送到数据中心或云端。

这里列出了一些边缘计算的优势:

  • 高速、低延迟和极高的可靠性,能够更快地处理数据和传输内容。

  • 通过将处理、存储和应用分散在各种设备和数据中心之间,提高了安全性,使得单一故障难以导致整个网络崩溃。

  • 提供了一条更具成本效益的扩展和多功能性路径,允许企业通过将物联网设备与边缘数据中心结合来增强其计算能力。

尽管边缘计算有许多好处,但它也可能带来操作和架构上的挑战。边缘处理高度去中心化,通常包括分布广泛和/或难以访问的地点,其中包括办公室、工厂、校园、管道以及其他偏远的现场。任何给定的组织中都有成千上万的设备和数百个网关。这些边缘节点配备了固件、操作系统、虚拟化和容器以及应用程序,其中一些由制造商提供,另一些由解决方案提供商提供。这些边缘节点必须由其所有者/管理员正确管理和维护,因此需要高水平的自动化(例如备份、打补丁、更新和监控)。

需要处理的一些操作和设计挑战在这里列出:

  • 边缘节点的更新:边缘处理涉及在所有边缘节点和集群中执行和复制数据中心操作,如资源配置、更新、变更管理和监控,以及其他服务,如设备管理和更新机器学习模型。

  • 政策与实践:边缘部署分布在多个站点,相较于传统数据中心具有更高的动态性,通常不适用于传统数据中心的规章制度和流程。承担这样一个系统的运营管理是困难的。

  • 成本:虽然云提供按需扩展,并且易于修改、自动化和维护,但在边缘实现类似的功能可能会很昂贵且耗时。扩展现有的边缘部署以容纳更多的设备和边缘节点可能需要大量的硬件和软件投资,并且需要大量的时间和精力。

  • 网络安全:当云和数据中心扩展到边缘,涉及众多节点和设备时,网络攻击的表面积会显著增加。不安全的端点,如设备和边缘节点,可能被用来获取公司宝贵资产,也可能用于其他目的,如分布式拒绝服务(DDoS)攻击。确保所有边缘设备在物理上安全是一项具有挑战性且至关重要的任务。

边缘计算只是许多物联网应用场景中,如果当前存在操作技术,它是一个必要的或强制性的功能。即使只是一个网关,将云托管组件添加到物联网系统中也需要某种形式的边缘计算能力。例如,添加智能功能到当前系统架构并提供基于云的组合视图,就需要某种边缘处理能力。我们将在下一节中探讨启用边缘计算所需的条件。

启用边缘计算、边缘分析和边缘智能需要哪些条件?

为了满足现代制造、汽车或电信行业的需求,重要的是要认识到,企业在推动工厂车间创新和效率方面面临着巨大的压力。这需要一种综合方法,将新的/现有的 IT 产品与新兴方法论结合,并且目标是避免在大规模配置多个设备和应用程序时所带来的时间消耗和错误易发性。

在构建边缘架构时,必须注意以下几点:

  • 自主性和弹性:因为该解决方案要求自主性,所以不能容忍连接中断。

  • 资源限制:设备的低计算能力和小尺寸。

  • 安全挑战:数据隐私、物理设备安全性以及连接设备的网络安全。

  • 可管理性:跨越来自多个不同供应商的数千台设备管理应用程序软件。

  • 可靠性:在应用程序的构建、部署和维护中的一致性。

  • 自动化:通过高水平的自动化,提供自动化机制以在任意数量的物理或虚拟计算机上部署和维护多个分布式应用程序。

鉴于建立基于边缘的架构的困难,最好先决定是否有必要使用边缘计算。仅依赖云的解决方案可能是最佳选择。下一步是确定在边缘需要哪些能力,然后根据边缘处理可能发生在设备、网关、边缘服务器(可能跨多个层次)或微型数据中心上,选择最佳部署方案。

总结

总结来说,物联网及其生成的数据正在改变世界及我们与世界的互动方式。云计算在大多数连接消费者物联网世界中居于核心地位,这要归功于其众多优势。另一方面,物联网解决方案几乎总是会涉及边缘计算和云计算。将计算带到边缘有助于组织通过减少延迟、增加可扩展性和改善信息访问,做出更好的、更快速的决策,变得更加灵活。

在为物联网解决方案决定边缘与云计算能力的正确平衡时,重要的是要记住,边缘计算有多种形式,每种形式都有其独特的优点和缺点。运营上的重大困难和成本可能会迅速出现,因此企业在规划和实施任何物联网系统时,应该审慎考虑一系列问题。

在下一章中,我们将探讨 Kubernetes 如何为边缘计算提供一个有吸引力的价值主张,以及展示 Kubernetes 如何用于边缘工作负载的不同架构方法,并支持满足企业应用需求的架构——低延迟、资源受限、数据隐私、带宽可扩展性等。

第四章:处理物联网和边缘计算的 Kubernetes 平台

自 2014 年推出以来,Kubernetes 在数据中心和云环境中取得了显著的采纳。Kubernetes 从编排轻量级应用容器发展到管理和调度多样化的 IT 工作负载,涵盖从虚拟化网络操作到 AI/ML 和 GPU 硬件资源的各种工作负载。

Kubernetes 正迅速成为分布式系统中调度和管理工作的最受欢迎的控制平面。这些活动可能涉及在实际主机上部署虚拟机、将容器推送到边缘节点,甚至扩展控制平面以整合额外的调度器,例如无服务器环境。其可扩展性使其成为通用调度器和最受欢迎的管理平台。在本章中,我们将探讨各种部署方法,以探索 Kubernetes、边缘和云如何协作推动智能业务决策。

重申我们在上一章讨论的观点,建立边缘架构时必须注意以下考虑因素:

  • 自主性和弹性:由于解决方案需要自主性,不能允许连接中断。

  • 资源限制:低计算能力和小设备占地面积。

  • 安全挑战:数据隐私、物理设备安全以及连接设备的网络安全。

  • 可管理性:管理来自许多不同供应商的成千上万设备的应用软件。

  • 可靠性:在应用程序的构建、部署和维护中保持一致性。

  • 自动化:通过高度自动化,提供自动化机制来部署和维护多个分布式应用程序,涵盖任意数量的物理或虚拟计算机。

让我们看看满足这些标准的四种架构方法。在本章中,我们将涵盖以下主要主题:

  • 边缘计算的部署方法

  • Kubernetes 提供的主张

边缘计算的部署方法

以下方法展示了如何使用 Kubernetes 处理边缘工作负载,并支持满足企业应用需求的架构——低延迟、资源限制、数据隐私、带宽可伸缩性等。

在边缘完全部署整个 Kubernetes 集群

在这种方法中,在边缘节点之间部署完整的 Kubernetes 集群。这种解决方案更适合边缘节点容量有限且不希望为控制平面和节点消耗额外资源的情况。

最简单的生产级上游K8sMicroK8s,这是一个轻量级且专注的 Cloud Native Computing Foundation 认证的 Kubernetes 分发版,可选择在 Linux、Windows 和 macOS 上安装。

另一个示例是 Rancher 的 K3s,它是云原生计算基金会认证的 Kubernetes 发行版,专为在资源受限环境(如物联网和边缘计算部署)中运行的生产工作负载设计。

下图展示了在边缘节点上运行的最小 K3s Kubernetes 集群:

图 4.1 – K3s 架构

图 4.1 – K3s 架构

MicroK8s 和 K3s 可以安装在公有云虚拟机上,甚至可以安装在 Raspberry Pi 设备上。该架构高度优化,适用于在资源受限设备上进行无人值守的远程安装,同时保持与云原生计算基金会 Kubernetes 合规性测试的完全兼容性。

通过使 Kubernetes 易于访问且轻量化,MicroK8s 和 K3s 将 Kubernetes 引入到边缘计算层。

以下表格展示了 MicroK8s 和 K3s 的快速比较:

表格 4.1 – MicroK8s 与 K3s 的比较

表格 4.1 – MicroK8s 与 K3s 的比较

可选地,你还可以使用 Google Anthos 或 AKS 等平台,在多个集群上管理和编排容器工作负载,示例如下:

图 4.2 – 云上的 Google Anthos 和边缘的 MicroK8s

图 4.2 – 云上的 Google Anthos 和边缘的 MicroK8s

在接下来的章节中,我们将探讨如何使用 MicroK8s 实现常见的边缘计算应用。

边缘节点上的 Kubernetes 节点部署

在这种方法中,核心 Kubernetes 集群安装在云服务提供商或数据中心,而 Kubernetes 节点部署在边缘节点上。这种方式更适合边缘基础设施受限的应用场景。

KubeEdge 是一个开源应用程序,它将原生容器化应用编排和设备管理扩展到边缘主机上,便是这种方法的一个示例。KubeEdge 由两部分组成:云端和边缘。

它基于 Kubernetes,能够在云端和边缘基础设施之间实现网络、应用部署和元数据同步。开发人员还可以使用 MQTT 编写自定义逻辑,实现边缘设备间的资源受限通信。KubeEdge 可靠,并支持最常见的物联网和边缘计算应用场景。它可以在兼容的 Linux 发行版或 ARM 设备(如 Raspberry Pi)上运行。

这里展示了 KubeEdge 的架构:

图 4.3 – KubeEdge 架构

图 4.3 – KubeEdge 架构

这是 KubeEdge 不同组件的快速概览:

表格 4.2 – KubeEdge 组件

表格 4.2 – KubeEdge 组件

以下是 KubeEdge 的一些主要功能:

  • 用户可以像在云中使用常规 Kubernetes 集群一样,使用 KubeEdge 在边缘节点上编排应用、管理设备,并监控应用和设备状态。

  • 双向通信,能够与位于私有子网中的边缘节点进行通信。支持元数据和数据。

  • 即使边缘与云断开连接,它也能独立运行。每个节点的元数据是持久的,因此在节点恢复期间不需要监视列表。这使得你能够更快准备好。

  • 在边缘,资源使用得到优化。内存占用已减少到约 70MB。

  • 对于物联网和工业物联网,应用程序与设备之间的连接变得更加简化。

  • 原生支持 x86、ARMv7 和 ARMv8 架构。

  • 支持在边缘节点上运行依赖 Kubernetes API 的第三方插件和应用程序,边缘上有一个独立的 Kube-API 端点。

在边缘部署虚拟 Kubernetes 节点

在这种方式中,虚拟节点代理驻留在云端,但节点和 Pod 的抽象部署在边缘。携带容器的边缘节点通过虚拟节点代理获得指令控制。

尽管还有其他项目,微软的虚拟 Kubelet 项目是一个很好的示例,它是一个具有 Kubernetes API 扩展的 kubelet 代理。虚拟 Kubelet 是一个在远程环境中运行的 Kubernetes 代理,并将自己注册为集群节点。为了在集群上构建节点资源,代理使用 Kubernetes API。它使用污点和容忍度的概念,通过调用其本地 API 在外部环境中调度 Pods:

图 4.4 – 微软虚拟 Kubelet

图 4.4 – 微软虚拟 Kubelet

虚拟 Kubelet 与 AWS Fargate 控制平面、Azure 容器实例和 Azure IoT Edge 等提供商兼容。以下是当前提供商的列表(截至本文写作时)。

当前虚拟 Kubelet 提供商列表

在本节中,我们将介绍一些虚拟 Kubelet 提供商:

  • AWS Fargate:你的 Kubernetes 集群通过 AWS Fargate 虚拟 Kubelet 提供商连接到 AWS Fargate 集群。Fargate 集群作为一个虚拟节点,提供你选择的 CPU 和内存资源。

在虚拟节点上调度的 Pods 与在常规 Kubernetes 节点上的运行方式相同,都会在 Fargate 上执行。

  • 海军多集群调度器:海军是一个 Kubernetes 控制器系统,能够智能地在集群之间调度工作负载。它易于使用,并能与其他应用程序集成。

  • 阿里云弹性容器实例ECI):阿里云 ECI 提供商是一个适配器,它将 Kubernetes 集群连接到 ECI 服务,使 K8s 集群中的 Pod 能够在阿里云平台上运行。

  • Azure Batch:Azure Batch 在 Azure 上提供分布式 HPC 计算环境。Azure Batch 是一项服务,负责管理跨虚拟机池调度离散进程和任务。它通常用于批量处理作业,如渲染。

  • Azure 容器实例 (ACI):Azure 中的 ACI 提供了一个用于运行容器的托管环境。当使用 ACI 时,你无需担心管理底层计算基础设施,因为 Azure 会为你处理。当使用 ACI 运行容器时,你将按每秒钟收费,每个正在运行的容器都需要收费。

虚拟 Kubelet 的 ACI 提供程序将 ACI 实例配置为任何 Kubernetes 集群中的一个节点。当使用虚拟 Kubelet ACI 提供程序时,Pod 可以像调度到传统的 Kubernetes 节点一样调度到 ACI 实例上。

该设置使你能够同时受益于 Kubernetes 的功能和 ACI 的管理价值与成本节约。

  • Elotl Kip:Kip 是一个虚拟 Kubelet 提供程序,使得 Kubernetes 集群可以透明地在其云实例上启动 Pod。Kip Pod 在集群中运行,并在其中创建一个虚拟 Kubernetes 节点。

当 Pod 在虚拟 Kubelet 上调度时,Kip 为 Pod 的工作负载启动一个适当大小的云实例,并将 Pod 发送到该实例。一旦 Pod 操作完成,云实例会被终止。这些云实例被称为单元

当工作负载在 Kip 上运行时,集群规模会根据集群工作负载自然扩展,Pod 之间相互隔离,用户无需管理工作节点或将 Pod 战略性地打包到节点上。这将带来更低的云成本、提高的安全性以及更简单的操作开销。

  • Kubernetes 容器运行时接口 (CRI):CRI 提供程序的实现应视为一个最简化的实现,旨在测试虚拟 Kubelet 项目的核心与真实的 Pod 和容器之间的兼容性;换句话说,它比 MockProvider 更为广泛。

该提供程序的实现方式还可以用于在本地 Linux 基础设施上原型化新的架构特性。如果能够证明 CRI 提供程序能够在 Linux 客体中有效运行,那么可以假设该抽象也将适用于其他提供程序。

  • 华为云容器实例 (CCI):华为 CCI 虚拟 Kubelet 提供程序将 CCI 项目作为节点设置在任何 Kubernetes 集群中,包括华为云容器引擎 (CCE)。作为私有集群,CCE 提供原生 Kubernetes 应用和工具,使你能够快速构建容器运行时环境。虚拟 Kubelet 提供程序调度的 Pod 将在 CCI 中运行,充分利用 CCI 的高性能。

  • HashiCorp Nomad:通过将 Nomad 集群作为 Kubernetes 中的一个节点暴露,HashiCorp Nomad 为虚拟 Kubelet 提供程序连接了 Kubernetes 集群和 Nomad 集群。在 Kubernetes 中注册的虚拟 Nomad 节点上调度的 Pod 将作为任务在 Nomad 客户端上运行,就像它们在 Kubernetes 节点上一样,前提是你使用该提供程序。

  • Liqo:Liqo 是一个本地或托管平台,能够实现 Kubernetes 集群之间的动态和去中心化资源共享。Liqo 使得可以在远程集群上启动 Pods,而无需修改 Kubernetes 或应用程序。

使用 Liqo,您可以将 Kubernetes 集群的控制平面扩展到集群边界之外,使多集群变得原生且透明:将完整的远程集群折叠为一个虚拟本地节点,允许任务卸载和资源管理符合标准的 Kubernetes 实践。

  • OpenStack Zun:您的 Kubernetes 集群通过 OpenStack Zun 虚拟 kubelet 提供者连接到 OpenStack 云。由于每个 Pod 都在您的租户子网中分配了专用的 Neutron 端口,您的 OpenStack Pods 可以访问 OpenStack 租户网络。

  • 腾讯游戏 tensile-kube:这允许 Kubernetes 集群协作。Tensile-kube 基于 Virtual Kubelet,提供以下功能:

    • 集群资源会自动发现。

    • Pods 会实时收到变更通知,减少频繁列出操作的开销。

    • 所有 kubectl logs 和 kubectl exec 操作都受到支持。

    • 使用多调度器时,全球调度 Pods,以避免因资源碎片化导致的未调度 Pods。

    • 如果 Pods 无法在低级集群中调度,可以使用反调度器将其重新调度。

    • 支持 PV/PVC 和服务抽象。

边缘 Kubernetes 设备的部署

在这种方法中,Kubernetes 设备插件框架用于将叶设备作为资源暴露在 Kubernetes 集群中。

该技术由 微软 Akri 演示,它将多种传感器、控制器和 MCU 类叶设备作为资源暴露在 Kubernetes 集群中。Akri 项目将 Kubernetes 设备插件概念引入到边缘,那里有各种叶设备使用不同的通信协议并且可用性不稳定:

图 4.5 – Akri 架构

图 4.5 – Akri 架构

Akri 现在支持 ONVIF、Udev 和 OPC UA 发现处理程序。更多协议支持正在开发中。

如前所述,各种部署方法展示了 Kubernetes 如何用于边缘工作负载,以及支持满足企业应用需求的架构,如低延迟、资源限制、数据隐私和带宽可扩展性等。

在下一部分中,我们将探讨 Kubernetes 如何适用于运行边缘工作负载。

Kubernetes 提供的方案

就资源和工作负载控制而言,基于边缘的基础设施面临各种挑战。在短时间内,将有成千上万个边缘节点和远程边缘节点需要控制。组织的边缘架构旨在提供更多的云端自主性、安全标准和相对较低的延迟。让我们来看看 Kubernetes 在边缘领域的应用:

表 4.3 – Kubernetes 提供的建议

表 4.3 – Kubernetes 提供的建议

Kubernetes 是那些正在向数字优先企业转型的公司中的关键组成部分。根据报告,Kubernetes 目前已在 59%的数据中心部署,以提高资源效率并为软件开发周期提供灵活性。在分布式云环境中,Kubernetes 可以管理和协调容器化应用程序以及传统虚拟机。除了纯应用程序,Kubernetes 还可用于执行 AI/ML 和 GPU 工作负载。

根据 Linux 基金会的《边缘状态报告》,Kubernetes 无疑是边缘计算的首选平台,至少对于那些需要动态协调应用程序和集中管理工作负载的边缘场景而言。Kubernetes 将云原生计算软件开发的优势扩展到边缘,允许对跨分布式云环境的应用程序进行灵活和自动化的管理。

通过在边缘部署和测试 Kubernetes,企业和通信运营商可以实现高度的灵活性、可观察性和动态编排。

总结

随着企业拥抱数字化转型、工业 4.0、工业自动化、智能制造及这些举措所带来的各种先进应用场景,Kubernetes、边缘计算和云计算协作推动智能业务决策的相关性日益显现。我们探讨了一种不同的方法,展示了 Kubernetes 如何用于运行边缘工作负载。在接下来的章节中,我们将深入讨论在边缘部署整个 Kubernetes 集群的方法。其他方法超出了本书的范围。

在接下来的章节中,我们将详细介绍使用 MicroK8s 实现常见边缘计算应用的各个方面,如在多节点 Raspberry Pi 集群上运行应用程序;配置负载均衡;安装/配置不同的 CNI 插件以实现网络连接;配置日志记录、监控和告警选项;以及构建/部署 ML 模型和无服务器应用程序。

同时,我们还将探讨为有状态应用设置存储复制,实现跨领域关注点的服务网格,建立高可用集群以应对组件故障并持续提供工作负载,配置带有工作负载隔离的容器,并运行与主机系统隔离的安全容器。

第三部分:在 MicroK8s 上运行应用程序

本部分重点介绍适用于任何物联网/边缘计算应用的实现方面,如在多节点树莓派集群上运行应用程序、为网络连接安装/配置不同的 CNI 插件、配置负载均衡、配置日志记录、监控、报警选项、构建和部署机器学习模型以及无服务器应用程序。

本书的这一部分包含以下章节:

  • 第五章在多节点树莓派 Kubernetes 集群上创建和实施更新

  • 第六章为容器配置连接

  • 第七章为负载均衡设置 MetalLB 和 Ingress

  • 第八章监控基础设施和应用程序的健康状况

  • 第九章使用 Kubeflow 运行 AI/MLOps 工作负载

  • 第十章使用 Knative 和 OpenFaaS 框架实现无服务器架构

第五章:在多节点树莓派 Kubernetes 集群中创建和实施更新

正如我们在上一章中看到的,公司正在拥抱数字化转型、工业 4.0、工业自动化、智能制造以及这些举措所带来的所有先进应用场景。因此,Kubernetes、边缘计算和云计算的协同作用,推动智能商业决策的重要性变得越来越明显。Kubernetes 正稳步成为边缘计算的首选平台,并将云原生技术的优势扩展到边缘,从而实现跨分散云环境的应用灵活和自动化管理。在本章及随后的章节中,我们将探讨使用 MicroK8s Kubernetes 平台实现常见边缘计算应用的各个方面。

重申我们在上一章讨论的要点,Canonical MicroK8smicrok8s.io/)是一个功能强大的、Cloud Native Computing FoundationCNCF)认证的 Kubernetes 发行版。以下是它成为一个强大企业计算平台的一些关键原因:

  • 以 Snap 包形式交付:这些是适用于桌面、云,甚至物联网IoT)设备的应用包,安装简单,自动更新且安全,并且可以部署在任何支持 Snap 的 Linux 发行版上。

  • 严格限制:这确保了与底层操作系统OS)的完全隔离,并提供一个高度安全的 Kubernetes 生产环境。

  • 生产级附加组件:如 Istio、Knative、CoreDNS、Prometheus、Jaeger、Linkerd、Cilium 和 Helm 等附加组件均可用。这些附加组件易于设置,仅需几行命令。为了更好的人工智能AI)和机器学习ML)能力,Kubeflow 也可以作为 MicroK8s 的附加组件提供。

  • 自动化、自治、自愈的高可用性(HA):对于三个或更多节点的集群,MicroK8s 会自动启用高可用性。无需管理员干预,MicroK8s 提供具有弹性和自愈能力的高可用性。

本章将涵盖以下主要主题:

  • 使用树莓派创建一个 MicroK8s 多节点集群

  • 部署一个示例容器化应用程序

  • 使用新软件版本执行滚动更新

  • 扩展应用部署

  • 多节点集群配置指南

  • 容器生命周期管理

  • 部署和共享高可用性应用

使用树莓派创建一个 MicroK8s 多节点集群

在我们深入了解如何创建 MicroK8s 多节点集群的步骤之前,让我们回顾一下之前所讨论的 Kubernetes 关键概念,如下所示:

  1. 一个Kubernetes 集群(如图 5.1所示)将包含以下两种类型的资源:

a. 一个控制平面,用于控制集群

b. 节点—运行应用程序的工作节点

  1. 集群中的所有操作由控制平面协调,包括调度应用程序、保持应用程序的预期状态、扩展应用程序和发布新更新等。每个节点可以是虚拟机VM)或作为集群中工作机器的物理计算机。

  2. Kubernetes 控制平面是由三个进程组成的:应用程序编程接口(API)服务器控制器管理器调度器

  3. 集群中每个非控制平面节点都有一个kubelet进程,负责与 Kubernetes 控制平面通信,kube-proxy进程负责所有网络通信,并且有一个如 Docker 的容器运行时

  4. 当需要在 Kubernetes 上部署应用程序时,控制平面会发出命令启动应用程序容器。容器由控制平面调度到集群的节点上运行。

  5. 节点通过控制平面暴露的 Kubernetes API 与控制平面进行通信。

以下图所示的是典型的 Kubernetes 架构、系统和抽象:

图 5.1 – Kubernetes 系统和抽象

图 5.1 – Kubernetes 系统和抽象

现在我们已经清楚了 Kubernetes 架构、系统和抽象,接下来我们将深入了解创建 Kubernetes Raspberry Pi 多节点集群的步骤。

我们要实现的目标

在本节中,我们将列出我们要实现的步骤。通过以下步骤为 MicroK8s 安装准备 Raspberry Pi 4 板:

  1. 在每个板上安装和配置 MicroK8s

  2. 向集群中添加节点

  3. 将多个部署合并形成一个两节点集群(一个控制平面节点/一个工作节点)

我们将在这一步构建的 Raspberry Pi 集群如以下图所示:

图 5.2 – 我们要实现的目标

图 5.2 – 我们要实现的目标

现在我们知道要做什么,让我们来看一下要求。

在开始之前,以下是构建 Raspberry Pi Kubernetes 集群的先决条件:

  • 一张 microSD 卡(最低 4 GB;推荐 8 GB)

  • 一台带有 microSD 卡驱动器的计算机

  • 一块 Raspberry Pi 2、3 或 4(一个或多个)

  • 一根微型通用串行总线micro-USB)电源线(Pi 4 使用 USB-C)

  • 一张连接互联网的 Wi-Fi 网络或以太网电缆

  • (可选)带有高清多媒体接口HDMI)接口的显示器

  • (可选)Pi 2 和 3 的 HDMI 电缆,以及 Pi 4 的微 HDMI 电缆

  • (可选)USB 键盘

现在我们已经明确了要求,接下来将按照步骤说明完成整个过程。

步骤 1a将操作系统镜像安装到 SD 卡

第一步是将操作系统镜像安装到 microSD 卡中。为此,我们将使用Raspberry Pi Imager 工具,如下面的截图所示,将操作系统镜像安装到 microSD 卡,然后可以用在 Raspberry Pi 上:

图 5.3 – Raspberry Pi Imager

图 5.3 – Raspberry Pi Imager

从 Raspberry Pi 网站下载并安装Raspberry Pi Imager,在配备Secure DigitalSD)卡读卡器的计算机上运行 Raspberry Pi Imager,插入您将使用的 microSD 卡,并打开选择操作系统(CHOOSE OS)菜单。

从列出的选项中选择其他通用操作系统(Other general purpose OS),如下图所示:

图 5.4 – Raspberry Pi Imager 操作系统选项

图 5.4 – Raspberry Pi Imager 操作系统选项

从列出的选项中选择任何适用于 Raspberry Pi 2、3、3+和 4 的Ubuntu Server 64 位版本,如下图所示:

图 5.5 – 选择适用于 Raspberry Pi Imager 2/3/4 的任何 Ubuntu Server 版本

图 5.5 – 选择适用于 Raspberry Pi Imager 2/3/4 的任何 Ubuntu Server 版本

注意

MicroK8s 仅适用于 64 位 Ubuntu 镜像。

选择镜像后,打开SD 卡菜单。选择您插入的 microSD 卡。点击写入(WRITE)开始操作,Raspberry Pi Imager 将清除您的 microSD 卡数据。系统会提示您确认此过程。

该过程如下面的截图所示:

图 5.6 – Raspberry Pi Imager 写入操作

图 5.6 – Raspberry Pi Imager 写入操作

确认后,Raspberry Pi Imager 将开始将操作系统镜像写入 microSD 卡。完成需要一些时间。完成后显示的消息如下图所示:

图 5.7 – Raspberry Pi Imager:写入操作完成

图 5.7 – Raspberry Pi Imager:写入操作完成

完成后,继续配置 Wi-Fi 访问、远程访问、控制组和主机名设置。

配置 Wi-Fi 访问设置

打开文件管理器——如下面的截图所示——在 SD 卡仍然插入笔记本电脑时,查找卡上的system-boot分区。该分区包含在第一次启动时加载的初始配置文件:

图 5.8 – 配置 Wi-Fi 访问设置

图 5.8 – 配置 Wi-Fi 访问设置

修改network-config文件,加入您的 Wi-Fi 凭证。以下是 Wi-Fi 配置的示例:

wifis:
  wlan0:
    dhcp4: true
    optional: true
    access-points:
      "Karthik Home":
        password: "Karthik123"

我们已经完成了 Wi-Fi 访问的配置,并准备进入下一步。

步骤 1b配置远程访问设置

默认情况下,卡上的 system-boot 分区创建一个名为 ssh 的空文件,且没有文件扩展名。在启动过程中,如果检测到此文件,它将自动准备并设置树莓派上的 SSH。

我们已经完成了远程访问配置,准备进入下一步的控制组配置。

步骤 1c配置控制组设置

默认情况下,控制组被禁用。控制组(缩写为cgroups)是一个 Linux 内核特性,用于限制、统计和隔离一组进程的资源使用情况(中央处理单元CPU)、内存、磁盘 输入/输出I/O)、网络等)。

打开位于卡上的配置文件 /boot/firmware/cmdline.txt,并添加以下选项:

cgroup_enable=memory cgroup_memory=1

完整的命令行应该像这样:

cgroup_enable=memory cgroup_memory=1 net.ifnames=0 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

保存文件,从笔记本中取出卡片,并将其插入树莓派。在为树莓派供电之前,连接一个 HDMI 屏幕和一个 USB 键盘。打开树莓派电源,你将能够在屏幕上看到启动过程。通常启动过程会在 2 分钟内完成。

启动完成后,使用任何 SSH 客户端(例如 putty)连接到你的树莓派,并继续以下配置:

图 5.9 – PuTTY SSH 客户端

图 5.9 – PuTTY SSH 客户端

输入树莓派的 IP 地址并点击 Open 进行连接。首次连接时,系统会要求你确认连接:点击 Accept 确认。

在登录页面,输入 ubuntu 作为用户名和密码。Ubuntu 会要求你将密码更改为其他内容。之后,使用 ssh 命令和新密码重新连接。

成功!你现在已连接到运行在树莓派上的 Ubuntu Server

我们已经完成了大部分设置,准备进入下一步的主机名配置。

步骤 1d配置主机名

对于 Ubuntu 操作系统,默认情况下,主机名为 ubuntu,但由于我们需要在集群中识别不同的主机名,因此需要根据需要进行更改。对于我们正在创建的集群,我将把一个节点命名为 controlplane,其他的命名为 WorkerXX

按照以下步骤更改主机名。

putty Shell 中,输入以下命令:

sudo nano /etc/hostname

修改主机名并使用 Ctrl + X 退出 nano 编辑器。

输入 sudo reboot 以使更改生效。

恭喜!你现在已经完成了树莓派的所有配置

步骤 1a1d 必须为集群中的所有树莓派重复执行。

系统启动成功后,确保你的包已更新到最新版本,并运行以下命令:

sudo apt update
sudo apt upgrade

我们已经完成了集群中所有树莓派的设置,并准备进入下一步的 MicroK8s 安装和配置。

安装和配置 MicroK8s

SSH 连接到控制平面节点并安装 MicroK8s snap,如下所示:

sudo snap install microk8s --classic

以下命令执行输出确认 MicroK8s snap 已成功安装:

图 5.10 – MicroK8s snap 安装成功

图 5.10 – MicroK8s snap 安装成功

现在我们已经安装了 MicroK8s snap,输入 microk8s status 命令以验证其运行状态,如下所示:

图 5.11 – 验证 MicroK8s 是否正在运行

图 5.11 – 验证 MicroK8s 是否正在运行

如前面的命令执行输出所示,使用以下一组命令将用户加入 MicroK8s 组并访问 .kube 缓存目录:

sudo usermod -a -G microk8s ubuntu
sudo chown -f -R ubuntu ~/.kube

重新输入 microk8s status 命令以验证它是否正在运行。以下命令执行输出确认 MicroK8s 正在成功运行:

图 5.12 – MicroK8s 正在运行

图 5.12 – MicroK8s 正在运行

现在我们已安装了 MicroK8s,接下来的步骤是使用以下命令创建一个 kubectl 别名:

sudo snap alias microk8s.kubectl kubectl

以下命令执行输出确认别名已成功添加:

图 5.13 – kubectl 别名已成功添加

图 5.13 – kubectl 别名已成功添加

如果安装成功,你应该会看到以下输出:

图 5.14 – 验证节点是否处于 Ready 状态

图 5.14 – 验证节点是否处于 Ready 状态

在其他节点上也重复进行 MicroK8s 安装过程。

以下是 worker 节点上 MicroK8s 安装的命令执行输出:

图 5.15 – MicroK8s snap 在 worker1 节点安装成功

图 5.15 – MicroK8s snap 在 worker1 节点安装成功

以下命令执行输出确认 MicroK8s 在 worker 节点上也成功运行:

图 5.16 – 验证 MicroK8s 是否正在运行

图 5.16 – 验证 MicroK8s 是否正在运行

现在 MicroK8s 正在运行,接下来的步骤是检查 kubectl get Nodes 命令是否显示节点处于 Ready 状态,正如以下命令执行输出所示:

图 5.17 – 验证节点是否处于 Ready 状态

图 5.17 – 验证节点是否处于 Ready 状态

我们已经在所有节点上完成了 MicroK8s 的安装。接下来的步骤是将 worker 节点添加到控制平面节点。打开 putty shell 连接到控制平面节点,并运行以下命令生成连接字符串:

sudo microk8s.add-node

以下命令执行输出验证命令已成功执行,并提供连接字符串的说明:

图 5.18 – 生成连接字符串以添加节点

图 5.18 – 生成添加节点的连接字符串

如前面的命令执行输出所示,生成了格式为 <control plane_ip>:<port>/<token> 的连接字符串。

添加工作节点

现在我们有了连接字符串,可以与控制平面节点进行连接。打开 putty 终端到工作节点,并运行 join 命令将其加入集群,如下所示:

microk8s join <control plane_ip>:<port>/<token>

命令已成功执行,节点已加入集群,如下所示的输出所示:

图 5.19 – 将 worker1 节点添加到集群

图 5.19 – 将 worker1 节点添加到集群

如前面的命令执行输出所示,您应该能在几秒钟内在控制平面看到新节点。

使用以下命令验证新节点是否已添加到集群:

kubectl get nodes

以下命令执行输出显示,controlplaneworker1 是集群的一部分:

图 5.20 – 集群已准备好;controlplane 和 worker1 是集群的一部分

图 5.20 – 集群已准备好;controlplane 和 worker1 是集群的一部分

完整的集群应类似于这里所示的:

图 5.21 – 我们的集群已准备就绪

图 5.21 – 我们的集群已准备就绪

注意

如果您想从集群中移除一个节点,可以在控制平面运行以下命令:sudo microk8s remove-node <node name>。或者,您可以通过在工作节点运行 sudo microk8s.leave 来离开集群。

此时,您已经拥有一个完全功能的多节点 Kubernetes 集群。总结一下,我们已经在 Raspberry Pi 板上安装了 MicroK8s,并将多个部署加入到集群中。我们还展示了如何向集群添加节点。接下来的部分,我们将把一个示例应用程序部署到刚刚创建的 MicroK8s 集群中。

部署示例容器化应用程序

在本节中,我们将部署来自 Kubernetes 示例仓库的以下 nginx 部署到我们的多节点 MicroK8s 集群:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: "app"
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname" 

以下命令将部署前面提到的示例应用程序:

kubectl apply -f deployment.yaml

以下命令执行输出表明部署没有错误,接下来的步骤我们可以使用 describe 命令来验证:

图 5.22 – 示例应用程序部署

图 5.22 – 示例应用程序部署

以下命令执行输出显示有关部署的信息:

图 5.23 – 描述示例应用程序部署

图 5.23 – 描述示例应用程序部署

检查 Pods 的状态,以验证应用程序是否已经部署并运行,如下所示:

kubectl get pods -l app=nginx

以下命令执行输出表明 Pods 已创建并且其状态为 Running

图 5.24 – 检查 Pods 是否处于运行状态

图 5.24 – 检查 Pods 是否处于 Running 状态

让我们也使用以下命令检查 Pods 的运行位置:

kubectl get pods -l app=nginx -o wide

以下命令执行输出表明 Pods 在节点之间均匀分布:

图 5.25 – 检查 Pods 是否均匀分布

图 5.25 – 检查 Pods 是否均匀分布

很棒!我们刚刚在树莓派多节点集群上部署了我们的示例应用程序。以下是 Kubernetes 为我们完成的操作:

  • 寻找一个合适的节点来运行应用程序实例(我们有两个可用节点),并根据 podAntiAffinity 规则调度应用程序在该节点上运行。

  • podAntiAffinity 规则限制 Pod 可以调度到的节点,这些节点是基于其他 Pods 当前在该节点上运行的标签进行选择的。

  • 配置集群在需要时将实例重新调度到新的节点。

Pod 拓扑分布约束也可用于调节 Pods 在故障域(如区域、可用区、节点和集群中其他用户定义的拓扑域)中的分布。这有助于实现高可用性(HA)和资源使用效率。

总结来说,我们构建了一个 Kubernetes 树莓派集群,并利用它部署了一个示例应用程序。在接下来的步骤中,我们将对刚刚部署的应用程序执行滚动更新。

执行应用程序的滚动更新,使用新版本的软件。

Kubernetes 的滚动更新功能允许部署在零停机的情况下进行更新。它以增量的方式处理 Pods 实例的升级,并且新的 Pods 会调度到具有可用资源的节点上。

以下是滚动更新的一些关键特性:

  • 将应用程序从一个环境转移到另一个环境(通过容器镜像更新)。

  • 回滚到先前版本的应用程序。

  • 通过最小的停机时间,持续集成持续交付CI/CD)的应用程序是可以实现的。

我们将重新使用之前使用过的 nginx 示例部署。我们可以通过应用以下新的 YAML 文件来更新相同的部署。

该 YAML 文件指定将部署更新为使用 nginx 1.16.1 容器镜像,而不是 nginx 1.14.2

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # Update the version of nginx from 1.14.2 to 1.16.1
        ports:
        - containerPort: 80
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: "app"
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname"

以下命令将部署之前更新的应用程序部署,使用 nginx 1.16.1 镜像,而不是 nginx 1.14.2 镜像:

kubectl apply -f deployment-update.yaml

执行之前命令后的以下输出确认部署没有错误,接下来我们可以验证 Pod 是否已重新创建并具有新名称,并删除旧的 Pods:

图 5.26 – 更新示例应用程序部署

图 5.26 – 更新示例应用程序部署

以下命令执行输出表明 Pod 已重新创建,且其状态为 Running

图 5.27 – 更新部署的新 Pods

图 5.27 – 更新部署的新 Pods

下面是滚动更新的工作原理(参见图 5.29):

  1. 使用修订后的配置,它会创建一个新的部署。

  2. 增加/减少新旧控制器上的副本数,直到达到正确的数量。

  3. 最终,原始部署及相关 Pods 将被删除。

下面是滚动更新功能的示意图:

图 5.28 – 示例应用程序的滚动更新

图 5.28 – 示例应用程序的滚动更新

在使用RollingUpdate方法来微调更新过程时,还有两个额外的选项,如下所示:

  • maxSurge:在更新过程中,最多可以创建的 Pods 数量大于所需的 Pods 数量。

  • maxUnavailable:在升级过程中可能变得不可用的 Pods 数量。

我们在示例应用程序部署中将maxSurge设置为0maxUnavailable设置为1,这表示一次最多可以生成的新 Pods 数量是 0,而一次最多可以销毁的旧 Pods 数量是1。这种策略表明,随着新 Pods 的创建,旧 Pods 会被一个接一个地销毁。

根据您的目标,可能需要使用不同的部署策略。例如,您可能需要将更改部署到特定的环境中进行更多测试,或部署到一组用户/客户,或者您可能希望进行用户测试。

这里概述了各种 Kubernetes 部署策略:

  • 重建:在这种非常简单的部署策略中,所有旧 Pods 会同时被销毁,并被新的 Pods 替换。

  • 蓝绿部署:在蓝绿部署策略中,旧版本(绿色)和新版本(蓝色)的应用程序同时部署。当两者都启动时,消费者可能只能访问绿色部署;蓝色部署则可以供质量保证QA)团队在不同服务或通过直接端口转发进行测试自动化。

  • 金丝雀部署:金丝雀部署类似于蓝绿部署,只是它更受控制,并采用渐进交付的分阶段技术。金丝雀部署包括多种方法,包括暗启动和 A/B 测试。

  • 暗部署或 A/B 部署:一种变体的金丝雀部署是暗部署。暗部署与金丝雀部署的区别在于,暗部署处理的是前端功能,而金丝雀部署则处理的是后端功能。

总结来说,我们已经启动了一个示例应用程序,并对已部署的应用程序执行了滚动更新。接下来的部分,我们将专注于如何扩展应用程序。

扩展应用程序部署

更改 Deployment 中的副本数量可以实现部署的扩展。当 Deployment 扩展时,新的 pods 将被创建并调度到具有可用资源的节点。pods 的数量将扩展到新的目标状态。Kubernetes 也支持自动扩展 pods。也可以扩展到零,即终止给定 Deployment 中的所有 pods。

运行多个应用实例需要一种方法来在它们之间分配流量。在Services对象中内建的负载均衡器将网络流量分配到所有暴露的 Deployment 中的 pods。端点将用于持续监控运行中的 pods,确保流量只会被导向那些可用的 pods。

我们将在以下示例中使用一个新的 YAML 文件来增加 Deployment 中的 pods 数量。在这个 YAML 文件中,副本数设置为4,意味着 Deployment 应包括四个 pods:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # Update the replicas from 2 to 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

以下命令将部署前面更新过的应用部署:

kubectl apply -f deployment-scale.yaml

以下命令输出表明命令已成功运行,并且 Deployment 已配置完成:

图 5.29 – 扩展示例应用部署

图 5.29 – 扩展示例应用部署

以下命令执行输出确认部署没有错误,接下来的步骤中,我们可以验证该 Deployment 已包含四个 pods:

kubectl get pods -l app=nginx

以下命令输出表明命令已成功运行,且部署已创建新的 pods 并调度到具有可用资源的节点:

图 5.30 – 扩展后创建了新的 pods

图 5.30 – 扩展后创建了新的 pods

这是一个扩展功能的示意图:

图 5.31 – 应用部署扩展

图 5.31 – 应用部署扩展

另一种方法是使用命令行(不编辑 YAML 文件)。假设我们想将 nginx 部署的数量增加到五个。为此,运行kubectl scale命令,如下所示:

kubectl scale deployments/nginx-deployment --replicas=5

这些 pods 也可以像扩展时一样缩减。你可以在 YAML 文件中修改 replicas: 设置。

使用kubectl命令,你还可以将副本数从5缩减到4,如下所示:

kubectl scale deployments/nginx-deployment --replicas=4

要查看 pods 如何在节点间分布,使用以下命令进行检查:

kubectl get pods -o wide 

以下命令执行输出表明控制平面节点上运行着两个 pods,而在worker1上也有两个 pods 正在运行:

图 5.32 – Pod 在节点间的分布

图 5.32 – Pod 在节点间的分布

在本章中,我们展示了如何在 Raspberry Pi 上获得 MicroK8s 的功能,并将多个 Pi 加入形成一个生产级的 Kubernetes 集群。目前,MicroK8s 被广泛应用于各种场景,从开发者桌面上的单节点安装到支持计算密集型 AI 和 ML 工作负载的部署。

MicroK8s 非常适合边缘计算、物联网和设备,因为它占用资源少,并且支持 高级 RISC 机器ARM)和英特尔架构。在 Raspberry Pi 上,MicroK8s 是常见的选择。我们将在接下来的章节中探讨常见边缘计算应用的实施方面。在下一节中,我们将讨论一些为你的生产级 Kubernetes 集群实施 Kubernetes 的最佳实践。

多节点集群配置指南

在本节中,我们将介绍一些创建可扩展、安全且高度优化的 Kubernetes 集群模型的最佳实践。

集群级别的配置/设置

你可以在 Kubernetes 中定义抽象层次,例如 pods 和 services,这样你就不必担心应用程序运行的位置或它们是否有足够的资源高效运行。然而,你必须监控你的应用程序、运行它们的容器,甚至是 Kubernetes 本身,以维持最佳性能。在本节中,我们将介绍一些设置和操作 Kubernetes 集群的最佳实践。具体如下:

  • 使用最新版本:Kubernetes 会通过定期版本更新提供新功能、修复漏洞和平台升级。作为一个基本原则,你应该始终在集群中使用最新的 Kubernetes 版本。

  • 当多个团队尝试同时使用相同的集群资源时,可以使用命名空间来实现团队级别的隔离。有效使用命名空间允许你构建多个逻辑集群划分,使你能够将不同的虚拟资源分配给不同的团队。

  • 尽可能使用较小的容器镜像以加速构建过程。由于较小的攻击面,较小的镜像也更不容易受到攻击向量的影响。

  • kube-bench 是其中一款工具,用于检查 Kubernetes 是否使用 CIS Kubernetes 基准 中提供的检查项安全部署。

  • Alpha 和 beta 版本的 Kubernetes 功能仍在开发中,可能包含缺陷或问题,从而导致安全漏洞。在使用 alpha 或 beta 功能时,务必权衡其带来的好处与对安全性的潜在威胁。如果有疑问,关闭任何你不使用的功能。

  • 对于你的 Kubernetes 集群以及其他使用单点登录SSO)的开发工具,使用 OpenID Connect 认证机制,例如 Google Identity。

  • 审查日志的保留和归档策略——理想情况下,应保留 30-45 天的历史日志。

  • 应收集所有节点、控制平面和审计的日志:

a. 节点(kubelet,容器运行时)

b. 控制平面(API 服务器、调度器、控制器管理器)

c. Kubernetes 审计(所有请求到 API 服务器)

  • 使用日志聚合工具,如Amazon Web ServicesAWS)CloudWatch,Elasticsearch, Fluentd 和 KibanaEFK)堆栈,Datadog,Sumo Logic,Sysdig,Google Cloud PlatformGCP)Stackdriver 或 Azure Monitor。

  • 应该监控控制平面组件,以帮助发现集群中的问题/威胁,并减少延迟。最好使用自动监控工具,而不是手动管理警报。

总结一下,我们已经覆盖了设置和操作 Kubernetes 集群时需要遵循的一些最佳实践。在下一节中,我们将讨论与容器生命周期管理相关的最佳实践。

容器生命周期管理

Kubernetes 及其架构有效地自动化了应用容器的生命周期管理,但它们的设置和管理可能较为复杂。在本节中,我们将查看最佳实践及如何快速、轻松地在集群中实现它们:

  • 没有资源限制的容器可能会导致与其他容器的资源冲突,并且计算资源使用效率低下。使用 ResourceQuotaLimitRange 来限制资源利用:

a. 你可以使用 ResourceQuotas 来设置所有容器在某个命名空间中消耗的资源总量的限制。其他 Kubernetes 对象(如当前命名空间中的 Pod 数量)也可以设置配额。

b. 如果你担心某人可能会利用你的集群生成大量 ConfigMap,可以使用 LimitRange 来防止这种情况发生。

  • 使用 Kubernetes 的Pod 安全策略来强制执行安全配置——例如,访问主机文件系统。

  • 虽然在某些情况下可能需要特权容器,但允许容器拥有特权通常是一个安全隐患。如果没有特定的使用场景,禁用特权容器。

  • 选择无根容器。如果用户成功突破以 root 身份运行的容器化应用,他们可能能够使用相同的 root 用户访问主机。

  • 为了避免通过 setuidsetgid 二进制文件提升权限,运行容器时应禁用特权升级。

  • 启用网络策略,以便在你的集群中的 Pod 之间建立防火墙。

  • 使用开放策略代理OPA)等工具应用策略,例如,仅使用批准的基础镜像,这些镜像可以在你的集群中部署。

总结一下,我们已经讨论了一些容器生命周期管理的最佳实践。接下来的章节将讨论如何在 Kubernetes 中部署和共享高可用(HA)应用程序的指南。

部署和共享高可用(HA)应用

正如你所知道的,在 Kubernetes 中部署一个基础应用程序是轻而易举的。然而,尽可能使你的应用程序具有高可用性和容错性则意味着面临许多挑战。在本节中,我们列出了在 Kubernetes 中部署和共享高可用应用程序的一些指南,如下所示:

  • 所有容器应设置就绪探针。如果没有设置就绪探针,kubelet 代理会假设应用程序在容器启动时已经准备好接收流量。

  • 存活性和就绪性探针不应指向相同的端点,因为当应用程序表示不准备好或不存活时,kubelet 代理会将容器从服务中断开并删除。

  • 运行多个或多个实例的 pod 可以确保删除单个 pod 时不会导致停机。同时,考虑使用 Deployment、DaemonSet、ReplicaSet 或 StatefulSet 来部署 pod,而不是单独运行 pod。

  • 应该在你的部署中应用反亲和规则,以确保 pod 在集群中的所有节点上分布。

  • 你可以设置一个Pod 中断预算,以保护部署免受可能导致多个 pod 同时宕机的不可预见事件。如果该部署的最终状态导致 pod 数量少于五个,Kubernetes 将防止发生驱逐事件。

  • 使用containerSpec的资源属性来指定资源限制,限制容器可使用的 CPU 和内存量。这些设置会被调度程序考虑,以确定哪个节点最适合当前的 pod。

  • 所有资源都应定义技术、业务和安全标签。它们应应用于集群中的所有资源,包括 pod、服务、Ingress 清单和端点。

  • 容器不应将任何状态存储在本地文件系统中。任何持久化数据应保存到 pod 之外的中央位置,例如,集群化的 PersistentVolume,或者更好的是,在集群外的存储系统中。

ConfigMap 应该用于管理所有不在应用程序代码中的配置。ConfigMap 只应用于保存非敏感的设置。对于敏感信息,应该使用密钥资源(例如凭证)。敏感资源不应作为环境变量传递,而应该作为卷挂载到容器中。总之,我们已经看过创建一个可扩展、安全且高度优化的 Kubernetes 集群模型的最佳实践。

摘要

在本章中,我们学习了如何设置一个 MicroK8s Raspberry Pi 多节点集群,部署了一个示例应用程序,并对已部署的应用程序执行了滚动更新。我们还发现了扩展已部署应用程序的方法。我们还了解到,尽管 Kubernetes 允许我们定义诸如 pod 和 service 等抽象层级来帮助应用程序部署,但我们必须监控应用程序、容器、集群以及 Kubernetes 本身,以确保最佳性能。在此背景下,我们学习了构建一个可扩展、安全且高度优化的 Kubernetes 集群模型的几项推荐实践。

在下一章中,我们将探讨如何配置 Kubernetes 集群的容器网络连接。

第六章:配置容器的连接性

在上一章中,我们学习了如何设置一个 MicroK8s Raspberry Pi 多节点集群,部署示例应用程序,并对已部署的应用程序执行滚动更新。我们还弄清楚了如何扩展已部署的应用程序。我们还了解了一些设计可扩展、安全和高度优化的 Kubernetes 集群的最佳实践。在本章及接下来的章节中,我们将继续使用 MicroK8s 实现各种常见边缘计算应用程序的用例。Kubernetes 提供了多种方式来将服务暴露到外部世界。

在本章中,我们将继续下一个用例,即 MicroK8s 上的容器网络连接性。Kubernetes 网络模型中的每个 Pod 默认都会被分配一个自己的互联网协议IP)地址。因此,你无需显式地将 Pods 连接或联网,也不必担心将容器端口映射到主机端口等问题。

Kubernetes 允许你声明性地描述应用程序的部署方式、它们之间以及与 Kubernetes 控制平面的通信方式,以及客户端如何访问它们。

Kubernetes 作为一个高度模块化的开源项目,允许极高的网络实现适应性。Kubernetes 生态系统催生了许多项目,旨在使容器通信变得简单、一致和安全。一个通过插件功能简化 Kubernetes 网络的项目是容器网络接口CNI)。CNI 的主要目标是为管理员提供足够的控制来监控流量,同时减少手动配置网络配置所需的时间。

Kubernetes 对任何网络实现施加的基本标准如下:

  • 没有网络地址转换NAT)的情况下,节点上的 Pods 可以与所有其他节点上的 Pods 通信。

  • 节点的代理(如系统守护进程和kubelet)可以与该节点的所有 Pods 进行通信。

  • 在没有 NAT 的情况下,节点主机网络中的 Pods 可以与所有其他节点上的 Pods 通信。

CNI 允许 Kubernetes 提供商开发独特的网络模型,旨在为所有 Pods 提供一致和可靠的网络。CNI 插件提供命名空间隔离、流量和 IP 过滤,而这些 Kubernetes 默认并不提供。假设程序员希望使用这些高级网络功能。在这种情况下,他们必须将 CNI 插件与 CNI 一起使用,以便促进网络的构建和管理。

市面上有多种 CNI 插件。在本章中,我们将介绍一些流行的选项——如 Flannel、Calico 和 Cilium——并且我们将涵盖以下主要主题:

  • CNI 概述

  • 配置 Calico

  • 配置 Cilium

  • 配置 Flannel

  • 选择 CNI 提供商的指南

CNI 概述

在深入了解 CNI 概述之前,让我们先理解一下 Kubernetes 集群内是如何处理网络的。

当 Kubernetes 将 Pod 调度到一个节点上执行时,节点的 Linux 内核为该 Pod 生成一个网络命名空间。这个网络命名空间建立了eth0接口,并允许数据包在 Pod 内外流动。节点根网络命名空间中的相关 VIF 连接到一个 Linux 桥接,允许同一节点上 Pod 之间的通信。Pod 也可以使用相同的 VIF 将数据包发送到节点外部。

在为节点上的 Pod 保留的地址范围内,Kubernetes 为 Pod 的网络命名空间中的 VIF 分配了一个 IP 地址(Pod IP 地址)。这个地址范围是集群中 Pod 的 IP 地址范围的一个子集,你可以在构建集群时指定该范围。

容器在 Pod 中运行时使用的网络命名空间是 Pod 的网络命名空间。从容器的角度看,Pod 像是一台有一个网络接口的物理机器。这个网络接口由 Pod 中的所有容器共享。每个容器的 localhost 通过 Pod 与节点的物理网络接口(如eth0)连接。每个 Pod 默认可以不受限制地访问集群中所有节点上运行的其他 Pod,但你可以限制 Pod 之间的访问。

以下图展示了一个节点运行两个 Pod 以及 Pod 之间的网络流量:

图 6.1 – Kubernetes 网络模型:Pod 间流量的流动

图 6.1 – Kubernetes 网络模型:Pod 间流量的流动

从 Pod 3 到 Pod 6 的通信流

让我们来看一下从 Pod3 到 Pod6 的通信流,这两个 Pod 位于同一个节点上:

  1. 数据包从 Pod 3通过eth3接口离开,经过veth1234虚拟接口到达cbr0桥接接口。

  2. 数据包从veth1234离开并到达cbr0,寻找 Pod 6的地址。

  3. 数据包从cbr0离开并被重定向到veth5678

  4. 数据包从cbr0通过veth5678接口离开,并通过eth6接口到达 Pod 6的网络。

Kubernetes 定期销毁并重建 Pod。因此,必须使用具有稳定 IP 地址并能够在一组 Pod 之间启用负载均衡的服务。节点中的kube-proxy组件负责 Pod 与服务之间的通信。

从客户端 Pod 3到位于不同节点的服务器 Pod 6的流量如下图所示。Kubernetes 中的kube-proxy代理进程在每个节点上配置一个iptables规则,将流量导向正确的 Pod:

图 6.2 – Kubernetes 网络模型:不同节点上 Pod 间流量的流动

图 6.2 – Kubernetes 网络模型:不同节点上 Pod 间流量的流动

不同节点上 Pod 3 到 Pod 6 的通信流

让我们来看一下从 Pod3 到 Pod6 的通信流,这两个 Pod 位于不同的节点上:

  1. 数据包从 Pod 3通过eth3接口离开,并通过veth1234虚拟接口到达cbr0桥接接口。

  2. 数据包从veth1234离开,抵达cbr0,寻找 Pod 6的地址。

  3. 数据包从cbr0离开,并被重定向到eth0

  4. 数据包然后从节点1eth0离开,抵达网关。

  5. 数据包从网关离开,抵达节点2eth0接口。

  6. 数据包从eth0离开,抵达cbr0,寻找 Pod 6的地址。

  7. 数据包从cbr0通过veth5678离开,经过eth6接口,抵达 Pod 6网络。

现在我们已经清楚了 Kubernetes 网络模型中流量的路由方式,接下来我们可以专注于 CNI 的概念。

CNI 是一个网络框架,使用一套标准和模块来实现网络资源的动态配置。插件的规范详细说明了配置网络、分配 IP 地址以及维持多主机通信的接口。

CNI 与 Kubernetes 上下文中的kubelet代理无缝连接,允许 Pod 之间的自动网络配置,使用底层或覆盖网络。我们将在这里更详细地了解这一点:

  • 覆盖模式——处于覆盖模式的容器与主机的 IP 地址范围相独立。在跨主机通信时,主机之间会建立隧道,容器内的所有数据包都位于无类域间路由CIDR)块内,并被封装(使用虚拟接口,如虚拟可扩展局域网,或VXLAN),作为底层物理网络中主机之间交换的数据包。这种模式消除了对底层网络的依赖,您可以在下图中查看其概述:

图 6.3 – 覆盖模式

图 6.3 – 覆盖模式

  • 底层模式——在底层模式下,容器和主机位于相同的网络层,并共享相同的位置。容器网络互联由底层网络(网络层的物理层)决定,其中包括路由器和交换机。因此,这种模式对底层能力高度依赖。您可以在下图中查看其概述:

图 6.4 – 底层模式

图 6.4 – 底层模式

一旦网络配置类型被定义,运行时会创建一个容器网络,并使用 CNI 插件将接口添加到容器命名空间,并使用IP 地址管理IPAM)插件来分配相关的子网和路由。除了 Kubernetes 网络,CNI 还支持软件定义网络SDN)方法,以提供集群间统一的容器通信。

现在我们已经清楚了 CNI 的概念,接下来我们将深入探讨如何配置 Calico CNI 插件以实现跨集群的网络连接。

配置 Calico

Calico 是 Kubernetes 环境中最流行的开源 CNI 插件。Tigera维护 Calico,它设计用于网络性能、灵活性和强大功能至关重要的环境。它具有强大的网络管理安全功能,以及对主机和 Pod 连接的全面视图。

它可以很容易地作为DaemonSet在常规 Kubernetes 集群中的每个节点上部署。为了管理大量的网络活动,集群中的每个节点都需要安装三个 Calico 组件:FelixBIRDconfd。节点路由由 Calico 代理Felix处理,而BIRDconfd则管理路由配置的变化。

Calico 使用边界网关协议BGP)路由协议,而不是覆盖网络来在节点之间路由消息。IP-IN-IP 或 VXLAN 可以封装跨子网传输的数据包,作为一种覆盖网络模式。它采用未封装的 IP 网络架构,减少了封装数据包的需求,从而提高了 Kubernetes 工作负载的网络性能。

WireGuard 通过在节点之间建立和管理隧道,确保安全通信,并加密集群内 Pod 之间的通信。与其他工具相比,它使追踪和调试变得更容易,因为它不使用包装器来操作数据包。开发者和管理员可以快速分析数据包行为,并利用复杂的网络功能,如策略管理和访问控制列表ACLs)。

Calico 的网络策略实现了拒绝/匹配规则,这些规则可以通过清单应用到 Pods,以分配入口策略。为了监控 Pod 流量、增强安全性并管理 Kubernetes 工作负载,用户可以构建全球范围的策略并与 Istio 服务网格进行交互。

在接下来的步骤中,我们将展示如何通过一个基本的 Kubernetes NetworkPolicy API 示例,使用 Calico 来保护你的 Kubernetes 集群。NetworkPolicy 是以应用为中心的结构,允许你声明 Pods 如何在网络中与各种网络实体进行通信。

Pod 可以通信的实体通过以下三种标识符IDs)的组合来识别:

  • 其他允许的 Pods(例外:Pod 不能阻止对自身的访问)

  • 允许的命名空间

  • 允许的 IP 块(例外:无论 Pod 或节点的 IP 地址如何,流量从 Pod 所在节点进出总是允许的)

现在我们对标识符有了清晰的了解,我们将深入探讨配置 Calico CNI 插件以实现集群间网络连接的步骤。以下图示展示了我们的树莓派集群设置:

图 6.5 – 树莓派集群设置

图 6.5 – 树莓派集群设置

现在我们知道了我们想做什么,让我们来看一下需求。

需求

在开始之前,以下是构建树莓派 Kubernetes 集群以及配置 CNI 所需的先决条件:

  • 一张 microSD 卡(最低 4 GB;推荐 8 GB)

  • 一台带有 microSD 卡槽的计算机

  • 一台树莓派 2、3 或 4(一个或多个)

  • 一根 micro-USB 电源线(Pi 4 使用 USB-C)

  • 一个带互联网连接的 Wi-Fi 网络或以太网电缆

  • (可选)带 高清多媒体接口HDMI)接口的显示器

  • (可选)用于 Pi 2 和 3 的 HDMI 电缆,以及用于 Pi 4 的 micro-HDMI 电缆

  • (可选)一个 通用串行总线USB)键盘

现在我们已经确定了要求,接下来是如何逐步完成该过程的说明。

步骤 1 – 创建一个 MicroK8s 树莓派集群

请按照我们在第五章中讲解的步骤,创建和实施多节点树莓派 Kubernetes 集群,来创建一个 MicroK8s 树莓派集群。这里是一个快速回顾:

  • 步骤 1:将 操作系统OS)镜像安装到 SD 卡

  • 步骤 1a:配置 Wi-Fi 访问设置

  • 步骤 1b:配置远程访问设置

  • 步骤 1c:配置控制组设置

  • 步骤 1d:配置主机名

  • 步骤 2:安装和配置 MicroK8s

  • 步骤 3:添加一个工作节点

一个完全功能的多节点 Kubernetes 集群看起来如下面所示。总结一下,我们已在树莓派板上安装了 MicroK8s,并加入了多个部署形成了集群。我们还向集群中添加了节点:

图 6.6 – 完全功能的 MicroK8s Kubernetes 集群

图 6.6 – 完全功能的 MicroK8s Kubernetes 集群

我们现在可以进入启用 Calico 插件的下一步,因为我们已经拥有一个完全功能的集群。

步骤 2 – 启用 Calico CNI 插件

默认情况下,如果启用了集群插件,Calico 会自动启用。我们可以使用以下命令来验证它是否已启用:

kubectl get pods – A | grep calico

以下命令执行输出表明 Calico 已启用,并且其 Pods 正在运行:

图 6.7 – 验证 Calico Pods 正在运行

图 6.7 – 验证 Calico Pods 正在运行

现在我们已经让 Calico CNI 正在运行,让我们创建一个示例 nginx 部署,以便在下一步中测试网络隔离。默认情况下,Pod 不会对出口和入口进行隔离——也就是说,所有的出站和入站连接都是允许的。

步骤 3 – 部署一个示例容器化应用

使用以下命令创建一个示例 nginx 部署:

kubectl create deployment nginx –-image=nginx

以下命令执行输出表明部署没有错误,在接下来的步骤中,我们可以公开我们创建的 nginx 部署:

图 6.8 – 示例应用部署

图 6.8 – 示例应用部署

使用以下命令暴露 nginx 部署,以便其他 Pods 可以访问:

kubectl expose deployment nginx –-port=80

以下命令执行输出确认暴露部署已成功:

图 6.9 – 暴露示例应用程序

图 6.9 – 暴露示例应用程序

使用以下命令查看服务是否已被暴露:

kubectl get svc

以下命令执行输出显示服务已被暴露,并且已经分配了集群 IP。使用集群 IP 和端口,我们可以从其他 Pods 访问该服务。请回顾 第六章**, 为负载均衡设置 MetalLB 和 Ingress,其中不会分配外部 IP,因为必须启用像 MetalLB 这样的外部负载均衡器:

图 6.10 – 为服务分配了集群 IP

图 6.10 – 为服务分配了集群 IP

我们将在服务暴露后创建一个新的 Pod 来访问服务。使用以下命令创建一个新的 Pod,并在 Pod 内打开一个 Shell 会话:

kubectl run access --rm -ti –-image busybox /bin/sh

以下命令执行输出确认 run 命令已成功执行,并且已在 access Pod 内打开了一个 Shell 会话:

图 6.11 –  Pod 的 Shell 会话

图 6.11 – access Pod 的 Shell 会话

使用以下命令从 access Pod 访问 nginx 服务:

wget -q nginx -O -

很棒!我们可以看到,nginx 服务可以从 access Pod 访问:

图 6.12 – nginx 响应

图 6.12 – nginx 响应

总结一下,我们已经设置了一个测试 nginx 应用程序,并从 access Pod 暴露并测试了服务。在下一步中,我们将通过使用 NetworkPolicy 应用隔离。

第 4 步 – 通过使用 NetworkPolicy 应用隔离

让我们为默认命名空间中的所有 Pods 创建一个实现默认拒绝行为的 NetworkPolicy,如下所示:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny
spec:
  podSelector:
    matchLabels: {}

从前面的代码可以看出,podSelector 被包含在每个 NetworkPolicy 中,用于选择应用此策略的 Pods 分组。在前面的策略中,空的 podSelector 表示它适用于命名空间中的所有 Pods。

使用以下命令通过 NetworkPolicy 创建隔离:

kubectl apply -f calico-policy.yaml

以下命令执行输出确认部署没有错误,Calico 将阻止对该命名空间中所有 Pods 的连接:

图 6.13 – 创建的 NetworkPolicy

图 6.13 – 创建的 NetworkPolicy

为了测试对nginx服务的访问,请在 BusyBox access Pod 内运行以下命令:

图 6.14 – 测试从  Pod 访问 nginx 服务

图 6.14 – 测试从 access Pod 访问 nginx 服务

使用相同的wget命令从access Pod 访问nginx服务,如下所示:

wget -q nginx -O -

以下命令输出确认nginx服务不可访问,因此让我们在下一步中尝试带超时设置,如下所示:

图 6.15 – 使用 wget 命令访问 nginx

图 6.15 – 使用 wget 命令访问 nginx

以下命令输出确认请求在 5 秒后超时:

图 6.16 – 请求在 5 秒后超时

图 6.16 – 请求在 5 秒后超时

现在我们已经通过拒绝规则测试了隔离,接下来是提供访问权限并测试传入连接。

步骤 5 – 启用访问

让我们修改相同的 NetworkPolicy,以授予对nginx服务的访问权限。只允许来自我们的 access Pod 的传入连接,其他地方的连接不允许。代码如下所示:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
    - from:
      - podSelector:
          matchLabels:
            run: access

从前面的代码中,我们可以注意到以下几点:

  • podSelector选择与app: nginx类型标签匹配的 Pods。

  • ingress规则允许流量,如果它与from部分匹配。

使用以下命令应用修改后的策略:

kubectl apply -f calico-policy.yaml

以下命令执行输出确认部署没有错误。来自具有run: access标签的 Pod 到具有app: nginx标签的 Pod 的流量已被 NetworkPolicy 允许。标签是由kubectl自动创建的,并基于资源名称:

图 6.17 – 修改后的策略部署

图 6.17 – 修改后的策略部署

使用wget命令从access Pod 访问nginx服务,如下所示:

wget -q nginx -O -

前面命令的输出确认我们可以从access Pod 访问nginx服务:

图 6.18 – 使用 wget 命令测试访问

图 6.18 – 使用 wget 命令测试访问

为了重新确认,让我们使用以下命令创建一个没有run: access标签的 Pod,并测试它是否正常工作:

kubectl run access1 --rm -ti --image busybox /bin/sh

如下命令执行输出所示,这应该会在access1 Pod 内启动一个 shell 会话:

图 6.19 – 在 access1 Pod 内的 shell 会话

图 6.19 – 在 access1 Pod 内的 shell 会话

为了测试对nginx服务的访问,从 BusyBox access1 Pod 中运行wget命令,如下所示:

wget -q nginx -O -

请求应该会超时,因为 NetworkPolicy 只会允许来自具有run: access标签的 Pod 的访问,如下所示:

图 6.20 – 请求超时

图 6.20 – 请求超时

这只是一个 Kubernetes NetworkPolicy API 的快速演示,以及 Calico 如何帮助你保护 Kubernetes 集群。有关 Kubernetes 网络策略的更多信息,请参考以下链接:kubernetes.io/docs/concepts/services-networking/network-policies/

Calico 强大的网络策略框架使得限制通信变得简单,确保只有你希望的流量通过。此外,通过内置的 WireGuard 加密功能,保护你的 Pod 到 Pod 的网络流量变得前所未有的简单。

Calico 的策略引擎可以在主机网络层强制执行相同的策略模型,从而保护你的基础设施免受受损工作负载的影响,并保护你的工作负载免受受损基础设施的威胁。

Calico 是希望完全控制网络组件的消费者的绝佳选择。它也与多种 Kubernetes 平台兼容,并通过 Calico Enterprise 提供商业支持。

由 Linux 内核开发者创建的高度可扩展的 Cilium CNI 解决方案将在接下来的部分中讨论。

配置 Cilium

Cilium 在每个 Kubernetes 集群的节点上使用cilium-agent守护进程。Pod 之间通过覆盖网络或路由机制进行通信;例如,支持 IPv4 和 IPv6 地址。VXLAN 隧道用于覆盖网络中的数据包封装,而本地路由则通过未封装的 BGP 协议完成。

eBPF 是 Linux 内核的一项功能,允许在 Linux 内部动态插入复杂的安全可视性和控制逻辑,如下图所示。Cilium 安全策略可以应用和修改,而无需更改应用代码或容器配置,因为 eBPF 在 Linux 内核内部运行。它还具有超文本传输协议HTTP)请求过滤器,支持 Kubernetes 网络策略。可以对入站和出站进行强制执行,策略配置可以用YAML 不是标记语言YAML)或JavaScript 对象表示法JSON)格式表示。在与服务网格(如 Istio)集成时,管理员可以根据请求方法或路径头批准或拒绝请求:

图 6.21 – Cilium:基于 eBPF 的网络、可观察性和安全性

图 6.21 – Cilium:基于 eBPF 的网络、可观察性和安全性

关于 eBPF 技术的更多详情可以在这里找到:ebpf.io/

Cilium 可以跨多个 Kubernetes 集群使用,并提供多种 CNI 功能、高级检查以及 Pod 间交互。数据包检查和应用协议数据包由其网络和应用层感知来管理。

在接下来的步骤中,我们将使用 Kubernetes 的NetworkPolicyCiliumNetworkPolicyCiliumClusterwideNetworkPolicy资源来为我们的集群应用策略。Kubernetes 会自动将这些策略分发给所有代理。

由于 Cilium 不支持arm64架构,本部分将使用 Ubuntu 虚拟机VM)。设置 MicroK8s 集群的步骤与第五章**, 在多节点 Raspberry Pi Kubernetes 集群中创建和实现更新相同。

第 1 步 – 启用 Cilium 插件

使用以下命令启用 Cilium 插件:

microk8s enable cilium

以下命令执行输出表明 Cilium 插件已成功启用:

图 6.22 – 启用 Cilium 插件

图 6.22 – 启用 Cilium 插件

启用插件需要一些时间,但以下命令执行输出显示 Cilium 已成功启用:

图 6.23 – Cilium 插件已启用

图 6.23 – Cilium 插件已启用

Cilium 现在已配置!我们可以使用microk8s.cilium 命令行界面CLI)来检查 Cilium 配置的状态。以下命令执行输出表明 Cilium CNI 已成功启用,并且控制器状态健康:

图 6.24 – Cilium CNI

图 6.24 – Cilium CNI

现在 Cilium CNI 已成功启用,并且控制器状态已验证为健康,下一步是启用域名系统DNS)插件。

第 2 步 – 启用 DNS 插件

由于我们需要地址解析服务,我们将启用 DNS 插件。以下命令执行输出表明 DNS 已成功启用:

图 6.25 – 启用 DNS 插件

图 6.25 – 启用 DNS 插件

既然 Cilium CNI 已运行,接下来让我们创建一个示例nginx部署,以便我们在下一步测试网络隔离。

第 3 步 – 部署示例容器化应用程序

使用以下命令创建一个示例nginx部署:

kubectl create deployment nginx-cilium –-image=nginx

以下命令执行输出表明部署没有错误,在接下来的步骤中,我们可以暴露刚刚创建的nginx-cilium部署:

图 6.26 – 示例应用程序部署

图 6.26 – 示例应用程序部署

使用kubectl expose命令暴露nginx部署,使其可以从其他 Pod 访问,命令如下:

kubectl expose deployment nginx-cilium –-port=80

以下命令执行输出确认暴露部署已成功:

图 6.27 – 暴露示例应用程序

图 6.27 – 暴露示例应用程序

现在服务已暴露,我们将创建一个新的 Pod 来访问它们。要创建一个新的 Pod 并在其中打开一个 shell 会话,请运行以下命令:

kubectl run access --rm -ti --image busybox /bin/sh

以下命令执行输出确认run命令已成功执行,并且已在access Pod 内打开了一个 shell 会话。我们现在可以确认nginx-cilium服务可以从access Pod 访问:

图 6.28 – nginx 响应

图 6.28 – nginx 响应

总结一下,我们创建了一个测试nginx-cilium应用,并从access Pod 暴露并进行了测试。在下一阶段,将使用 NetworkPolicy 来测试隔离。

步骤 4 – 使用 NetworkPolicy 应用隔离

让我们为默认命名空间中的所有 Pod 创建一个 NetworkPolicy,该策略实现默认拒绝行为,如下所示:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: cilium-deny
spec:
  podSelector:
    matchLabels: {}

从前面的代码中,我们可以注意到每个 NetworkPolicy 中都包含了podSelector,它选择了适用该策略的 Pod 分组。在前面的策略中,空的podSelector表示该策略适用于命名空间中的所有 Pod。

使用kubectl apply命令通过 NetworkPolicy 创建隔离,如下所示:

kubectl apply -f cilium-policy.yaml

以下命令执行输出确认部署没有错误,Cilium 将阻止对该命名空间中 Pod 的所有连接:

图 6.29 – NetworkPolicy 已创建

图 6.29 – NetworkPolicy 已创建

我们也可以通过 MicroK8s Cilium CLI 验证相同的内容。以下命令执行输出确认已创建策略:

图 6.30 – Cilium CLI 显示已创建策略

图 6.30 – Cilium CLI 显示已创建策略

要测试访问nginx-cilium服务,请从 BusyBox access Pod 中运行wget命令,如下所示:

wget -q –-timeout=5 nginx-cilium -O -

以下命令输出确认nginx-cilium服务不可访问,且请求在 5 秒后超时:

图 6.31 – 测试从 access Pod 访问 nginx-cilium 服务

图 6.31 – 测试从 access Pod 访问 nginx-cilium 服务

现在我们已经使用拒绝规则测试了隔离,是时候提供访问并测试传入连接了。

步骤 5 – 启用访问

让我们修改相同的 NetworkPolicy,以授予对nginx-cilium服务的访问权限。只有来自我们的访问 Pod 的传入连接会被允许,其他地方的连接将被拒绝。代码如下所示:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
    - from:
      - podSelector:
          matchLabels:
            run: access

从前面的代码中,我们可以注意到以下几点:

  • podSelector选择标签类型为app: nginx的 Pod。

  • ingress规则允许与from部分匹配的流量。

使用以下命令应用修改后的 NetworkPolicy 隔离:

kubectl apply -f cilium-policy.yaml

以下命令执行输出确认部署没有错误。NetworkPolicy 允许来自具有run: access标签的 Pod 的流量访问具有app: nginx标签的 Pod。标签由kubectl自动生成,并基于资源名称:

图 6.32 – 部署修改后的策略

图 6.32 – 部署修改后的策略

我们也可以通过 MicroK8s Cilium CLI 来验证这一点。以下命令执行输出确认策略已更新:

图 6.33 – Cilium CLI 显示更新后的策略

图 6.33 – Cilium CLI 显示更新后的策略

使用 wget 命令从 access Pod 访问 nginx-cilium 服务,如下所示:

wget -q nginx-cilium -O -

以下命令的输出确认我们可以从 access Pod 访问 nginx-cilium 服务:

图 6.34 – 使用 wget 命令测试访问

图 6.34 – 使用 wget 命令测试访问

现在我们已完成与 Cilium 的任务,可以禁用 Cilium CNI,以便 MicroK8s 恢复到默认的 CNI,即 Calico CNI。以下命令执行输出确认 Cilium 已被禁用,MicroK8s 已恢复为 Calico CNI:

图 6.35 – 禁用 Cilium

图 6.35 – 禁用 Cilium

以下命令执行输出确认 Calico Pods 正在运行,并且默认 CNI 已设置:

图 6.36 – Calico 默认 CNI 已设置,Pods 正在运行

图 6.36 – Calico 默认 CNI 已设置,Pods 正在运行

Cilium 保留了通过利用 Linux eBPF 无缝注入安全性可见性和执行的能力,但它是基于 Service/Pod/container 身份(而不是传统系统中的 IP 地址识别)来进行的,并且可以在应用层安全性上进行过滤(例如,HTTP)。由于将安全性与寻址解耦,Cilium 不仅使在高度动态的环境中应用安全策略变得简单,而且还可能提供更强的安全隔离。

在了解 Cilium CNI 后,我们将在下一节继续讨论 Flannel CNI。

配置 Flannel CNI

Flannel 是 Kubernetes 最成熟的开源 CNI 项目之一,由 CoreOS 开发。Flannel 是一种简单的网络模型,可用于覆盖最常见的 Kubernetes 网络配置和管理场景。它通过构建一个覆盖网络来为每个 Kubernetes 集群节点分配一个内部 IP 地址子网。子网的租赁和维护由 flanneld 守护进程代理处理,flanneld 被打包成一个单一的二进制文件,便于在 Kubernetes 集群和发行版中安装和配置。

禁用 HA 集群以启用 Flannel 插件

要设置 Flannel 为 CNI,必须禁用 高可用性 (HA) 集群,以将 CNI 设置为 Flannel。以下命令执行输出确认 HA 集群已被禁用,Flannel CNI 已设置:

图 6.37 – 禁用 HA 集群以设置 Flannel CNI

图 6.37 – 禁用 HA 集群以设置 Flannel CNI

现在 Flannel CNI 已设置完成,我们可以部署一个示例应用并测试网络。

Flannel 使用 Kubernetes 的etcd集群或 API 来存储主机映射和其他与网络相关的配置,并在分配 IP 地址后通过封装数据包维持主机/节点之间的连接。默认情况下,它使用VXLAN配置进行封装和通信,尽管也有多种后端可用,包括host-gwUDP。使用 Flannel 启动VXLAN-GBP进行路由也是可行的,这在多个主机连接到同一网络时是必需的。

以下图表展示了通过VXLAN隧道在节点之间流量的流动:

图 6.38 – Flannel CNI

默认情况下,Flannel 不提供任何加密封装流量的手段。然而,它启用了IP 安全IPsec)加密,允许 Kubernetes 集群在工作节点之间创建加密隧道。这是一个非常适合初学者的 CNI 插件,特别是对于希望从集群管理员的角度开始 Kubernetes CNI 之旅的用户。直到用于调控主机间的流量传输之前,它简单的网络模型没有任何缺点。

让我们总结一下到目前为止学到的内容:我们已经看过了三种最流行的 CNI 插件:Flannel、Calico 和 Cilium。当容器被构建或销毁时,CNI 使得容器网络的配置变得简单。这些插件确保了 Kubernetes 的网络需求得到满足,并且集群管理员可以访问所需的网络功能。在接下来的章节中,我们将探讨选择适合你需求的 CNI 提供商的一些指南。

选择 CNI 提供商的指南

没有单一的 CNI 供应商可以满足项目的所有需求。Flannel 是一个非常适合快速设置和配置的选择。Calico 因为采用 BGP 基础网络而具有更优的性能。Cilium 使用 BPF 实现了一种完全不同的应用层过滤模型,更注重企业安全。

在下表中,我们比较了三种最流行的 CNI 插件:

表 6.1 – 流行 CNI 插件的比较

表 6.1 – 流行 CNI 插件的比较

依赖单一的 CNI 提供商是不必要的,因为不同项目的操作要求差异较大。为了满足复杂的网络需求,并提供更可靠的网络体验,将使用并测试多种解决方案。接下来,我们将探讨选择 CNI 提供商时需要考虑的一些重要因素。

选择 CNI 提供商时的关键考虑因素

Calico、Flannel 和 Cilium 只是可用的 CNI 插件中的一部分。让我们看看在为生产环境选择合适的 CNI 插件时需要考虑的各种方面。

你应该根据你所操作的环境选择插件,如下所示:

  • VXLAN 和 Calico-IPIP 用于限制的底层网络。

  • VXLAN 封装可能会导致性能下降。在这种情况下,你可以使用像 Calico-BGP 和 Flannel-HostGW 这样的插件。

  • 对于云环境—在这种虚拟环境中,基本能力受到严重限制。另一方面,每个公有云都会对容器进行性能优化,并可能提供配置额外网卡或路由功能的 API。为了兼容性和最佳性能,建议使用公有云供应商提供的 CNI 插件。

在评估环境约束之后,你可能会更清楚哪些插件可以使用,哪些插件不能使用。在下一节中,我们将讨论业务需求。

根据业务需求,功能标准也可能会影响你的插件选择。以下是一些应考虑的因素:

  • 安全要求—Kubernetes 包括 NetworkPolicy,它允许你设置规则来支持诸如是否允许 Pods 之间访问等策略。并非所有 CNI 插件都支持 NetworkPolicy 声明。如果你需要 NetworkPolicy 支持,Calico 是一个不错的选择。

  • 与集群内外资源的连接—运行在虚拟机或物理机上的应用程序不能同时全部迁移到容器化环境中。因此,虚拟机或物理机与容器之间的 IP 地址连接必须通过互联或在同一层级部署来配置。在这种情况下,选择Underlay模式下的插件。例如,Calico-BGP 插件允许 Pod 和传统的虚拟机或物理机共享一个层。即使容器与历史虚拟机或物理机位于不同的 CIDR 块中,Calico-BGP 也可以用于将 BGP 路由发布到原始路由器,使虚拟机和容器能够相互通信。

  • kube-proxy 在主机上设置。在这种情况下,插件无法使用 Kubernetes 的服务发现功能。如果你需要这些功能,可以选择Underlay模式下的插件,以启用服务发现和负载均衡。

现在我们已经讨论了选择插件的业务标准,接下来可以讨论性能要求。

根据性能要求,Pod 创建速度和 Pod 网络性能可用于评估性能。根据实现模式的不同,可能会有性能损失。选择插件时需要考虑以下几点:

  • Pod 创建速度—例如,在业务高峰期间需要立即扩展时,可能需要构建和配置更多的网络资源。在使用 Overlay 模式的 CNI 插件的情况下,你可以轻松地扩展 Pods,因为该插件在节点上实现了虚拟化,创建 Pods 就像调用内核接口一样简单。而在 Underlay 模式下,必须先生成底层网络资源,这会减慢 Pod 创建的过程。因此,当你需要快速扩展 Pods 或构建大量 Pods 时,选择 Overlay 模式插件。

  • Pod 网络性能—通过诸如 Pod 之间的网络转发、网络带宽和 每秒脉冲 (PPS) 延迟等指标来评估 Pod 网络性能。在 Overlay 模式下的插件性能会低于 Underlay 模式的插件,因为前者在节点上实现了虚拟化并封装了数据包。因此,如果你需要在 机器学习 (ML) 和大数据场景中获得卓越的网络性能,请不要选择 Overlay 模式的插件,而应使用 Underlay 模式的 CNI 插件。

总结

在本章中,我们探讨了 Kubernetes 集群中的网络处理方式。我们还学习了 CNI 如何支持动态网络资源配置,例如网络配置、IP 地址分配和多主机通信。我们了解了 CNI 如何使用下层或覆盖网络自动配置 Pods 之间的网络。

我们还介绍了如何使用 Calico、Cilium 和 Flannel CNI 插件来实现集群网络。我们发现了每种 CNI 的优缺点。我们还发现,没有任何一个 CNI 供应商能够满足项目的所有需求。Flannel 是一个非常适合快速设置和配置的解决方案。Calico 由于使用了 BGP 下层网络,具有更优越的性能。Cilium 使用 BPF 技术创建了更加专注于企业安全性的应用层过滤方法。我们已经探讨了选择 CNI 服务时需要考虑的一些重要因素。

在下一章,我们将继续讨论下一个用例,涉及如何将你的服务暴露到集群外部。

第七章:设置 MetalLB 和 Ingress 进行负载均衡

在上一章中,我们已经了解了 Kubernetes 网络模型是如何工作的,并学会了如何使用 Calico、Cilium 和 Flannel CNI 插件来对集群进行网络配置。我们还讨论了选择 CNI 提供商时需要考虑的一些最重要的因素。

在深入探讨 MetalLB 负载均衡器和 Ingress 的负载均衡概念之前,我们应该回顾一下第一章中关于 Kubernetes 服务抽象机制的内容。简单来说,Kubernetes 服务将一组 Pod 与抽象的服务名称和 IP 地址连接起来。服务提供了 Pod 之间的发现和路由。例如,服务将一个应用程序的前端连接到其后端,这些组件都部署在不同的集群部署中。

这里列出了最常见的服务类型:

  • ClusterIP:这是默认类型,通过集群的内部 IP 地址暴露服务。这些服务仅在集群内部可访问。

  • NordPort 服务,一个 ClusterIP 服务会自动创建。

  • LoadBalancer 类型的服务将创建一个负载均衡器并将服务暴露到外部。它还会自动创建 ClusterIPNodePort 服务,并相应地路由流量。

  • 通过返回 规范名称CNAME)记录的值来配置 externalName ex.sampleapp.test.com 字段。

最常见的服务类型如下面的图示所示:

图 7.1 – 常见的服务类型

图 7.1 – 常见的服务类型

既然我们已经覆盖了基础知识,接下来让我们深入探讨 MetalLB 和 Ingress 配置。在本章中,我们将涵盖以下主要主题:

  • MetalLB 和 Ingress 概述

  • 配置 MetalLB 在集群中进行负载均衡

  • 配置 Ingress 以将服务暴露到集群外部

  • 选择合适的负载均衡器以服务于你的应用程序的指南

MetalLB 和 Ingress 概述

尽管 Kubernetes 被广泛采用,但它并没有提供负载均衡器的实现。如果你的 Kubernetes 集群运行在云平台(如 Azure)上,Pending 状态将无限期持续。

NodePortexternalIPs 服务是将用户流量引入裸金属集群的唯一选项。这两种策略在输出方面都有相当大的缺陷。MetalLB 通过提供一个网络负载均衡器的实现来解决这个问题,该负载均衡器与传统的网络设备连接,从而允许裸金属集群上的外部服务。

简而言之,MetalLB 使你能够在没有云服务提供商的集群中建立 LoadBalancer Kubernetes 服务。地址分配和外部公告是共同作用来提供此服务的两个特点。接下来我们将详细了解这些内容,具体如下:

  • 地址分配:MetalLB 无法自行生成 IP 地址;相反,我们必须提供 IP 地址池供其选择。当 Services 创建和删除时,MetalLB 会负责分配和取消分配个别地址,但它只会分发预设池中的 IP 地址。设置 IP 地址池的方式取决于我们的环境;例如,如果你在一个共置设施中运行裸金属集群,你的托管服务提供商可能会提供 IP 地址供租用,或者如果你在一个私有局域网LAN)中运行,你可以选择来自某个私有地址空间的 IP 地址范围。

  • 外部公告:在为 Service 分配外部 IP 地址后,MetalLB 必须通知集群外部网络该 IP 地址已驻留在集群中。MetalLB 使用传统的路由协议来实现这一点,如地址解析协议ARP)、邻居发现协议NDP)或边界网关协议BGP)。在第二层L2)模式下,如 ARP/NDP,集群中的一个节点会接管该 Service,并使用标准的地址发现协议(IPv4 的 ARP;IPv6 的 NDP)使这些 IP 在本地网络中可见;而在 BGP 模式下,集群中的所有节点都会与你控制的相邻路由器创建 BGP 对等会话,并告知这些路由器如何将流量转发到 Service IP。BGP 的策略机制使得在多个节点之间实现真正的负载均衡,并提供精细化的流量控制。

另一种选择是使用Ingress(Kubernetes 对象)来暴露你的 Service。尽管它充当集群的入口点,但 Ingress 并不是一种 Service 类型。

Ingress 的工作原理如下图所示:

图 7.2 – Ingress 的工作原理

图 7.2 – Ingress 的工作原理

Ingress 控制器帮助将不同应用程序的路由规则整合成一个实体。在 NodePortLoadBalancer 的帮助下,Ingress 控制器暴露给外部世界。它更适合 nginxHAProxy 的内部负载均衡。对于 LoadBalancer 类型的服务,MetalLB 在这种情况下为我们提供了帮助。

在下一节中,我们将讨论如何将 MetalLB 设置为集群的负载均衡器。

配置 MetalLB 以实现集群负载均衡

现在我们已经明确了 MetalLB 的概念,接下来我们将深入探讨如何配置 MetalLB 实现集群的负载均衡。下图展示了我们的 Raspberry Pi 集群设置:

图 7.3 – MicroK8s Raspberry Pi 集群

图 7.3 – MicroK8s Raspberry Pi 集群

现在我们知道了我们要做什么,接下来让我们看看要求。

要求

在开始之前,以下是构建 Raspberry Pi Kubernetes 集群和配置 MetalLB 负载均衡器所需的先决条件:

  • 一张 microSD 卡(最小容量为 4 GBGB),推荐使用 8 GB)

  • 一台带有 microSD 卡读卡器的计算机

  • 一个 Raspberry Pi 2、3 或 4(一个或多个)

  • 一根 micro-USB 电源线(Pi 4 使用 USB-C)

  • 一个 Wi-Fi 网络或带有互联网连接的以太网电缆

  • (可选)带有 High-Definition Multimedia InterfaceHDMI)接口的显示器

  • (可选)适用于 Pi 2 和 3 的 HDMI 线缆以及适用于 Pi 4 的 micro-HDMI 线缆

  • (可选)一个 Universal Serial BusUSB)键盘

现在我们已经确定了所需的要求,接下来我们将逐步介绍如何完成此过程的说明。

步骤 1 – 创建 MicroK8s Raspberry Pi 集群

请按照我们在 第五章 中介绍的步骤,在多节点 Raspberry Pi Kubernetes 集群上创建和实施更新,创建 MicroK8s Raspberry Pi 集群。以下是一个快速复习:

  1. 操作系统OS)镜像安装到 Secure DigitalSD)卡:

    1. 配置 Wi-Fi 访问设置

    2. 配置远程访问设置

    3. 配置控制组设置

    4. 配置主机名

  2. 安装和配置 MicroK8s

  3. 添加工作节点

完全功能的多节点 Kubernetes 集群应如下截图所示。总结一下,我们已在 Raspberry Pi 板上安装了 MicroK8s,并加入了多个部署以形成集群。我们还向集群添加了节点:

图 7.4 – 完全功能的 MicroK8s Raspberry Pi 集群

图 7.4 – 完全功能的 MicroK8s Raspberry Pi 集群

现在我们可以进入下一步,启用 MetalLB 插件,因为我们已经拥有了一个完全功能的集群。

步骤 2 – 启用 MetalLB 插件

MicroK8s 支持各种插件 (microk8s.io/docs/addons),这些是预打包的组件,为您的 Kubernetes 集群提供额外的功能。

使用以下命令轻松设置:

microk8s enable <<add-on>>

使用以下命令启用 MetalLB 负载均衡器:

microk8s enable metallb <<list of IP address>>

下面的命令执行输出表示 MetalLB 插件已成功启用:

图 7.5 – 启用 MetalLB 插件

图 7.5 – 启用 MetalLB 插件

我们已指示 MetalLB 使用以下命令在 192.168.1.10 - 192.168.1.15 范围内分配地址。要检查可用和已安装的插件列表,请使用 status 命令,如下所示:

microk8s status

注意

或者,您可以使用 192.168.2.1/24 子网,并选择为 MetalLB 分配一半的 IP 地址。IP 数字 192.168.2.1192.168.2.126 组成了子网的第一部分。可以使用 /25 子网来表示这个范围:192.168.2.1/25

一个 /25 子网也可以用来表示网络的第二部分,例如,192.168.2.128/25。每半部分有 126 个 IP 地址。

确保选择适合您网络的子网,并正确配置您的路由器和 MetalLB。

以下命令执行输出表明(请参阅高亮部分)MetalLB 插件已启用:

图 7.6 – MicroK8s 状态

图 7.6 – MicroK8s 状态

对于其组件,MetalLB 使用 metallb-system 命名空间。要验证所有组件是否正在运行,请使用以下命令:

kubectl get all -n metallb-system

以下命令执行输出表明所有组件处于 Running 状态:

图 7.7 – MetalLB 组件及其状态

图 7.7 – MetalLB 组件及其状态

在前述 MetalLB 命令执行输出中看到的组件将在此进行更详细的说明:

  • metallb-system/controller(部署):这是整个集群的 IP 地址分配控制器。

  • metallb-system/speaker(DaemonSet):该组件使用您选择的协议与服务进行通信。

现在我们已经激活了 MetalLB 插件,下一步是启动一个示例应用程序,查看它是否能够进行负载均衡。

已启用的插件可以随时通过使用 disable 命令禁用,如下所示:

microk8s disable <<add-on>>

此时,您已经拥有了一个完全功能的多节点 Kubernetes 集群,并启用了 MetalLB 插件。

注意

节点之间,端口 7946(TCP 和 UDP)必须被允许。此外,在安装 MetalLB 之前,请确保没有其他软件在节点上的端口 7946 上运行。

第三步 – 部署示例容器化应用程序

在此步骤中,我们将会在多节点 MicroJ8s 集群设置中部署以下 Apache web 服务器,如下所示:

apiVersion: v1
kind: Namespace
metadata:
  name: web
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
  namespace: web
spec:
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: httpd
        image: httpd:2.4-alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-server-service
  namespace: web
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

在我们部署 web 服务器之前,先使用以下命令验证集群节点是否已准备就绪:

kubectl get nodes

以下命令执行输出显示所有节点处于 Ready 状态。现在我们已准备好开始:

图 7.8 – 检查节点是否处于 Ready 状态

图 7.8 – 检查节点是否处于 Ready 状态

以下命令将部署 web 服务器应用程序:

kubectl apply -f webserver-deploy.yaml

以下命令执行输出表明部署没有错误,在接下来的步骤中,我们可以使用 get deployments 命令验证相同内容:

图 7.9 – 部署 web 服务器

图 7.9 – 部署 web 服务器

使用以下命令检查部署状态,以验证应用程序已成功部署并正在运行:

kubectl get deployments -n web

以下命令执行输出确认部署已 Ready

图 7.10 – 确认部署已就绪

图 7.10 – 确认部署已就绪

现在我们已准备好示例 web 服务器应用程序,接下来可以测试负载均衡机制。

第四步 – 验证负载均衡机制

总结一下,我们已部署了一个示例 web 服务器应用程序。接下来我们将在本部分测试负载均衡机制。

使用以下命令查看负载均衡器是否已分配外部 IP 和端口:

kubectl get all -n web

以下命令执行输出(参见高亮部分)显示外部 IP 和端口已分配:

图 7.11 – 检查是否已分配外部 IP 和端口

图 7.11 – 检查是否已分配外部 IP 和端口

现在已经分配了外部 IP,我们可以使用任意节点的外部 IP 地址或外部网络访问应用程序,如下所示:

curl 192.168.1.10 

如果你已经按照前面的步骤操作,你应该能够看到 <html><body><h1>It works!</h1></body></html> Apache web 服务器输出,如以下命令执行输出所示:

图 7.12 – Apache web 服务器输出

图 7.12 – Apache web 服务器输出

让我们扩展部署,看看负载均衡器是否仍然正常工作。为此,请运行 kubectl scale 命令,如下所示:

kubectl scale deployments/web-server --replicas=5 -n web

以下命令执行输出确认部署没有错误,接下来的步骤中,我们可以验证部署中有五个 Pods:

图 7.13 – 扩展部署

图 7.13 – 扩展部署

使用以下命令检查部署状态:

kubectl get deployments -n web

以下命令输出显示命令已成功执行,且部署已更新:

图 7.14 – 检查部署状态

图 7.14 – 检查部署状态

部署已更新后,让我们使用以下命令检查 Pods 如何在节点间分布:

kubectl get pods -n web -o wide

以下命令执行输出显示,三个 Pods 正在 master 节点上运行,其中两个在 worker1 上运行:

图 7.15 – Pods 如何在节点间分布

图 7.15 – Pods 如何在节点间分布

让我们使用以下命令检查外部 IP 和端口是否有任何变化:

kubectl get all -n web

以下命令执行输出显示分配的外部 IP 和端口没有变化:

图 7.17 – Apache web 服务器输出

图 7.16 – 重新检查外部 IP 和端口是否有变化

让我们使用相同的curl命令访问应用程序并验证其是否正常工作,如下所示:

图 7.17 – Apache web 服务器输出

命令执行输出确认负载均衡器正常工作。即使 Pods 分布在各个节点上,我们的 web 服务器应用程序仍然能够有效地处理用户请求。

下图展示了 MetalLB 的负载均衡功能。MetalLB 实现了LoadBalancer Kubernetes 服务。当外部请求LoadBalancer服务时,MetalLB 会从预设范围内为客户端分配一个 IP 地址,并通知网络该 IP 地址在集群内:

图 7.18 – MetalLB 负载均衡功能

图 7.18 – MetalLB 负载均衡功能

默认情况下,MetalLB 在 MicroK8s 中配置为 L2 模式。可以使用以下命令确认这一点:

kubectl describe configmap config -n metallb-system

以下命令执行输出确认了 MetalLB 处于 L2 模式,并且我们还可以看到 IP 范围:

图 7.19 – MetalLB ConfigMap

图 7.19 – MetalLB ConfigMap

要将 MetalLB 设置为 BGP 模式,需要重新配置ConfigMap config,将操作模式设置为BGP并配置外部 IP 地址范围。

在 BGP 模式下,speakers 会与集群外的路由器建立 BGP 对等连接,并指示这些路由器如何将流量重定向到服务 IP。BGP 的策略机制使得跨多个节点实现真正的负载均衡,并且能够进行精细化的流量控制。

你可以使用普通的路由器硬件配合 BGP 作为负载均衡方法。然而,它确实存在一些缺点。有关这些限制的更多信息,以及如何克服它们的方法,可以在 MetalLB BGP 文档页面找到(metallb.universe.tf/configuration/#bgp-configuration)。

总结来说,MetalLB 使你能够在无需将集群部署在云平台上的情况下,建立 Kubernetes LoadBalancer服务。MetalLB 提供两种操作模式:一种是无需外部硬件或配置的基本 L2 模式,另一种是更为强大且适合生产环境的 BGP 模式,但需要更多的网络设置任务。在下一节中,我们将介绍如何使用Ingress方法进行负载均衡配置。

配置 Ingress 以将服务暴露到集群外部

正如我们在MetalLB 和 Ingress 概述部分中讨论的那样,Ingress 提供了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。Ingress 上定义的规则控制流量的路由。NGINX Ingress Controller 是常见的 Kubernetes Ingress,也是 MicroK8s 的默认 Ingress 控制器。

另一种选择是使用负载均衡器,如 MetalLB,它可以部署在同一个 Kubernetes 集群中,然后服务可以暴露到外部网络。

下面是两种方法的图示:

图 7.20 – Ingress 负载均衡功能

图 7.20 – Ingress 负载均衡功能

以下选项将在后续部分中更详细地讨论。

选项 1 – 使用 Ingress NodePort 方法

对于这一选项和下一个选项,我们将使用为 MetalLB 配置建立的相同 MicroK8s Raspberry Pi 集群。

第 1 步 – 启用 Ingress 插件

可以使用与启用 MetalLB 时相同的命令启用 Ingress 插件,如此处所示:

microk8s enable ingress

以下命令执行输出表示 Ingress 插件已成功启用:

图 7.21 – 启用 Ingress 插件

图 7.21 – 启用 Ingress 插件

现在 Ingress 插件已经启用,下一步是部署一个示例应用程序来测试负载均衡功能。

第 2 步 – 部署示例容器化应用程序

我们将在我们的多节点 MicroK8s 集群上应用以下 whoami 部署,这是一个 Tiny Go Web 服务器,能够输出操作系统信息和 HTTP 请求:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - name: whoami-container
        image: containous/whoami
---
apiVersion: v1
kind: Service
metadata:
  name: whoami-service
spec:
  ports:
  - name: http
    targetPort: 80
    port: 80
  selector:
    app: whoami
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whoami-ingress
spec:
  rules:
  - http:
      paths:
      - path: /whoami
        pathType: Exact
        backend:
          service:
            name: whoami-service
            port:
              number: 80

以下命令将部署 whoami 应用程序:

kubectl apply -f whoami-deployment.yaml

以下命令执行输出表示部署没有错误,在接下来的步骤中,我们可以使用 get deployments 命令验证这一点:

图 7.22 – 部署 whoami 应用程序

图 7.22 – 部署 whoami 应用程序

使用以下命令检查部署状态,验证应用程序是否已部署并正在运行:

kubectl get deployments

以下命令执行输出表示部署已 Ready

图 7.23 – 检查部署状态

图 7.23 – 检查部署状态

使用以下命令检查是否已创建 Ingress 对象:

kubectl get ingress

前述命令的输出表示已创建一个名为 whoami-ingress 的 Ingress 对象:

图 7.24 – 检查我们创建的 Ingress 对象

图 7.24 – 检查我们创建的 Ingress 对象

使用 describe 命令查看我们刚创建的 Ingress 对象的详细信息:

图 7.25 – 使用 Ingress 描述命令检查已创建的 Ingress 对象的详细信息

图 7.25 – 使用 Ingress 描述命令检查已创建的 Ingress 对象的详细信息

从命令执行输出来看,以下是各个字段的表示:

  • Host:由于在之前的输出中没有提到主机,规则适用于通过提供的 IP 地址进入的所有 HTTP 流量。如果提供了主机(例如 foo.com),则规则适用于该站点。

  • Path:由 service.nameservice.port.nameservice.port.number 描述的后端的路由列表(例如 /whoami)。在负载均衡器将流量分发到指定服务之前,主机和路径必须与传入请求的内容匹配。

  • Backends:该服务将后端描述为服务和端口名称的组合。所述后端接收与规则的主机和路径匹配的 HTTP(和 HTTPS)请求,传递给对应的 Ingress 对象。

有关 Ingress 的更多信息,请参阅 Kubernetes 的官方文档:kubernetes.io/docs/concepts/services-networking/ingress/

步骤 3 – 验证负载均衡器机制

使用curl命令检查是否能够访问应用程序,如下所示:

图 7.26 – 访问已部署的应用程序

图 7.26 – 访问已部署的应用程序

让我们扩展部署,看看负载均衡器是否正常工作。为此,请运行kubectl scale命令,如下所示:

kubectl scale deployments/whoami-deployment --replicas=5

以下命令执行输出确认部署没有错误,接下来的步骤中让我们检查 Pods 如何在节点间分布:

图 7.27 – 扩展部署

图 7.27 – 扩展部署

以下命令执行输出表明,两个 Pods 正在 master 节点上运行,三个 Pods 正在 worker1 节点上运行:

图 7.28 – 检查 Pods 如何在节点之间分布

图 7.28 – 检查 Pods 如何在节点之间分布

我们使用相同的curl命令访问应用程序并验证其是否正常工作,如下所示:

图 7.29 – 重新检查负载均衡器是否正常工作

图 7.29 – 重新检查负载均衡器是否正常工作

命令执行输出确认负载均衡器正常工作。尽管 Pods 分布在不同节点上,我们的 whoami 应用程序仍然能够有效地响应用户请求。

选项 2 – 使用 Ingress 和负载均衡器

在此方法中,我们必须为 Kubernetes 集群配置负载均衡器才能继续。由于我们已经安装并配置了 MetalLB 负载均衡器,因此将重用它。然而,我们仍然需要为我们的示例 whoami 应用程序定义一个简单的负载均衡器服务,以确保它获得外部 IP 和端口。

以下是简单负载均衡器服务部署 whoami 应用程序的代码:

apiVersion: v1
kind: Service
metadata:
  name: metallb-load-balancer
spec:
  selector:
    app: whoami
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

以下命令将部署前述应用程序:

kubectl apply -f loadbalancer.yaml

以下命令输出表明命令已成功执行,并且已创建负载均衡器。我们将在接下来的步骤中确认这一点:

图 7.30 – 创建的负载均衡器服务

图 7.30 – 创建的负载均衡器服务

使用以下命令检查是否已创建负载均衡器服务,并且外部 IP 和端口已分配。如果负载均衡器不可用,EXTERNAL-IP 不会获得 IP 地址,而是显示为 <pending>。在这种情况下,请检查负载均衡器的可用性:

kubectl get svc

以下命令输出显示已分配外部 IP 和端口:

图 7.31 – 负载均衡器已分配外部 IP 和端口

图 7.31 – 负载均衡器已分配外部 IP 和端口

既然外部 IP 已经分配,我们可以通过任何节点或外部网络访问应用,如下所示:

curl 192.168.1.11/whoami 

以下命令输出显示命令已成功运行,且负载均衡器正常工作。尽管应用分布在各个节点上,我们的 whoami 应用仍然能够有效地处理用户请求:

图 7.32 – 检查负载均衡器是否正常工作

图 7.32 – 检查负载均衡器是否正常工作

总结一下,Kubernetes 提供了几种将服务暴露到外部世界的方法。LoadBalancer 和 Ingress 控制器是最常见的选择。在本章中,我们通过示例探讨了这两种选项。

选择合适的负载均衡器的指南

现在我们已经了解了不同的选择,拥有一份备忘单来快速比较一些关键特性将会非常有帮助,帮助我们决定使用哪个选项。

在下表中,我们将介绍选择正确选项时需要考虑的一些重要参数:

表 7.1 – 如何选择合适的负载均衡器表 7.1 – 如何选择合适的负载均衡器

表 7.1 – 如何选择合适的负载均衡器

总结一下,最终选择可以归结为几个选项。当使用 LoadBalancer,特别是在裸机环境下,它表现非常好,因为服务可以选择其希望使用的端口。缺点是,它可能比较昂贵,因为每个服务都需要自己的负载均衡器和外部 IP 地址,而这在云环境中都是需要付费的。Ingress 变成了最受欢迎的服务,尤其是当与 MetalLB 负载均衡器一起使用时,因为它减少了使用的 IP 数量,同时仍然允许每个服务拥有自己的名称和/或 统一资源标识符 (URI) 路由。

总结

在本章中,我们讨论了将服务暴露到集群外部的技术,并且我们已经看到负载均衡器如何将应用暴露到外部网络。传入的请求会通过负载均衡器的单一 IP 地址路由到你的应用。MetalLB 实现了 LoadBalancer Kubernetes 服务。当请求 LoadBalancer 服务时,MetalLB 从预设的范围中为客户端分配一个 IP 地址,并通知网络该 IP 地址位于集群内。

我们还看到了 NGINX Ingress 控制器选项,这是一个常见的 Kubernetes Ingress 选项。MetalLB 可以与 Ingress 一起部署在同一个 Kubernetes 集群中,也可以用作负载均衡器。NodePort 是另一种将 Ingress 控制器暴露到外部世界的方式。在本章中,我们讨论了这两种选项,并提供了不同的示例。

在下一章中,我们将介绍如何使用像 Prometheus、Grafana、Elastic、Fluentd、Kibana 和 Jaeger 等工具来监控基础设施和应用程序的健康状况。你还将学习如何配置和访问各种仪表盘/指标。

第八章:监控基础设施和应用程序的健康状况

在上一章中,我们探讨了如何将服务暴露到集群外部,并使用负载均衡器将应用程序暴露到外部网络。单一的LoadBalancer Kubernetes 服务是通过MetalLB实现的。当请求LoadBalancer服务时,MetalLB会从预定义的范围中分配一个 IP 地址给客户端,并通知网络该 IP 地址属于集群内。MetalLB可以与 Ingress 一起在同一 Kubernetes 集群中部署,也可以作为负载均衡器使用。另一种将 Ingress 控制器暴露到外部世界的方式是通过NodePort。这两种选项在上一章中已经详细探讨,并给出了各种示例。

在本章中,我们将探讨监控、日志记录和警报的各种选项,适用于您的基础设施和应用程序。在传统的以主机为中心的基础设施中,曾经只有两种监控层级:应用程序和运行它们的主机。随后,容器抽象技术出现了,位于主机和应用程序之间,之后 Kubernetes 用于协调这些容器。

为了全面管理基础设施,Kubernetes 本身现在也必须进行监控。因此,必须监控四个不同的组件,每个组件都有自己的一组挑战:

  • 主机

  • 容器

  • 应用程序

  • 最终,Kubernetes 集群本身

为了跟踪 Kubernetes 基础设施的健康状况,需要从所有容器和 Pod 中收集指标和事件。然而,为了完全理解客户或用户的经历,现在还需要跟踪在这些 Pod 中运行的应用程序。需要注意的是,当使用 Kubernetes 时,您通常对工作负载的运行位置几乎没有控制权,因为 Kubernetes 会自动安排它们。

在监控方面,Kubernetes 迫使你重新考虑策略。但如果你知道该关注什么,在哪里查找以及如何聚合和分析数据,你可以确保你的应用程序运行顺利,同时 Kubernetes 能有效地执行其任务。

对于从集群中聚合和报告监控数据,Kubernetes 生态系统目前提供了两个内置的附加组件,具体细节如下。

Metrics Server 从每个节点上的每个 kubelet 收集资源消耗统计信息,并通过 Metrics 返回汇总的指标;kube-state-metrics 插件使集群状态数据公开。与提供 Pod 和节点资源利用率指标的 Metrics Server 不同,kube-state-metrics 会查询控制平面 API 服务器,获取有关 Kubernetes 对象(节点、Pod、部署等)整体状态的信息,以及资源限制和分配。然后,利用这些信息生成指标,用户可以通过 Metrics API 访问。这意味着,kube-state-metrics 侧重于从 Kubernetes 的对象状态生成全新的指标,而 metrics-server 只是保存最新数据,并不负责将指标传输到第三方目的地。

在接下来的章节中,我们将详细介绍如何使用 Metrics Server 和 kube-state-metrics 获取指标的各种选项。MicroK8s 的优点是监控工具只需几条命令即可在一分钟内启用。它足够小,可以运行在 Raspberry Pi 上,并且可以用于开发一个可以部署到任何地方的监控堆栈,甚至是边缘环境。此外,它是使用一些最流行的开源组件构建的,这些组件在 MicroK8s 中已经预装。

在本章中,我们将探讨如何轻松地在边缘部署监控工具。这样的部署提供了隐私保护、低延迟和最小带宽成本,适用于 物联网IoT)/边缘应用程序。在本章中,我们将讨论以下几个主要主题:

  • 监控、日志记录和警报选项概述

  • 使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和警报堆栈

  • 使用 Elasticsearch、Fluentd 和 KibanaEFK)工具集配置监控、日志记录和警报堆栈

  • 需要监控的关键指标

监控、日志记录和警报选项概述

Kubernetes 有很多优点,但它也增加了很多复杂性。它能够将容器化应用分布在多个节点甚至不同的数据中心(例如云提供商),这就需要一个全面的监控解决方案,能够从多个来源收集和汇总指标。

许多免费和付费的解决方案提供 Kubernetes 集群及其托管的应用程序的实时监控,持续监控系统和应用程序健康至关重要。这里列出了一些著名的开源 Kubernetes 监控工具:

  1. kubelets 并通过以下 Metrics API 端点在 Kubernetes API 服务器中公开它们:

表 8.1 – 指标 API 端点

表 8.1 – 指标 API 端点

  1. Kubernetes Dashboard(内置)是一个为 Kubernetes 集群提供的网页用户界面UI)插件,允许您跟踪工作负载的健康状况。通过简单的网页界面,Kubernetes Dashboard 使您能够管理集群资源并排查容器化应用程序的故障。它提供了集群范围和单个节点资源的简明概览。它还列出了所有集群的命名空间以及所有已声明的存储类。

  2. Prometheus 是一个开源系统,用于收集 Kubernetes 健康状况的度量数据。它在每个集群节点上部署节点出口(node exporter)Pod,并且其服务器收集来自节点、Pod 和任务的数据。最终的时间序列度量数据保存在数据库中,并且可以根据预定义条件自动生成警报。

Prometheus 具有自己的仪表板,功能有限,但通过使用其他可视化工具(如 Grafana)得到了扩展,Grafana 使用 Prometheus 数据库提供针对开发、测试和生产团队设计的高级查询、调试和报告功能。

它的设计目标是大规模监控容器中的应用程序和微服务,能够连接各种第三方数据库,并支持桥接来自其他工具的数据。它的核心由三个组件组成,如下所示:

  • 所有度量数据将存储在内置的时间序列数据库中。

  • 数据检索工作者负责从外部来源获取度量指标,并将其输入数据库。

  • 配备简单网页界面的 web 服务器,用于配置和查询存储的数据。

下面是 Prometheus 的一些关键特性:

  • 时间序列数据按度量名称和键/值对进行分类,采用多维数据模型。

  • 使用 Prometheus 查询语言PromQL),这是一种灵活的查询语言,允许我们在不依赖分布式存储的情况下利用这种维度。

  • 单服务器节点是自包含的,时间序列通过 超文本传输协议HTTP)的拉取模型进行收集。

  • 另外,可以使用中介网关将时间序列推送到通过服务发现或静态配置发现的目标。

  • 支持多种图表和仪表板选项。

  1. Grafana 是一个开源分析和度量可视化平台,包含四个仪表板:集群节点Pod/容器部署。Kubernetes 管理员常常使用 Grafana 和 Prometheus 数据源来创建信息丰富的仪表板。

  2. Elasticsearch、Fluentd 和 Kibana组成了 EFK 堆栈,这是三个协同工作的工具的组合。Fluentd 是 Kubernetes 集群节点的日志收集器,它从 Pods 收集日志,并将这些日志发送到 Elasticsearch 搜索引擎,后者将数据摄取并存储在一个集中位置。EFK 堆栈的 UI 是 Kibana,它是一个 Elasticsearch 的可视化插件,允许用户可视化收集的日志和度量指标,并构建自定义仪表板。

现在我们已经看到各种监控、日志记录和告警的选择,我们将讨论如何配置它们。

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

MicroK8s 随 Prometheus Operator 一起提供预集成的插件,处理 Kubernetes 服务的简化监控定义,以及 Prometheus 实例的部署和管理。

注意

Operators 是 Kubernetes 特定的应用程序(Pods),它们自动化配置、管理和优化其他 Kubernetes 部署。Operators 通常负责以下任务:

a. 安装 Kubernetes 集群的规格,并为您的部署提供初始设置和大小调整。

b. 实时重新加载部署和 Pod,以适应任何用户请求的参数变化(热配置重载)。

c. 根据性能数据自动进行上下扩展。

d. 应执行备份、完整性检查和其他维护任务。

启用 Prometheus 插件后,Prometheus Operator 负责安装和配置以下项目:

  • Kubernetes-Prometheus 堆栈

    1. Prometheus 服务器

    2. Alertmanager

    3. Grafana

    4. 主机节点出口器

    5. kube-state-metrics

  • ServiceMonitor实体定义了度量端点的自动配置。

  • Operator 自定义资源定义(CRDs)和 ConfigMaps,可以用来定制和扩展服务,从而使我们的配置完全可移植和声明式。

以下 CRD 由 Prometheus Operator 管理:

  • PrometheusDeployment—Operator 确保与资源定义匹配的部署始终处于运行状态。

  • ServiceMonitor—声明性地指定如何监控一组服务。根据定义,Operator 会自动生成 Prometheus 抓取设置。

  • PrometheusRule—指定一个 Prometheus 规则文件,供 Prometheus 实例加载并执行 Prometheus 告警规则。

  • AlertManager—指定所需的 Alertmanager 部署。Operator 确保与资源定义匹配的部署始终处于运行状态。

如需了解有关 Prometheus Operator 的更多信息,请参阅以下链接:

github.com/prometheus-operator/prometheus-operator

以下图表显示了我们之前讨论的组件:

图 8.1 – Prometheus Operator 组件

图 8.1 – Prometheus Operator 组件

总结一下,我们将使用以下工具来收集、汇总和可视化度量:

  • 从度量端点拉取的 Kubernetes 度量。

  • 使用 Prometheus Node Exporter 监控主机度量。

  • 使用 Prometheus Alertmanager 进行告警。

  • Prometheus 从配置的目标(即前一节讨论的 Kubernetes 端点)以预定的时间间隔收集数据,分析规则表达式,展示结果,还能在匹配特定标准时发送告警。

  • 使用 Grafana 预构建的仪表板进行可视化。

现在我们已经了解了所需工具,接下来我们将深入配置监控和告警堆栈的步骤。以下图示展示了我们的 Raspberry Pi 集群设置:

图 8.2 – Raspberry Pi 集群设置

图 8.2 – Raspberry Pi 集群设置

现在我们已经知道了要做什么,接下来看看需求。

设置 MicroK8s Raspberry Pi 集群的要求

在开始之前,以下是构建 Raspberry Pi Kubernetes 集群的前提条件:

  • 一张 microSD 卡(最低 4 千兆字节GB);推荐 8 GB)

  • 一台带 microSD 卡驱动的计算机

  • 一台 Raspberry Pi 2、3 或 4(一个或多个)

  • 一条 micro-USB 电源线(Pi 4 使用 USB-C)

  • 一条带有互联网连接的 Wi-Fi 网络或以太网电缆

  • (可选)带有高清多媒体接口HDMI)接口的显示器

  • (可选)Pi 2 和 3 的 HDMI 电缆,Pi 4 的 micro-HDMI 电缆

  • (可选)一款通用串行总线USB)键盘

现在我们已经明确了需求,接下来是一步一步的操作指导,教你如何完成整个过程。

第一步 – 创建一个 MicroK8s Raspberry Pi 集群

请按照我们在第五章《在多节点 Raspberry Pi Kubernetes 集群上创建和实施更新》一章中讲解的步骤,来创建一个 MicroK8s Raspberry Pi 集群。以下是快速回顾:

  • 步骤 1:将操作系统OS)镜像安装到安全数字SD)卡上

  • 步骤 1a:配置 Wi-Fi 访问设置

  • 步骤 1b:配置远程访问设置

  • 步骤 1c:配置控制组设置

  • 步骤 1d:配置主机名

  • 步骤 2:安装和配置 MicroK8s

  • 步骤 3:添加工作节点

完全可用的多节点 Kubernetes 集群应该像以下截图所示。总结一下,我们已经在 Raspberry Pi 板上安装了 MicroK8s,并将多个部署加入到集群中。我们还添加了节点到集群中:

图 8.3 – 完全可用的 MicroK8s Kubernetes 集群

图 8.3 – 完全可用的 MicroK8s Kubernetes 集群

现在,我们可以进入下一步,部署监控工具,因为我们已经有了一个完全可用的集群。

默认情况下,MicroK8s 的所有附加组件都是关闭的。因此,必须在安装后激活 Grafana 和 Prometheus。

步骤 2 – 配置 Prometheus、Grafana 和 Alertmanager

在本节中,我们将启用 Prometheus 附加组件并访问 Prometheus 和 Grafana 仪表盘,以便我们可以监控 Kubernetes 集群,并在出现问题时查看警报。使用以下命令启用仪表盘和 Prometheus 附加组件:

microk8s enable dashboard prometheus

以下命令执行输出表明仪表盘和 Prometheus 附加组件已成功启用:

图 8.4 – 启用仪表盘和 Prometheus 附加组件

图 8.4 – 启用仪表盘和 Prometheus 附加组件

启用附加组件需要一些时间,但以下命令执行输出表明 Prometheus 已成功启用:

图 8.5 – 已激活的附加组件

图 8.5 – 已激活的附加组件

Grafana 无法通过命令启用。当启用 Kubernetes 仪表盘时,它会自动启动。

要访问 Kubernetes 仪表盘,我们需要创建一个用户和管理员角色绑定。在接下来的步骤中,我们将为此创建一个部署:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system

创建一个名为 dashboard-adminuser.yaml 的文件,包含前述内容,并使用以下命令创建用户和管理员角色绑定:

kubectl apply -f dashboard-adminuser.yaml

以下命令执行输出确认部署没有错误:

图 8.6 – 创建用户和管理员角色绑定

图 8.6 – 创建用户和管理员角色绑定

要访问仪表盘,我们需要一个访问令牌,可以通过调用 kubectl 命令获取,方法如下:

kubectl -n kube-system describe secret $(microk8s kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')

从命令的输出中复制令牌,并在下一步中使用它。

需要使用以下命令构建与集群的安全通道,以便访问 Kubernetes 仪表盘:

kubectl proxy &

以下命令执行输出确认已创建安全通道,我们可以在下一步中访问仪表盘:

图 8.7 – 为仪表盘创建安全通道

图 8.7 – 为仪表盘创建安全通道

之后,您将能够通过以下地址访问仪表盘:

http://<ip address>>:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/

通过复制和粘贴上一步生成的令牌,您将能够访问集群的基于 Web 的 命令行界面 (CLI),如下图所示:

图 8.8 – Kubernetes 仪表盘

图 8.8 – Kubernetes 仪表盘

如前所述,仪表盘是一个可以通过网页访问的 Kubernetes UI。它可以用于将容器化应用部署到 Kubernetes 集群、进行故障排除并控制集群的资源。仪表盘可以用于多种用途,包括:

  • 所有节点和持久存储卷都列在 管理员概览 中,并显示每个节点的汇总指标。

  • 工作负载视图显示按命名空间分类的所有运行应用程序的列表,以及当前 Pod 的内存利用率和 Deployment 中当前就绪的 Pod 数量。

  • 发现视图显示已公开并启用集群发现的服务列表。

  • 通过使用日志查看器功能,可以深入查看属于单个 Pod 的容器日志。

  • 对于集群中的每个应用程序和所有正在运行的 Kubernetes 资源,存储视图会标识持久卷声明。

启用了所有必需的附加组件后,我们将进入下一步,访问 Prometheus、Grafana 和 Alertmanager。

第 3 步 – 访问 Prometheus、Grafana 和 Alertmanager

在继续执行其他步骤之前,我们可以验证 Grafana、Prometheus 和 Alertmanager 是否在集群上运行。

在 Kubernetes 仪表盘中,导航到 命名空间 下的 监控,然后点击 服务。将显示运行在集群上的监控服务列表、集群 IP 地址、内部端点和端口,如下图所示:

图 8.9 – 验证 Grafana、Prometheus 和 Alertmanager 是否在集群上运行

图 8.9 – 验证 Grafana、Prometheus 和 Alertmanager 是否在集群上运行

从 Kubernetes 仪表盘中(如图 8.9所示),我们可以确保以下组件处于正常运行状态:

  • prometheus-operator Pod——堆栈的核心,负责管理其他部署,例如 Prometheus 服务器或 Alertmanager 服务器

  • node-exporter Pod——每个物理主机(本示例中为一个)

  • kube-state-metrics 导出器

  • prometheus-k8s(副本数:1)

  • alertmanager-main(副本数:1)

  • grafana(副本数:1)

然后,只需将服务 IP 和端口输入浏览器,格式为 <IP 地址>:<端口>,即可访问 Grafana 和 Prometheus 的 UI。Grafana 的登录用户名和密码为 admin/admin

默认情况下,Grafana 使用端口 3000,因此,请在网页浏览器中导航到 http://localhost:3000,你将能够访问 Grafana 界面,其中已经填充了一些有趣的仪表板,如下所示:

图 8.10 – Grafana 预构建仪表板

图 8.10 – Grafana 预构建仪表板

Grafana 自带预安装的 Prometheus 作为数据源,如下图所示:

图 8.11 – Grafana/Prometheus 数据源

图 8.11 – Grafana/Prometheus 数据源

同样,Prometheus UI 也可以访问。无需用户名或密码。默认情况下,Prometheus 使用端口 9090,并公开其内部指标和性能。Node Exporter Prometheus 进程运行在端口 9100 上。它公开有关节点的详细信息,包括存储空间,路径为http://<IP 地址:9090/metrics。你可以在此查看 Prometheus UI 概述:

图 8.12 – Prometheus UI

图 8.12 – Prometheus UI

Prometheus 将根据预定义的配置抓取和存储数据。请访问仪表盘查看 Prometheus 是否获取了此端点在节点上暴露的时间序列信息。

要查看此服务器收集的度量指标列表,请使用下拉菜单,node_旁边的指标可在列表中找到。以cpu metric节点为例,它显示了节点的 CPU 利用率,如下截图所示:

图 8.13 – Prometheus 度量指标可视化

图 8.13 – Prometheus 度量指标可视化

ServiceMonitor 会自动检测并注册 Prometheus 配置中的每个目标,如此处所示:

图 8.14 – Prometheus 抓取目标

图 8.14 – Prometheus 抓取目标

在 Prometheus 服务器界面中,告警标签页显示已创建的告警,如下截图所示:

图 8.15 – Prometheus Alertmanager

图 8.15 – Prometheus Alertmanager

请参考 Prometheus 社区的 GitHub 仓库,查看预定义的告警规则,链接如下:

github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack/templates/prometheus/rules-1.14

总结来说,我们了解了如何通过 Prometheus 插件快速部署 Kubernetes 监控和告警堆栈,同时,这个堆栈也很容易根据需求进行扩展、修改或迁移到新的服务器集群。

生产环境部署时需要注意以下事项:

  • 长期存储——Prometheus 数据库默认存储过去 15 天的度量指标。Prometheus 不提供长期存储功能。没有备份、数据冗余、趋势分析、数据挖掘等选项。

  • 授权和认证——Prometheus 及其组件不提供服务器端的认证、授权或加密功能。

  • 不支持垂直/水平可扩展性。

我们已经了解了如何使用 Prometheus 插件启用 Kubernetes 监控和告警,接下来,我们将探讨如何使用 EFK 工具集配置日志记录、监控和告警堆栈。

使用 EFK 工具集配置日志记录、监控和告警堆栈

在需要分析由多个服务和应用程序在 Kubernetes 集群中运行的 Pods 收集的大量日志数据时,集中式集群级别的日志堆栈可能非常有用。EFK 是最流行的集中式日志解决方案。Elasticsearch 是一个实时搜索引擎,支持全文和结构化搜索,以及分析功能,且是分布式和可扩展的。它最常用于索引和搜索大量的日志数据。Elasticsearch 通常与 Kibana 一起使用,后者是一个强大的数据可视化前端和 Elasticsearch 的仪表盘。Kibana 是一个基于 Web 的工具,可以通过查看 Elasticsearch 日志数据、创建仪表盘和查询来快速查询并深入了解 Kubernetes 应用程序。为了收集、转换并将日志数据传输到 Elasticsearch 后端,我们将使用 Fluentd,这是一个流行的开源数据收集器,它可以跟踪容器日志文件、过滤和更改数据,并将其传输到 Elasticsearch 集群进行索引和存储。下图展示了我们希望通过 EFK 工具集实现的目标:

图 8.16 – 集中式日志解决方案:EFK 工具集

图 8.16 – 集中式日志解决方案:EFK 工具集

由于 EFK 不支持 arm64 架构,我将在本节使用 Ubuntu 虚拟机VM)。设置 MicroK8s 集群的步骤与 第五章**,创建和实施多节点树莓派 Kubernetes 集群更新 中的指示相同。

现在我们已经清楚了要实现的目标,接下来将详细介绍步骤。

步骤 1 – 启用 Fluentd 插件

我们将在本节启用 Fluentd 插件,它允许 EFK 工具集收集日志数据,传递给 Elasticsearch 进行索引,然后通过 Kibana 仪表盘查看聚合日志。使用以下命令启用 Fluentd 插件:

microk8s enable fluent

启用此插件后,Elasticsearch、Fluentd 和 Kibana(EFK 堆栈)将会添加到 MicroK8s 中。

以下命令执行输出确认已启用 EFK 插件:

图 8.17 – 启用 Fluentd 插件

图 8.17 – 启用 Fluentd 插件

在进入下一步之前,让我们确认插件是否已激活。

为此,请使用 microk8s status 命令。以下命令执行输出表示 Fluentd 插件已启用:

图 8.18 – 验证插件是否已激活

图 8.18 – 验证插件是否已激活

所有 EFK 服务均已激活,如下列命令输出所示:

图 8.19 – 验证 EFK Pods 是否正在运行

图 8.19 – 验证 EFK Pods 是否正在运行

现在我们已经启动了 EFK 的所有服务。要访问 Kibana 仪表板,我们需要构建一个安全通道(就像我们为 Kubernetes 仪表板做的那样)到集群,使用下列命令:

microk8s kubectl port-forward -n kube-system service/kibana-logging 8181:5601

以下命令执行输出确认端口转发成功:

图 8.20 – 为 Kibana 仪表板创建安全通道

图 8.20 – 为 Kibana 仪表板创建安全通道

Kibana 仪表板现在应该可以通过以下地址访问:

http://<IP address>:8001/api/v1/namespaces/kube-system/services/kibana-logging/proxy/app/kibana

总结一下,我们现在已经拥有一个完全功能性的 EFK 堆栈,可以进行配置。下一步是开始在 Kibana 仪表板中定义索引模式。

第 2 步 – 定义索引模式

我们将分析 EFK 容器是否能自行启动日志。为此,我们需要建立一个索引模式。具有相似特征的文档集合称为一个索引。索引会被赋予一个名称,在执行索引、搜索、更新和删除其包含的文档时使用该名称来引用它。

启动 Kibana 仪表板,你应该看到 Kibana 欢迎页面,如下图所示:

图 8.21 – Kibana 欢迎页面

图 8.21 – Kibana 欢迎页面

单击logstash-*,如下面的截图所示:

图 8.22 – 创建索引模式

图 8.22 – 创建索引模式

Kibana 将请求一个带时间戳的字段,以便它可以用来可视化时间序列数据。对我们来说,这是@timestamp字段,如下图所示:

图 8.23 – 创建带时间戳字段的索引模式

图 8.23 – 创建带时间戳字段的索引模式

单击创建索引模式,现在只需几分钟,既然我们已经构建了索引模式。你可以在这里看到输出:

图 8.24 – 索引模式创建

图 8.24 – 索引模式创建

从左侧下拉菜单中选择发现选项,你应该看到容器日志事件,如下图所示:

图 8.25 – 使用索引模式发现数据

图 8.25 – 使用索引模式发现数据

下一步是过滤并查看容器启动日志事件,现在我们已经创建了索引模式并整理了数据。

第 3 步 – 过滤并查看数据

左侧将列出所有日志事件,并提供可用于过滤的字段,如下图所示。你可以创建新的过滤器,或者使用Kibana 查询语言KQL)中的kubernetes.podname参数来过滤事件:

图 8.26 – 为特定 Pod 过滤日志事件

图 8.26 – 为特定 Pod 过滤日志事件

日志列表现在已过滤,只显示来自特定 Pod 的日志事件。你可以查看任何事件或过滤以查看更多信息。

启用 Fluent Bit 日志处理器后,它将读取、解析并过滤 Kubernetes 集群中每个 Pod 的日志,为每条日志条目添加以下数据:

  • Pod 名称

  • Pod 标识符 (ID)

  • 容器名称

  • 容器 ID

  • 标签

  • 注释

一旦所有事件被索引,Kibana 的告警配置可以用来创建规则,检测失败场景,并在满足这些条件时执行操作。

有关告警的更多细节,请参见这里:

www.elastic.co/guide/en/kibana/current/alerting-getting-started.xhtml

Fluentd 有一个更轻量的版本,Fluent Bit,这是同一团队为资源有限的情况而创建的。从功能上讲,Fluentd 是一个日志聚合器,而 Fluent Bit 只是一个转发器。Fluentd 提供了一个更强大的生态系统,而 Fluent Bit 更常见于物联网设备中。

有关 Fluent Bit 的更多细节,请参见这里:

fluentbit.io/

恭喜!通过使用 EFK 栈,我们已经学会了如何聚合所有 Kubernetes 容器日志并集中分析它们。

总结一下,我们查看了一些最流行的监控、日志记录和告警栈选项。下一步是确定哪些关键指标需要被监控,以便有效管理你的基础设施和应用程序。

需要监控的关键指标

容器在企业组织中的快速普及为开发人员带来了许多好处。然而,Kubernetes 在部署容器化应用程序时提供的灵活性和可扩展性,也带来了新的复杂性。跟踪由容器抽象出来的应用程序的健康状况,然后再由 Kubernetes 抽象出来,可能没有合适的工具就会变得非常困难,因为应用程序和其运行的服务器之间不再是一对一的关系。

容器化应用程序可以分布在多个环境中,而 Kubernetes 是一个复杂的环境。监控工具应该具备从分布式环境中收集指标的能力,并处理容器化资源的瞬态特性。监控工具依赖服务作为其端点,因为 Pods 和它们的容器处于不断移动和动态调度状态。服务广播一个可以从 Pods 外部访问的 IP 地址,允许服务在 Pods 和容器创建和移除时实时通信。

在 Kubernetes 中,有两个级别的监控,如下所述:

  • 集群监控—监控整个 Kubernetes 集群的健康状态。帮助检查节点是否是最新并且正在运行,每个节点上运行了多少个应用程序,以及集群整体如何使用资源。

  • Pod 监控—跟踪影响单个 Pod 的问题,包括 Pod 的资源使用情况、应用程序度量以及与复制或自动扩展相关的度量。

正如我们在前面章节中讨论的,基于 Kubernetes 的架构已经提供了分析和监控应用程序的框架。即使执行这些应用程序的容器不断在主机之间移动或上下扩展,您仍然可以通过适当的监控解决方案,结合 Kubernetes 内置的抽象,全面了解应用程序的健康状况和性能。

接下来,我们将查看一些应该监控的关键度量。以下列出了这些度量。

集群级别—以下集群状态度量可以为您提供集群当前状态的概览。它们能够揭示节点或 Pods 的问题,提醒您可能存在瓶颈或需要扩展集群的风险:

表 8.2 – 集群状态度量

表 8.2 – 集群状态度量

节点级别—以下措施为您提供节点健康状况的概览,并帮助判断调度程序是否可以在该节点上调度 Pods。当您将资源利用率与资源请求和限制进行比较时,您可以更好地了解集群是否有足够的资源来处理工作负载并容纳新的工作负载。维护并跟踪集群各个级别的资源利用率,尤其是节点及其上运行的 Pods,非常关键:

表 8.3 – 节点级别度量

表 8.3 – 节点级别度量

Pod 级别—尽管一个 Pod 可能正在运行,但如果它不可访问,意味着它还不能接收流量。在某些情况下,这是正常现象,例如当 Pod 刚启动时,或者当 Pod 的规格发生变化并进行部署时。然而,如果您注意到不可用的 Pods 数量激增,或者 Pods 一直不可用,这可能意味着存在配置问题。跟踪以下度量,以评估 Pods 的健康状况:

表 8.4 – Pod 级别度量

表 8.4 – Pod 级别度量

容器级别—以下是一些应跟踪的容器度量,用于评估容器健康状况:

表 8.5 – 容器度量

表 8.5 – 容器度量

存储—卷在 Kubernetes 存储架构中作为关键的抽象层存在。容器可以通过称为卷声明的机制动态请求存储资源,且卷可以是持久的或非持久的,具体详情如下:

表 8.6 – 存储度量

表 8.6 – 存储度量

控制平面—集群中的工作节点和 Pods 由控制平面管理。以下是需要监控的控制平面组件:

  • etcd—存储集群中每个节点可以使用的配置信息

  • API 服务器——验证并配置如 Pods、服务、复制控制器等 API 对象的数据。

  • 调度器——管理工作负载的使用和 Pod 分配到可用节点的任务。

  • 控制器管理器——一个守护进程,负责收集并将数据发送到 API 服务器。

你可以在这里查看更多关于这些组件的详细信息:

表 8.7 – 控制平面指标

表 8.7 – 控制平面指标

Kubernetes 事件

Kubernetes 事件是 Kubernetes 中的一种资源类型,当其他资源的状态变化、发生错误或需要向系统广播其他消息时,事件会自动创建。以下是需要监控的各种事件类型:

  1. 失败事件——容器经常被创建,但操作经常会失败;因此,Kubernetes 无法成功创建该容器。失败的事件通常与镜像拉取错误相关。这些失败可能是由于拼写错误、权限不足或上游构建失败造成的。此外,节点本身也可能会失败。当这些失败发生时,应用程序应回退到功能正常的剩余节点,但需要某种警报系统来确定故障原因。因为失败是一个决定性问题——即你的容器在问题解决之前无法运行——你应该特别关注此事件类型。

  2. 驱逐事件——某些 Pods 相较于其运行时可能消耗过多的计算和内存资源。Kubernetes 通过驱逐这些 Pods,并将磁盘、内存或 CPU 空间重新分配到其他地方来解决这个问题。

  3. 存储特定事件——Pod 内的存储通常被应用程序和工作负载使用。各个提供商提供的卷存储着应用运行时所需的重要内容。Pod 在创建时会挂载这些卷,为成功运行铺平道路。这些事件可以在存储卷出现异常行为时发出警报。此外,节点的健康状况可能不足以挂载卷,这些错误可能让 Pod 看起来像是刚刚启动。但发现这些事件可以帮助你解决由错误的卷挂载导致的潜在问题。

  4. FailedScheduling 事件发生在 Kubernetes 调度器无法找到合适的节点时。

  5. NodeNotReady 事件表示某个节点尚未准备好进行 Pod 调度。

以下是需要监控的 Kubernetes 事件、指标和警报标准:

表 8.8 – Kubernetes 事件指标

表 8.8 – Kubernetes 事件指标

总结

总结一下,我们已经覆盖了关键的 Kubernetes 组件,以及可以帮助你跟踪它们健康状况和性能的指标和事件。我们还讨论了如何使用 Kubernetes 内置的 API 和工具收集所有指标,从而让你能够全面了解容器基础设施和工作负载的状态。

我们已经研究了 Prometheus、Grafana 和 Alertmanager 作为设置监控和告警堆栈的工具。我们还介绍了如何使用 EFK 工具集设置集中式的集群级别日志记录堆栈,该工具集能够处理大量日志数据。最后,我们回顾了应该监视的关键指标,以便成功管理基础设施和应用程序。

下一章,我们将探讨如何使用 Kubeflow MLOps 平台开发和部署机器学习模型。Kubeflow 和 MicroK8s 提供可靠高效的操作以及基础设施优化。

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

使用 Prometheus、Grafana 和 Alertmanager 工具配置监控和告警堆栈

第九章:使用 Kubeflow 运行 AI/MLOps 工作负载

在上一章中,我们讨论了几种日志记录、监控和警报选项,以便全面了解我们的容器基础设施和工作负载。关于设置监控和警报堆栈的工具,我们介绍了 Prometheus、Grafana 和 Alert Manager。我们还讨论了如何使用 EFK 工具集设置一个集中式的集群级日志堆栈,以处理大量的日志数据。最后,我们探讨了应密切关注的关键指标,以便有效管理你的基础设施和应用程序。

在本章中,我们将介绍使用 Kubeflow MLOps 平台创建 机器学习 (ML) 流水线的步骤,该流水线将构建并部署一个示例 ML 模型。ML 是人工智能(AI)领域的一个子领域。ML 的目的是教会计算机从你提供的数据中学习。机器并不通过描述行为来进行操作,而是根据预期行为的样本来调整其算法。训练好的模型是算法与学习到的参数结合后所生成的代码。

以下是典型 ML 工作流中各个阶段的高级概览:

  1. 获取并准备相关数据

  2. 开发 ML 模型

  3. 训练模型、评估模型精度,并调整模型

  4. 部署训练好的模型

  5. 从模型中获取预测结果

  6. 监控正在进行的预测

  7. 管理模型及其版本

这些是迭代阶段。在整个过程中,你可能需要重新思考并返回到之前的阶段。

ML 流水线有助于自动化 ML 工作流,并允许将序列数据转换并相关联,以便在模型中进行评估。它还允许生成输出。ML 流水线旨在将数据从原始数据格式转换为有意义的信息。它提供了一种构建多 ML 并行流水线系统的方法,用于调查各种 ML 算法的输出。流水线中有多个阶段。每个阶段接收来自前一阶段处理的数据 —— 例如,处理单元的输出会输入到下一阶段。

Kubeflow 是一个供数据科学家构建和实验 ML 流水线的平台。它允许你部署和构建 ML 工作流。你可以使用 Kubeflow 配置来指定工作流所需的 ML 工具。然后,可以将工作流部署到云端、本地及本地部署的多个平台进行测试和生产使用。

数据科学家和 ML 工程师使用 Kubeflow 在 MicroK8s 上快速原型开发、构建和部署 ML 流水线。Kubeflow 通过弥合 AI 工作负载与 Kubernetes 之间的差距,使 MLOps 更加可管理。

此外,Kubeflow 在 MicroK8s 上的设置和配置非常简便,并且轻量级,能够模拟生产环境以进行流水线的创建、迁移和部署。

在本章中,我们将涵盖以下主要内容:

  • 机器学习工作流概览

  • 部署 Kubeflow

  • 访问 Kubeflow 仪表板

  • 创建一个 Kubeflow 管道来构建、训练和部署一个示例机器学习模型

  • 推荐 — 在 Kubernetes 上运行 AL/ML 工作负载

机器学习工作流概览

Kubeflow 旨在成为您的 Kubernetes 机器学习工具包。您可以使用 Kubeflow 配置来指定工作流所需的机器学习工具,然后将工作流部署到各种平台进行测试和生产使用。

在深入了解机器学习工作流的复杂性之前,让我们先来看看 Kubeflow 的各个组件。

介绍 — Kubeflow 及其组件

Kubeflow 是一个基于 Kubernetes 部署、扩展和管理复杂系统的系统。对于数据科学家来说,Kubeflow 是构建和测试机器学习管道的首选平台。它也适用于希望在不同环境中部署机器学习系统的机器学习开发人员和运维团队,涵盖开发、测试和生产等各个环节。

Kubeflow 是一个框架,用于在 Kubernetes 之上构建机器学习系统的各个组件,如下图所示:

图 9.1 — Kubeflow 组件在 Kubernetes 之上的布局

图 9.1 — Kubeflow 组件在 Kubernetes 之上的布局

我们可以通过利用 Kubeflow 配置接口来指定工作流所需的机器学习工具。然后,工作流可以部署到多个云平台、本地和内部平台进行测试和生产使用。

以下机器学习工具由 Kubeflow 支持:

  • Chainer:基于 Python 的深度学习框架。

  • Jupyter:一个互动式开发环境,适用于笔记本、代码和数据,可以通过网页访问。用户可以通过其多功能的界面在数据科学、科学计算、计算新闻学和机器学习中创建和安排工作流。

  • MPI:这是一个标准化且可移植的消息传递标准,可用于并行计算平台。

  • MXNet:这是一个开源深度学习软件框架,用于训练和部署深度神经网络。

  • PyTorch:这是一个基于 Torch 库的开源机器学习框架,广泛应用于计算机视觉和自然语言处理等领域。

  • Scikit-learn:这是一个 Python 机器学习库,支持支持向量机、随机森林、梯度提升、k-means 和 DBSCAN 等分类、回归和聚类技术,并且设计上与 Python 的数值计算和科学计算库 NumPy 和 SciPy 兼容。

  • TensorFlow:这是一个开源的机器学习和人工智能软件库,适用于各种应用,特别是深度神经网络的训练和推理。

  • XGBoost:这是一个开源软件库,提供了一个正则化的梯度提升框架,支持 C++、Java、Python、R、Julia、Perl 和 Scala 等语言。

以下是构成 Kubeflow 的逻辑组件:

  • Dashboard 允许你快速访问集群中安装的 Kubeflow 组件。

  • Kubeflow Notebooks 允许你通过将其封装在 pods 中,在 Kubernetes 集群内运行基于 Web 的开发环境。

  • Kubeflow Pipelines 是一个基于 Docker 的平台,用于创建和部署可移植、可扩展的机器学习工作流。

  • KServing 提供高性能、高抽象接口,用于使用标准机器学习框架(如 TensorFlow、XGBoost、scikit-learn、PyTorch 和 ONNX)为生产模型提供服务,解决生产模型服务的应用场景。

  • TensorFlow Serving 负责为 TensorFlow 模型提供服务功能。

  • PyTorch Serving 负责与 Seldon 一起为 PyTorch 模型提供服务功能。

  • Seldon 在 Kubernetes 上管理、提供和扩展任何语言或框架的模型。

  • Katib 是一个可扩展且灵活的超参数调优框架,紧密集成了 Kubernetes。

  • 训练操作符 通过操作符训练机器学习模型。

  • Istio 集成(针对 TF Serving)提供了诸如指标、身份验证和配额、发布和 A/B 测试等功能。

  • Argo workflows 是一个工作流引擎,Kubeflow Pipelines 使用它来执行各种操作,如监控 pod 日志、收集工件、管理容器生命周期等。

  • Prometheus 负责 Kubeflow 指标和 Kubernetes 组件的日志记录和监控。

  • 多租户 是自服务的 – 新用户可以通过 UI 自行注册来创建和拥有他们的工作区。当前是围绕 用户命名空间 构建的。

现在我们已经了解了各种 Kubeflow 组件和机器学习工具,让我们深入理解机器学习工作流的具体内容。

机器学习工作流简介

机器学习工作流通常包含多个阶段,在开发和部署机器学习系统时,这是一个迭代过程。为了确保模型继续生成所需的结果,必须审查机器学习工作流各个阶段的输出,并根据需要调整模型和参数。

下图展示了实验阶段工作流的各个阶段顺序:

图 9.2 – 实验阶段工作流

图 9.2 – 实验阶段工作流

在实验阶段,模型会基于初始假设构建,并通过迭代测试和更新,以实现预期的结果:

  1. 确定需要由机器学习系统解决的问题。

  2. 收集并分析训练机器学习模型所需的数据。

  3. 选择一个机器学习框架和算法,然后编写模型的第一个版本。

  4. 实验数据和模型的训练。

  5. 调整模型的超参数。

下图展示了生产阶段工作流的各个阶段顺序:

图 9.3 – 生产阶段工作流

图 9.3 – 生产阶段工作流

在生产阶段,将部署一个处理以下任务的系统:

  • 将数据转化为训练系统所需的格式。为了确保模型在训练和预测过程中表现一致,实验阶段和生产阶段的转化过程必须相同。

  • 开发 ML 模型。

  • 为在线预测或批处理提供模型服务。

  • 监控模型的性能,并将结果反馈到模型中,以进行调优或重新训练过程。

现在我们知道了实验和生产阶段的各项活动,接下来看看每个阶段涉及的 Kubeflow 组件。

每个阶段的 Kubeflow 组件

以下图表展示了实验阶段工作流的各个步骤以及每个阶段涉及的 Kubeflow 组件:

图 9.4 – 实验阶段步骤和 Kubeflow 组件

图 9.4 – 实验阶段步骤和 Kubeflow 组件

以下图表展示了生产阶段工作流的各个步骤以及每个阶段涉及的 Kubeflow 组件:

图 9.5 – 生产阶段步骤和 Kubeflow 组件

图 9.5 – 生产阶段步骤和 Kubeflow 组件

Kubeflow 中一些最重要的组件如下:

  • 可以使用 Kubeflow 的服务创建和管理 Jupyter 笔记本。笔记本用于交互式数据科学和 ML 工作流实验。

  • Kubeflow Pipelines 是一个基于容器的平台,用于创建、部署和管理基于 Docker 容器的多步骤 ML 过程。

  • Kubeflow 拥有多个组件,可用于 ML 训练、超参数调整以及跨多个平台的工作负载服务。

现在我们已经看过了涉及 ML 过程各个阶段的 Kubeflow 组件和步骤,接下来让我们来看一下 Kubeflow Pipelines。

Kubeflow Pipelines

Kubeflow Pipelines 是 Kubeflow 中最重要的元素之一,使 AI/ML 实验具有可重现性、可组合性、可扩展性,并且易于共享。每个管道组件,表示为一个块,都是一个自包含的代码片段,打包为 Docker 镜像。它具有输入(参数)和输出,并完成管道中的一个阶段。

最后,当管道运行时,每个容器将根据 Kubernetes 调度在集群中执行,同时考虑依赖关系。这种容器化架构使得在工作流发生变化时,轻松重用、共享和替换组件,符合常见的需求。

运行管道后,结果可以在 Kubeflow 仪表板上的管道 UI 中查看。在这里,您可以调试和调整参数,并创建更多的“运行”。

总结一下,Kubeflow 是为 Kubernetes 提供的 ML 工具包,提供以下功能:

  • 为希望构建和实验 ML 管道的数据科学家提供的平台

  • 为希望将 ML 系统部署到各种环境中进行开发、测试和生产级服务的 ML 工程师和运维团队提供的平台。

  • 用于创建和管理 Jupyter notebook 的服务,支持交互式数据科学和 ML 工作流的实验。

  • 一个基于 Docker 容器的平台,用于构建、部署和管理多步骤的机器学习工作流。

  • 一些组件,可用于跨多个平台构建你的 ML 训练、超参数调优和服务工作负载。

在下一部分中,我们将回顾部署 Kubeflow 的步骤。

部署 Kubeflow。

从 MicroK8s 版本 1.22 开始,Kubeflow 不再作为附加组件提供;相反,Ubuntu 发布了 Charmed Kubeflow (charmed-kubeflow.io/),这是一整套 Kubernetes 操作器,用于提供构成最新版本 Kubeflow 的 30 多个应用和服务,便于在任何地方进行简单操作,从桌面到本地、公共云和边缘设备。

Kubeflow 作为 charm 提供,这是一个包含 Kubernetes 操作器的软件包,此外还包含允许你将多个操作器集成到统一系统中的信息。这项技术利用了 Juju 操作生命周期管理器OLM)来提供从第 0 天到第 2 天的 Kubeflow 操作。

为了概述 Juju,它是一个用于云软件操作的开源建模工具。它使你能够快速高效地在公共云、物理服务器、OpenStack 和容器上部署、设置、管理、维护和扩展云应用。更多细节请参阅 ubuntu.com/blog/what-is-juju-introduction-video

Juju 提供了部署的 Kubernetes 操作器的集中视图,包括它们的配置、可扩展性和状态,以及连接它们的集成线。它跟踪每个操作器的潜在升级和更新,并协调它们之间事件和通信的流动。

Charmed Kubeflow 提供两种套件:

  • Full (charmhub.io/kubeflow): 包含所有 Kubeflow 服务。需要至少 14 GB 内存和 60 GB 存储空间。

  • Lite (charmhub.io/kubeflow-lite): 移除了完整套件中较少使用的服务,同时保持了用户友好的仪表盘。此套件专为资源有限的环境设计。

现在我们对 Charmed Kubeflow 有了更好的了解,让我们来回顾一下 Kubeflow 的部署过程。

我们想要实现的目标。

我们希望在这一部分中做以下事情:

  1. 安装并配置 Microk8s。

  2. 安装 Juju 操作生命周期管理器。

  3. 部署 Kubeflow。

现在我们知道了要做的事情,让我们看看设置 Kubeflow 平台的前提条件:

  • 一台运行 Ubuntu 20.04(focal)或更高版本的虚拟机。

  • 至少需要 16 GB 的空闲内存和 20 GB 的磁盘空间

  • 访问互联网以下载所需的 snaps 和 charms

既然我们已经确定了前提条件,让我们学习如何设置 Kubeflow 平台。

第 1 步 – 安装和配置 MicroK8s

以下步骤与我们在第五章中跟随的步骤类似,创建并实施多节点 Raspberry Pi Kubernetes 集群的更新,用于创建 MicroK8s 集群。

我们将设置 snap 来安装 Kubernetes 的 1.21 版本,因为 Kubeflow 目前不支持更新的 1.22 版本。

使用以下命令安装 MicroK8s:

sudo snap install microk8s --classic --channel=1.21/stable

以下输出表明 MicroK8s 已成功安装:

图 9.6 – MicroK8s 安装

图 9.6 – MicroK8s 安装

正如我们之前看到的,MicroK8s 创建了一个名为 microk8s 的组,这样就可以在不使用 sudo 执行每个命令的情况下工作。我们将把当前用户添加到该组,以便更轻松地运行命令:

sudo usermod -a -G microk8s $USER
newgrp microk8s

确保能够正确访问 kubectl 配置文件:

sudo chown -f -R $USER ~/.kube

一旦 MicroK8s 安装完成,它将开始启动。Kubernetes 集群现在完全运行,如下图所示:

图 9.7 – MicroK8s 完全运行中

图 9.7 – MicroK8s 完全运行中

在安装 Kubeflow 之前,让我们先启用一些插件。我们将设置 DNS 服务,以便应用程序能够发现彼此,还包括存储、用于访问 Kubeflow 组件的入口控制器,以及 MetalLB 负载均衡器应用程序。所有这些都可以通过以下命令同时启用:

microk8s enable dns storage ingress metallb:10.64.140.43-10.64.140.49

通过此命令,我们已经指示 MetalLB 在 10.64.140.43 - 10.64.140.49 范围内分配地址。

以下输出显示插件正在启用:

图 9.8 – 插件成功启用

图 9.8 – 插件成功启用

MicroK8s 可能需要几分钟时间来安装和配置这些额外的功能。在继续之前,我们应该再次检查插件是否已正确启用,以及 MicroK8s 是否已准备好使用。从以下输出可以推断,所有必需的插件都已启用:

图 9.9 – 插件成功启用

图 9.9 – 插件成功启用

我们可以使用 microk8s status 命令并指定 --wait-ready 选项来实现这一点,该选项指示 MicroK8s 在返回之前完成其当前正在进行的所有进程。

既然我们已经有了一个正在运行的 Kubernetes 集群,让我们来安装 Juju。

第 2 步 – 安装 Juju 操作生命周期管理器

正如我们之前讨论的,Juju 是云端、裸机或 Kubernetes 的 OLM。它可以用于部署和管理构成 Kubeflow 的各种组件。

与 MicroK8s 类似,Juju 也可以通过以下命令从 snap 包安装:

sudo snap install juju –-classic

以下输出显示 Juju snap 已成功安装:

图 9.10 – 已安装 Juju

图 9.10 – 已安装 Juju

由于 Juju OLM 可识别 MicroK8s,因此无需进一步设置或配置。要在我们使用 MicroK8s 架设的 Kubernetes 会话中部署 Juju 控制器,我们只需运行以下命令:

juju bootstrap microk8s --agent-version="2.9.22"

注意

在最新版本中,您可以只使用latest而不是指定代理版本。

以下输出显示 Juju OLM 引导配置已成功:

图 9.11 – 已引导 Juju OLM

图 9.11 – 已引导 Juju OLM

控制器是 Juju 的基于 Kubernetes 的代理,可用于部署和控制 Kubeflow 组件。控制器可以与多种模型配合工作,这些模型对应 Kubernetes 命名空间。建议为 Kubeflow 设置新模型:

juju add-model kubeflow

注意

在撰写本文时,模型必须命名为kubeflow,但计划在未来版本中解决此问题。

以下输出显示kubeflow模型已成功添加:

图 9.12 – Juju 模型添加

图 9.12 – Juju 模型添加

现在 Kubeflow 模型已添加,我们的下一步是部署 Charmed Kubeflow bundle。Charmed Kubeflow 本质上是一个魅力收藏。每个魅力部署和控制一个单独的应用程序,这些应用程序组成 Kubeflow。您可以通过逐个部署魅力并将它们连接在一起来创建 Kubeflow,仅安装所需的组件。但是,为方便起见,提供了三个捆绑包。这些捆绑包本质上是某种 Kubeflow 部署的配方,以这种方式设置和连接应用程序,最终您将获得一个可工作的部署,而付出的努力最少。

完整的 Kubeflow bundle 将需要大量资源(至少 4 个 CPU、14 GB 的空闲 RAM 和 60 GB 的磁盘空间),因此从kubeflow-lite bundle 开始是最佳选择:

juju deploy kubeflow-lite –-trust

以下输出显示 Juju 将开始获取应用程序并将它们部署到 MicroK8s Kubernetes 群集。这个过程可能需要相当长的时间,但根据您的硬件配置可能有所不同:

图 9.13 – Juju 部署

图 9.13 – Juju 部署

通过运行juju status命令,您可以跟踪部署的进度:

图 9.14 – Juju 部署成功

图 9.14 – Juju 部署成功

由于许多组件依赖于其他操作的运行,可能会出现错误消息,因此在一切正常运行之前可能需要一些时间。

现在捆绑已部署,让我们完成一些安装后的配置。

步骤 3 – 安装后配置

需要设置一些配置项,包含 URL,这样我们就可以进行身份验证并访问仪表盘服务。具体配置依赖于底层网络提供者,但对于这个本地部署,我们知道在本地 MicroK8s 上运行时的 URL。可以使用以下命令在 Juju 中配置:

juju config dex-auth public-url=http://10.64.140.43.nip.io
juju config oidc-gatekeeper public-url=http://10.64.140.43.nip.io

以下输出确认 dex-auth public-urloidc-gatekeeper public-url 已成功设置:

图 9.15 – 设置仪表盘服务的 public-url

图 9.15 – 设置仪表盘服务的 public-url

运行以下命令以启用基本身份验证,并为 Kubeflow 部署创建用户名和密码:

juju config dex-auth static-username=admin
juju config dex-auth static-password=admin

以下输出确认 Kubeflow 部署的用户名和密码已成功设置:

图 9.16 – 设置 Kubeflow 部署的用户名和密码

图 9.16 – 设置 Kubeflow 部署的用户名和密码

至此,我们已经安装并配置了 MicroK8s。我们还部署了 Juju Charmed Operator 框架来管理应用程序和自动化操作,并集成了所有所需的 Kubeflow 组件。最后,我们配置了 Kubeflow 仪表盘服务的身份验证。现在,让我们来学习如何访问 Kubeflow 仪表盘。

访问 Kubeflow 仪表盘

Kubeflow 仪表盘让你可以轻松访问集群中安装的所有 Kubeflow 组件。将浏览器指向 http://10.64.140.43.nip.io(我们之前设置的 URL),你将进入登录界面,在那里我们可以输入用户名 admin 和密码 admin(这些组件之前我们已经设置过)。

欢迎 页面应该会出现。点击 开始设置 将引导你进入 创建命名空间 页面。当你输入命名空间并点击 完成 按钮时,仪表盘将会出现,如下截图所示:

图 9.17 – Kubeflow 仪表盘

图 9.17 – Kubeflow 仪表盘

很棒!我们刚刚安装了 Kubeflow。

既然 Kubeflow 已经安装并且可以运行,让我们来学习如何将 ML 模型转化为 Kubeflow 管道。

创建一个 Kubeflow 管道来构建、训练和部署一个示例 ML 模型

在本节中,我们将使用 Fashion MNIST 数据集和 TensorFlow 的基础分类方法,逐步构建管道,并将示例 ML 模型转化为 Kubeflow 管道。

在部署 Kubeflow 之前,我们先来看一下我们将要使用的数据集。Fashion-MNIST (github.com/zalandoresearch/fashion-mnist) 是一个 Zalando 文章图像数据集,包含 60,000 个训练样本和 10,000 个测试样本。每个样本都是一个 28 x 28 的灰度图像,标签来自 10 个类别之一。

数据集中的每个训练或测试项都被分配到以下标签之一:

表 9.1 – Fashion MNIST 数据集中的类别表 9.1 – Fashion MNIST 数据集中的类别

表 9.1 – Fashion MNIST 数据集中的类别

现在我们的数据集已准备好,可以通过 Kubeflow 仪表板启动一个新的笔记本服务器。

步骤 1 – 从 Kubeflow 仪表板启动新的笔记本服务器

你可以通过点击新建服务器按钮来启动一个新的笔记本。在笔记本服务器标签页中,选择一个 Docker 镜像用于笔记本服务器,并为其命名名称。选择适当的CPURAM工作空间卷值,然后点击启动

要浏览由服务器公开的 Web 界面,请点击连接

图 9.18 – 启动新的笔记本服务器

图 9.18 – 启动新的笔记本服务器

确保新笔记本上启用了允许访问 Kubeflow Pipelines,这样我们才能在下一步中使用 Kubeflow Pipelines SDK:

图 9.19 – 新的笔记本配置

图 9.19 – 新的笔记本配置

从右侧菜单启动一个新的终端(git命令):

$ git clone https://github.com/manceps/manceps-canonical.git

这将克隆并打开KF_Fashion_MNIST笔记本,如下图所示:

图 9.20 – Fashion MNIST 笔记本

图 9.20 – Fashion MNIST 笔记本

在笔记本的第一部分中,我们可以熟悉我们拥有的数据集;我们将通过探索数据进行快速分析。以下是对数据集的简要回顾:

  • 训练标签有 60,000 个,测试标签有 10,000 个

  • 每个标签对应 10 个类别名称中的一个,是一个介于 0 和 9 之间的数字

在开始任何形式的分析之前,理解数据总是一个好主意。在笔记本的1.4 节中,我们将对数据进行预处理——即,数据必须进行归一化,使每个值都在 0 和 1 之间,以便成功训练模型。

以下截图显示了值在 0 和 255 之间:

图 9.21 – 数据集中的第一张图片

图 9.21 – 数据集中的第一张图片

在下一步,我们必须将训练集和测试集的值除以255来缩放数据。关键的是,训练集和测试集必须以相同的方式进行预处理:

图 9.22 – 将训练集和测试集的值除以 255

图 9.22 – 将训练集和测试集的值除以 255

以下是 Jupyter 笔记本执行前述代码的输出:

图 9.23 – 数据预处理

图 9.23 – 数据预处理

让我们检查训练集中的前 25 张图片及其类别名称,确保数据格式正确。然后,我们就可以开始构建和训练网络了:

图 9.24 – 训练集前 25 张图片的图表

图 9.24 – 训练集前 25 张图片的图示

以下是前面代码的 Jupyter notebook 输出:

图 9.25 – 检查训练集中的 25 张图片

图 9.25 – 检查训练集中的 25 张图片

现在数据已经预处理完毕,我们可以构建模型。

我们正在处理的 TensorFlow 模型是一个基本分类示例(www.tensorflow.org/tutorials/keras/classification)。

步骤 2 – 创建一个 Kubeflow 管道

我们将使用 Kubeflow Pipelines SDK,它是一组用于指定和运行机器学习工作流的 Python 包。一个管道是机器学习工作流的描述,包含了工作流中各个步骤的所有组件,以及它们如何相互作用。

在当前用户空间中安装 Kubeflow Pipelines SDK(kfp),以确保您可以在 Jupyter notebook 实例中访问所需的包:

图 9.26 – 安装 Kubeflow Pipelines SDK

图 9.26 – 安装 Kubeflow Pipelines SDK

既然我们已经安装了 Kubeflow Pipelines SDK,下一步是使用func_to_container_op为 Docker 容器创建 Python 脚本。

为了将我们的 Python 代码打包到容器中,我们必须创建一个包含管道中逻辑步骤的标准 Python 函数。在这个例子中,已经定义了两个函数:trainpredict。我们的模型将通过训练组件进行训练、评估并保存(参考笔记本中的第 2.2 节):

图 9.27 – 训练并保存的模型

图 9.27 – 训练并保存的模型

神经网络的层必须在模型编译之前进行配置。层是神经网络中最基本的组成部分。数据被输入到层中,然后从中提取表示:

图 9.28 – 需要配置的神经网络层

图 9.28 – 需要配置的神经网络层

预测组件将模型应用于测试数据集中的一张图片,从而生成预测结果:

图 9.29 – 从测试数据集中进行预测

图 9.29 – 从测试数据集中进行预测

我们的最终步骤是将这些函数转换为容器组件。可以使用func_to_container_op方法来实现这一点:

图 9.30 – 将 Python 脚本转换为 Docker 容器

图 9.30 – 将 Python 脚本转换为 Docker 容器

在将 Python 脚本转换为 Docker 容器之后,下一步是定义一个 Kubeflow 管道。

Kubeflow 使用 YAML 模板来定义 Kubernetes 资源。通过 Kubeflow Pipelines SDK,无需手动修改 YAML 文件,您可以描述代码如何执行。它在编译时生成一个压缩的 YAML 文件,定义我们的管道。这个文件可以在未来重用或共享,使工作流具有可扩展性和可重复性。

我们的第一步是启动一个 Kubeflow 客户端,它包含 Kubeflow Pipelines API 的客户端库,允许我们直接在 Jupyter notebook 中构建更多实验并运行这些实验:

图 9.31 – 启动 Kubeflow 客户端

图 9.31 – 启动 Kubeflow 客户端

前面的组件构建了一个客户端来与管道的 API 服务器进行通信。下一步将是设计管道的各个组件。

管道函数已经定义,并且包含一些在执行期间将传递给不同组件的参数。Kubeflow Pipelines 是声明式构建的。这意味着代码在管道编译之前不会执行:

图 9.32 – 定义管道

图 9.32 – 定义管道

为了在组件之间保存和持久化数据,可以使用VolumeOp方法快速创建持久化卷声明。

VolumeOp的参数包括以下内容:

  • name:在创建卷操作时显示的名称

  • resource_name:可以被其他资源引用的名称

  • size:卷声明的大小

  • modes:卷的访问模式

现在终于是定义我们管道的组件和依赖关系的时候了。可以使用ContainerOp来完成这一任务,ContainerOp是一个从容器中定义管道组件的对象:

图 9.33 – 创建训练和预测组件

图 9.33 – 创建训练和预测组件

train_oppredict_op组件接受来自原始 Python 函数的参数。我们在函数末尾附加VolumeOp,并提供一个包含路径和与之关联的持久化卷的字典,这些卷将在执行前挂载到容器中。

train_op使用pvolumes字典中的vop.volume值时,<Container Op>中的pvolume参数确保使用来自前一个ContainerOp的卷,而不是创建一个新的卷:

图 9.34 – 将卷附加到容器

图 9.34 – 将卷附加到容器

ContainerOp的参数包括以下内容:

  • name:在运行时显示的组件执行名称

  • image:用于 Docker 容器的镜像标签

  • pvolumes:一个字典,包含路径和与之关联的持久化卷,将在执行前挂载到容器中

  • arguments:容器在运行时执行的命令

现在我们已经为训练和预测创建了独立的组件,我们可以开始在笔记本中编译和运行管道代码。

步骤 3 – 编译和运行

最后,是时候在笔记本中编译和运行管道代码了。笔记本指定了运行名称和实验(多个运行的集合),然后在 Kubeflow 仪表板中显示。通过点击笔记本中的运行链接,你可以看到管道在 Kubeflow Pipelines UI 中运行:

图 9.35 – 编译和运行管道

图 9.35 – 编译和运行管道

现在管道已经创建并设置为运行,我们可以查看其结果。通过点击笔记本中的运行链接,我们可以进入 Kubeflow Pipelines 仪表板。管道定义的组件将显示出来。随着它们的完成,数据管道的路径会被更新:

图 9.36 – Kubeflow Pipelines 仪表板

图 9.36 – Kubeflow Pipelines 仪表板

要查看组件的详细信息,我们可以直接点击它,并通过几个标签进行导航。要查看在运行组件时生成的日志,请前往日志标签:

图 9.37 – 模型训练日志

图 9.37 – 模型训练日志

随着模型训练的进行,损失和准确率指标会被显示。在训练数据上,模型的准确率约为 0.91(即 91%),如图 9.37所示。

一旦echo_result组件执行完毕,你可以检查该组件的日志,查看发生了什么。它将显示预测的图像类别、模型对预测的信心以及图像的实际标签:

图 9.38 – 来自管道的最终预测结果

图 9.38 – 来自管道的最终预测结果

通过这些,我们使用了 Fashion MNIST 数据集和 TensorFlow 的基础分类将示例模型转化为一个 Kubeflow 管道。

现在,让我们来看一下在 Kubernetes 上运行 AI/ML 工作负载的最佳实践。

建议 – 在 Kubernetes 上运行 AL/ML 工作负载

如果你是数据科学家或 ML 工程师,你可能在思考如何高效地部署你的 ML 模型。你本质上会寻找方法来扩展模型,将其分布到服务器集群中,并利用各种技术优化模型性能。

这些都是 Kubernetes 非常擅长的任务。但 Kubernetes 并不是为了成为一个 ML 部署平台而设计的。然而,随着越来越多的数据科学家转向 Kubernetes 来运行他们的模型,Kubernetes 和 ML 正成为流行的技术栈。

作为一个训练和部署 ML 模型的平台,Kubernetes 提供了几个关键的优势。为了理解这些好处,让我们比较一下 Kubernetes 解决方案提供的主要挑战和解决方案:

表 9.2 – Kubernetes 解决方案提供表 9.2 – Kubernetes 解决方案提供

表 9.2 – Kubernetes 解决方案

Kubernetes 有助于抵消数据科学家在大规模运行模型时面临的一些最重要的挑战。现在,让我们来看一下运行 AI/ML 工作负载的一些最佳实践。

运行 AI/ML 工作负载的最佳实践

随着机器学习从研究走向实际应用,我们必须提升其运营过程的成熟度。为了解决这些挑战,我们需要结合 DevOps 和数据工程方法,以及一些专门针对机器学习的方法。

MLOps 将机器学习、DevOps 和数据工程结合成一套方法。MLOps 旨在以可靠且高效的方式在生产环境中部署和管理机器学习系统。以下是一些 MLOps 的主要实践:

  • 数据管道:机器学习模型始终需要某种类型的数据转换,通常通过脚本或甚至笔记本中的单元格来完成,这使得它们难以管理并保持一致运行。创建一个独立的数据管道在代码重用、运行时可见性、管理和可扩展性方面带来了许多好处。

  • 管道版本:大多数机器学习模型需要两种管道版本:一个用于训练,一个用于服务。这是因为数据格式和访问方法通常彼此非常不同,特别是对于需要在实时请求中提供服务的模型(与批处理预测不同)。机器学习管道应该是一个独立于任何特定数据的纯代码产物。这意味着可以在源代码控制中跟踪它的版本,并通过常规的 CI/CD 管道自动部署。这将使我们能够以结构化和自动化的方式连接代码和数据平面。

  • 多重管道:此时,显而易见,机器学习管道有两种类型:训练管道和服务管道。它们有一个共同点:必须进行的数据转换必须产生相同格式的数据,但它们的实现可能大不相同。例如,训练管道通常运行在包含所有特征的批处理文件上,而服务管道则经常在线运行,并且仅接收请求中的部分特征,剩余部分从数据库中检索。然而,确保这两种管道的一致性至关重要,因此应尽可能重用代码和数据。

  • 模型和数据版本控制:一致的版本跟踪对于可重复性至关重要。在传统软件领域,版本控制代码就足够了,因为它定义了所有行为。而在机器学习中,我们还必须跟踪模型版本,以及用于训练它们的数据和一些元信息,如训练超参数。同时,还需要对数据进行版本控制,并将每个训练好的模型与我们所使用的代码、数据和超参数的确切版本相关联。

  • 模型验证:为了确定模型是否适合部署,必须确定正确的跟踪指标和可接受值的阈值,通常是通过经验并与之前的模型或基准进行对比。

  • 数据验证:一个好的数据流水线通常会从验证输入数据开始。文件格式和大小、列类型、空值或无效值都是常见的验证内容。这些都是机器学习训练和预测所必需的,否则您可能会得到一个表现异常的模型。输入数据的更高层次统计特性也应由 ML 流水线验证。例如,如果某一特征的平均值或标准差在不同训练数据集之间变化显著,训练出来的模型及其预测结果很可能会受到影响。

  • 监控:对于机器学习系统来说,监控变得至关重要,因为它们的性能不仅依赖于我们可以控制的因素,如基础设施和软件,还依赖于我们控制较少的数据。此外,除了标准的指标(如延迟、流量、错误和饱和度)外,我们还必须监控模型的预测性能。为了检测影响特定分段的问题,我们必须跨切片(而不仅仅是全局)监控指标,就像验证模型时一样。

总结来说,将机器学习(ML)应用于生产环境不仅仅是将模型发布为预测 API。实际上,它还包括建立一个能够自动化重新训练和部署新模型的 ML 流水线。设置 CI/CD 系统可以让您自动测试和发布新的流水线实现。这个系统使我们能够应对快速变化的数据和商业环境。作为一个新兴领域,MLOps 正在迅速获得数据科学家、ML 工程师和 AI 爱好者的关注。

总结

总结来说,Kubeflow 提供了一个易于部署和使用的工具链,允许数据科学家整合他们需要在 Kubernetes 上运行模型的各种资源,如 Jupyter Notebooks、Kubernetes 部署文件以及 PyTorch 和 TensorFlow 等 ML 库。

另一个 Kubeflow 显著简化的流行 ML 任务是与 Jupyter Notebooks 的工作。您可以使用 Kubeflow 内置的笔记本服务构建笔记本并与您的团队或多个团队分享,您可以通过 UI 访问这些服务。在本章中,我们学习了如何设置一个 ML 流水线,该流水线将使用 Kubeflow ML 平台开发和部署示例模型。我们还意识到,Kubeflow 在 MicroK8s 上的设置和配置既简单又轻量,同时能够在构建、迁移和部署流水线的过程中模拟现实世界的条件。

在下一章中,您将学习如何使用 Knative 和 OpenFaaS 框架部署和运行无服务器应用程序。

第十章:使用 Knative 和 OpenFaaS 框架实现无服务器

在上一章中,我们讨论了 Kubeflow,它为数据科学家提供了一个易于部署、简便使用的工具链,使他们能够集成运行模型所需的各种资源,如 Jupyter 笔记本、Kubernetes 部署文件以及像 PyTorch 和 TensorFlow 这样的机器学习库。

通过使用 Kubeflow 内置的 Notebooks 服务,您可以创建笔记本并与团队共享。我们还讲解了如何设置机器学习管道,利用 Kubeflow 机器学习平台开发和部署示例模型。此外,我们已经确认,Kubeflow 在 MicroK8s 上的设置和配置简单、轻量,能够在构建、迁移和部署管道时模拟真实世界的条件。

在本章中,我们将探讨最受欢迎的开源无服务器框架,这些框架通过为部署、操作和管理无服务器云原生应用程序提供组件,扩展了 Kubernetes。这些框架使您能够通过将代码封装在容器镜像中并提供所需的功能来创建服务。无服务器框架会自动启动和停止实例,因此您的代码仅在需要时运行。除非您的代码需要完成某些任务,否则不会使用资源。

Kubernetes 的容器编排能力(如调度、负载均衡和健康监控)使容器的扩展变得更加容易。然而,这要求开发者执行或模板化多个重复性任务,如从代码仓库中拉取应用源代码、围绕代码构建和配置容器镜像,以及使用各种工具配置 Kubernetes 之外的网络连接。此外,将 Kubernetes 管理的容器集成到自动化的持续集成/持续交付CI/CD)管道中,需使用新的工具和脚本。

通过无服务器框架在 Kubernetes 内自动化上述活动,消除了复杂性。开发者可以在单一的 YAML 清单文件中定义容器的内容和配置,其他工作将由无服务器框架处理,包括构建容器、进行网络编程以设置路由,应该是 Ingress、负载均衡等。

无服务器计算正在成为首选的云原生执行方式,因为它使得开发和运行应用程序变得更加容易和具有成本效益。

无服务器计算模型提供以下优势:

  • 按需提供资源,基于需求透明地进行扩展,并在没有请求时扩展到零

  • 将所有基础设施管理责任转移给基础设施提供商,让开发者可以将时间和精力用于创造和创新

  • 仅按实际使用的资源收费,避免闲置容量的费用

Kubernetes 本身无法运行无服务器应用;我们需要定制的软件,将 Kubernetes 与特定基础设施提供商的无服务器平台结合起来。通过抽象代码并处理网络路由、事件触发和自动扩展,服务器无框架将使任何容器都能在任何 Kubernetes 集群上作为无服务器工作负载运行;无论该容器是围绕无服务器函数构建的,还是其他应用程序代码(例如微服务)。

无服务器计算,尤其是在网络边缘部署时,被认为是未来构建日益复杂的物联网IoT)系统的关键推动力。然而,在为无服务器工作负载安装新的边缘基础设施时,必须特别关注资源使用和网络连接性。研究表明,面向边缘的分发版,如 MicroK8s,在大多数测试中表现更好,包括冷启动延迟、串行执行性能、单副本并行执行以及使用各种自动扩展技术的并行执行。

本章我们将讨论 MicroK8s 附带的两大流行无服务器框架:Knative 和 OpenFaaS。这两大无服务器框架都是基于 Kubernetes 的平台,用于构建、部署和管理现代的无服务器工作负载。本章我们将涵盖以下主要内容:

  • Knative 框架概述

  • 启用 Knative 插件

  • 在 Knative 上部署和运行示例服务

  • OpenFaaS 框架概述

  • 启用 OpenFaaS 插件

  • 在 OpenFaaS 上部署和运行示例函数

  • 开发和部署无服务器应用的最佳实践

Knative 框架概述

Knative 是一个基于 Kubernetes 的平台,用于部署、管理和扩展现代的无服务器工作负载。Knative 具有以下三个主要组件:

  • 构建:提供简化的源代码到容器的构建,使用起来非常方便。通过利用常见的构建模块,你将获得优势。

  • 服务:Knative 处理所有的网络、自动扩展和版本跟踪。现在你只需要专注于你的核心逻辑。

  • 事件处理:处理事件的订阅、传递和管理。通过声明式事件连接和开发者友好的对象架构将容器连接到数据流,你可以创建现代应用程序。

MicroK8s 是启动 Knative(构建、服务和事件处理)所有组件的最佳解决方案,因为它原生支持 Knative。我们将在下一节详细讨论每个组件。

构建组件

Knative Build 组件简化了从源代码构建容器的过程。这个过程通常包括以下步骤:

  1. 从代码仓库(如 GitHub)下载源代码

  2. 安装代码运行所需的基础依赖项,如环境变量和软件库

  3. 容器镜像创建

  4. 将容器镜像放置在 Kubernetes 集群可访问的镜像库中

在其构建过程中,Knative 利用 Kubernetes 应用程序编程接口 (APIs) 及其他技术。开发人员可以使用单一清单(通常是 YAML 文件)来描述源代码的位置、所需的依赖项等所有变量。Knative 利用该清单自动化容器构建和镜像创建过程。

Serving 组件

容器通过 Serving 组件作为可扩展的 Knative 服务进行部署和运行。以下是 Serving 组件提供的关键功能:

  • 配置:服务的状态由配置定义并维护。它还具有版本控制功能。每次更改配置时,都会创建服务的新版本,并与早期版本一起保存。

  • 智能服务路由:开发人员可以使用智能服务路由将流量引导到服务的不同版本。例如,你已创建了服务的新版本,并希望在将所有用户切换过去之前,先在小范围内测试。智能服务路由允许你将一部分用户请求发送到新服务,其余请求发送到旧版本。随着对新服务的信心增加,你可以将更多流量发送到它。

  • 自动扩缩:Knative 可以将服务扩展到数千个实例,或缩减到零实例,这对于无服务器应用程序至关重要。

  • Istio (istio.io/): 这是一个开源 Kubernetes 服务网格,与 Knative 一同部署。它提供服务请求认证、自动流量加密以保证服务间的安全通信,并为开发人员和管理员提供微服务和无服务器函数操作的广泛指标,用于改进基础设施。

Knative Serving 由一组被称为 Kubernetes 自定义资源定义 (CRDs) 的对象定义。以下组件定义并管理集群中无服务器工作负载的行为:

  • 服务:控制工作负载的整个生命周期。它通过控制附加对象的创建,确保应用程序始终有路由、配置和每次服务更新时的新修订版本。可以将服务配置为始终将流量发送到最新的修订版本,或固定某个修订版本。

  • 路由:网络端点映射到一个或多个修订版本。流量可以通过多种方式进行管理,包括流量分配和命名路由。

  • 修订版本:这是工作负载在特定时间点对代码和配置的快照。修订版本是不可变对象,可以根据需要保留。Knative Serving 修订版本可以根据流量的变化自动进行扩缩容。

  • 配置:这保持了部署在所需的状态下。它遵循十二因素应用(Twelve-Factor App)范式,并提供代码与配置之间的清晰分离。每当更改配置时,都会创建一个新的修订版本。

总结来说,Serving 组件负责部署和运行容器,作为可扩展的 Knative 服务。

Eventing 组件

Knative 的 Eventing 组件允许各种事件触发基于容器的服务和函数。无需开发脚本或实现中间件,因为 Knative 队列处理事件的分发到相应的容器。一个分发事件到容器和通道的消息总线(通道仅仅是事件队列,开发者可以选择)也由 Knative 处理。开发人员还可以建立连接事件和特定操作的馈送,指示容器应执行的动作。

Knative 事件源让开发人员与第三方事件提供者的集成变得更加简单。Eventing 组件将连接到事件生产者,并自动路由生成的事件。它还提供了将事件从事件生产者路由到接收器的工具,允许开发人员构建使用事件驱动架构的应用程序。

Knative Eventing 资源是松散耦合的,可以单独开发和部署。任何生产者都可以生成事件,任何事件消费者都可以对该事件或一组事件表示兴趣。Knative Eventing 还负责通过标准的 HTTP POST 请求在事件生产者和接收器之间发送和接收事件。以下是 Eventing 组件:

  • 事件源:这些是 Knative Eventing 部署中的主要事件生产者。事件被路由到接收器或订阅者。

  • Broker 和 Trigger:这些提供了一个事件网格模型,允许事件生产者将事件交付给 Broker,Broker 然后通过 Trigger 均匀地分发给消费者。

  • 通道与订阅:这些组件共同作用,创建一个事件管道模型,通过订阅在通道之间转换和路由事件。该模型适用于事件流水线,其中来自一个系统的事件必须在路由到另一个进程之前进行转换。

  • 事件注册:Knative Eventing 定义了一个 EventType 对象,帮助消费者发现来自 Broker 的可用事件类型。注册表由各种事件类型组成。存储在注册表中的事件类型包含了消费者创建触发器所需的所有信息,而无需使用带外机制。

在下图中,展示了 Knative 组件。Serving 和 Eventing 共同协作执行任务和应用程序,以自动化和管理它们:

图 10.1 – Knative 组件

图 10.1 – Knative 组件

总结一下,Knative 提供了以下组件:

  • 无服务器容器可以快速部署。

  • 可以将 Pod 缩放到零,并根据需求进行自动缩放。

  • 支持多个网络层的集成,包括 Contour、Kourier 和 Istio。

  • 支持已部署代码和配置的时间点快照。

  • 支持 HTTP 和 HTTPS 网络协议。

现在我们已经覆盖了 Knative 的基础知识,接下来我们将在下一部分启用附加组件并部署其中一个示例。

启用 Knative 附加组件

由于 Knative 不支持 ARM64 架构,我们将在本部分使用 Ubuntu 虚拟机。设置 MicroK8s 集群的说明与第五章《创建和实现多节点树莓派 Kubernetes 集群的更新》相同。

我们将启用 Knative 附加组件,将 Knative 中间件添加到您的集群中。使用以下命令启用 Knative 附加组件:

microk8s enable knative

启用此附加组件时,Istio 和 DNS 也将添加到 MicroK8s。

以下命令执行输出确认 Knative 附加组件正在启用:

图 10.2 – 启用 Knative 附加组件

图 10.2 – 启用 Knative 附加组件

启动附加组件需要一些时间。以下命令执行输出显示 Knative 已成功启用:

图 10.3 – Knative 附加组件已激活

图 10.3 – Knative 附加组件已激活

在继续下一步之前,让我们验证附加组件是否已启用,并且所有必需的 Pod 都在运行。

要查看附加组件是否已激活,请使用kubectl get pods -n knative-serving命令。以下命令执行输出表明 Knative Serving 组件正在运行:

图 10.4 – Knative Serving 组件的 Pod 正在运行

图 10.4 – Knative Serving 组件的 Pod 正在运行

在继续下一步之前,让我们使用以下命令确认所有 Knative Eventing 组件是否都已启动并正常运行:

kubectl get pods –n knative-eventing

以下命令执行输出表明 Knative Eventing 组件也在运行:

图 10.5 – Knative Eventing 组件正在运行

图 10.5 – Knative Eventing 组件正在运行

现在我们已经启动并运行了所有 Knative 组件。

我们将继续安装 Knative kn 的下一步。无需手动创建或编辑 YAML 文件,kn 提供了一个快速简便的界面来构建 Knative 资源,如服务和事件源。它还简化了诸如自动扩展和流量分配等任务,这些任务否则可能会比较复杂。

可以从发布页面下载 kn 二进制文件(github.com/knative/client/releases),并使用以下命令将其复制到 /usr/local/bin 目录:

sudo curl –o /usr/local/bin/kn –sL https://github.com/knative/client/releases/download/knative-v1.3.1kn-linux-amd64

以下命令执行输出确认 kn CLI 已成功下载,并且可在 /usr/local/bin 目录下使用:

图 10.6 – 安装 Knative CLI

图 10.6 – 安装 Knative CLI

在进入下一步之前,让我们通过运行以下 kn version 命令来验证 kn CLI 是否正常工作:

kn version

以下输出确认 kn CLI 正常运行,并显示其版本和构建日期:

图 10.7 – 验证 kn CLI 是否正常运行

图 10.7 – 验证 kn CLI 是否正常运行

为了让 kn CLI 访问 Kubernetes 配置文件,请将 MicroK8s 配置文件复制到 $HOME/.kube/config,操作命令如下:

图 10.8 – 将 MicroK8s 配置文件复制到 $HOME 文件夹

图 10.8 – 将 MicroK8s 配置文件复制到 $HOME 文件夹

所有 Knative 组件以及 Knative CLI kn 配置现在都已启动并运行。接下来我们将进入下一步:部署并运行示例服务。

在 Knative 上部署并运行示例服务

在本节中,我们将从 Knative 示例仓库中部署 Hello world 示例服务。该示例服务会读取 TARGET 环境变量并打印 Hello $TARGET!。如果没有提供 TARGET,默认值为 "World"。

接下来的步骤中,我们将通过指定镜像位置和 TARGET 环境变量来部署服务。我们将创建一个 Knative 服务(Serving 组件),它是基于时间的单一无服务器容器环境(如微服务)的表示形式。它包含访问该服务的网络地址以及运行该服务所需的应用程序代码和设置。

Knative 服务的生命周期由 serving.knative.dev CRD 控制。为了创建 Knative 服务,我们将使用 kn CLI,具体命令如下:

kn service create kn-serverless --image gcr.io/knative-samples/helloworld-go --env TARGET=upnxtblog.com

以下命令执行输出表示服务创建成功,并且可以通过 URL http//kn-serverless.default.example.com 访问该服务:

图 10.9 – 创建新的 Knative 服务

图 10.9 – 创建新的 Knative 服务

恭喜!我们已经成功创建了一个新的 Knative 服务并进行了部署。

以下是 Serving 组件的概览:

  • 服务:管理工作负载的整个生命周期

  • 路由:负责将网络端点映射到一个或多个修订版本

  • 配置:保持部署的期望状态

  • 修订:工作负载代码和配置的某一时刻快照

在定义和控制无服务器工作负载在集群上行为的 Serving 组件,如下图所示:

图 10.10 – Knative Serving 组件

图 10.10 – Knative Serving 组件

我们现在可以像下面这样使用curl命令调用我们之前创建的服务:

curl http://$SERVICE_IP:$INGRESS_PORT/ -H 'Host: kn-serverless.default.example.com'

$SERVICE_IP$INGRESS_PORT指向 Knative 服务和暴露的 Ingress 端口。以下命令的输出确认 Knative 服务已经被调用,并且已显示输出:

图 10.11 – 调用 Knative 服务

图 10.11 – 调用 Knative 服务

要观察 Pod 如何被创建以处理请求,请在新的终端标签页中运行watch kubectl get pods命令。如果 60 秒内没有传入请求,Knative 将自动将该 Pod 缩减至零,如下所示的命令执行输出:

图 10.12 – 如果 60 秒内没有传入请求,Pods 将被终止

图 10.12 – 如果 60 秒内没有传入请求,Pods 将被终止

在 Pods 缩减至零后,您也可以发出前述的curl命令,以查看 Pod 如何启动并服务请求零,如下所示的命令执行输出:

图 10.13 – Pods 被启动以服务请求

图 10.13 – Pods 被启动以服务请求

简而言之,Knative 是一个基于 Kubernetes 的开发、部署和管理现代无服务器工作负载的平台。我们还发现,MicroK8s 原生支持 Knative,并且是入门 Knative 所有组件(构建、服务和事件)的最佳方式。

我们已部署了一个示例应用程序,并使用其端点从命令行调用它。接下来,我们将在下一节中查看选择 OpenFaaS 以运行示例应用程序,并分析它提供的功能。

OpenFaaS 框架概述

OpenFaaS(FaaS代表函数即服务)是一个使用 Docker 和 Kubernetes 容器技术创建无服务器函数的框架。任何进程都可以被封装为一个函数,从而无需重复编写样板代码即可消费各种 Web 事件。这是一个开源项目,在社区中正获得越来越多的关注。

OpenFaaS 框架的一些关键优势如下:

  • 在任何基础设施上运行函数,无需担心与开源函数框架的锁定问题。

  • 在任何编程语言中创建函数并将其打包在 Docker/OCI 容器中。

  • 内置 UI、强大的 CLI 和一键安装使其使用起来非常简单。

  • 随着需求增长进行扩展——处理流量峰值,并在不使用时缩减规模。

  • 提供社区版和专业版,并附带生产支持。

现在我们已经介绍了 OpenFaaS 的基本概念,接下来我们将在下一部分启用插件并部署其中一个示例。

启用 OpenFaaS 插件

由于 OpenFaaS 不支持 ARM64 架构,我们将在本部分使用 Ubuntu 虚拟机。设置 MicroK8s 集群的指令与 第五章 相同,创建和实施多节点 Raspberry Pi Kubernetes 集群的更新

在启用 OpenFaaS 插件之前,使用以下命令启用 DNS 和注册表插件:

microk8s enable dns

DNS 用于为 Kubernetes 提供地址解析服务,以便各个服务能够相互通信。以下命令执行输出确认 DNS 插件已启用:

图 10.14 – 启用 DNS 插件

图 10.14 – 启用 DNS 插件

DNS 插件已启用后,我们将继续启用注册表插件,使用以下命令:

microk8s enable registry

注册表插件在 Docker 中创建一个私有注册表,并将其暴露在 localhost:32000。作为此插件的一部分,存储插件也将按以下方式启用:

图 10.15 – 启用注册表插件

图 10.15 – 启用注册表插件

在启用了 DNS 和注册表插件后,我们可以继续下一步启用 OpenFaaS 插件。

使用以下命令启用 OpenFaaS 插件:

microk8s enable openfaas

以下命令执行输出确认 OpenFaaS 插件正在启用:

图 10.16 – 启用 OpenFaaS 插件

图 10.16 – 启用 OpenFaaS 插件

启用插件需要一些时间。以下命令执行输出显示 OpenFaaS 插件已成功启用:

图 10.17 – OpenFaaS 插件已启用

图 10.17 – OpenFaaS 插件已启用

如你所见,部署脚本在安装过程中会生成一个用户名(admin)和密码组合。请保存凭据,以便在后续步骤中使用。

在继续下一步之前,让我们验证插件是否已启用,并且所有所需的 pods 都在运行。

要查看插件是否已激活,请使用 kubectl get pods 命令。以下命令执行输出表明 OpenFaaS 组件正在运行:

图 10.18 – OpenFaaS pods 正在运行

图 10.18 – OpenFaaS pods 正在运行

我们现在已经启动并运行了 OpenFaaS 的以下所有组件:

  1. nats 提供异步执行和排队功能。

  2. prometheus 提供指标并通过 alertmanager 启用自动扩展。

  3. gateway 提供了外部访问函数的路由,并根据需求对函数进行扩展。

  4. queue-worker 负责处理异步请求。

接下来,我们将继续安装 OpenFaaS CLI 工具。该 CLI 可用于创建和部署 OpenFaaS 函数。通过一组支持的语言模板,您可以创建 OpenFaaS 函数(例如 Node.js、Python、C# 和 Ruby)。更多信息,请参见 github.com/openfaas/templates

您可以使用 curl 命令安装 CLI,方法是从发布页面获取二进制文件,如下所示:

curl –sSL –– insecure https://cli.openfaas.com | sudo –E sh

注意

这里我们使用 –insecure 标志来避免任何证书下载问题。

以下命令执行结果确认 CLI 安装成功。安装完成后,faas-cli 命令和 faas 别名可用,如下所示:

图 10.19 – 安装 OpenFaaS CLI

图 10.19 – 安装 OpenFaaS CLI

现在我们已经安装了 faas-cli,接下来我们可以使用 faas-cli 命令来创建和部署函数,进入下一部分。

在 OpenFaaS 上部署并运行一个示例函数

本节将介绍创建、构建和部署新的 FaaS Python 函数。我们还将使用 OpenFaaS CLI 命令来测试已部署的函数。OpenFaaS CLI 拥有一个模板引擎,可以用来设置任何编程语言的新函数。要创建一个新函数,请使用以下命令:

faas-cli new –lang <language template> ––prefix localhost:32000 <function name>

这里的 –prefix localhost:32000 指的是我们在前面步骤中启用的本地 MicroK8s 注册表。

此命令通过读取当前工作文件夹中 ./template 目录下的模板列表来工作。

您还可以使用 faas-cli template pull 命令,从 GitHub 拉取官方 OpenFaaS 语言模板。

要查看支持的语言列表,请使用 faas-cli new –list 命令。

以下命令执行结果表明新的 openfaas-serverless Python 函数已创建:

图 10.20 – 使用 CLI 创建新函数

图 10.20 – 使用 CLI 创建新函数

在当前工作文件夹中,生成了一个堆栈文件和一个以函数名命名的新文件夹,如下所示:

图 10.21 – 生成堆栈文件和包含函数名称的新文件夹

图 10.21 – 生成堆栈文件和包含函数名称的新文件夹

现在我们已经创建了一个新函数,接下来我们需要构建它,以便在后续步骤中创建和使用容器镜像。

使用以下命令来构建新函数:

faas-cli build –f ./openfaas-serverless.yml

faas-cli build 命令在本地 MicroK8s 注册中心创建一个 Docker 镜像,该镜像可以在本地使用,也可以上传到远程容器注册中心(如果是多节点集群设置)。每次更改函数时,都需要执行新的 faas-cli build 命令。

以下命令执行表明新的 openfaas-serverless Python 函数已成功构建:

图 10.22 – 构建新的 OpenFaaS 函数

图 10.22 – 构建新的 OpenFaaS 函数

构建过程可能需要一些时间,但一旦完成,您应该看到以下输出:

图 10.23 – 成功构建 OpenFaaS 函数

图 10.23 – 成功构建 OpenFaaS 函数

由于镜像已构建完成,我们现在可以进入下一步,将 Docker 镜像推送到注册中心。

使用以下命令将 Docker 镜像推送到我们的本地注册中心:

faas-cli push –f ./openfaas-serverless.yml

以下命令执行输出表明 openfaas-serverless 函数已成功推送到注册中心:

图 10.24 – OpenFaaS 函数已推送到本地注册中心

图 10.24 – OpenFaaS 函数已推送到本地注册中心

在进入下一步部署函数之前,让我们设置 OPENFAAS_URL 环境变量并检索所需的管理员凭证。

一个 OPENFAAS_URL 环境变量定义了 CLI 用来联系 OpenFaaS 服务器的默认网关 URL,如下所示:

图 10.25 – 设置 OPENFAAS_URL 环境变量

图 10.25 – 设置 OPENFAAS_URL 环境变量

要检索管理员凭证,请使用在安装过程中打印的以下命令:

echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode)

以下命令执行输出表明命令已成功执行,并且密码已被检索:

图 10.26 – 检索管理员凭证

图 10.26 – 检索管理员凭证

让我们使用管理员凭证登录到 OpenFaaS 服务器,以便我们可以部署该函数。以下命令执行结果表示登录成功,并且凭证已保存到本地存储:

图 10.27 – 使用检索到的密码登录到 OpenFaaS 服务器

图 10.27 – 使用检索到的密码登录到 OpenFaaS 服务器

凭证已保存后,我们可以继续部署该函数到 OpenFaaS 服务器的下一步骤。

使用以下命令来部署该函数:

faas-cli deploy –f ./openfaas-serverless.yml

以下命令执行输出表明部署成功,并且我们现在拥有访问该函数的 URL:

图 10.28 – 函数部署成功

图 10.28 – 函数部署成功

或者,你可以使用 faas-cli up 命令在一个命令中构建、推送并部署函数。

恭喜!我们成功创建了一个新函数并部署了它。

要调用函数,我们将使用 CLI 的 invoke 功能,如下所示:

faas-cli invoke –f openfaas-serverless.yml openfaas-serverless

默认情况下,函数接受一个输入参数并输出该参数值。要更改逻辑,需修改堆栈文件和处理程序文件,然后重新部署函数:

图 10.29 – 调用函数

图 10.29 – 调用函数

你还可以使用 OpenFaaS 用户界面来调用已部署的函数,如下所示:

图 10.30 – OpenFaaS 用户界面

图 10.30 – OpenFaaS 用户界面

简而言之,OpenFaaS 提供以下功能:

  • 一种简单的方法来打包任何代码或二进制文件,以及一个多样化的语言模板生态系统

  • 内置的自动扩展和用于协作与共享度量的函数库

  • 原生支持 Kubernetes 体验以及一个专注的社区

接下来将讨论开发和部署无服务器应用程序的最佳实践。

开发和部署无服务器应用程序的最佳实践

我们必须遵循最佳实践,以保护我们的资源、应用程序和基础设施服务提供商账户。以下是需要考虑的一些指导原则。

无服务器函数 = 特定函数

无服务器函数必须完成特定任务。无服务器函数应该执行一个逻辑功能,类似于任何代码中的函数或方法应该完成一件事情。

使用微服务

微服务使我们能够以可管理的方式将数据存储和功能链接在一起。微服务将受合同约束,规定允许和禁止的操作。例如,一个支付微服务可以用来创建、更新和删除用户支付记录。除用户账户数据存储之外,这个微服务不应修改任何数据。它还会有自己的 API。其他微服务现在可以以一致的方式与用户账户无服务器函数交互,而无需修改任何用户账户数据存储。

为不同资源使用适当的堆栈

在部署资源时,无服务器框架允许我们使用多种语言栈,每个框架配置都部署相应的栈。每种资源类型应该有一个目标栈。例如,我们的用户支付微服务可能会有一个数据库栈(用于存储 MongoDB 中的账户元数据)、一个身份提供者IdP)栈(用于设置和维护与 OAuth2 提供者的用户会话)、一个函数栈(用于部署提供用户支付微服务 API 的函数)和一个对象存储栈(用于在 S3 中捕捉用户账户的头像)。这使得我们能够在不影响其他资源的情况下编辑某一资源类型。例如,如果你在部署函数栈时出错,其他栈不会受到影响。

应用最小权限原则

应该对所有资源应用最小权限的 IAM 权限。例如,一个读取 MongoDB 表格的无服务器函数,只应包含该 MongoDB 表格的读取权限。在定义权限时,应尽量避免使用星号(*)。如果你的函数被攻击者利用且使用了星号,那么它就能读取和删除所有数据库数据,从而使得所有 MongoDB 资源可访问,且每个操作都可执行。

执行负载测试

对无服务器函数进行负载测试有助于你识别需要分配多少内存以及使用什么超时值。在无服务器环境中,可能会有复杂的应用程序,而且你可能没有意识到应用程序内部的依赖关系,这些依赖关系会在高负载下阻止其执行功能。负载测试使你能够识别可能会影响高可用性应用程序运行的关键问题。

使用 CI/CD 管道

当你开始构建应用程序时,通过 CLI 进行部署是可以的。理想情况下,你应该在将代码发布到生产环境之前,使用 CI/CD 管道来部署代码。在启用拉取请求合并之前,管道的 CI 部分允许你进行代码风格检查、单元测试和其他各种自动化检查。当 PR 合并或分支更新时,管道的 CD 部分允许你自动部署无服务器应用程序。使用 CI/CD 管道可以消除人为错误,并确保你的流程可重复。

需要持续监控

我们应该使用 Knative 监控、Prometheus 等服务来监控我们的无服务器资源。可能会有许多资源,而且它们的使用频繁,手动检查它们是否存在故障将会很困难。健康状况、执行时间过长、延迟和错误等都可以通过监控服务报告。当我们的无服务器应用和资源出现问题时,拥有一个服务(如 Alert Manager)来向我们发出警报,可以让我们更快地定位和解决问题。

除了监控,还需要审计

我们希望除了监控之外,还能进行审计。当任何事情停止工作或出现问题时,监控会发出警报。当我们的资源偏离已知配置或配置错误时,审计会发出警报。我们可能会开发规则,使用 Knative 配置或 OpenFaaS 堆栈文件等服务来审计我们的资源及其配置。

审计软件依赖

我们还希望审计我们的软件依赖。仅仅因为我们不再有服务器,并不意味着我们免于“打补丁”。我们希望确保我们指定的任何软件依赖都是最新的,并且不包含已知的漏洞。我们可以利用自动化工具来跟踪哪些软件包需要更新。

总结

在本章中,我们研究了 MicroK8s 中最流行的两种无服务器框架:Knative 和 OpenFaaS,它们都是基于 Kubernetes 的平台,用于开发、部署和管理现代的无服务器工作负载。我们部署了一些示例,并通过 CLI 调用它们的端点。我们还看到了无服务器框架如何在没有请求时将 Pod 缩减到零,并在有更多请求时启动新的 Pod。

我们意识到,MicroK8s 的部署简便性似乎与无服务器框架的实现难易度相关。我们还讨论了一些在开发和部署无服务器应用时需要牢记的指导原则。然而,部署无服务器资源非常简单。我们还意识到,为了保护我们的资源、应用和基础设施服务提供商账户,我们需要遵循最佳实践。

在下一章中,我们将学习如何使用 OpenEBS 实现存储复制,从而在多个节点之间同步数据。

第四部分:在 MicroK8s 上部署和管理应用

本部分重点介绍了典型物联网/边缘计算应用的部署和管理方面,例如为有状态应用设置存储复制,为跨切关注点实现服务网格,以及设置高可用集群以应对组件故障并继续无中断地提供工作负载服务,配置带有工作负载隔离的容器,并在与主机系统隔离的环境中运行受保护的容器。

本书的这一部分包括以下章节:

  • 第十一章**,使用 OpenEBS 管理存储复制

  • 第十二章**,为跨切关注点实现服务网格

  • 第十三章**,通过 HA 集群抵御组件故障

  • 第十四章**,用于安全容器的硬件虚拟化

  • 第十五章**,为隔离容器实现严格限制

  • 第十六章**,深入未来

第十一章:使用 OpenEBS 管理存储复制

在上一章中,我们介绍了 MicroK8s 提供的两种无服务器框架,这两种框架都是基于 Kubernetes 的平台,用于设计、部署和管理现代无服务器工作负载。我们还注意到,无服务器框架的实施易用性似乎与 MicroK8s 的部署易用性密切相关。创建和部署无服务器应用时需要记住的一些指导原则也已被强调。我们还意识到,为了保护我们的资源、应用程序和基础设施服务提供商账户,我们需要遵循最佳实践。

在本章中,我们将研究支持云原生存储解决方案(如 OpenEBS)的下一个用例,以为我们的容器应用程序提供持久存储。云原生存储解决方案提供了全面的存储机制。这些解决方案模拟了云环境的特性,如可扩展性、可靠性、容器架构和高可用性。这些特性使得与容器管理平台的接口变得简单,并为基于容器的应用程序提供持久存储。

首先,我们将先了解 Kubernetes 存储基础知识,再深入了解 OpenEBS 的概念。容器是短暂的,这意味着它们是为特定目的建立的,在完成任务后会被关闭。容器本身不维护状态数据,新创建的容器实例无法记住之前容器的状态/数据。虽然容器提供存储,但它只是短暂存储,因此当容器关闭时,存储也会被清除。开发人员需要管理持久存储,作为容器化应用程序的一部分,因为他们在为新用例采用容器时,可能需要让容器持续存储数据。例如,开发人员可能希望在容器中运行数据库,并将数据存储在一个在容器关闭时仍然存在的卷中。

Kubernetes 为容器集群提供了多种管理选项。管理持久存储的能力是其中之一。管理员可以使用 Kubernetes 持久存储来跟踪 Kubernetes 集群中的持久数据和非持久数据。集群中的多个应用程序可以动态地利用存储资源。

为了帮助管理持久存储,Kubernetes 支持两种主要机制:

  • PersistentVolumePV)是一个存储元素,可以根据存储类别手动或动态创建。它的生命周期不受 Kubernetes Pod 生命周期的影响。Pod 可以挂载一个 PV,但 PV 在 Pod 关闭后仍然存在,其数据仍然可以访问。每个 PV 可以有自己的一组参数,例如磁盘类型、存储层次和性能。

  • PersistentVolumeClaim (PVC) 是 Kubernetes 用户提出的存储请求。根据自定义参数,任何在容器上运行的应用程序都可以请求存储,并定义其所需的大小和其他属性(例如,特定类型的存储,如 SSD 存储)。根据可用的存储资源,Kubernetes 集群可以供给一个 PV。

StorageClass 是一个 Kubernetes API 对象,用于配置存储参数。它是一种动态配置方法,根据需求生成新卷。StorageClass 定义了卷插件的名称,以及任何外部提供者和 容器存储接口 (CSI) 驱动程序,允许容器与存储设备进行通信。CSI 是一种标准,允许容器化工作负载访问任何块存储和文件存储系统。

StorageClass 可以由 Kubernetes 管理员定义并分配 PV。每个 StorageClass 表示不同类型的存储,例如快速的 SSD 存储与传统的磁盘驱动器或远程云存储。这使得 Kubernetes 集群能够根据工作负载的变化需求提供不同类型的存储。

动态卷供给是 Kubernetes 的一项功能,允许根据需求创建存储卷。管理员不再需要手动在云端或存储提供商中构建新存储卷,然后创建 PV 对象使其在集群中可用。当用户请求特定的存储类型时,整个过程会自动化并进行供给。StorageClass 对象由集群管理员根据需要定义。像 OpenEBS 这样的卷插件,也称为供给器,被每个 StorageClass 引用。当存储卷被自动供给时,卷插件提供一组参数并将其传递给供给器。

管理员可以定义多个 StorageClass,每个 StorageClass 可以代表不同类型的存储或具有不同规格的相同存储。这使得用户可以选择多种存储解决方案,而无需担心实施细节。

容器附加存储 (CAS) 正迅速成为管理有状态工作负载的可行选项,并且正成为执行耐久性和容错有状态应用程序的首选方法。CAS 是通过 OpenEBS 项目引入 Kubernetes 平台的。它可以在本地集群、公共云中的托管集群,甚至在隔离的气隙集群中轻松部署。MicroK8s 通过附加组件内置支持 OpenEBS,使其成为在气隙 Edge/IoT 场景中运行 Kubernetes 集群的最佳解决方案。

本章将涵盖以下主要主题:

  • OpenEBS 概述

  • 配置和实现 PostgreSQL 有状态工作负载

  • Kubernetes 存储最佳实践

OpenEBS 概述

在 Kubernetes 中,存储通常作为操作系统内核模块与各个节点集成。即便是 PV 也属于单体化和传统资源,因为它们与底层组件紧密绑定。CAS 允许 Kubernetes 用户将存储实体视为微服务。CAS 由两部分组成:控制平面和数据平面。控制平面作为一组 自定义资源定义CRDs)实现,处理低级存储实体。数据平面作为靠近工作负载的一组 pod 运行,负责 I/O 事务,即读写操作。

控制平面和数据平面的清晰分离提供了与在 Kubernetes 上运行微服务相同的好处。该架构将持久化与底层存储实体解耦,使得工作负载更加可移植。它还为存储增加了扩展能力,允许管理员和运维人员根据工作负载动态扩展卷。最后,CAS 确保数据(PV)和计算(pod)始终以超融合模式共置,以最大化吞吐量和容错能力。

数据通过 OpenEBS 的同步复制功能跨多个节点进行复制。节点故障只会影响该节点上的卷副本。其他节点上的数据将保持可用,并且性能不受影响,从而使应用程序对故障具有更高的容错性:

图 11.1 – 同步复制

图 11.1 – 同步复制

使用 OpenEBS CAS 架构还可以创建即时快照。这些快照可以通过常规的 kubectl 命令进行创建和管理。与 Kubernetes 的深度集成使得作业具有更好的可移植性,同时也简化了数据备份和迁移。

以下图展示了 OpenEBS 的典型组件:

图 11.2 – OpenEBS 控制平面和数据平面

图 11.2 – OpenEBS 控制平面和数据平面

OpenEBS 是一个基于 CAS 概念精心设计的系统。我们将在以下章节中更详细地了解其架构。

控制平面

控制平面、磁盘管理器和数据平面被分配给每个已安装的存储卷。控制平面更接近存储基础设施;它跟踪通过 SAN 或块存储连接到每个集群节点的存储卷。卷的配置、快照的启动、克隆的创建、存储策略的制定、存储策略的执行以及将卷指标导出到其他系统(如 Prometheus)等操作,都是由控制平面直接处理的。

OpenEBS 存储管理员通过控制平面与集群范围的存储活动进行交互。通过 API 服务器,OpenEBS 控制平面可以对外访问。Pod 暴露 REST API 用于控制资源,如卷和策略。声明最初作为 YAML 文件提交到 API 服务器,然后启动工作流。API 服务器与 Kubernetes 主控的 API 服务器进行通信,以便在数据平面中调度卷 Pod。

动态供应通过控制平面的供应器组件使用标准 Kubernetes 外部存储插件来实现。当应用程序从现有存储类创建 PVC 时,OpenEBS 供应器会从存储类中的原语构建一个 PV,并将其绑定到 PVC。

OpenEBS 控制平面在很大程度上依赖于 etcd 数据库,它作为集群的单一数据源。

现在我们已经了解了控制平面,接下来让我们更深入地了解数据平面。

数据平面

数据平面接近工作负载,并且仍然位于卷的 I/O 路径中。它在用户空间中运行的同时管理 PV 和 PVC 的生命周期。数据平面上提供了多种具有不同功能的存储引擎。在写作时,JivacStorLocal PV 是可用的三种存储引擎。Jiva 提供标准存储能力(块存储),通常用于较小规模的工作负载,而 cStor 提供企业级功能和广泛的快照功能。另一方面,Local PV 通过如复制和快照等高级功能提供更好的性能。

让我们更深入地了解每个存储引擎。

存储引擎

OpenEBS 的首选存储引擎是 cStor。它是一个功能丰富且轻量级的存储引擎,旨在满足高可用性工作负载的需求,例如数据库。它包括企业级功能,如同步数据复制、快照、克隆、精简数据供应、高数据弹性、数据一致性和按需的容量或性能增加。通过仅一个副本,cStor 的同步复制确保了有状态 Kubernetes 部署的优秀可用性。当有状态应用需要高数据可用性时,cStor 会设置为三个副本,数据会同步写入每个副本。终止和调度新的 Pod 到不同节点时不会导致数据丢失,因为数据已经写入多个副本。

Jiva 是首个被包含在早期 OpenEBS 版本中的存储引擎。Jiva 是所有选项中最简单的,因为它完全运行在用户空间,并具有常规的块存储功能,如同步复制。运行在没有额外块存储设备的节点上的小型应用程序将受益于 Jiva。因此,它不适用于需要高性能或高级存储能力的关键任务。

本地持久卷Local PV)是 OpenEBS 的第三个也是最简单的存储引擎。Local PV 是直接附加到单个 Kubernetes 节点的本地磁盘。Kubernetes 应用程序现在可以使用传统的卷 API 消耗高性能本地存储。OpenEBS 的 Local PV 是一种存储引擎,可以在工作节点上使用本地磁盘或主机路径构建 PV。Local PV 可用于不需要高级存储功能(如复制、快照或克隆)的云原生应用程序。例如,管理复制和高可用性的 StatefulSet 可以基于 OpenEBS 设置 Local PV。

除了前面提到的存储引擎之外,Mayastor 数据引擎是一种低延迟引擎,目前正在开发中,具有声明式的数据平面,为有状态应用提供灵活、持久的存储。它是 Kubernetes 原生的,提供快速、冗余的存储,可在任何 Kubernetes 集群中使用。Mayastor 插件将在 MicroK8s 1.24 中提供:microk8s.io/docs/addon-mayastor

OpenEBS 的另一个可选且受欢迎的功能是写时复制快照。快照可以即时创建,并且没有创建快照数量的限制。增量快照功能改善了 Kubernetes 集群之间以及不同云服务商或数据中心之间的数据迁移和可移植性。常见的应用场景包括高效的备份复制和利用克隆进行故障排除或开发,针对数据的只读副本。

OpenEBS 卷还支持与 Kubernetes 备份和恢复解决方案(如 Velero)兼容的备份和恢复功能 (velero.io/)。

要了解更多信息,您可以查看我关于如何备份和恢复 Kubernetes 集群资源(包括 PVs)的博客文章:www.upnxtblog.com/index.php/2019/12/16/how-to-back-up-and-restore-your-kubernetes-cluster-resources-and-persistent-volumes/

通过容器附加存储技术,OpenEBS 将软件定义存储的优势扩展到云原生应用程序。有关各存储引擎的详细比较和推荐使用场景,请参阅 OpenEBS 文档:openebs.io/docs/

综述一下,OpenEBS 可以从 Kubernetes 工作节点可用的任何存储中创建本地或分布式的 Kubernetes PV。这使得应用和平台团队可以轻松实现需要快速、可靠和可扩展 CAS 的 Kubernetes 有状态工作负载。它还确保每个存储卷都有一个独立的 Pod 以及一组副本 Pod,这些 Pod 像其他容器或微服务一样,在 Kubernetes 中进行管理和部署。OpenEBS 也作为容器安装,允许按应用、集群或容器分配存储服务。

现在,让我们学习如何在利用 OpenEBS Jiva 存储引擎的同时,配置并实现一个 PostgreSQL 有状态应用。

配置并实现 PostgreSQL 有状态工作负载

在本节中,我们将配置并实现一个 PostgreSQL 有状态工作负载,同时使用 OpenEBS 存储引擎。我们将使用 Jiva 存储引擎来确保 PostgreSQL 的持久性,创建测试数据,模拟节点故障以查看数据是否仍然完整,并确认 OpenEBS 的复制功能是否正常工作。

现在我们了解了 OpenEBS,我们将深入探讨在集群上配置和部署 OpenEBS 的步骤。下图展示了我们的树莓派集群设置:

图 11.3 – MicroK8s 树莓派集群

图 11.3 – MicroK8s 树莓派集群

现在我们知道了想要做什么,让我们来看看需求。

需求

在开始之前,您需要以下前提条件来构建一个树莓派 Kubernetes 集群并配置 OpenEBS:

  • 一张 microSD 卡(最小 4GB,推荐 8GB)

  • 一台带有 microSD 卡驱动器的计算机

  • 一台树莓派 2、3 或 4(1 台或更多)

  • 一根 micro-USB 电源线(Pi 4 需要 USB-C)

  • 一个 Wi-Fi 网络或一根带有互联网连接的以太网线

  • (可选)一台带有 HDMI 接口的显示器

  • (可选)Pi 2 和 3 使用 HDMI 电缆,Pi 4 使用 micro-HDMI 电缆

  • (可选)一只 USB 键盘

现在我们已经了解了测试由 OpenEBS 支持的 PostgreSQL 有状态工作负载的需求,接下来我们开始实施。

步骤 1 – 创建 MicroK8s 树莓派集群

请按照我们在第五章中介绍的步骤,在多节点树莓派 Kubernetes 集群上创建并实施更新,来创建 MicroK8s 树莓派集群;下面是简短的回顾:

  1. 在 SD 卡上安装操作系统镜像:

    1. 配置 Wi-Fi 访问设置。

    2. 配置远程访问设置。

    3. 配置控制组设置。

    4. 配置主机名。

  2. 安装并配置 MicroK8s。

  3. 添加一个工作节点。

一个完全功能的多节点 Kubernetes 集群如下所示。总结一下,我们在树莓派板上安装了 MicroK8s,并将多个部署加入到集群中。我们还向集群中添加了节点:

图 11.4 – 完全功能的 MicroK8s 树莓派集群

图 11.4 – 完全功能的 MicroK8s 树莓派集群

现在,让我们启用 OpenEBS 插件。

步骤 2 – 启用 OpenEBS 插件

OpenEBS 插件在 MicroK8s 中默认可用。使用以下命令启用 OpenEBS:

microk8s enable openebs

以下命令的输出表明,必须启用 iscsid 控制器作为前提条件。对于存储管理,OpenEBS 使用 互联网小型计算机系统接口 (iSCSI) 技术。iSCSI 协议是一种基于 TCP/IP 的协议,用于创建存储区域网络并在 IP 存储设备、主机和客户端之间建立和管理互联(SAN)。这些 SAN 允许在高速数据传输网络中使用 SCSI 协议,进行不同数据存储网络之间的块级数据传输:

图 11.5 – 启用 OpenEBS 插件

图 11.5 – 启用 OpenEBS 插件

使用以下命令启用 iscsid 控制器:

sudo systemctl enable iscsid

以下输出表明 iscsid 已成功安装。现在,我们可以启用 OpenEBS 插件:

图 11.6 – 启用 iSCSI 控制器

图 11.6 – 启用 iSCSI 控制器

以下输出表明 OpenEBS 插件已成功启用:

图 11.7 – 启用 OpenEBS 插件

图 11.7 – 启用 OpenEBS 插件

Helm3 插件默认也已启用。在继续之前,让我们通过以下命令确保所有 OpenEBS 组件都已启动并运行:

kubectl get pods –n openebs

以下输出表明所有组件都在 运行

图 11.8 – OpenEBS 组件已启动并运行

图 11.8 – OpenEBS 组件已启动并运行

现在 OpenEBS 插件已启用,让我们部署 PostgreSQL 有状态工作负载。

步骤 3 – 部署 PostgreSQL 有状态工作负载

回顾第一章Kubernetes 入门,StatefulSet 是 Kubernetes 工作负载 API 对象,用于管理有状态应用程序。在典型的部署中,用户并不关心 Pod 是如何调度的,只要它不会对部署的应用程序产生负面影响。然而,为了在具有持久存储的有状态应用中保持状态,Pod 必须被标识。这个功能由 StatefulSet 提供,它创建具有持久标识符的 Pod,这些标识符在重新调度时保持一致。这样,即使 Pod 被重新创建,它也会正确地映射到存储卷,应用程序的状态将被保留。

随着在 Kubernetes 中部署数据库集群的普及,在容器化环境中管理状态变得更加重要。

我们需要设置以下资源来使 PostgreSQL 配置正常运行:

  • 存储类

  • PersistentVolume

  • PersistentVolumeClaim

  • StatefulSet

  • ConfigMap

  • 服务

为了管理持久存储,Kubernetes 提供了 PersistentVolumePersistentVolumeClaim 存储机制,我们在简介中简要讨论过。这是它们的快速概述:

  • PersistentVolume (PV) 存储在由集群管理员预配置的集群中,或通过存储类动态配置。

  • PersistentVolumeClaim (PVC) 是用户(开发者)对存储的请求。它类似于 pod。PVC 使用 PV 资源,而 pod 使用节点资源:

图 11.9 – PV 和 PVC 存储基础

图 11.9 – PV 和 PVC 存储基础

在创建 PV 和 PVC 之前,先查看一下 OpenEBS 为我们创建的存储类。

StorageClass 允许管理员描述他们提供的 存储类。不同的类可能对应不同的 服务质量 (QoS) 等级、备份策略或由集群管理员确定的任意策略。

使用以下命令检索 OpenEBS 创建的存储类:

kubectl get sc

以下输出显示有三个可用的 StorageClass

图 11.10 – OpenEBS 存储类

图 11.10 – OpenEBS 存储类

openebs-hostpathopenebs-device 推荐用于单节点集群,对于多节点集群,推荐使用 openebs-jiva-csi-default

现在,我们必须定义 PersistentVolume,它将使用存储类,以及 PersistentVolumeClaim,它将用于声明此卷:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: postgres-pv
  labels:
    app: postgres
    type: local
spec:
  storageClassName: openebs-jiva-csi-default
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/var/data"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: postgres-pv-claim
  labels:
    app: postgres
spec:
  storageClassName: openebs-jiva-csi-default
  capacity:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

因为我们使用的是 OpenEBS 磁盘供应程序,所以我们需要指定数据在主机节点上的存储位置。在此我们使用/var/data/accessMode 选项也至关重要。此处我们使用ReadWriteOnce。这确保每次只有一个 pod 可以写入。因此,不会有两个 pod 同时写入同一个卷。我们还可以指定该卷的大小,这里选择了 5 GB。

访问模式说明

即使一个卷支持多种访问模式,它们也只能一次挂载一个模式:

ReadOnlyMany (ROX):可以由多个节点以只读模式挂载

ReadWriteOnce (RWO):可以由单个节点以读写模式挂载

ReadWriteMany (RWX):多个节点可以以读写模式挂载

使用以下命令创建 PV 和 PVC:

kubectl apply –f postgres.yaml

以下输出表示 PersistentVolumePersistentVolumeClaim 已成功创建:

图 11.11 – PV 和 PVC 创建成功

图 11.11 – PV 和 PVC 创建成功

在继续之前,先检查 PV 和 PVC 是否处于 Bound 状态。Bound 状态表示应用程序已访问所需的存储:

图 11.12 – PV 和 PVC 已绑定

图 11.12 – PV 和 PVC 已绑定

如果 PVC 变得卡住等待,StatefulSet也会被卡住,因为它将无法访问其存储。因此,请仔细检查StorageClassPersistentVolume是否已正确设置。

现在我们已经设置了 PV 和 PVC,我们将设置ConfigMap,并包含一些如用户名和密码等配置,以便我们的设置能正常工作。为了简化,本示例中我们将这些值硬编码到ConfigMap中:

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-configuration
  labels:
    app: postgres
data:
  POSTGRES_DB: postgresdb
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: postgrespassword

使用以下命令创建ConfigMap

kubectl apply –f postgres-config.yaml

以下输出表明postgres-configuration.yaml文件的ConfigMap已成功创建:

图 11.13 – PostgreSQL ConfigMap 已创建

图 11.13 – PostgreSQL ConfigMap 已创建

让我们使用describe命令获取我们创建的ConfigMap对象的详细信息。以下输出显示我们的 PostgreSQL 设置所需的配置已就绪:

图 11.14 – PostgreSQL ConfigMap

图 11.14 – PostgreSQL ConfigMap

现在我们已经定义了ConfigMap和存储卷,我们可以定义一个StatefulSet来使用它们:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres-statefulset
  labels:
    app: postgres
spec:
  serviceName: "postgres"
  replicas: 2
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:12
        envFrom:
        - configMapRef:
            name: postgres-configuration
        ports:
        - containerPort: 5432
          name: postgresdb
        volumeMounts:
        - name: pv-data
          mountPath: /var/lib/postgresql/data
      volumes:
      - name: pv-data
        persistentVolumeClaim:
          claimName: postgres-pv-claim
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-service
  labels:
    app: postgres
spec:
  ports:
  - port: 5432
    name: postgres
  type: NodePort 
  selector:
    app: postgres

StatefulSet的定义与部署类似。我们增加了两项内容:

  • 我们已将ConfigMap中的环境变量加载到 Pod 中。

  • 我们定义了卷,它将在 Pod 内映射到/var/lib/PostgreSQL/data。该卷是通过我们之前讨论的 PVC 来定义的。

最后,我们还创建了一个Service资源,将暴露我们的数据库。

使用以下命令创建StatefulSetService资源以暴露数据库:

kubectl apply –f postgres-deployment.yaml

以下输出表明StatefulSetService已成功创建:

图 11.15 – Postgres 部署成功

图 11.15 – Postgres 部署成功

在继续之前,让我们验证一下 Pod 和 Service 是否已创建。以下输出显示 Pod 正在Running

图 11.16 – Postgres Pod 正在运行

图 11.16 – Postgres Pod 正在运行

以下输出显示服务已通过端口5432暴露:

图 11.17 – Postgres 服务已暴露

图 11.17 – Postgres 服务已暴露

让我们使用以下命令查看StatefulSet的 Pod 是如何分布在集群中的:

kubectl get pods –o wide | grep post

以下输出显示 PostgreSQL 数据库 Pod 正在两个节点上运行(1controlplane1worker1):

图 11.18 – 数据库 Pod 运行在两个节点上

图 11.18 – 数据库 Pod 运行在两个节点上

至此,我们已经成功配置了 PostgreSQL 并使其正常运行。

现在,让我们创建一个测试数据库和表,并添加一些记录。

第 4 步 – 创建测试数据

为了创建测试数据,使用 PgSQL 客户端或登录到其中一个 Pod,这样我们可以创建测试数据库和表。

使用以下命令登录到 PostgreSQL Pod:

kubectl exec –it postgres-statefulset-0 -- psql –U postgres

以下输出显示我们可以登录到 PostgreSQL Pod:

图 11.19 – 登录到其中一个 PostgreSQL Pod

图 11.19 – 登录到其中一个 PostgreSQL Pod

现在我们已经登录到 Pod,我们可以使用 psql PostgreSQL 客户端。使用以下命令创建测试数据库:

CREATE DATABASE inventory_mgmt;

以下输出显示我们的测试数据库inventory_mgmt已成功创建:

图 11.20 – 创建测试数据库

图 11.20 – 创建测试数据库

让我们通过使用 \c inventory_mgmt 切换到我们创建的新数据库。以下输出显示我们已成功切换到新数据库。现在,我们可以创建表了:

图 11.21 – 切换到新数据库连接

图 11.21 – 切换到新数据库连接

在新数据库中,使用 CREATE TABLE 命令创建测试表。以下输出表示新表 products_master 已成功创建:

图 11.22 – 创建测试表

图 11.22 – 创建测试表

现在测试表已经创建,使用 INSERT 命令添加一些记录,如下所示:

图 11.23 – 向测试表中添加一些记录

图 11.23 – 向测试表中添加一些记录

在这里,我们已经向测试表中添加了记录。在继续之前,让我们使用SELECT命令列出记录,如下图所示:

图 11.24 – 测试表中的记录

图 11.24 – 测试表中的记录

总结一下,在这一部分中,我们已经创建了一个测试数据库,创建了一个新表,并向表中添加了一些记录。现在,让我们模拟节点故障。

第五步 – 模拟节点故障

为了模拟节点故障,我们将使用 cordon 命令将节点标记为 不可调度。如果节点是 不可调度 的,Kubernetes 控制器将不会在该节点上调度新的 Pod。

让我们定位 PostgreSQL 数据库 Pod 所在的节点,并将其封锁,防止新的 Pod 在该节点上调度。

以下输出显示数据库 Pod 正在 2 个节点上运行(1controlplane1worker1):

图 11.25 – PostgreSQL 数据库 Pods

图 11.25 – PostgreSQL 数据库 Pods

让我们在 worker1 节点上使用 cordon,以防止新的 Pod 在该节点上被调度:

kubectl cordon worker1

以下输出显示 worker1 已被封锁:

图 11.26 – 已被封锁的 Worker1 节点

图 11.26 – 已被封锁的 Worker1 节点

即使worker1节点已经被隔离,现有的 pod 仍然会运行,因此我们可以使用 drain 命令删除所有的 pod:

kubectl drain --force --ignore-daemonsets worker1

以下输出显示,由于存在具有本地存储配置的 pod,worker1 无法被清空:

图 11.27 – 清空 Worker1 节点

图 11.27 – 清空 Worker1 节点

最后,我们将使用kubectl delete命令删除当前在已隔离节点上运行的 pod。

以下输出显示,运行在 worker1 上的 pod 已成功删除:

图 11.28 – 删除在 Worker1 节点上运行的 pod

图 11.28 – 删除在 Worker1 节点上运行的 pod

一旦 pod 被删除,Kubernetes 控制器将重新创建一个新 pod,并将其调度到不同的节点上。由于调度被禁用,它不能被放置到同一节点上;这是因为我们已经隔离了worker1节点。

让我们使用 kubectl get pods 命令查看 pod 的运行位置。以下输出显示,新 pod 已被重新调度到 controlplane 节点:

图 11.29 – PostgreSQL 数据库 pod

图 11.29 – PostgreSQL 数据库 pod

即使 PVC 的访问模式为ReadWriteOnce,并且由特定节点进行读写挂载,重新创建的新 pod 仍然可以使用由底层 OpenEBS 卷抽象出的 PVC,这些卷已合并为单一的存储层。

为了验证新 pod 是否正在使用相同的 PVC,我们可以连接到新 pod,并使用 kubectl exec 命令查看数据是否仍然完好无损:

图 11.30 – 登录 PostgreSQL pod

图 11.30 – 登录 PostgreSQL pod

以下输出显示,即使删除了 pod 并将其重新调度到不同的节点上,数据仍然完好无损。这证明了复制的OpenEBS正在正常工作:

图 11.31 – 数据完好无损

图 11.31 – 数据完好无损

总结来说,数据引擎负责维护由有状态应用生成的实际状态,并提供足够的存储容量来保留这些信息,确保它随时间保持完好无损。例如,状态可以创建一次,在接下来的几分钟或几天内访问、更新,或干脆在几个月或几年后再取回。根据 Kubernetes 工作节点关联的存储类型和应用的性能需求,你可以使用本地 PVJivacStorMayastor

选择引擎完全取决于你的平台(资源和存储类型)、应用工作负载以及应用当前和未来的容量和/或性能增长。在下一节中,我们将探讨一些 Kubernetes 存储最佳实践,并提供一些数据引擎的推荐。

Kubernetes 存储最佳实践

对于在 Kubernetes 上部署的现代容器化应用程序,存储是一个至关重要的问题。Kubernetes 从容器中挂载的本地节点文件系统,逐步发展到 NFS,最终到本地存储,如 CSI 规范所描述的那样,它支持数据持久性和共享。在本节中,我们将探讨在配置 PV 时需要考虑的一些最佳实践:

  • 避免静态创建和分配 PV,以减少管理成本并促进扩展。改为使用动态供应。为您的存储类定义适当的回收策略,以便在删除 Pod 时减少存储成本。

  • 每个节点只能支持一定数量的大小,因此不同的节点大小提供不同的本地存储和容量。为了安装最佳的节点大小,请根据应用程序的需求进行规划。

  • PV 的生命周期与集群中任何单个容器无关。PVC 是由容器用户或应用程序发出的对特定类型存储的请求。Kubernetes 文档建议构建 PV 时执行以下操作:

    • PVC 应始终包含在容器设置中。

    • PV 永远不应在容器配置中使用,因为它会将容器绑定到特定的卷。

    • 如果 PVC 未指定特定的类,并且没有默认的 Storage Class,则会失败。

    • 给存储类起具有意义的名称。

  • 在命名空间级别,也提供资源配额,您可以通过这种方式对集群资源的使用进行更细粒度的控制。命名空间中所有容器可以使用的总 CPU、内存和存储资源受资源限制的限制。还可以根据服务级别或备份需求设置存储资源限制。

  • 持久化存储硬件有多种形状和尺寸。例如,SSD 在读写性能方面优于 HDD,而 NVMe SSD 尤其适合高负载工作。某些 Kubernetes 提供商会在 PVC 描述中添加 QoS 标准。这意味着它优先处理特定安装的读写卷,从而在应用程序需要时提供更高的性能。

现在,让我们看看选择 OpenEBS 数据引擎的一些指南。

选择 OpenEBS 数据引擎的指南

每个存储引擎都有其优点,如下表所示。选择引擎完全取决于您的平台(资源和存储类型)、应用程序工作负载以及应用程序当前和未来的容量和/或性能增长。以下指南将帮助您选择合适的引擎:

表 11.1 – 选择 OpenEBS 数据引擎

表 11.1 – 选择 OpenEBS 数据引擎

总结来说,OpenEBS 提供了一套数据引擎,每个引擎都针对在 Kubernetes 节点上执行有状态工作负载而设计并优化,且节点的资源水平各异。在 Kubernetes 集群中,平台 SRE 或管理员通常会选择一个或多个数据引擎。这些数据引擎的选择依据节点能力或有状态应用的需求。

总结

在本章中,我们学习了 Kubernetes 持久化存储如何为 Kubernetes 应用程序提供便捷的存储资源请求和消耗方式。PVC 由用户的 pod 声明,Kubernetes 会找到一个 PV 来与之匹配。如果没有可匹配的 PV,它将进入相应的StorageClass,帮助创建一个 PV,并将其绑定到 PVC。新创建的 PV 必须使用附加的主节点为主机创建远程磁盘,然后通过每个节点的kubelet组件将附加的远程磁盘挂载到主机目录。

Kubernetes 在促进运行有状态工作负载方面取得了显著进展,通过为平台(或集群管理员)和应用程序开发者提供必要的抽象。这些抽象确保了不同类型的文件和块存储(无论是临时的还是持久的,本地的还是远程的)能够在容器调度的任何地方使用(包括配备/创建、附加、挂载、卸载、分离和删除卷),存储容量管理(容器临时存储使用、卷大小调整和常规操作),以及基于存储影响容器调度(数据重力、可用性等)。

在下一章中,你将学习如何部署 Istio 和 Linkerd 服务网格。你还将学习如何部署和运行一个示例应用程序,以及如何配置和访问仪表盘。

第十二章:实现跨切关注点的服务网格

在上一章中,我们研究了 OpenEBS 云原生存储解决方案,以便为我们的容器应用程序提供持久存储。我们还讨论了 容器附加存储CAS)如何迅速获得作为管理有状态工作负载并利用持久、容错的有状态应用程序的可行解决方案的接受。MicroK8s 内置支持 OpenEBS,使其成为在隔离的 Edge/IoT 环境中运行 Kubernetes 集群的理想选择。通过 OpenEBS 存储引擎,我们配置并实现了 PostgreSQL 有状态工作负载。我们还回顾了一些创建持久卷和选择 OpenEBS 数据引擎时需要牢记的最佳实践。

云原生应用程序的兴起与服务网格的出现密切相关。在云原生世界中,一个应用程序可能由数百个服务组成,每个服务可能有成千上万个实例,而这些实例由于 Kubernetes 等调度器的动态调度而可能不断变化。服务之间的通信不仅极为复杂,而且是应用程序运行时行为的一个关键组成部分。管理它对于确保端到端的性能、可靠性和安全性至关重要。

服务网格,如 Linkerd 或 Istio,是一种工具,用于将可观察性、安全性和可靠性功能透明地嵌入到云原生应用程序的基础设施层,而不是应用程序层。服务网格正在迅速成为云原生技术栈的一个关键组成部分,尤其是在 Kubernetes 用户中。通常,服务网格构建为一组可扩展的网络代理,这些代理与应用程序代码(边车模式)一起运行。这些代理调解微服务之间的通信,并作为实现服务网格功能的一个切入点。

服务网格层可以作为边车与应用程序一起在容器中运行。每个应用程序都有多个相同的边车副本附加到其上,如下图所示:

图 12.1 – 服务网格边车模式

图 12.1 – 服务网格边车模式

边车代理处理来自单一服务的所有进出网络流量。因此,边车控制微服务之间的流量,收集遥测数据并应用策略。在某些方面,应用服务对网络并不知情,只知道连接到它的边车代理。

在服务网格中,存在数据平面和控制平面:

  • 数据平面协调网格中各服务之间的通信,并执行诸如服务发现、负载均衡、流量管理、健康检查等功能。

  • 控制平面管理和配置边车代理,以便执行策略并收集遥测数据。

服务网格提供服务发现、自动负载均衡、通过路由规则、重试、故障转移等对流量行为的精细控制等功能。它还具有可插拔的策略层和 API 配置,支持访问控制、速率限制和配额。最后,它还提供服务监控,自动收集所有流量的度量、日志和跟踪数据,并在网格中实现安全的服务间通信。

在本章中,我们将看两个流行的服务网格提供商来实现此模式:Linkerd 和 Istio。我们不会深入探讨服务网格的所有功能,而是将重点介绍使用示例应用程序进行的监控方面。

在本章中,我们将覆盖以下主题:

  • Linkerd 服务网格概述

  • 启用 Linkerd 附加组件并运行示例应用程序

  • Istio 服务网格概述

  • 启用 Istio 附加组件并运行示例应用程序

  • 服务网格的常见用例

  • 选择服务网格的指南

  • 配置服务网格的最佳实践

Linkerd 服务网格概述

Linkerd 是一个基于 Kubernetes 的服务网格。它通过提供运行时调试、可观察性、可靠性和安全性,简化并保障了服务的运行,而无需任何代码更改。

每个服务实例通过一套超轻量、透明的代理系统与 Linkerd 连接。这些代理自动处理进出服务的所有流量。它们作为高度仪表化的外部进程网络堆栈运行,向控制平面发送遥测数据并接收控制信号。Linkerd 还可以在不引入不必要延迟的情况下,衡量和管理进出服务的流量。

如前一章所讨论的,Linkerd 由控制平面组成,控制平面是一组服务,用于控制整个 Linkerd,并由数据平面组成,数据平面由透明的微代理构成,这些代理更靠近每个服务实例,并作为 Sidecar 容器运行在 Pod 中。这些代理自动处理所有进出服务的 TCP 流量,并与控制平面进行配置通信。

以下图展示了 Linkerd 的架构:

图 12.2 – Linkerd 服务网格组件

图 12.2 – Linkerd 服务网格组件

现在我们已经提供了一个高层次的概述并查看了架构,接下来让我们更详细地了解每个组件:

  • 目标服务:数据平面代理使用目标服务来确定它们行为的各个方面。它用于检索服务发现信息,获取关于哪些类型的请求被允许的策略信息,以及检索服务配置文件信息,这些信息用于指导每条路由的度量、重试、超时等。

  • 身份服务:身份服务充当 TLS 证书颁发机构,接受来自代理的 CSR 并签发证书。这些证书在代理初始化时颁发,并用于在代理间连接上实现 mTLS。

  • linkerd.io/inject: enabled) 在资源中。当该注解存在时,注入器会修改 pod 的规格,添加 proxy-initlinkerd-proxy 容器,并将相关的启动时配置添加到 pod 中。

  • Linkerd2-proxy:Linkerd2-proxy 是一个超轻量级、透明的微代理,专为服务网格用例设计,并非通用代理。

  • linkerd-init 容器作为 Kubernetes 的 init 容器,在其他容器启动之前运行。所有进出 pod 的 TCP 流量都通过 iptables 路由到代理。

现在我们已经掌握了基础知识,接下来启用 Linkerd 插件并运行一个示例应用程序。

启用 Linkerd 插件并运行示例应用程序

在本节中,您将启用 MicroK8s Kubernetes 集群中的 Linkerd 插件。然后,为了展示 Linkerd 的功能,您将部署一个示例应用程序。

注意

我将在本节中使用 Ubuntu 虚拟机。设置 MicroK8s 集群的说明与第五章中的内容相同,创建和实施多节点 Raspberry Pi Kubernetes 集群的更新

步骤 1 – 启用 Linkerd 插件

使用以下命令启用 Cilium 插件:

microk8s enable linkerd 

以下输出表示 Linkerd 插件已经启用:

图 12.3 – 启用 Linkerd 插件

图 12.3 – 启用 Linkerd 插件

启用插件的过程需要一些时间。以下输出表明 Linkerd 已成功启用:

图 12.4 – 成功启用 Linkerd

图 12.4 – 成功启用 Linkerd

在进入下一步之前,让我们使用以下命令确保所有 Linkerd 组件都已启动并运行:

kubectl get pods –n linkerd

以下输出表示所有组件均处于 Running 状态:

图 12.5 – Linkerd pod 正在运行

图 12.5 – Linkerd pod 正在运行

现在 Linkerd 插件已经启用,让我们部署一个示例 Nginx 应用程序。

步骤 2 – 部署示例应用程序

在此步骤中,我们将从 Kubernetes 示例库部署一个示例 Nginx 应用程序。

使用以下命令创建示例 Nginx 部署:

kubectl apply –f https://k8s.io/examples/application/deployment.yaml

以下输出表示部署过程中没有错误。现在,我们可以确保 pod 已被创建:

图 12.6 – 示例 Nginx 应用程序部署

图 12.6 – 示例 Nginx 应用程序部署

现在部署成功了,让我们使用 kubectl 命令来检查 pods 是否处于 Running 状态:

图 12.7 – 示例应用程序的 pods

图 12.7 – 示例应用程序的 pods

在这里,我们可以看到示例应用程序的部署已成功,并且所有 pods 都在运行。我们的下一步是通过将 linkerd injectkubectl apply 命令串联起来,向其注入 Linkerd。在没有任何停机时间的情况下,Kubernetes 将执行滚动部署,并更新每个 pod 的数据平面代理。

使用以下命令将 Linkerd 注入到示例应用程序中:

kubectl get deployment nginx-deployment –n default –o yaml | microk8s linkerd inject – | kubectl apply –f –

下面的输出确认了 linkerd inject 命令已成功,并且示例应用程序部署已重新配置:

图 12.8 – 将 Linkerd 注入示例应用程序

图 12.8 – 将 Linkerd 注入示例应用程序

linkerd inject 命令只是简单地向 pod 规范添加注释,指示 Linkerd 在创建时将代理注入到 pods 中。

恭喜!现在已将 Linkerd 添加到示例 Nginx 应用程序中!我们在不改动原始 YAML 的情况下,向示例应用程序服务添加了 Linkerd。

在数据平面方面,可以通过以下命令双重检查一切是否正常运行。使用以下命令检查数据平面:

microk8s linkerd check --proxy

Linkerd CLI (microk8s linkerd) 是与 Linkerd 一起工作的主要接口。它可以在集群上设置控制平面,向服务添加代理,并为服务提供详尽的性能指标。

下面的输出确认了 linkerd check 命令已开始对数据平面进行检查:

图 12.9 – Linkerd 检查

图 12.9 – Linkerd 检查

数据平面检查需要一些时间来完成。下面的输出显示 Linkerd 检查已完成:

图 12.10 – Linkerd 检查完成

图 12.10 – Linkerd 检查完成

现在数据平面检查已完成,我们可以使用 kubectl describe 命令查看是否已向示例应用程序部署添加了 Linkerd 注释。

下面的输出确认了已添加 Linkerd 注释:

图 12.11 – 添加了 Linkerd 注释

图 12.11 – 添加了 Linkerd 注释

此外,我们已经成功注入了 Linkerd,无需编写任何特殊配置或更改应用程序的代码。如果可以提供给 Linkerd 附加信息,它将能够强加各种限制,如超时和重试。然后,它可以为每个路由提供统计信息。

接下来,我们将开始检索关于示例 Nginx 部署中每个服务表现的重要信息。由于已向应用程序注入 Linkerd,我们将查看各种指标和仪表板。

步骤 3 – 探索 Linkerd 仪表板

Linkerd 提供了一个集群内度量堆栈,包括一个 Web 仪表板和预配置的 Grafana 仪表板。在这一步,我们将学习如何启动 Linkerd 和 Grafana 仪表板。

使用以下命令启动 Linkerd 仪表板:

microk8s linkerd viz dashboard

以下输出表明 Linkerd 仪表板已启动,并且可以在端口50750上访问。

要查看这些度量数据,您可以使用 Grafana 仪表板,Grafana 仪表板可以通过http://localhost:50750/grafana访问:

图 12.12 – 启动 Linkerd 仪表板

图 12.12 – 启动 Linkerd 仪表板

Linkerd 仪表板为您提供了实时查看服务状态的鸟瞰图。它可以用来查看如成功率、每秒请求数和延迟等度量数据,并可视化服务依赖关系,了解某些服务路由的健康状况:

图 12.13 – Linkerd 仪表板

图 12.13 – Linkerd 仪表板

虽然 Linkerd 仪表板为您提供了实时查看服务状态的鸟瞰图,但 Grafana 仪表板也是 Linkerd 控制面的一部分,提供了开箱即用的服务仪表板。这些仪表板还可以用来监控服务。即使是 Pods,我们也能获取高层次的统计数据并深入了解细节:

图 12.14 – Linkerd 顶级度量仪表板

图 12.14 – Linkerd 顶级度量仪表板

总结一下,我们已在 MicroK8s Kubernetes 集群上启用 Linkerd,并使用它来监控示例 Nginx 应用程序的服务。我们还收集了相关的遥测数据,如成功率、吞吐量和延迟。之后,我们查看了一些开箱即用的 Grafana 仪表板,以查看高层次的度量数据并深入探讨细节。

在下一节中,我们将介绍 Istio,另一个著名的服务网格提供商。

Istio 服务网格概述

Istio 是一个开源的、平台无关的服务网格,它管理流量、执行策略并收集遥测数据。它是一个管理微服务和应用程序之间通信的平台。它还为所有服务间通信提供自动化的基准流量弹性、服务度量收集、分布式跟踪、流量加密、协议升级和高级路由功能,而无需对底层服务进行任何更改。

以下是 Istio 的一些重要功能:

  • 通过 TLS 加密、基于强身份的认证和授权实现安全的服务间通信

  • HTTP、gRPC、WebSocket 和 TCP 流量的自动负载均衡

  • 通过丰富的路由规则、重试、故障转移和故障注入实现精细化流量控制

  • 一个可插拔的策略层和配置 API,支持访问控制、速率限制和配额

  • 自动化的度量、日志和跟踪,涵盖所有集群流量,包括集群的入口和出口流量

一个 Istio 服务网格在逻辑上被划分为两个平面:数据平面和控制平面。

数据平面由一组智能代理组成,这些代理作为边车部署。所有微服务之间的网络通信都由这些代理调解和控制。此外,它们收集并报告所有网格流量的遥测数据。

控制平面负责管理和配置用于路由流量的代理。

图 12.15 – Istio 组件

图 12.15 – Istio 组件

现在我们已经提供了高层次的概述并看过架构,让我们更详细地了解每个组件:

  • Istiod:它管理服务发现、配置和证书。它将高层的流量路由规则转换为 Envoy 特定的配置,并在运行时将其传播到边车。Pilot 组件将平台特定的服务发现机制进行抽象,并将其合成成标准格式,可以被任何遵循 Envoy API 的边车使用。Istio 还支持多种环境下的发现,包括 Kubernetes 和虚拟机。

  • Envoy:这是一款高性能的代理,调解所有进出服务网格的流量。唯一与数据平面流量交互的 Istio 组件是 Envoy 代理。Envoy 代理作为服务的边车部署,在逻辑上增强了服务,提供了许多内置功能,例如:

    • 动态服务发现

    • 负载均衡

    • TLS 终止

    • HTTP/2 和 gRPC 代理

    • 熔断器

    • 健康检查

    • 基于百分比流量分配的分阶段发布

    • 故障注入

    • 丰富的指标

以下是 Istio 系统的各种组件,以及它所使用的抽象:

  • 流量管理:Istio 提供的流量路由规则使你可以轻松地控制服务之间流量和 API 调用的流向。Istio 简化了配置服务级属性(如熔断器、超时和重试)以及 A/B 测试、金丝雀发布和基于百分比的分阶段发布等重要任务。它还包括开箱即用的可靠性功能,有助于提高应用程序对依赖服务或网络故障的容错能力。

  • 可观察性:对于所有网格服务通信,Istio 创建了广泛的遥测数据。这些遥测数据让用户能够观察服务行为,帮助他们调试、维护和优化应用程序,而无需给服务开发人员增加额外负担。用户可以全面了解被监控服务如何相互交互以及与 Istio 组件的关系。

Istio 创建以下几种遥测,以实现对整个服务网格的可观察性:

  • 指标:基于四个监控属性,Istio 创建了一组服务指标(延迟、流量、错误和饱和度)。此外,Istio 提供了广泛的网格控制平面度量。在这些指标基础上,提供了一组基础的网格监控仪表盘。

  • 分布式追踪:分散的追踪会生成每个服务的分布式追踪跨度,提供用户一个全面的调用流和服务关系视图,展示网格内部的调用关系。

  • 访问日志:当流量进入网格中的服务时,Istio 会生成每个请求的完整记录,包括源和目标的元数据。用户可以利用这些信息检查服务行为,直到单个工作负载实例。

  • 安全性:为了保护托管服务和数据,Istio 安全性包括强大的身份认证、强大的策略管理、透明的 TLS 加密、身份验证和审计工具。

现在我们已经涵盖了基础内容,可以继续下一步,即启用 Istio 插件并运行示例应用。

启用 Istio 插件并运行示例应用

在本节中,您将启用 MicroK8s Kubernetes 集群中的 Istio 插件。然后,您将启动示例应用来展示 Istio 的能力。

注意

在本节中,我将使用一台 Ubuntu 虚拟机。设置 MicroK8s 集群的指令与第五章中的创建和实现多节点 Raspberry Pi Kubernetes 集群更新相同。

第一步 – 启用 Istio 插件

使用以下命令启用 Istio 插件:

microk8s enable istio

以下输出表示 Istio 插件已成功启用:

图 12.16 – 启用 Istio 插件

图 12.16 – 启用 Istio 插件

启用插件需要一些时间。以下输出显示 Istio 已成功启用:

图 12.17 – Istio 插件已启用

图 12.17 – Istio 插件已启用

在继续下一步之前,让我们通过以下命令确保所有 Istio 组件都已启动并正常运行:

kubectl get pods –n istio-system

以下输出表明所有组件都处于Running状态:

图 12.18 – Istio pod 正在运行

图 12.18 – Istio pod 正在运行

现在 Istio 插件已经启用,我们可以部署示例应用了。

第二步 – 部署示例应用

在部署示例 Nginx 应用之前,我们需要将命名空间标记为istio-injection=enabled,这样 Istio 就可以将 sidecar 注入到部署的 pod 中。

使用以下命令标记命名空间:

microk8s kubectl label namespace default istio-injection=enabled

以下输出表明部署没有错误。现在我们可以部署示例应用了:

图 12.19 – 标记命名空间

图 12.19 – 标记命名空间

使用以下命令创建一个示例 Nginx 部署:

kubectl apply –f https://k8s.io/examples/application/deployment.yaml

以下输出表示部署中没有错误。现在,我们可以确保 Istio 已经被注入到 pods 中:

图 12.20 – 示例应用部署

图 12.20 – 示例应用部署

部署完成后,我们可以使用 kubectl describe 命令检查 Istio 标签是否已添加到示例应用部署中。

以下输出确认 Istio 标签已被添加:

图 12.21 – 已添加 Istio 注解

图 12.21 – 已添加 Istio 注解

我们还可以使用 istioctl 命令行工具获取 Istio 网格的概况:

microk8s istioctl proxy-status

以下输出表示我们的示例 Nginx 部署已与 Istiod 控制平面 SYNCED

图 12.22 – Istio 代理状态

图 12.22 – Istio 代理状态

如果某些 sidecar 没有收到配置或不同步,可以使用 proxy-status 命令。

如果没有列出某个代理,说明它当前没有连接到 Istiod 实例:

  • SYNCED 表示 Envoy 代理已确认接收到 Istiod 提供的最新配置。

  • NOT SENT 表示 Istiod 尚未向 Envoy 代理发送任何消息。这通常是因为 Istiod 没有要发送的内容。

  • STALE 表示 Istiod 向 Envoy 代理发送了更新,但未收到响应。这通常表示 Envoy 代理与 Istiod 之间的网络存在问题,或 Istio 本身存在缺陷。

恭喜!你已将 Istio 代理添加到示例应用中!我们将 Istio 添加到现有服务中,而没有修改原始的 YAML。

步骤 3 – 探索 Istio 服务仪表盘

在网格中的所有服务通信中,Istio 创建了广泛的遥测数据。这些遥测数据可以帮助观察服务行为,使服务网格用户能够进行故障排除、维护和优化应用,而不会增加服务开发人员的工作负担。

正如我们之前讨论的,为了启用整体服务网格可观测性,Istio 创建了以下几种形式的遥测数据:

  • 指标:根据监控性能,Istio 会生成一组服务指标(延迟、流量、错误和饱和度)。对于网格控制平面,Istio 还提供详细的指标。在这些指标的基础上,提供了一套默认的网格监控仪表盘:

图 12.23 – Istio 服务仪表盘

图 12.23 – Istio 服务仪表盘

资源使用仪表盘如下所示。在这里,我们可以查看关于内存、CPU 和磁盘使用的详细信息:

图 12.24 – Istio 资源使用仪表盘

图 12.24 – Istio 资源使用仪表盘

  • 分布式追踪:Istio 为每个服务创建分布式追踪跨度,向用户展示完整的调用流和服务依赖关系图:

图 12.25 – Istio 分布式追踪

图 12.25 – Istio 分布式追踪

  • 访问日志:Istio 为每个请求生成完整的记录,当流量进入网格中的某个服务时,包括源和目的地的元数据。用户可以利用这些数据审计服务行为,直至单个工作负载实例级别。

总结来说,我们在 MicroK8s Kubernetes 集群上部署了 Istio,并使用它来监控一个示例 Nginx 应用程序的服务。我们还查看了 Istio 服务仪表盘,利用它可以检查遥测数据进行调试、维护和改进应用程序。最后,我们了解了如何使用度量、分布式追踪和访问日志来增强整体的服务网格可观察性。

简而言之,服务网格提供了统一的发现、安全、追踪、监控和故障管理。因此,如果 Kubernetes 集群中有一个服务网格,你可以在不改变应用代码的情况下实现以下功能:

  • 自动负载均衡

  • 通过路由规则、重试、故障转移等实现流量行为的精细控制

  • 可插拔的策略层

  • 一个支持访问控制、速率限制和配额的配置 API

  • 服务发现

  • 对所有流量进行自动指标、日志和追踪的服务监控

  • 安全的服务间通信

在大多数实现中,服务网格作为微服务架构的单一视窗。它是你用于故障排查、执行流量策略、设置速率限制和测试新代码的地方。它作为你监控、追踪和控制所有服务交互的中心点——即它们是如何连接、执行和保障的。在接下来的部分中,我们将看看一些最常见的使用案例。

服务网格的常见使用案例

从运营角度来看,服务网格对任何类型的微服务架构都非常有用。这是因为它允许你控制流量、安全性、权限和可观察性。以下是当前服务网格最常见、标准化且广泛接受的使用案例:

  • 提升可观察性:通过服务级别的可见性、追踪和监控,我们可以提升分布式服务的可观察性。服务网格的一些主要特性大幅提升了可见性,以及故障排查和管理的能力。例如,如果架构中的某个服务成为瓶颈,重试通常是一个选择,尽管这可能由于超时而加剧瓶颈问题。使用服务网格,你可以快速断开与故障服务的连接,禁用无法正常工作的副本,并保持 API 的响应性。

  • 蓝绿部署:服务网格使你能够利用蓝绿部署来成功推出新的应用升级,而不会因其流量控制功能而影响现有服务。你可以首先将新版本暴露给有限的用户群体,进行测试,之后再推广到所有生产实例。

  • 混沌猴子/生产环境测试:为了提高部署的稳健性,还可以注入延迟和错误。

  • 现代化你的遗留应用:如果你正在将旧应用升级为基于 Kubernetes 的微服务,可以利用服务网格作为支持工具来拆解应用。你可以将现有的应用注册为服务到服务目录中,并随着时间的推移将其迁移到 Kubernetes 上,而无需更改它们之间的通信方式。

  • API 网关技术:借助服务网格,你可以利用 API 网关技术实现集群内服务间连接以及复杂的 API 管理方案。服务网格充当超级粘合剂,动态地将微服务连接在一起,并提供流量控制、限制和测试功能。

随着服务网格的普及,许多新的、被广泛接受的使用案例将加入到之前列出的那些案例中。现在,让我们来看看选择服务网格提供商时需要考虑的因素。

选择服务网格的指南

在本节中,我们将简要比较各个服务网格提供商所提供的功能。选择一个满足基本要求的服务网格,关键在于你是否需要超出基础功能的更多特性。Istio 提供了最多的功能和灵活性,但请记住,灵活性也意味着复杂性。Linkerd 可能是支持 Kubernetes 的基本策略的最佳选择:

表 12.1 – Istio 与 Linkerd 服务网格的比较

现在,我们已经看过了一些选择服务网格的建议,接下来让我们看看配置服务网格的最佳实践。

配置服务网格的最佳实践

尽管服务网格对开发团队非常有益,但实施一个服务网格需要一些努力。服务网格提供了极大的灵活性和定制空间,因为它有许多可调组件。灵活性通常伴随着复杂性。在使用服务网格时,以下最佳实践将为你提供一些有用的指导:

  • 采用 GitOps 方法:流量管制、速率限制和网络配置都是服务网格配置的一部分。该配置可以用来从零开始安装服务网格、更新其版本并在集群之间迁移。因此,建议将配置视为代码,并结合持续部署管道使用 GitOps。

  • 使用较少的集群:较少的集群并且有大量服务器的表现优于多个集群和较少实例的服务网格产品。因此,最好将冗余集群的数量保持在最低限度,这样你就可以利用服务网格方法的简单操作和集中配置。

  • 使用适当的监控警报和请求追踪:服务网格应用是管理日益复杂的分布式应用流量的高级应用。对于系统可观察性,指标收集、可视化和仪表板是至关重要的。通过你的服务网格提供的 Prometheus 或 Grafana,你可以根据需要创建警报。

  • 关注全面的安全性:大多数服务网格系统提供互相 TLS、证书管理、身份验证和授权等安全功能。为了限制集群应用之间的通信,你可以设计并执行网络策略。然而,应该强调的是,设计网络策略并非一项简单的操作。你必须考虑未来的可扩展性,并覆盖当前正在运行的应用的所有可能性。因此,使用服务网格来执行网络策略是不方便的,并且容易出现错误和安全漏洞。谁在传输或接收数据对服务网格解决方案来说并不重要。如果网络策略允许,任何恶意或故障的应用都可以获取你的敏感数据。因此,除了依赖服务网格设备的安全功能外,思考更广泛的整体安全措施是至关重要的。

总之,服务网格使你能够将应用的业务逻辑与可观察性、网络和安全策略解耦。你可以使用它来连接、保护和监控你的微服务。

总结

随着单体应用被拆分为微服务,构成应用的服务数量急剧增加。管理大量实体并不容易。通过标准化和自动化服务之间的通信,Kubernetes 本地服务网格(如 Istio 或 Linkerd)解决了微服务架构中容器和服务扩展带来的问题。服务网格对安全性、服务发现、流量路由、负载均衡、服务故障恢复和可观察性进行了标准化和自动化。

在本章中,我们学习了如何启用 Linkerd 和 Istio 插件,并将边车注入到示例应用中。然后,我们检查了各自的仪表板,这使我们能够查看遥测数据,以调试、维护和改进应用。我们还研究了如何使用指标、分布式跟踪和访问日志来提升整体服务网格的可观察性。

之后,我们讨论了今天服务网格最常见的一些使用场景,并提供了一些关于如何选择合适的服务网格的建议。我们还列出了服务网格配置的最佳实践。

在下一章中,你将学习如何设置一个高可用性集群。一个高可用的 Kubernetes 集群能够承受组件故障,并且在不中断的情况下继续为工作负载提供服务。

第十三章:使用高可用性集群抵御组件故障

在上一章中,我们探讨了如何启用 Linkerd 或 Istio 服务网格附加组件并将边车注入到示例应用程序中。我们还查看了允许我们查看遥测数据的仪表板,以便进行故障排除、管理和改进应用程序。然后,我们了解了指标、分布式追踪和访问日志如何帮助整体服务网格的可观察性。我们还查看了当前一些最常见的服务网格用例,并提供了一些关于如何选择正确服务网格的建议。我们还涵盖了服务网格配置的最佳实践清单。

通过动态容器调度,Kubernetes 为分布式应用程序提供了更高的可靠性和韧性。但如何确保当组件,甚至整个数据中心站点故障时,Kubernetes 本身仍能保持运行呢?在本章中,我们将探讨如何为高可用性(HA)集群配置 Kubernetes 的下一个用例。

高可用性(HA)确保即使站点部分或完全故障,应用程序仍能持续运行。高可用性的基本目标是消除潜在的故障点。它可以在许多基础设施层级和不同的集群组件中实现。然而,适合特定情况的可用性水平由多个因素决定,包括你的业务需求、与客户的服务级别协议以及资源可用性。

Kubernetes 旨在为应用程序和基础设施提供高可用性。每个控制平面(主节点)组件都可以配置为多节点复制(多主节点设置),以提高可用性。然而,重要的是要记住,高可用性和多主节点设置并不等同。即使你有三个或更多的控制平面节点,并且只有一个 NGINX 实例在这些主节点前进行负载均衡,你也有一个多主节点集群设置,但不是高可用性设置,因为 NGINX 仍然可能随时发生故障并导致故障。

控制平面节点至关重要,因为它们运行控制、监视和维护 Kubernetes 集群状态的服务。API 服务器、集群状态存储、调度器和控制器管理器都是控制平面的一部分。如果集群中的某个控制平面节点发生故障,集群的操作和稳定性可能会受到严重影响。高可用性集群通过同时运行多个控制平面节点来解决这个问题,虽然这不能完全消除风险,但可以大大减少风险。

MicroK8s 的高可用性选项已经简化并默认启用。这意味着集群可以在节点故障的情况下继续为工作负载提供服务,不会中断。高可用性是企业部署容器和 Pod 时所需的关键特性,能够提供在大规模工作时所需的稳定性。

Canonical 的轻量级 Dqlite SQL 数据库用于实现 HA 集群功能。通过将数据库嵌入 Kubernetes,Dqlite 减少了集群的内存占用并消除了进程开销。这对于物联网和边缘应用程序来说至关重要。当 Dqlite 作为 Kubernetes 数据存储使用时,边缘的弹性 Kubernetes 集群部署得以简化。边缘应用程序现在可以在 x86 或 ARM 的普通设备上,如 Intel NUC 或树莓派板等集群,低成本地实现卓越的可靠性。本章将涵盖以下主要内容:

  • HA 拓扑概述

  • 设置 HA Kubernetes 集群

  • Kubernetes HA 最佳实践

HA 拓扑概述

在本节中,我们将介绍两种最常见的 HA 拓扑,以启用 HA 集群。Kubernetes 集群的控制平面大多是无状态的。作为整个集群唯一真实数据来源的集群数据存储是控制平面中唯一有状态的组件。内外部用户可以通过 API 服务器访问和修改状态,API 服务器作为集群数据存储的网关。MicroK8s 使用 Dqlite,这是一种分布式且高可访问性的 SQLite 变体,作为关键值数据库来保持集群的状态。

在查看 HA 拓扑之前,让我们先来看一些可能会妨碍集群操作的潜在故障场景:

  • 控制平面(主节点)丧失:主节点或其服务的丧失将产生重大影响。集群将无法响应 API 命令或节点的部署。主节点中的每个服务,以及存储层,都是至关重要的,必须设计为 HA。

  • 集群数据存储丢失:无论集群数据存储是运行在主节点上,还是单独设置,丢失集群数据将是灾难性的,因为它包含所有集群信息。为了避免这种情况,集群数据存储必须配置在 HA 集群中。

  • 工作节点故障:在大多数情况下,Kubernetes 将能够自动识别并故障转移 Pods。应用程序的最终用户可能不会察觉到任何差异,具体取决于服务的负载均衡方式。如果某个节点上的 Pods 变得无响应,kubelet 将检测到这一点并通知主节点启动另一个 Pod。

  • 网络故障:网络中断和分区可能导致 Kubernetes 集群中的主节点和工作节点变得无法访问。在某些情况下,它们将被归类为节点故障。

现在我们已经看到了几个可能的故障情况,接下来让我们看看如何通过 HA 拓扑来缓解这些问题,这些拓扑可以在运行 Kubernetes 生产工作负载时,承受一个或多个主节点的故障。

高可用 Kubernetes 集群的拓扑可以通过两种方式进行配置,具体取决于集群数据存储的配置方式。第一种拓扑基于堆叠式集群设计,每个节点承载一个 Dqlite 实例以及控制平面。在每个控制平面节点上运行 kube-apiserver 实例、kube-scheduler 实例和 kube-controller-manager 实例。负载均衡器将 kube-apiserver 实例暴露给工作节点。

每个控制平面节点生成一个本地 Dqlite 成员,该成员仅与该节点的 kube-apiserver 实例进行通信。本地的 kube-controller-manager 实例和 kube-scheduler 实例相同。

在这种拓扑结构中,控制平面和本地 Dqlite 成员在同一节点上相连。该设计更容易设置和管理用于复制。然而,堆叠式集群容易受到失效耦合的影响。当一个节点发生故障时,本地 Dqlite 成员和控制平面实例会丢失,从而使冗余性面临风险。通过增加更多的控制平面节点,可以降低这种威胁。

因此,对于高可用 Kubernetes 集群,这种设计至少需要三个堆叠式控制平面节点,如图 13.1所示:

图 13.1 – 堆叠式控制平面拓扑

图 13.1 – 堆叠式控制平面拓扑

第二种拓扑利用了安装并由一组独立主机控制的外部 Dqlite 集群。

在这种架构中,每个控制平面节点运行一个 kube-apiserver 实例、一个 kube-scheduler 实例和一个 kube-controller-manager 实例,每个 Dqlite 主机与每个控制平面节点的 kube-apiserver 实例进行通信:

图 13.2 – 外部集群数据存储的拓扑结构

图 13.2 – 外部集群数据存储的拓扑结构

在这种拓扑结构中,控制平面和本地 Dqlite 成员是解耦的。因此,它提供了一种高可用配置,其中失去一个控制平面实例或 Dqlite 成员的影响较小,并且不像堆叠式高可用架构那样影响集群的冗余性。

然而,这种设计需要比堆叠式高可用拓扑多出两倍的主机数量。采用这种拓扑的高可用集群至少需要三台主机作为控制平面节点,以及三台主机作为 Dqlite 节点。

在下一节中,我们将介绍使用堆叠式集群高可用拓扑设置高可用集群的步骤。高可用 MicroK8s 所需的仅仅是集群中的三个或更多节点,之后 Dqlite 会自动变为高可用。如果集群有超过三个节点,额外的节点将被指定为数据存储的备用候选节点,并在数据存储的某个节点发生故障时自动提升。备用节点自动提升为 Dqlite 投票集群,使得高可用 MicroK8s 自给自足,并确保即使没有执行任何管理操作,也能保持法定人数。

设置 HA Kubernetes 集群

我们将配置并实施一个高可用性(HA)MicroK8s Kubernetes 集群,采用之前讨论过的堆叠集群 HA 拓扑结构。我们将使用三台节点,在每个节点上安装并配置 MicroK8s,并模拟节点故障,查看集群是否能抵抗组件故障并按预期运行。

总结一下,控制平面由 HA 集群中的所有节点运行。集群中的一部分节点(至少三个)保留 Kubernetes 集群数据存储的副本(Dqlite 数据库)。使用投票程序选举数据库维护的领导节点。除了投票节点之外,还有非投票节点,它们悄悄地存储数据库的副本。这些节点准备替代离开的投票节点。最后,有些节点既不投票也不复制数据库,这些节点被称为备用节点。总结一下,三个节点角色如下:

  • 投票节点:复制数据库,参与选举领导节点

  • 待命节点:复制数据库,参与选举领导节点

  • 备用节点复制数据库,参与选举领导节点

管理员不需要监控集群的形成、数据库同步或投票和领导选举的过程,因为这些都是透明的并且已经处理好了。图 13.3 展示了我们的 Raspberry Pi 集群设置:

图 13.3 – 完全功能的 HA 集群设置

图 13.3 – 完全功能的 HA 集群设置

现在我们知道了想要做什么,让我们来看一下要求。

要求

在开始之前,以下是构建 Raspberry Pi Kubernetes 集群的先决条件:

  • 一张 microSD 卡(至少 4 GB,推荐 8 GB)

  • 一台带有 microSD 卡槽的计算机

  • 一台 Raspberry Pi 2、3 或 4(至少三台节点)

  • 一根 micro-USB 电源线(Pi 4 使用 USB-C 电缆)

  • 一条 Wi-Fi 网络或带有互联网连接的以太网电缆

  • 一台带有 HDMI 接口的显示器(可选)

  • 一根适用于 Pi 2 和 Pi 3 的 HDMI 电缆,或适用于 Pi 4 的 micro-HDMI 电缆(可选)

  • 一只 USB 键盘(可选)

现在我们已经确定了设置 HA MicroK8s Kubernetes 集群的要求,接下来将进入逐步说明如何完成这一过程。

步骤 1 – 创建 MicroK8s Raspberry Pi 集群

请按照我们在 第五章 中介绍的步骤,创建并实现多节点 Raspberry Pi Kubernetes 集群的更新,来创建 MicroK8s Raspberry Pi 集群。以下是简要回顾:

  1. 将操作系统镜像安装到 SD 卡中:

    1. 配置 Wi-Fi 访问设置。

    2. 配置远程访问设置。

    3. 配置控制组设置。

    4. 配置主机名。

  2. 安装并配置 MicroK8s。

  3. 向集群添加额外的控制平面节点和工作节点。

注意

从 MicroK8s 1.23 版本开始,现在可以选择添加仅作为工作节点的节点。这类节点不执行控制平面,也不为集群的高可用性做出贡献。另一方面,它们消耗较少的资源,因此适用于低端设备。仅作为工作节点的节点在执行 Kubernetes 工作负载的节点不可靠或无法信任其承载控制平面的系统中也非常适用。

要将一个仅作为工作节点的节点添加到集群中,请在运行 microk8s join 命令时使用 --worker 标志:

microk8s join 192.168.1.8:25000/92b2db237428470dc4fcfc4ebbd9dc81/ 2c0cb3284b05 --worker

一个 Traefik 负载均衡器运行在工作节点上,允许本地服务(kubelet 和 kube-proxy)与多个控制平面节点上运行的 API 服务器之间的通信。当添加一个工作节点时,MicroK8s 会尝试发现集群中所有 API 服务器的端点,并正确设置新节点。在本节中,我们将不使用仅作为工作节点的节点,而是使用同时承载控制平面的工作节点。

我们将重复在第五章中介绍的方法,创建和实施多节点 Raspberry Pi Kubernetes 集群的更新,以用于我们当前的设置,具体内容如下表所示:

表 13.1 – Raspberry Pi 集群设置

表 13.1 – Raspberry Pi 集群设置

现在我们已经明确了目标,我们将逐步在每个 Raspberry Pi 板上安装和配置 MicroK8s,然后将多个部署结合起来,构建一个功能完整的集群。

安装和配置 MicroK8s

SSH 登录到控制平面节点并安装 MicroK8s Snap:

sudo snap install microk8s --classic

以下命令执行输出确认 MicroK8s Snap 已成功安装:

图 13.4 – MicroK8s 安装

图 13.4 – MicroK8s 安装

以下命令执行输出确认 MicroK8s 正在成功运行:

图 13.5 – 检查您的 MicroK8s 集群

图 13.5 – 检查您的 MicroK8s 集群

如果安装成功,您应该会看到以下输出:

图 13.6 – 验证节点是否处于准备就绪状态

图 13.6 – 验证节点是否处于准备就绪状态

在其他节点上重复 MicroK8s 安装过程。

下一步是将控制平面节点和工作节点添加到集群中。打开 PuTTY shell 连接到控制平面节点并运行以下命令以生成连接字符串:

sudo microk8s.add-node

以下命令执行输出验证该命令已成功执行,并提供连接字符串的说明:

图 13.7 – 生成用于添加节点的连接字符串

图 13.7 – 生成用于添加节点的连接字符串

如前面命令执行的输出所示,连接字符串以 <control plane_ip>:<port>/<token> 的形式生成。

添加额外的控制平面节点

我们现在拥有与控制平面节点连接的连接字符串。打开 PuTTY shell 连接到 controlplane1 节点,运行 join 命令将其添加到集群:

microk8s join <control plane_ip>:<port>/<token>

命令成功执行,节点已加入集群,输出如下所示:

图 13.8 – 向集群添加额外的控制平面节点

图 13.8 – 向集群添加额外的控制平面节点

由于我们已经添加了一个额外的控制平面节点,接下来我们将向集群添加一个工作节点,以便模拟节点故障,看看集群是否能抵抗组件故障并按预期工作。

添加一个工作节点

我们现在拥有与控制平面节点连接的连接字符串。打开 PuTTY shell 连接到工作节点,运行 join 命令将其添加到集群:

microk8s join <control plane_ip>:<port>/<token>

命令成功执行,节点已加入集群,输出如下所示:

图 13.9 – 向集群添加一个工作节点

图 13.9 – 向集群添加一个工作节点

如前面命令执行的输出所示,您应该能够在几秒钟内在控制平面上看到新节点。

使用以下命令验证新节点是否已被添加到集群:

kubectl get nodes

以下命令执行输出显示 controlplanecontrolplane1worker2 是集群的一部分:

图 13.10 – 集群已准备就绪,控制平面和 worker2 是集群的一部分

图 13.10 – 集群已准备就绪,控制平面和 worker2 是集群的一部分

一个完全功能的多节点 Kubernetes 集群看起来就像图 13.3所示。总结一下,我们已经在 Raspberry Pi 板上安装了 MicroK8s,并将多个部署加入集群。我们还向集群中添加了控制平面节点和工作节点。

现在我们已经有了一个完全功能的集群,我们将继续下一步,检查高可用性(HA)设置。

步骤 2 – 检查 HA 设置

由于我们有多个节点运行控制平面,MicroK8s 的 HA 会自动实现。要实现 HA Kubernetes 集群,需要满足三个条件:

  • 在任何时候,都必须有多个节点可用。

  • 控制平面必须运行在多个节点上,以确保即使一个节点失败,集群也不会变得不可用。

  • 集群状态必须存储在一个高度可访问的数据存储中。

我们可以使用以下命令检查当前 HA 集群的状态:

microk8s status

以下命令执行输出确认 HA 已经实现,并显示了数据存储主节点。由于我们只有三台节点,备用节点被设置为 none;如果增加节点,这些额外的节点将会被指定为数据存储的备用候选节点,如果其中一个数据存储节点发生故障,将会自动晋升为主节点:

图 13.11 - 检查 MicroK8s 高可用集群

图 13.11 - 检查 MicroK8s 高可用集群

恭喜!你现在已经拥有一个安全、分布式、高可用的 Kubernetes 集群,已经准备好作为生产级别的 MicroK8s 集群环境使用。在下一节中,我们将会在我们刚刚创建的 MicroK8s 集群上部署一个示例应用程序。

第三步 - 部署示例容器化应用程序

在本节中,我们将在我们的多节点高可用 MicroK8s 集群环境中,从 Kubernetes examples 仓库部署 NGINX 部署。

以下命令将部署示例应用程序:

kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml

以下命令执行输出表明部署没有错误,在接下来的步骤中,我们可以使用 get deployments 命令验证这一点:

图 13.12 - 示例应用程序部署

图 13.12 - 示例应用程序部署

以下命令执行输出显示了部署的相关信息:

图 13.13 - 示例应用程序部署已处于就绪状态

图 13.13 - 示例应用程序部署已处于就绪状态

让我们使用以下命令检查 Pods 的运行位置:

kubectl get pod –o=custom-columns=NODE:.spec.nodeName,NAME:.metadata.name

以下命令执行输出表明 Pods 在各节点之间分布均匀:

图 13.14 - Pods 在各节点上的分布

图 13.14 - Pods 在各节点上的分布

太棒了!我们刚刚在 Raspberry 多节点高可用集群上部署了示例应用程序。总结来说,我们构建了一个 Kubernetes Raspberry Pi 集群,并使用它来部署了一个示例应用程序。接下来,我们将进行一些测试,检查我们的集群在面对故障时的抗压能力。

注意

如果在多节点高可用集群上启用了附加组件,并且为附加组件下载并安装了客户端二进制文件,则这些二进制文件只会在启用了附加组件的特定节点上可用。

第三步 - 模拟控制平面节点故障

为了模拟节点故障,我们将使用cordon命令将节点标记为unschedulable。如果节点处于不可调度状态,Kubernetes 控制器将不会在该节点上调度新的 Pods。

让我们在 controlplane1 节点上使用 cordon,以模拟控制平面故障。使用以下命令将节点标记为不可调度:

kubectl cordon controlplane1

以下命令执行输出显示 controlplane1 已被标记为不可调度:

图 13.15 - 将 controlplane1 节点标记为不可调度

图 13.15 - 将 controlplane1 节点标记为不可调度

即使 controlplane1 节点已被隔离,现有的 Pod 仍然会继续运行。现在我们可以使用 drain 命令删除所有 Pod。使用以下命令来排空该节点:

kubectl drain –force --ignore-daemonsets controlplane1

使用 --ignore-daemonsets 标志来排空包含由 DaemonSet 管理的 Pod 的节点。

以下命令执行输出显示,运行在 controlplane1 上的 Pod 已成功删除:

图 13.16 – 排空 controlplane1 节点

图 13.16 – 排空 controlplane1 节点

因为控制平面由 HA 集群中的所有节点运行,如果某个控制平面节点(例如 controlplane1)失败,集群决策可以切换到另一个控制平面节点,并继续正常工作,几乎不会有中断。

作为新控制平面决策的一部分,Kubernetes 控制器将在 Pod 被删除后尽快重建新的 Pod,并将其调度到其他节点。由于调度已被禁用(因为我们已经隔离了 controlplane1 节点),新 Pod 不能被调度到同一节点上。

让我们使用 kubectl get pods 命令查看 Pod 正在运行的位置。以下命令执行输出显示,新 Pod 已重新调度到 controlplane 节点:

图 13.17 – Pod 在节点间的重新分配

图 13.17 – Pod 在节点间的重新分配

以下命令执行输出显示,尽管控制平面节点之一失败,部署也已恢复:

图 13.18 – 部署已处于就绪状态

图 13.18 – 部署已处于就绪状态

几乎所有的 HA 集群管理对于管理员来说都是不可见的,并且需要最小的配置。只有管理员才能添加和移除节点。为了确保集群的健康,应该考虑以下参数:

  • 如果领导节点被移除,比如崩溃后永远无法恢复,集群可能需要最多 5 秒钟的时间来选举新的领导者。

  • 将非选民节点转换为选民节点最多可能需要 30 秒。当一个新节点加入集群或选民节点失败时,就会发生这个提升。

总结来说,我们使用堆叠式集群 HA 拓扑配置并实现了一个高可用的 MicroK8s Kubernetes 集群。我们使用三个节点,在每个节点上安装并配置了 MicroK8s,同时模拟节点故障以查看集群是否能承受组件故障并继续按预期工作。在下一节中,我们将讨论一些实施生产级 Kubernetes 集群的最佳实践。

Kubernetes HA 最佳实践

随着人们对 Kubernetes 的熟悉,平台的使用趋势趋向于更高级的应用,比如用户在高可用性架构中部署 Kubernetes,以确保生产环境的持续正常运行。根据最近的 Kubernetes 和云原生操作报告,2022juju.is/cloud-native-kubernetes-usage-report-2022),许多受访者似乎正在利用 Kubernetes 的高可用架构来处理对数据安全性要求较高的应用。

在本节中,我们将介绍一些在 Kubernetes 中部署高可用应用的最佳实践。这些指南基于我们在 第五章 中看到的内容,在多节点 Raspberry Pi Kubernetes 集群上创建和实现更新

如你所知,在 Kubernetes 中部署一个基本的应用设置非常简单。然而,要使你的应用程序可用且具有容错能力,则意味着会遇到一系列挑战和问题。通常,实施高可用性(HA)需要以下几个方面:

  • 确定应用程序的预期可用性水平:允许的停机时间因应用程序和业务目标而异。

  • 为你的应用提供冗余且可靠的控制平面:控制平面管理集群状态,并有助于应用程序对用户的可用性。

  • 为你的应用提供冗余且可靠的数据平面:这意味着将数据复制到所有集群节点。

在部署 Kubernetes 高可用性时,需要考虑和决策的因素有很多,这些因素可能会影响应用程序及其操作和存储资源的消耗。以下是一些需要考虑的事项:

  • 使用副本:使用副本而非 Pods 来部署高可用应用。使用副本可以确保你的应用始终在一致的 Pod 集合上运行。为了使应用程序被声明为最小可访问,必须至少有两个副本。

  • Ready 状态下降至更新前 75% 的水平。因此,在更新过程中,应用程序的计算能力可能降至正常水平的 75%,从而导致部分失败(应用程序性能降级)。RollingUpdate.maxUnavailable 参数让你选择升级过程中可以下线的最大 Pod 百分比。因此,要么确保你的应用程序在 25% 的 Pod 不可用的情况下仍能正常运行,要么降低 maxUnavailable 参数。根据应用的需求,还可以评估其他部署策略,如蓝绿部署和金丝雀发布等,以获得比默认策略更好的替代方案。

  • 正确的节点大小配置:可以分配给 Pod 的最大内存量由节点的大小决定。对于生产集群,节点的大小通常足够大(2.5 GB 或更大),能够承载任何宕机节点的工作负载。

  • 用于 HA 的节点池:生产工作负载的节点池应至少包含三个节点,以提供高可用性(HA)。这可以让集群在某个节点不可用时,能够将工作调度到其他节点。

  • 在应用程序规范中为所有部署使用 requestlimit 对象:

    1. 请求:指定一个 Pod 在节点上调度前所需的资源数量(如 CPU 和内存)。如果节点缺乏所需资源,Pod 将无法调度。这可以防止将 Pod 调度到已经超负荷的节点上。

    2. 限制:指定一个 Pod 在节点上允许使用的资源数量(如 CPU 和内存)。这可以防止 Pod 占用过多资源,从而可能影响其他 Pod 的运行。

  • 设置 Pod 中断预算:为了避免生产环境中的中断,例如在集群升级时,可以配置 Pod 中断预算,限制同一时间内可以停机的副本 Pod 数量。

  • 确保集群已升级:确保利用最新的功能、安全补丁和稳定性改进。

  • 避免单点故障:Kubernetes 通过提供冗余组件并确保应用容器可以跨多个节点调度,从而提高可靠性。为了实现高可用性(HA),可以使用反亲和性或节点选择来帮助将应用分散到 Kubernetes 集群中。基于标签,节点选择允许你指定集群中哪些节点有资格运行你的应用。标签通常描述节点属性,如带宽或专用资源,如 GPU。例如,为了正确地提交数据变更,Apache ZooKeeper 需要一个服务器的多数节点。一个三节点的集群中,两个节点必须是健康的,写操作才会成功。因此,可靠的部署必须确保服务器分布在不同的故障域中。

  • 使用存活性和就绪性探针:默认情况下,Kubernetes 会立即将流量转发到应用程序容器。通过配置健康检查,可以在应用程序 Pod 准备好接收流量或变得不可响应时通知 Kubernetes,从而提高应用程序的稳定性。

  • startupProbereadinessProbe,可以使用 initContainers 来检查外部依赖项。对于 initContainers,无需更改应用程序代码。也不需要在容器中嵌入额外工具来检查应用程序容器中的外部依赖项。

  • 使用大量描述性标签:标签非常强大,因为它们是任意的键值对,可以帮助你在集群中逻辑地组织所有 Kubernetes 工作负载。

  • 使用 sidecar 进行代理和监视:有时,需要一组进程与另一个进程进行通信。然而,你不希望所有这些进程都在一个容器中运行,而是希望它们在一个 Pod 中运行。当你在运行一个代理或监视程序,而你的进程依赖于它时,这种情况也是如此。例如,对于一个依赖的数据库,凭据不会硬编码到每个容器中。相反,你可以将凭据作为一个代理部署在 sidecar 中,确保安全地处理连接。

  • 自动化你的 CI/CD 流水线,避免手动 Kubernetes 部署:因为每天可能有大量部署,这种策略通过消除易出错的手动任务,为团队节省了大量时间。

  • 在同一集群中使用 ProdDevTest 命名空间,你还可以利用命名空间限制资源数量,从而避免一个有问题的进程占用所有集群资源。

  • 监控控制平面:这有助于识别集群中的问题或威胁,并增加延迟。建议使用自动化监控工具,而不是手动管理警报。

总结一下,我们回顾了优化 Kubernetes 环境的一些最佳实践。

摘要

在本章中,我们探讨了如何使用堆叠集群 HA 拓扑设置 HA MicroK8s Kubernetes 集群。我们利用三台节点在每台节点上安装和配置 MicroK8s,并模拟节点故障,查看集群是否能够容忍组件故障并继续正常运行。

我们讨论了一些在生产就绪集群中实现 Kubernetes 应用程序的最佳实践。我们还提到,MicroK8s 的 HA 选项已经简化,并默认启用。

高可用性(HA)是组织在部署容器和 Pod 时,提供大规模可靠性所需的重要特性。我们还认识到 Canonical 的轻量级 Dqlite SQL 数据库的价值,它用于提供 HA 集群。通过将数据库嵌入 Kubernetes,Dqlite 减少了集群的内存占用,并消除了进程开销。对于物联网(IoT)或边缘计算应用,这一点至关重要。

在下一章中,我们将探讨如何使用 Kata Containers,一种安全的容器运行时,通过利用硬件虚拟化技术提供更强的工作负载隔离。

第十四章:用于保护容器的硬件虚拟化

在上一章中,我们看到如何使用堆叠集群高可用性HA)拓扑创建高可用的 MicroK8s Kubernetes 集群。我们在三个节点上安装和配置了 MicroK8s,并模拟了节点故障,以查看集群是否能够抵抗组件故障并正常工作。我们还讨论了在生产就绪的集群上部署 Kubernetes 应用程序的一些最佳实践。我们注意到 MicroK8s 的 HA 选项现在也已经被简化,并默认激活。

容器技术在近年来主导了行业,并成为构建现代 IT 基础设施的事实标准。由于其轻量化设计和接近裸机的性能,它们经常优先于标准的虚拟机(VMs)。然而,安全性和隔离性是最常见的采用问题之一(参考 2022 年发布的 Kubernetes 和云原生运营报告,网址为juju.is/cloud-native-kubernetes-usage-report-2022)。在本章中,我们将看看如何使用 Kata Containers 创建安全的容器运行时,并利用硬件虚拟化技术提供更好的工作负载隔离。

在了解什么是 Kata Containers 之前,让我们回顾一下容器的运作方式及其与虚拟化技术的关系。容器更像是一个VM,允许将软件及其所有依赖项打包为一个单独的实体,在任何支持的环境中执行。

另一方面,VMs 更大且设置时间更长。与 VMs 相比,容器的占用空间大大降低,因此设置(和拆除)速度更快。与保留整个操作系统副本的 VMs 不同,容器仅共享主机系统的操作系统内核。

图 14.1 – VMs 与容器的比较

图 14.1 – VMs 与容器的比较

容器运行时是桥接软件,允许主机系统为容器分离其资源,拆除容器镜像,并管理容器生命周期。Kubernetes 集群的每个节点都必须安装容器运行时。

Canonical MicroK8s 已经简化了启用 Kata Containers(一种容器运行时)的过程,只需一条命令即可大大提高您的容器操作的安全性和隔离性。它结合了增强安全性等优点的虚拟化监控程序与 Kubernetes 的容器编排功能。在本章中,我们将介绍以下主要内容:

  • Kata Containers 概述

  • 启用 Kata 插件并运行示例应用程序

  • 容器安全最佳实践

Kata Containers 概述

开放容器接口OCI)是 Linux 基金会的一个倡议,旨在为 Linux 容器建立原则、标准和规范。OCI 运行时规范主要涉及多系统(包括 Linux、Windows 和 Solaris)的容器生命周期管理和配置。低级运行时是符合 OCI 规范的容器运行时,主要负责容器的创建和管理。由 Docker 设计,runC 是低级容器运行时的一个示例,并且是低级容器运行时的标准。

低级运行时是本机运行时,这意味着它们在主机内核上运行容器化进程。还有一些沙盒化和虚拟化运行时,通过不在主机内核上运行进程来提供改进的进程隔离。Kata Containers 是虚拟化运行时之一。为了运行容器化进程,这些运行时使用类似于容器的 VM 接口,但具有 VM 的工作负载隔离和安全性优势。

最初发布 Kubernetes 时,Docker 运行时是默认的容器运行时。随着平台的发展,支持不同运行时的需求也在增加。

容器运行时接口CRI)旨在使 Kubernetes 更具运行时无关性。这是一个高级规范,主要关注容器编排。与 OCI 不同,CRI 处理容器管理的额外方面,如镜像管理、快照和网络,而将容器执行留给符合 OCI 规范的运行时(例如 runC)。

Kata Containers(katacontainers.io/)是一个开源项目,旨在创建一个安全且符合 OCI 标准的容器运行时,通过将每个容器封装在轻量级 VM 中并利用硬件虚拟化来提高容器工作负载的安全性和隔离性。每个 VM 都有自己的内核。

图 14.2所示,传统容器采用 runC 作为容器运行时,依赖诸如 cgroups 和命名空间等内核特性以在共享内核中实现隔离;然而,Kata Containers 利用硬件虚拟化将容器隔离在其自己的轻量级 VM 中,具体如下:

图 14.2 – 传统容器与 Kata Containers 的对比

图 14.2 – 传统容器与 Kata Containers 的对比

Kata Containers 在与现有容器编排技术(如 Kubernetes)无缝集成的能力方面具有多个优势。在启动 VM 时,仍然可以使用本地 Kubernetes 功能,例如自动扩展和滚动更新。这使得虚拟化技术的优势能够与容器编排能力结合起来。在接下来的部分中,我们可以看看如何使用 kata-runtime 实例化 Kata 容器。

Kata 容器的工作原理

当 Kubernetes 集群配置了高层次运行时,如 containerd 或 CRI-O 时,会安装一个容器运行时 shim,以确保 CRI(containerd 或 CRI-O)与低级容器运行时(如 runC,默认运行时)之间的顺畅通信,且该低级容器运行时负责在 Pod 中运行容器。

图 14.3 – Kata 容器的工作原理

图 14.3 – Kata 容器的工作原理

创建具有隔离内核和命名空间的 Kata 容器的步骤如下:

  1. Kubernetes 被配置为使用高层次容器运行时,如containerdCRI-O

  2. 容器运行时 shim(containerd-shim)充当 CRI(containerd 或 CRI-O)与低级容器运行时(如 runC,默认运行时)之间的桥梁,以确保顺畅的通信。

  3. 低级容器运行时(如 runC 或 kata-runtime)负责在 Pod 中运行容器。

  4. Kata 容器使用运行时类(kata-runtime)在独立的内核和命名空间中运行容器。

容器可以通过containerd-shim-kata-v2在轻量级虚拟机中运行,这是一种新型 shim,充当 containerd 与kata-runtime之间的桥梁,同时还需要启用 Kata 容器的运行时类以便在独立的内核和命名空间中运行容器。

简而言之,Kata 是一个容器运行时,它提供了容器之间更强的隔离,同时保持其他运行时的性能和效率。以下是它的一些显著特点:

  • 安全性:它运行在专用和隔离的内核中,可以轻松与 containerd 或其他任何容器运行时集成。它还支持多个虚拟化管理程序,如 QEMU、Cloud Hypervisor 和 Firecracker。

  • 与 Docker 和 Kubernetes 的兼容性:通过提供 kata-runtime 作为容器运行时,它可以与 Docker 和 Kubernetes 轻松配合使用。

  • 性能:它与其他任何 Linux 容器具有相同的一致性,但隔离性更强。它还支持 AMD64、ARM、IBM pSeries 和 IBM zSeries 平台。

  • 简化:无需在虚拟机内嵌套容器,也无需牺牲容器速度。

除了虚拟化运行时或 Kata 容器,还有各种技术可以隔离容器(参考thenewstack.io/how-to-implement-secure-containers-using-googles-gvisor/),每种技术都有一套适合特定应用的属性。选择适合您应用的技术是容器安全架构的关键部分。

现在我们已经掌握了 Kata 容器的基本原理,可以继续下一步,启用 Kata 附加组件并运行示例应用程序。

启用 Kata 附加组件并运行示例应用程序

在本节中,我们将介绍在 MicroK8s Kubernetes 集群中启用 Kata 插件的过程。然后,为了展示 Kata 的能力,我们将部署一个示例应用。

注意

本节我们将使用一台 Ubuntu 虚拟机。设置 MicroK8s 集群的步骤与第五章**, 创建并实施多节点树莓派 Kubernetes 集群更新中的说明相同。

步骤 1 – 启用 Kata 插件

从 MicroK8s v1.24 开始,必须执行enable community命令才能启用 community 插件仓库。

使用以下命令启用community仓库:

microk8s enable community 

启用插件需要一些时间,以下命令执行输出显示community仓库已成功启用:

图 14.4 – 启用 community 仓库

图 14.4 – 启用 community 仓库

现在我们已经启用了community仓库,接下来可以进行启用 Kata 插件的下一步操作。

使用以下命令启用 Kata 插件:

microk8s enable kata 

以下命令执行输出表明 Kata 插件正在启用中:

图 14.5 – 启用 Kata 插件

图 14.5 – 启用 Kata 插件

从前面的命令执行输出中,我们可以看到kata runtimeClassName(kata)已被添加,这使我们可以指定哪些工作负载应在 Kata 容器中启动。

注意

--runtime-path参数还可以用来指定 Kata 运行时的安装位置。

使用以下命令启用带有运行时路径的 Kata 插件:

microk8s enable kata --runtime-path=<<kata-runtime-binary-path>>

在继续下一步之前,让我们确保通过microk8s status命令确认 Kata 插件已成功启用:

图 14.6 – Kata 插件已启用

图 14.6 – Kata 插件已启用

现在 Kata 插件已启用,我们可以在下一步中部署一个示例 nginx 应用。

关于在多节点集群上使用 Kata 容器的注意事项

microk8s enable kata必须在多节点集群的每个节点上执行,才能在目标节点上启用 Kata 运行时。

步骤 2 – 部署示例应用

在这一步,我们将部署以下示例 nginx 应用部署清单,该清单使用了 Kata 运行时:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: kata
  name: nginx-kata
spec:
  runtimeClassName: kata
  containers:
    - name: nginx
      image: nginx

使用以下命令创建一个示例 nginx 部署:

kubectl apply –f kata-nginx.yaml

以下命令执行输出表明部署没有错误,接下来的步骤中我们可以确保 Pods 已创建:

图 14.7 – 部署示例 nginx 应用

图 14.7 – 部署示例 nginx 应用

部署成功后,使用以下kubectl命令检查 Pods 是否处于Running状态:

图 14.8 – 检查 pods 是否处于运行状态

图 14.8 – 检查 pods 是否处于运行状态

nginx-kata现在处于Running状态,这意味着容器正在一个轻量级虚拟机中运行,并使用 containerd 运行时。它使用了containerd-shim-kata-v2,该工具充当 containerd 和 kata-runtime 之间的桥梁——kata-runtime 是一种随 Kata Containers 提供的运行时类,允许容器在自己的内核和命名空间中运行。

现在我们已经了解了启用 Kata 插件并运行示例应用程序是多么简单,接下来让我们进入下一节,讨论运行容器的最佳实践。

容器安全最佳实践

容器提供了许多优势,但也存在一些安全问题,这些问题可能难以解决。由于基于许多不同底层镜像的大量容器,每个容器都可能存在漏洞,因此容器比传统工作负载具有更广泛的攻击面。

另一个重要考虑因素是容器共享的内核架构。仅确保主机安全不足以保证安全。你还必须保持安全配置,限制容器权限,并确保容器隔离有效。例如,具有可利用漏洞、暴露元数据和错误凭证配置的容器,可能会危及整个基础设施。

我们将介绍运行容器时需要考虑的一些最重要因素。

利用 DevSecOps

在软件开发和部署生命周期中无缝集成安全性测试和保护,称为 DevSecOps。你可以在发布或构建应用程序之前扫描代码以检测缺陷或可能的漏洞代码。针对应用程序代码有各种静态应用程序安全测试SAST)工具,例如SonarQube,它为多种编程语言提供漏洞扫描,并基于规则、代码检查器等检测漏洞。你可以在开发工作站上使用它们,但将代码扫描工具纳入 CI/CD 工作流程可以确保代码质量的最低标准。例如,如果某些检查失败,你可以默认拒绝拉取请求。

此外,移除任何应用程序不需要的组件。例如,移除默认安装在任何 UNIX 系统上的sedawk二进制文件。这可以帮助你降低攻击面。

通过依赖扫描扫描外部漏洞

外部依赖项,例如应用程序中使用的第三方库或框架,可能包含缺陷和漏洞。任何应用程序构建过程都应将依赖扫描作为最佳实践。

漏洞数据库(例如 NVD)还可以与应用程序依赖项配合使用,利用 npmmavengo 等包管理工具生成有用的警报/警告。

使用镜像扫描工具分析容器镜像

使用镜像扫描工具分析你的容器镜像。镜像扫描工具会查找容器镜像基础发行版提供的操作系统包中的漏洞(如rpmdpkgapk等)。它还会发现 Java、Node.js、Python 等语言的包依赖项中的漏洞。

自动化和强制执行镜像扫描非常简单。它可以集成到你的 CI/CD 流水线中,当新镜像上传到注册库时触发扫描,确保不合规的镜像不再被允许运行。

强制执行镜像内容信任

如果你不是从零开始制作镜像,应该选择可靠的镜像。任何人都可以使用公共镜像库,例如 Docker Hub,但这些镜像可能包含病毒或配置错误。

通过使用 Docker Notary 或类似服务为镜像添加数字签名,也可以强制执行容器镜像的完整性,这样就可以在容器运行时验证这些签名。

保护注册库

容器镜像通常保存在私有或公共注册库中。确保这些注册库的安全至关重要,这样所有团队成员和合作人员就能使用尽可能安全的镜像。

如果你有自己的私有注册库,必须设置访问控制,明确谁可以访问和发布镜像。访问控制是一项基础安全措施,可以防止未授权人员篡改、发布或删除你的镜像。

保护宿主机

保证宿主机的安全和容器的安全同样重要。容器运行的宿主机通常由操作系统(Linux 内核)、一系列库、容器运行时以及各种后台服务和帮助程序组成。任何这些组件可能都不安全或配置错误,从而允许未经授权的访问容器或遭受拒绝服务DoS)攻击。

例如,容器运行时本身出现问题,例如 DoS 攻击导致宿主机无法创建新容器,可能会影响正在运行的容器。你可以使用宿主扫描工具识别宿主机容器运行时、服务、标准库(如 glibc)以及内核中的已知安全漏洞(这与镜像扫描为容器镜像所做的类似)。

保护你的运行时

以下是一些确保运行时安全的最佳实践:

  • 为容器创建独立的虚拟网络:这可以增加一层隔离,帮助限制攻击面。

  • 使用最小权限原则:仅允许真正需要连接的容器之间的互联。

  • 仅暴露应用所需的端口:除了 SSH 外,不要暴露任何额外的端口。将这一思想应用于容器和底层计算机。

  • 使用 TLS 来保护服务通信:这种方法加密流量,并确保只有授权的端点才能访问。

  • 使用 Docker 镜像策略插件:该插件可以防止任何进程获取未被允许列入白名单的镜像。

审查容器权限

容器内漏洞被利用的范围在很大程度上取决于容器的权限以及与主机和其他资源的隔离。现有和潜在的漏洞可以通过以下方式使用运行时设置来减轻:

  • 以用户身份运行容器,而不是以 root 身份运行。如果可能,使用随机的 UID。

  • Docker 和 Kubernetes 都允许移除某些能力并禁用特权容器。Seccomp 和 AppArmor 可以限制容器执行的操作类型。

  • 为了避免某个容器占用所有内存或 CPU 并导致其他应用程序资源匮乏,请使用资源限制。

  • 定期检查共享存储或卷,特别注意主机路径和共享的文件系统。

  • 可以使用 Pod 安全策略在集群中创建保护措施,并防止容器配置错误。

使用实时事件和日志审计

通过评估异常活动和审计多个日志和事件来源,可以检测到容器安全威胁。以下是一些事件来源的示例:

  • 主机和 Kubernetes 的日志

  • 容器对操作系统的调用

使用工具(如 Falco 和 Sysdig Secure)来追踪系统调用,并在出现任何异常时发送警报。它应该带有预配置的规则库,并且可以使用简单的语法编写自己的规则。它还应该能够监控 Kubernetes 审计日志。

监控资源使用

过度的资源使用(CPU、RAM 和网络)、可用磁盘空间的急剧下降、异常的错误率或延迟增加,可能都是系统出现问题的迹象。

以与 Prometheus 相同的方式收集指标(参考 第八章监控基础设施和应用程序的健康状况)。设置警报,当数据超过预定阈值时及时通知。使用有用的仪表板来跟踪指标的变化,并查看它们如何与系统中的其他指标和事件相关联。

常见的安全配置错误及修复方法

配置错误的主机、容器运行时、集群、资源等可能为攻击者提供提升权限并向上移动的途径。

学习如何识别配置错误,了解它们为何成问题,以及如何通过使用基准测试、最佳实践和加固指南来修正这些问题。互联网安全中心(CIS) (www.cisecurity.org/benchmark/kubernetes) 是提供各种情境下免费基准测试的最权威的信息来源,任何人或公司都可以贡献其专业知识。

确保安全的最简单方法是尽可能地自动化。有许多工具,例如 kube-bench (github.com/aquasecurity/kube-bench),它们大多基于静态配置分析,可以让你在不同层次上评估配置参数,并提供关于如何修改它们的建议。

总结一下,保护容器和基础设施的安全控制应该作为容器安全的一部分进行实施和维护。将安全性集成到开发流水线中可以确保所有组件从开发阶段开始直到生命周期结束都得到保护。

摘要

我们探讨了如何使用 Kata Containers 构建一个安全的容器运行时,并如何利用硬件虚拟化技术来改善工作负载隔离。我们还介绍了如何启用 Kata 插件并运行示例应用程序。

我们讨论了在生产级集群上建立容器安全性的最佳实践。我们还注意到,MicroK8s 插件选项使得启用 Kata Containers 更加简便,这能显著提高容器操作的安全性和隔离性。

随着 Kata Containers 成熟为生产级容器运行时,并且逐步被采纳,改进托管的构建和开发环境以解决“噪声邻居”问题,并处理独特和特权需求,而不影响当前主机设置或政策,成为了一个巨大的机会。

在接下来的章节中,我们将继续探讨实施严格隔离的容器使用案例。

第十五章:为隔离容器实现严格限制

在上一章中,我们探讨了如何使用 Kata Containers 构建安全的容器,并如何通过硬件虚拟化技术提高工作负载的隔离性。我们还讨论了如何通过容器保护您的生产级集群的最佳实践。MicroK8s 插件选项还简化了 Kata Containers 的激活,使容器操作的安全性和隔离性显著增强。

本章中,我们将探讨使用 Snap 限制选项进行隔离的另一种方法,运行容器时实现完全隔离,这意味着没有文件、网络、进程或任何其他系统资源的访问,除非通过接口请求特定的访问权限。限制模型描述了特定 Snap 对用户机器的访问程度。目前有三种可供选择的模型,如下所示:

  • 严格限制级别在完全隔离的环境中运行,访问权限仅限于始终被视为安全的程度。因此,未经通过接口请求特定访问权限的严格限制 Snap 无法访问您的文件、网络、进程或任何其他系统资源。

  • 经典的限制级别类似于传统的 Linux 包,可以访问系统的资源。

  • 开发模式限制级别在一个有限的环境中运行,具有对系统资源的完全访问权限,并生成调试输出,以便定位未识别的接口。这专为 Snap 创建者和开发人员设计。

正如我们在 第二章 中讨论的那样,介绍 MicroK8s,MicroK8s 是一个 Snap,在本书中我们使用了经典的限制模型。每个 Snap 的接口都由作者精心挑选,以便根据 Snap 的需求启用对某个资源的专门访问。例如,网络访问、桌面访问和音频访问都是通过常见的接口提供的。

在本章中,我们将涵盖以下主要主题:

  • Snap、Snapcraft 和 Ubuntu Core 概述

  • 在树莓派板上设置 Ubuntu Core

  • 在 Ubuntu Core 上设置 MicroK8s

  • 部署一个示例容器化应用程序

Snap、Snapcraft 和 Ubuntu Core 概述

在详细讨论严格限制的 Snap 如何为应用程序提供隔离之前,我们将简要回顾一下在 Snap 出现之前,嵌入式 Linux 开发是如何进行的。

传统上,将软件安装到嵌入式 Linux 系统上一直是一个困难的问题。不同的 Linux 包格式(RPM、DEB 等)没有统一的标准化格式。此外,软件包通常需要复杂的代码来管理安装和更新,而这些代码相互不兼容、存在未满足的依赖关系,或者会写入整个系统。

Snaps 的初衷是解决嵌入式 Linux 开发人员对运行应用程序时所需的安全环境和精确配置的需求。它们允许软件发布者和开发者管理其提供的二进制文件以及用户可以访问的精确版本。

相比于标准的 Linux 软件分发方式,Snaps 更容易创建、构建和部署。Snaps 会自动 通过空中升级 (OTA) 和增量更新,保持嵌入式 Linux 设备的功能始终保持最新,减少了出现故障的风险。Snaps 兼容所有主要的 Linux 发行版,并且可以在从桌面到云端以及物联网设备的任何类型设备上使用。

Snapcraft (snapcraft.io/) 是一个框架,用于通过将应用程序的不同组件整合成一个单一、协调的包来创建和分发 snaps。开发人员将他们的 snaps 提交到一个中央存储库——Snap Store,一个通用的应用商店,允许用户发布、浏览、安装、分发并在云端、桌面和物联网设备上部署应用程序,无论是任何 Linux 发行版。

Ubuntu Core 嵌入式操作系统 (ubuntu.com/core) 是建立在 snaps 之上的,并且是免费开源的。在 Ubuntu Core 中,一切都是 snap。甚至连内核也是一个 snap。在 Ubuntu Core 中,只有使用严格限制模型的 snaps 才能被安装。它是一个全新设计的操作系统(OS),从零开始构建,具有零信任安全性。它通过容器化 Linux 内核和运行时环境有效地解耦了基础系统和已安装的应用程序。容器化使得您可以分离并提供封锁功能,应用程序默认在安全沙箱中运行(利用内核功能,如 AppArmor、seccomp、安全策略和设备权限)。

MicroK8s 和 Ubuntu Core 具有一些共同特征,包括自愈、高可用性、自动 OTA 更新、可靠性和安全性。在 Ubuntu Core 上运行 MicroK8s 为 Kubernetes 提供了坚实的计算基础。此外,将 Ubuntu Core 和 MicroK8s 结合起来为物联网和边缘应用程序提供了一个简化的嵌入式 Kubernetes 体验,具有小巧的占用空间和性能优化。

在下一节中,我们将介绍设置一个使用 snap 严格限制的 Kubernetes 树莓派集群的过程。

在树莓派板上设置 Ubuntu Core

现在我们已经了解了 snap 限制概念,接下来我们将深入探讨创建一个使用 snap 严格限制的 Kubernetes 树莓派集群的步骤。

我们试图实现的目标

在本节中,我们将列出我们要进行的步骤如下:

  1. 将 Ubuntu Core 镜像设置到 SD 卡

  2. 创建 Ubuntu SSO 账户

  3. 生成 SSH 密钥对

  4. 启动树莓派上的 Ubuntu Core

我们将在这一步构建的 Raspberry Pi 集群如图 15.1所示:

图 15.1 – 使用 Snap 严格限制的 Raspberry Pi 集群

图 15.1 – 使用 Snap 严格限制的 Raspberry Pi 集群

现在我们已经知道想做什么,接下来来看一下需求。

需求

以下是构建 Ubuntu Core Raspberry Pi Kubernetes 集群的前提条件:

  • 一张 microSD 卡(最小 4 GB,推荐 8 GB)

  • 一台配有 microSD 卡驱动器的计算机

  • Raspberry Pi 2、3 或 4(1 台或更多)

  • 一根 micro-USB 电源线(Pi 4 使用 USB-C)

  • 一种 Wi-Fi 网络或带有互联网连接的以太网电缆

  • (可选)带 HDMI 接口的显示器

  • (可选)用于 Pi 2 和 3 的 HDMI 电缆,Pi 4 使用 micro-HDMI 电缆

  • (可选)一只 USB 键盘

现在我们已经确定了需求,接下来我们将进入一步步的指导,讲解如何创建使用 Snap 严格限制的 Kubernetes Raspberry Pi 集群。

步骤 1 – 将 Ubuntu Core 镜像设置到 SD 卡

第一步是将 Ubuntu Core 镜像安装到 microSD 卡上。为此,我们将使用Raspberry Pi Imager 工具将操作系统镜像写入 microSD 卡,然后可以用它在 Raspberry Pi 上运行。

从 Raspberry Pi 网站下载并安装Raspberry Pi Imager到一台配有 SD 卡读卡器的计算机上。

图 15.2所示,运行 Raspberry Pi Imager 并插入 microSD 卡,然后打开选择操作系统菜单,如下所示:

图 15.2 – Raspberry Pi Imager

图 15.2 – Raspberry Pi Imager

从操作系统菜单中,选择其他通用操作系统,如以下所示:

图 15.3 – Raspberry Pi Imager 操作系统选项

图 15.3 – Raspberry Pi Imager 操作系统选项

从选项中选择与 Raspberry Pi 2、3 和 4 兼容的Ubuntu Core 64 位版本(参见图 15.4),如以下所示:

图 15.4 – 选择 Ubuntu Core 64 位版本

图 15.4 – 选择 Ubuntu Core 64 位版本

图 15.5所示,选择Ubuntu Core 64 位镜像后,打开存储菜单。选择您插入的 microSD 卡,如下所示:

图 15.5 – Raspberry Pi Imager 写入操作

图 15.5 – Raspberry Pi Imager 写入操作

最后,点击写入以开始操作,Raspberry Pi Imager 会清除您的 microSD 卡数据;系统会提示您确认此过程。

确认后,Raspberry Pi Imager 将开始将操作系统镜像写入 microSD 卡。完成此操作需要一些时间。

完成后,继续创建一个 Ubuntu 单点登录(SSO)账户。

步骤 2 – 创建一个 Ubuntu SSO 账户

需要创建一个 Ubuntu SSO 帐户,以便存储并将安全外壳(SSH)公钥链接到电子邮件地址。这允许 Ubuntu Core 设备仅允许来自那些具有与 SSO 帐户中匹配的公钥的设备进行 SSH 连接。

访问 login.ubuntu.com/,填写相关信息,并在创建 SSO 帐户后,按照以下部分的说明生成 SSH 密钥对。

步骤 3 – 生成 SSH 密钥对

SSH,正如我们所知道的,是一种连接到远程 Linux 服务器的流行方式。身份验证过程涉及将本地私钥与远程公钥配对,用于保护从你的设备到托管应用程序的 Linux 服务器之间的通信。

使用 Windows 10 中包含的免费开源 OpenSSH 软件,可以生成 SSH 密钥。SSH 密钥还可以通过 PuTTYgen 工具生成,该工具支持各种平台。接下来的步骤我们将使用内置的 Windows OpenSSH 客户端。

从 PowerShell 窗口,输入以下命令:

ssh-keygen

第一步询问你希望将密钥保存在哪里,按Return键可以接受默认选项,如图 15.6所示的命令执行输出所示。第二步要求输入密码短语。当输入密码短语时,每次访问密钥时都需要使用该密码短语。密码短语是可选的;如果按Return键两次,则会创建一个没有密码短语的密钥对,具体如下:

图 15.6 – SSH 密钥生成

图 15.6 – SSH 密钥生成

完成过程后,私钥和公钥将位于同一文件夹中,具体如下:

图 15.7 – 生成的私钥和公钥

图 15.7 – 生成的私钥和公钥

现在,我们可以使用生成的公钥和私钥来安装 Ubuntu Core。接下来的步骤是将公钥添加到 Ubuntu SSO 帐户中,以便它可以用来允许拥有公钥的设备连接。

从 Ubuntu SSO 帐户登录后,在 ubuntu-core-rpi.pub(公钥)文件下导入公钥,如下所示:

图 15.8 – 导入 SSH 密钥

图 15.8 – 导入 SSH 密钥

以下截图显示了公钥已成功导入:

图 15.9 – SSH 密钥已导入

图 15.9 – SSH 密钥已导入

现在我们已经将 SSH 密钥导入到 Ubuntu SSO 帐户,接下来的步骤是为 Raspberry Pi 供电并启动 Ubuntu Core。

步骤 4 – 在 Raspberry Pi 上启动 Ubuntu Core

从笔记本电脑中取出 SD 卡并插入 Raspberry Pi。在为 Pi 供电之前,连接 HDMI 屏幕和 USB 键盘。启动 Pi 后,你将能够在屏幕上看到启动过程。通常启动过程会在 5 分钟内完成。

启动过程完成后,您将看到配置网络和创建管理员帐户的说明。在此配置下,您将能够配置 Wi-Fi 设置,下一步将要求您提供与 SSO 帐户相关的电子邮件地址。配置完成后,设备将自动更新,并在必要时重启。

在提供您的电子邮件并且 Pi 连接到您的帐户后,您将能够使用任何 SSH 客户端,例如 PuTTY,连接到您的 Pi。

成功!您现在已连接到运行 Ubuntu Core 的 Raspberry Pi。

我们已完成配置设置,现在可以进入下一步,安装并配置带有严格限制的 MicroK8s snap。

在 Ubuntu Core 上设置 MicroK8s

通过 SSH 登录到控制平面节点,并安装最新版本的 MicroK8s snap,并启用严格限制,如以下命令所示:

sudo snap install microk8s --channel=latest/edge/strict

以下是执行前述命令后的输出,确认带有严格限制的 MicroK8s snap 已成功配置:

图 15.10 – MicroK8s snap 安装成功

图 15.10 – MicroK8s snap 安装成功

现在我们已经安装了 MicroK8s snap,让我们运行 microk8s status 命令来验证其运行状态,如下所示:

microk8s status

以下是执行前述命令后的输出,确认带有严格限制的 MicroK8s snap 已成功运行:

图 15.11 – MicroK8s snap 正在运行

图 15.11 – MicroK8s snap 正在运行

严格限制(strict confinement)利用 Linux 内核的安全能力将应用程序锁定在 snap 中。对于没有明确接口的高度受限应用程序,访问将受到极大限制。MicroK8s 成功运行表明所有必要的接口已被指定,且满足应用程序访问要求。

要查看 MicroK8s snap 接口,请使用以下命令:

snap connections microk8s

以下是执行前述命令后的输出,列出了 MicroK8s snap 的接口:

图 15.12 – MicroK8s snap 接口

图 15.12 – MicroK8s snap 接口

最终,所有 snap 都需要达到严格限制级别,仅使用应用程序正常运行所需的 API,其他的则不使用。此外,Ubuntu Core 要求所有 snap 都采用严格限制模型。

在 Snapcraft 文档中了解更多关于接口和限制的信息,访问 docs.snapcraft.io

现在我们已经安装了 MicroK8s,让我们使用 kubectl get nodes 命令来验证节点状态是否为 Ready,如下所示:

kubectl get nodes

如果安装成功,您应该会看到以下输出:

图 15.13 – 验证节点是否处于 Ready 状态

图 15.13 – 验证节点是否处于 Ready 状态

由于 MicroK8s 被打包为 snap 包,它将自动升级到更新的版本。此外,MicroK8s 的严格封闭版本目前位于一个专用的 snap 通道中,与 Kubernetes 的上游最新版本同步,Kubernetes 是由云原生计算基金会管理和维护的开源版本。

基于 MicroK8s 的发布,通道由一个轨道(或系列)和一个预期的稳定性级别(stablecandidatebetaedge)组成。有关发布和通道的更多信息,请运行以下命令:

snap info microk8s

以下是执行上述命令后的输出,显示了各个通道(stablecandidatebetaedge)及其发布日期:

图 15.14 – MicroK8s 通道列表(stable、candidate、beta 和 edge)及其发布日期

图 15.14 – MicroK8s 通道列表(stable、candidate、beta 和 edge)及其发布日期

在其他节点上重复 MicroK8s 的安装过程。

在工作节点上安装 MicroK8s 的命令输出如下:

图 15.15 – 在 worker1 节点上成功安装 MicroK8s snap

图 15.15 – 在 worker1 节点上成功安装 MicroK8s snap

以下是执行microk8s status命令后的输出,确认 MicroK8s 也已在工作节点上成功运行:

图 15.16 – 验证 MicroK8s 是否正在运行

图 15.16 – 验证 MicroK8s 是否正在运行

现在 MicroK8s 已经运行,接下来的步骤是检查kubectl get nodes命令是否显示节点处于Ready状态,具体如下所示:

图 15.17 – 验证节点是否处于 Ready 状态

图 15.17 – 验证节点是否处于 Ready 状态

我们已经在所有节点上完成了 MicroK8s 的安装。接下来的步骤是将工作节点添加到控制平面节点中。打开 PuTTY 终端连接到控制平面节点,并运行以下命令生成连接字符串:

sudo microk8s.add-node

以下是执行上述命令后的输出。它验证了命令已成功执行,并提供了生成连接字符串的说明:

图 15.18 – 生成用于添加节点的连接字符串

图 15.18 – 生成用于添加节点的连接字符串

如前述命令执行输出所示,连接字符串以<control plane_ip>:<port>/<token>的形式生成。

添加工作节点

我们现在有连接字符串来与控制平面节点连接。打开 PuTTY 终端连接到 worker 节点,并运行 join 命令将其添加到集群中,以下是该命令的执行情况:

microk8s join <control plane_ip>:<port>/<token>

命令成功执行,节点已成功加入集群,输出如下所示:

图 15.19 – 将 worker#1 节点添加到集群

图 15.19 – 将 worker#1 节点添加到集群

图 15.19所示的命令执行输出所示,您应该能在几秒钟内在控制平面节点上看到新节点。

使用以下命令验证新节点是否已添加到集群中:

kubectl get nodes

以下命令执行输出显示控制平面和worker1节点是集群的一部分:

图 15.20 – 集群已就绪,控制平面和 worker1 节点已加入集群

图 15.20 – 集群已就绪,控制平面和 worker1 节点已加入集群

此时,您已拥有一个完全功能的多节点 Kubernetes 集群,并启用了严格的隔离。总结一下,我们已在运行 Ubuntu Core 的树莓派板上安装了 MicroK8s snap,并将多个部署连接起来形成集群。我们也看到了如何向集群中添加节点。接下来,我们将要在刚刚创建的 MicroK8s 集群上部署一个示例应用程序。

部署一个示例容器化应用程序

在本节中,我们将在我们的 MicroK8s 集群环境中部署 Kubernetes 示例库中的 nginx 部署,具体如下:

kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml

以下是前述命令执行后的输出,显示部署没有错误,接下来的步骤中,我们可以使用 get pods 命令来验证部署是否成功:

图 15.21 – 示例应用程序部署

图 15.21 – 示例应用程序部署

检查 pods 状态,以验证应用程序是否已成功部署并运行,如下所示:

kubectl get pods -l app=nginx

以下是前述命令执行后的输出,显示了 pods 已被创建,并且其状态为Running

图 15.22 – 检查 pods 是否处于 Running 状态

图 15.22 – 检查 pods 是否处于 Running 状态

太棒了!我们刚刚在运行 Ubuntu Core 的树莓派多节点集群上部署并检查了我们的示例应用程序部署。

总结来说,利用快照、Snapcraft 和 Ubuntu Core 进行嵌入式 Linux 开发比市场上现有的选项更快捷、更安全、更可靠。由于它能够通过全球 Snap Store 打包、分发和更新任何应用程序,Snapcraft 使得为嵌入式设备寻找新软件变得更加简单。此外,应用程序更新要么完全成功,要么根本不会部署。在进行应用程序和系统更新时,运行 Ubuntu Core 的嵌入式设备始终保持完全功能。

总结

在这一章中,我们学习了如何使用严格限制选项安装 MicroK8s 快照,监控安装进度,并管理在 Ubuntu Core 上运行的 Kubernetes 集群。我们还部署了一个示例应用程序,并检查了该应用程序是否能够在启用了严格限制的 Kubernetes 集群上运行。

我们还介绍了一个新的嵌入式操作系统,Ubuntu Core,它通过启用自动更新、应用商店和软件管理来符合企业标准。我们还了解到,它从零开始构建,旨在成为最安全的连接设备平台。此外,Ubuntu Core 提供了基于快照的模块化设计、防弹的应用更新、通过 Snapcraft 无缝的开发者体验以及内置的安全性,以应对嵌入式 Linux 开发中的挑战。

在这一章及之前的章节中,我们详细讨论了使用 MicroK8s 进行物联网/边缘计算应用程序所需的多数实施方面;包括在多节点 Raspberry Pi 集群上运行应用程序、配置负载均衡机制、为网络连接安装/配置不同的 CNI 插件、为集群配置日志、监控和警报选项,以及构建/部署机器学习模型和无服务器应用程序。

此外,我们还探讨了为有状态应用程序设置存储复制、为跨领域问题实现服务网格、设置高可用性集群以承受组件故障并持续为工作负载提供服务、配置具有工作负载隔离的容器以及运行与主机系统隔离的安全容器。在接下来的章节中,我们将探讨 MicroK8s 如何在加速物联网(IoT)和边缘计算部署方面独树一帜,并分析塑造我们新未来的关键趋势。

第十六章:展望未来

根据最近的 CNCF 调查报告(www.cncf.io/reports/cncf-annual-survey-2021/),96% 的企业正在使用或考虑使用 Kubernetes。一般来说,容器技术,尤其是 Kubernetes,似乎随着技术的成熟而使用频率降低。组织似乎比以往更 intensively 使用无服务器和托管服务,且用户不再需要了解或理解底层的容器技术。

近年来,随着云原生技术的广泛应用,行业中出现了指数级增长。使用 Kubernetes 和容器对应用程序进行现代化已经成为许多企业的共同主题。对于成熟企业和初创公司而言,基于容器的持续集成/持续部署CI/CD)已成为事实上的 DevOps 标准。Kubernetes 是执行边缘计算工作负载的理想平台。此外,Kubernetes 已发展为一个混合计算平台,允许公共云服务提供商在本地环境中设置的集群中运营其托管服务。

基于边缘的基础设施在资源和工作负载管理方面面临诸多挑战。在较短的时间内,成千上万的边缘节点和远程边缘节点需要被控制。组织的边缘架构旨在提供更多的云独立性、高安全性要求和最低延迟。

本书中,我们已覆盖了以下实施方面,解决了使用 MicroK8s 的 IoT/边缘计算场景:

  • 启动并运行 Kubernetes 集群

  • 启用核心的 Kubernetes 插件,如 DNS 和仪表盘

  • 创建、扩展并执行多节点 Kubernetes 集群的滚动更新

  • 使用各种容器网络选项进行网络配置——Calico/Flannel/Cilium

  • 设置 MetalLB 和 Ingress 选项以进行负载均衡

  • 使用 OpenEBS 存储复制进行有状态应用程序的管理

  • 配置 Kubeflow 并运行 AI/ML 用例

  • 配置与 Istio/Linkerd 的服务网格集成

  • 使用 Knative 和 OpenFaaS 运行无服务器应用程序

  • 配置日志记录/监控选项(Prometheus、Grafana、Elastic、Fluentd 和 Kibana)

  • 配置多节点高可用的 Kubernetes 集群

  • 配置 Kata 容器以实现安全容器

  • 配置严格的隔离运行环境

此外,在每一章节中,我们还讨论了设计和有效实施 Kubernetes 用于边缘工作负载的指南和最佳实践。

随着企业拥抱数字化转型、工业 4.0、工业自动化、智能制造及其他先进用例,Kubernetes、边缘计算与云计算的协同作用,推动理性商业决策的重要性变得越来越明显。

正在转型为数字优先企业的企业越来越依赖 Kubernetes。Kubernetes 显然是边缘计算的首选平台,至少对于那些需要动态编排应用程序和集中管理工作负载的边缘设备来说。通过在解耦的云环境中实现灵活和自动化的应用管理,Kubernetes 将云原生计算软件开发的优势扩展到边缘。在本章的最后,我们将讨论以下主要主题:

  • MicroK8s 如何在加速 IoT 和边缘部署方面具有独特的优势

  • 展望未来 – Kubernetes 趋势与行业前景

MicroK8s 如何在加速 IoT 和边缘部署方面具有独特的优势

边缘网关必须高效利用计算资源,同时处理多种协议,包括蓝牙、Wi-Fi、3G、4G 和 5G。由于边缘网关的计算能力有限,直接在边缘服务器上操作 Kubernetes 是具有挑战性的。以下是一些问题:

  • 为了更好的监控和管理,将控制平面和工作节点从边缘分离,并将控制平面转移到云端,在那里控制平面和工作节点承担工作负载。

  • 将集群数据存储分离以处理重负载。

  • 专门为进出流量设置工作节点将有助于改善流量管理。

这些问题将导致多个集群的开发,从而使得整个基础设施的管理变得更加具有挑战性。

MicroK8s 迎难而上,它作为边缘集群和主流 Kubernetes 之间的桥梁。由于运行资源有限,因此需要较小的占用空间,同时可以协调完整的云资源池。我们在前面的章节中看到,MicroK8s 利用 Kubernetes 中的不可变容器来提高安全性并简化操作。它有助于创建自愈的高可用集群,自动选择最佳节点来处理 Kubernetes 数据存储。当集群数据库节点之一发生故障时,另一个节点会自动提升,无需管理员干预。MicroK8s 易于安装和升级,并且具备强大的安全性,非常适合微型云和边缘计算。

操作 IoT 边缘的一些显著挑战

本节将探讨与 IoT 边缘操作相关的一些重大问题:

  • 计算和资源限制:IoT 边缘设备的 CPU 和内存资源通常受到限制,因此必须明智地利用这些资源,并保持解决方案的关键功能。

  • 远程和资源管理:当集群或边缘网络快速扩展时,手动部署、管理和维护设备将变得困难且耗时。以下是一些突出问题:

    • 高效利用设备资源,包括 CPU、内存、网络和边缘设备的 I/O 端口,以及它们的远程监控和管理。

    • 将 CPU 核心和协处理器(例如 GPU)分配给特定工作负载,并承载和扩展任意混合的应用程序。

    • 自动化的远程更新,并具有回滚功能,以避免设备“砖化”。

    • 容易迁移到不同的后端,并自动连接到一个或多个后端(如云或本地基础设施)。

    • 一个分布式、安全的防火墙,根据定义的策略安全地在网络中路由数据。

  • 安全性和可信度:IoT 边缘设备必须防止未经授权的访问。大规模环境对设备匿名性和可追溯性、发现、认证以及在 IoT 边缘构建信任提出了严峻挑战。为了确保多个 IoT 应用在设备中相互隔离运行,额外的安全层是至关重要的。

  • 可靠性和容错性:由于系统中 IoT 设备的数量庞大,边缘网络需要自我管理和自我配置的解决方案。IoT 应用需要具备解决在其生命周期内出现的任何问题的能力。IoT 边缘的常见要求包括抗故障能力和缓解拒绝服务攻击。

  • 可扩展性:在 IoT 生态系统中,传感器或执行器正在逐步负责所有操作。数据收集点的数量和规模都在迅速增长。在 IoT 环境中(如智能城市和智能交通系统)短时间内添加数百个新传感器或执行器是常见的,而该环境仍在多个应用中运行。因此,扩展 IoT 生态系统和数据管理的需求至关重要。此外,边缘服务还面临成本以及其他因素的挑战,包括工作负载监控、存储容量、动态资源分配和数据传输速率。

  • 调度和负载均衡:为了支持多个服务之间共享数据的大型系统,边缘计算完全依赖于负载均衡和调度方法。必须以安全、可靠和灵活的方式,以更低的成本提供数据、软件和基础设施,以确保计算资源的最优使用。此外,可靠的调度和负载均衡系统也是必不可少的。

既然我们已经了解了管理 IoT 边缘基础设施的主要难点,我们将探讨 MicroK8s Kubernetes 如何有效地解决这些挑战。

MicroK8s Kubernetes 如何使边缘设备受益。

由于 MicroK8s 能够提高 Kubernetes 的生产力并减少复杂性,它在加速 IoT 和边缘部署方面占据了有利位置。在本节中,我们将看看 MicroK8s Kubernetes 如何使边缘设备受益:

  • 可扩展性:对于许多物联网解决方案来说,可扩展性是主要关注点。必须有一个能够独立水平或垂直扩展的基础设施,才能支持额外的设备并实时处理 TB 级数据。与传统的虚拟机相比,容器因其轻量性而能更快速地生成。MicroK8s Kubernetes 的主要优势之一就是它在网络集群中扩展的简便性、容器独立扩展的能力以及自动重启而不影响服务的能力。

  • 高可用性:为了让物联网解决方案能够进行关键业务功能,边缘设备必须随时可用且可靠。由于每个容器都有自己的 IP 地址,因此可以轻松地在容器之间分配负载,并在容器停止工作时重新启动应用程序。我们已经看到如何利用负载均衡功能并运行多个副本以实现高可用性的各种示例。此外,我们还研究了如何设置 HA 集群以应对组件故障的步骤。

  • 资源的高效利用:由于其有效的资源管理,Kubernetes 降低了托管物联网应用的成本。MicroK8s 是 Kubernetes 的紧凑版、优化版,它在托管的虚拟机、裸金属实例或云上提供了一层抽象。管理员可以专注于将应用服务部署到尽可能多的基础设施上,这样可以降低运行物联网应用的整体基础设施成本。

  • 部署到物联网边缘:向边缘设备部署软件更新而不干扰服务是一个重要的物联网挑战。通过 Kubernetes 可以运行逐步推出更新的微服务。Kubernetes 安装中通常采用滚动更新策略来推出 Pod 版本更新。通过在更新过程中保持某些实例(例如 Pod 中断预算)运行,能够实现零服务停机。旧的 Pod 仅在新部署版本的流量准备好的 Pod 启用并准备替换时才会被驱逐。因此,应用程序可以通过单个命令水平或垂直扩展。

  • 为物联网启用 DevOps:为了满足消费者需求,物联网解决方案必须能够平滑地更新,且不会造成用户停机。开发团队可以利用 Kubernetes 中可用的 CI/CD 工具高效地验证、推出并部署物联网服务的变更。此外,Kubernetes 得到多个云服务提供商的支持,包括 Azure、Google Cloud 和 AWS。因此,将来切换到任何云服务都将变得简单。

依赖物联网的行业正在集中力量在边缘设备上实现关键任务服务,以提高解决方案的响应能力并降低成本。基于 Kubernetes 平台构建的解决方案为在边缘实施物联网服务提供了标准框架。Kubernetes 社区的持续进步使得构建可扩展、可靠且可以在分布式环境中部署的物联网解决方案成为可能。

在下一节中,我们将探讨一些推动 Kubernetes 及其采用的趋势。

展望未来——Kubernetes 趋势和行业前景

根据 Gartner 于2021 年 10 月发布的《新兴技术:Kubernetes 与云原生基础设施之争》报告,“到 2025 年,85%的组织将在生产环境中运行容器,较 2020 年不到 30%的比例大幅增长。”在本节中,我们将探讨一些将推动企业采用 Kubernetes 的关键趋势。

趋势 1——安全仍然是每个人的关注点

容器和 Kubernetes 已经带来了显著的安全隐患。过去 12 个月,93%的 Kubernetes 环境至少发生了一次安全事件。这可能是由于多个问题所致,例如缺乏容器和 Kubernetes 的安全专业知识、安全工具不足或不适用,以及安全团队未能跟上迅速发展的应用开发团队的步伐,后者往往将安全视为事后考虑。

应用程序的安全态势可能会受到 Kubernetes 中几个配置选项的影响。由于容器和 Kubernetes 环境中的配置错误,可能会出现暴露风险。现在企业已经知道,如果没有在开发生命周期的每个阶段都融入安全措施,就无法充分保障容器化环境的安全。DevSecOps 方法论现在正成为管理容器化环境的重要组成部分。

我在最近的Kubernetes 和云原生操作报告,2022中强调了 DevSecOps 的必要性。有关分析和要点,敬请阅读:juju.is/cloud-native-kubernetes-usage-report-2022#key-takeaways

另一个可能受到影响的方面是软件行业的供应链。创建现代软件的过程涉及将多个开放源代码项目自由访问的部分进行组合和合并。一个脆弱的软件组件可能会严重损害应用程序的其他部分以及整个部署,甚至影响复杂的软件供应链。在接下来的日子里,可能会推出新的举措、项目等,以保障软件供应链的安全。

下一个突破是扩展伯克利数据包过滤器eBPF),它为云原生开发人员提供了灵活性,可以创建用于安全网络、服务网格和可观测性的组件。我们在第六章配置容器连接性中已经看到了使用 eBPF 和 Cilium 的示例。在未来的日子里,eBPF 可能会在安全和网络领域变得普及。

趋势 2 – GitOps 用于持续部署

GitOps 提供了著名的基于 Git 的流程,是一个重要的工具,因为它支持快速回滚,并可以作为状态协调的唯一真实来源。

本地集成 GitOps 的方法有很多,包括 Flux CD、Argo CD、Google Anthos 配置管理、Codefresh 和 Weaveworks。

成千上万的 Kubernetes 集群在边缘或混合环境中运行,现在可以轻松通过 GitOps 对多租户和多集群部署的支持进行管理。

因此,GitOps 正在成为持续部署的首选方法。在即将到来的日子里,GitOps 将成为运行和部署 Kubernetes 应用及集群的黄金标准。

趋势 3 – 操作员的应用商店

Kubernetes 无需额外的技术专长即可扩展和管理无状态应用,包括 web 应用、移动后端和 API 服务。Kubernetes 内置的功能简单地处理这些任务。

然而,像数据库和监控系统这样的有状态应用需要额外的领域专长,而 Kubernetes 本身并不具备这些。为了扩展、更新和重新配置这些应用,需要额外的了解已部署应用的能力。为了管理和自动化应用生命周期,Kubernetes 操作员将这些独特的领域知识包含在其扩展中。

Kubernetes 操作员通过消除繁琐的手动应用管理任务,使这些流程可扩展、可重复和标准化。

操作员使得应用开发人员更容易部署和维护其应用所需的支持服务。此外,操作员提供了在 Kubernetes 集群上分发应用的标准化方法,并通过发现并修复基础设施工程师和供应商的应用问题,减轻了对支持的要求。我们在第八章监控基础设施和应用健康中已经看到了操作员模式的示例,在该示例中,我们为 Kubernetes 部署了 Prometheus Operator,简化了 Kubernetes 服务的监控定义,以及 Prometheus 实例的部署和管理。

然而,有关操作员的“真实”来源和可访问性存在担忧,以缓解组织采纳新技术,特别是开源解决方案时的根本性顾虑。

像 Charmhub.io(charmhub.io/)一样,应该有一个类似应用商店的中心平台,供人们发布和使用运维工具。这里会有特定的工件所有权、验证和不同的版本。而且这个“商店”将包含足够的信息,供人们根据文档、评分、不同的发布者等,选择合适的版本。

我在最近的 Kubernetes 和云原生操作报告 2022 中概述了 面向运维人员的应用商店 的想法。阅读更多分析和收获请访问:juju.is/cloud-native-kubernetes-usage-report-2022#key-takeaways

趋势 4 – 无服务器计算与容器

Gartner 的分析师早在很久以前就预测了无服务器计算或 功能即服务 (FaaS) 的增长(blogs.gartner.com/tony-iams/containers-serverless-computing-pave-way-cloud-native-infrastructure/)。

想象一下,你有一个复杂的容器化系统,正在执行由事件触发的共享服务(例如集成、数据库操作和身份验证)。为了减轻容器化设置的复杂性,你可以将这些任务分离成一个无服务器函数,而不是在容器中运行它们。

此外,使用容器可以轻松扩展无服务器应用程序。在大多数场景中,无服务器函数用于保存数据;你可以通过将这些服务挂载为 Kubernetes 持久化存储卷,来在无服务器和容器架构之间集成并通信有状态数据。

基于 Kubernetes 的事件驱动自动扩展器 (KEDA)(keda.sh/)帮助运行事件驱动的 Kubernetes 工作负载,如容器化函数,因为它提供了细粒度的自动扩展功能。函数的运行时通过 KEDA 接受事件驱动的扩展功能。根据负载,KEDA 可以从 个实例(当没有事件发生时)扩展到 n 个实例。通过使 Kubernetes 自动扩展器(水平 Pod 自动扩展器)能够使用自定义指标,它实现了自动扩展。任何 Kubernetes 集群都可以通过利用函数、容器和 KEDA 来复制无服务器函数的能力。

Knativeknative.dev/docs/)是另一个框架,它整合了扩展、Kubernetes 部署模型以及事件和网络路由。通过 Knative 服务资源,基于 Kubernetes 构建的 Knative 平台在工作负载管理方面采取了明确的立场。CloudEvents 是 Knative 的基础,Knative 服务本质上是由事件触发并由事件进行扩展的函数,无论它们是 CloudEvents 事件还是简单的 HTTP 请求。Knative 能够快速响应事件速率的变化,因为它使用 Pod 辅助容器来监控事件速率。此外,Knative 提供了零扩展功能,使工作负载扩展更加精确,特别适用于微服务和函数。

传统的 Kubernetes 部署/服务用于实现 Knative 服务,并且对 Knative 服务的更改(如添加新的容器镜像)会生成相应的 Kubernetes 部署/服务资源。由于 HTTP 流量的路由是 Knative 服务资源描述的一部分,Knative 利用这一点实现蓝绿部署和金丝雀部署模式。

因此,在设计应用程序在 Kubernetes 上的部署时,开发人员应使用 Knative 服务资源及其相关资源来指定事件路由。使用 Knative 意味着开发人员主要关注 Knative 服务,而部署由 Knative 平台处理,这类似于我们今天通过部署资源处理 Kubernetes,并让 Kubernetes 管理 Pods。

OpenFaaS 是一个使用 Docker 和 Kubernetes 容器技术创建无服务器函数的框架。任何进程都可以包装成一个函数,使其能够消费各种 Web 事件,而无需一遍又一遍地编写模板代码。这是一个开源倡议,在社区中获得了广泛的关注。

我在我的博客中介绍了 OpenFaaS 框架:www.upnxtblog.com/index.php/2018/10/19/openfaas-tutorial-build-and-deploy-serverless-java-functions/

第十章,《使用 Knative 和 OpenFaaS 框架无服务器化》一章中,我们已经看过如何在 Knative 和 OpenFaaS 平台上部署示例,并通过 CLI 使用其端点进行调用。我们还看到了无服务器框架如何在没有请求时将 Pods 缩放到零,并在有更多请求时启动新的 Pods。我们还讨论了一些在开发和部署无服务器应用程序时需要牢记的指导原则。

在接下来的日子里,可能会有新的倡议和开源项目推出,这些项目有可能促进在无服务器和容器运行方面的创新。

趋势 5 – 人工智能/机器学习与数据平台

对于包括机器学习人工智能MLAI)在内的工作负载,Kubernetes 已被广泛使用。组织已尝试多种技术来交付这些能力,包括裸金属上的手动扩展、公共云基础设施上的虚拟机扩展以及高性能计算HPC)系统。然而,AI 算法通常需要大量的计算能力。

Kubernetes 可能是最有效且直接的选择。将 AI/ML 工作负载打包成容器并在 Kubernetes 上作为集群运行的能力,为 AI 项目提供了灵活性,最大化了资源使用,并为数据科学家提供了自服务环境。

容器让数据科学团队能够构建并可靠地重现验证的设置,无需为每个工作负载调整 GPU 支持。NVIDIA 和 AMD 已经为最新版本的 Kubernetes 添加了实验性的 GPU 支持。此外,NVIDIA 还提供了一系列预加载容器和 GPU 优化的容器化 ML 应用程序(developer.nvidia.com/ai-hpc-containers)。

第九章,《使用 Kubeflow 运行 AI/MLOps 工作负载》中,我们介绍了如何设置一个 ML 管道,使用 Kubeflow ML 平台开发和部署一个示例模型。我们还注意到,Kubeflow 在 MicroK8s 上的设置和配置非常简单,且轻量级,能够在构建、迁移和部署管道时模拟现实世界的条件。

我们可以预见,越来越多的 AI/ML 和数据平台将转向 Kubernetes。

趋势 6 – 有状态应用

如今,有状态应用已成为常态。尽管容器和微服务等技术创新简化了基于云系统的开发,但它们的敏捷性使得管理有状态进程变得更加困难。

有状态应用必须更频繁地在容器中执行。在边缘、公共云和混合云等复杂环境中,容器化应用可以简化部署和运营。为了使CI/CD能够顺利从开发过渡到生产,保持状态同样至关重要。

Kubernetes 通过为平台管理员和应用开发者提供必要的抽象,做出了重大改进,以便运行有状态的工作负载。这些抽象确保了无论容器被调度到哪里,不同类型的文件和块存储都能可用。

第十一章,《使用 OpenEBS 管理存储复制》中,我们讨论了如何配置和实现利用 OpenEBS 存储引擎的 PostgreSQL 有状态工作负载。我们还讨论了一些 Kubernetes 存储的最佳实践,以及选择数据引擎的指导方针。

总结来说,我们可以预见到,2022 年容器和 Kubernetes 基础设施的自动化安全性和持续合规性将成为强劲趋势,同时最佳实践的发展也会成为重点。这对那些必须遵守严格合规标准的企业尤为重要。

总结

总结来说,Kubernetes、边缘计算和云计算的协同作用,推动明智的商业决策,随着企业拥抱数字化转型、工业 4.0、工业自动化、智能制造以及所有先进的应用案例,变得越来越显著。我们还探索了不同的部署方法,展示了 Kubernetes 如何用于运行边缘工作负载。本书中,我们涵盖了使用 MicroK8s 实现物联网/边缘计算应用的大部分实施方面。Kubernetes 无疑是边缘计算的首选平台。

我们还看到,MicroK8s 在加速物联网和边缘部署方面具有独特优势。此外,我们还探讨了将塑造未来的一些关键趋势。

恭喜!您已经成功完成了本书。在继续您的 Kubernetes 之旅时,我相信您会从我们在本书中讨论的示例、场景、用例、最佳实践和建议中受益匪浅。

总结来说,Kubernetes 以其快速扩展的生态系统以及多样化的工具、支持和服务,正迅速成为一种有用的工具,尤其是在越来越多的组织转向云计算的过程中。

根据 Canonical 2022 年的 Kubernetes 和云原生操作调查https://juju.is/cloud-native-kubernetes-usage-report-2022),48% 的受访者表示,迁移到或使用 Kubernetes 和容器的最大障碍是缺乏内部能力和人员有限。

如报告所示,存在技能短缺和知识空白,我相信本书可以通过涵盖关键领域来帮助您快速掌握相关内容。

若要跟上更新,您可以订阅我的博客:www.upnxtblog.com

我也期待听到您在 twitter.com/karthi4india 上分享您的经历、意见和建议。

以下是一些优秀的 MicroK8s 资源,助您在旅程中获得支持。

进一步阅读

关于 MicroK8s 的常见问题解答

以下常见问题并不全面,但它们对于运行 Kubernetes 集群非常重要:

  1. 如何查看部署的状态?

使用 kubectl get deployment <deployment> 命令。如果 DESIREDCURRENTUP-TO-DATE 都相等,则说明部署已成功。

  1. 如何排查状态为 Pending 的 pod?

状态为 Pending 的 pod 不能被调度到节点上。使用 kubectl describe pod <pod> 命令可以查看 pod 卡住的原因。此外,你还可以使用 kubectl logs <pod> 命令来了解是否存在竞争问题。

这个问题最常见的原因是某些 pod 请求了更多的资源。

  1. 如何排查状态为 ContainerCreating 的 pod?

Pending 状态的 pod 不同,ContainerCreating 状态的 pod 已经被调度到节点上,但由于某些其他原因,它无法正确启动。使用 kubectl describe pod <pod> 可以查看 pod 卡住在 ContainerCreating 状态的原因。

上述问题的最常见原因包括 CNI 启动错误,也可能是由于卷挂载失败导致的错误。

  1. 如何排查状态为 CrashLoopBackoff 的 pod?

当一个 pod 因为错误失败时,这是标准的错误信息。kubectl logs <pod> 命令通常会显示最近执行的错误信息。通过这些信息,你可以找出导致问题的原因并加以解决。

如果容器仍在运行,你可以使用 kubectl exec -it <pod> -- bash 命令进入容器的 shell 进行调试。

  1. 如何回滚特定的部署?

如果你在执行 kubectl apply 命令时使用了 –record 参数,Kubernetes 会将上次的部署记录存储在历史记录中。你可以使用 kubectl rollout history deployment <deployment> 命令查看历史部署。

可以使用 kubectl rollout undo deployment <deployment> 命令恢复上次的部署。

  1. 如何强制将 pod 运行在特定的节点上?

一些常用的方法包括使用 nodeSelector 字段以及亲和性和反亲和性。

最简单的建议是,在 pod 定义中使用 nodeSelector 字段来定义目标节点应该具有的节点标签。Kubernetes 使用这些信息只将 pod 调度到带有你指定标签的节点上。

  1. 如何强制将副本分配到不同的节点?

Kubernetes 默认尝试节点反亲和性,但这不是一个硬性要求;它是尽力而为,但如果没有其他选择,它将在同一节点上调度许多 pod。您可以在这里了解更多关于节点选择的信息:kubernetes.io/docs/user-guide/node-selection/

  1. 如何列出节点上的所有 pod?

使用以下命令:

kubectl get pods -A  --field-selector spec.nodeName=<node name> | awk '{print $2"  "$4}'

更详细的 kubectl 技巧表可以在 kubernetes.io/docs/reference/kubectl/cheatsheet/ 找到。

  1. 如何监视一个始终运行的 pod?

为此,您可以利用存活探针功能。

存活探针始终检查 pod 中的应用程序是否在运行,如果检查失败,则容器将被重新启动。这在容器运行但其中的应用程序崩溃的许多情况下非常有用。

以下代码片段演示了存活探针功能:

spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
args:
- /server
livenessProbe:
      httpGet:
        path: /healthcheck
  1. 复制控制器和副本集之间有什么区别?

选择器是复制控制器和副本集之间的唯一区别。 Kubernetes 的最新版本不再支持复制控制器,它们的规范也不提及选择器。

更多详细信息可在 Kubernetes.io/docs/concepts/workloads/controllers/replicaset/ 找到。

  1. kube-proxy的角色是什么?

下面是kube-proxy的角色和责任:

  • 对于每个服务,它会向其运行的节点分配一个随机端口,并为服务分配代理。

  • 安装并维护 iptable 规则,拦截传入到虚拟 IP 和端口的连接,并将其路由到端口。

kube-proxy 组件负责主机子网化,并使服务可用于其他组件。由于 kube-proxy 管理网络通信,关闭控制平面不会阻止节点处理流量。它的操作方式类似于服务。连接将通过 iptables 转发到 kube-proxy,然后 kube-proxy 将使用代理连接到服务的一个 pod。无论端点中有什么,kube-proxy 都将使用目标地址通过转发。

  1. 如何在不执行部署清单的情况下测试它?

要测试清单,请使用--dry-run标志。这对于确定 YAML 语法是否适合特定的 Kubernetes 对象非常有用,并确保规范包含所需的键值对:

kubectl create -f <test manifest.yaml> --dry-run
  1. 如何打包 Kubernetes 应用程序?

Helm 是一个允许用户打包、配置和部署 Kubernetes 应用程序和服务的包管理器。您可以在这里了解更多关于 Helm 的信息:helm.sh/

若要查看快速入门指南,请参见www.upnxtblog.com/index.php/2019/12/02/helm-3-0-0-is-outhere-is-what-has-changed/

  1. 什么是init容器?

在 Kubernetes 中,一个 Pod 可以包含多个容器。init 容器会在 Pod 中的其他容器之前运行。

以下是一个示例,定义了一个简单的 Pod,其中包含两个 init 容器。第一个等待 myservice,第二个等待 mydb。当两个 init 容器都完成后,Pod 会根据其 spec: 部分执行应用容器。

更多详情请参见此处:kubernetes.io/docs/concepts/workloads/pods/init-containers/

以下代码片段展示了initContainers是如何工作的:

apiVersion: v1
kind: Pod
metadata:
  name: sample-app-pod
  labels:
    app: sample-app
spec:
  containers:
  - name: sample-app-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
  1. 如何将 Pod 从节点中驱逐进行维护?

使用drain命令,方法如下:

kubectl drain <node>

当你执行上述命令时,它会将节点标记为不调度新的 Pod,然后驱逐或删除现有的 Pod。

一旦你完成了节点的维护并希望将其重新加入集群,执行uncordon命令,方法如下:

kubectl uncordon <node>

更多详情请参见kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/

  1. 什么是 Pod 安全策略?

Kubernetes 中的 Pod 安全策略是控制 Pod 可以访问哪些安全功能的配置。它们是一种集群级资源,帮助你控制 Pod 的安全性。

更多详情请参见kubernetes.io/docs/concepts/security/pod-security-policy/

  1. 什么是ResourceQuota,我们为什么需要它?

ResourceQuota 对象限制每个命名空间的资源总消耗。它可以限制在命名空间中根据类型生成的对象数量,以及该项目中资源所能消耗的计算资源总量。

更多详情请参见kubernetes.io/docs/concepts/policy/resource-quotas/

posted @ 2025-06-30 19:29  绝不原创的飞龙  阅读(131)  评论(0)    收藏  举报