要完整地了解 Kubernetes API 的所有细节,这超出了本书的范围。为了达到这些目的,有一些书籍,如《Kubernetes: Up and Running》,以及在线资源,可以让你获得在 Kubernetes 上构建应用所需的知识。如果你对 Kubernetes 完全陌生,并且对在系统之上构建应用程序感兴趣,我们绝对建议你利用这些资源来补充本章的信息。

另一方面,如果你负责管理 Kubernetes 集群,或者你对 Kubernetes API 有高层次的理解,本章提供了 Kubernetes 的基本概念及其在应用开发中的作用的介绍。如果在阅读完本章后,你仍然觉得不方便与用户就 Kubernetes 的使用进行对话,我们强烈建议你利用这些额外的资源。

在本章中,我们首先介绍容器的概念,以及如何使用它们来打包和部署你的应用程序。然后,我们介绍了 Kubernetes API 背后的核心概念,最后,我们以 Kubernetes 为使具体任务变得更容易而添加的一些更高层次的概念作为结束。

容器

容器是由 Docker 流行起来的,它使开发者打包和部署应用程序的方式发生了革命。然而,在这个过程中,容器这个词对许多不同的人来说有许多不同的含义。因为 Kubernetes 是一个容器编排器,要理解 Kubernetes,就必须理解我们说的容器是什么意思。

实际上,一个容器是由两个不同的部分,以及一组相关的功能组成的。一个容器包括:

  • 一个容器镜像
  • 一套操作系统的概念,隔离一个或多个运行中的进程

容器镜像包含了应用程序运行时,它由二进制文件、库和运行容器所需的其他数据组成。开发者可以将他们的应用程序打包成容器映像在他们的开发笔记本电脑上,并相信当该镜像被部署并在不同的环境中运行时 — 无论是其他用户的笔记本电脑还是数据中心的服务器 — 容器将与它在开发者的笔记本电脑上的表现完全一样。这种可移植性和在各种环境中的一致执行是容器镜像的主要价值之一。

当容器镜像运行时,它也会使用操作系统中的命名空间来执行。这些命名空间包含了进程,并为它及其同行提供了与机器上运行的其他事物的隔离。这种隔离意味着,例如,每个正在运行的容器都有自己独立的文件系统(像 chroot)。此外,每个容器都有自己的网络和 PID 命名空间,这意味着一个容器中的 42 号进程与另一个容器中的 42 号进程是不同的。内核中还有许多其他的命名空间,可以将不同的运行容器相互分开。此外,控制组(cgroups)允许隔离资源的使用,比如内存或 CPU。最后,标准的操作系统安全特性,如 SELinux 或 AppArmor,也可以用于运行中的容器。所有这些隔离结合在一起,使得运行在不同容器中的不同进程更难相互干扰。

:::info 当我们说隔离时,要知道这是在资源方面,比如 CPU、内存或文件,这是非常重要的。在 Linux 和 Windows 中实现的容器目前还不能为不同的进程提供强大的安全隔离。当容器与其他内核级隔离相结合时,可以为某些用例提供合理的安全隔离。然而,在一般情况下,只有管理程序级别的安全隔离足够强大,才能隔离真正的敌对工作负载。 :::

为了使所有这些工作,创建了一些不同的工具来帮助构建和部署容器化应用程序。

第一个是容器镜像构建器。通常,Docker 命令行工具用于构建容器镜像。然而,镜像格式已经通过开放容器倡议(OCI)标准进行了标准化。这使得其他镜像构建器的开发成为可能,可通过云 API、CI/CD 或新的替代工具和库获得。

Docker 工具使用 Dockerfile,它指定了一套如何构建容器镜像的指令。关于使用 Docker 工具的全部细节超出了本书的范围,但在 Docker 等书籍中可以获得大量资源。如果你从来没有构建过容器镜像,现在就放下这本书,去阅读有关容器的知识,等你构建了几个容器镜像后再来。

容器镜像构建完成后,我们需要一种方法将该镜像从用户的笔记本电脑上分发到其他用户、云端或私有数据中心。这就是镜像注册表(Registry)的作用。镜像注册表是一个用于上传和管理图像的 API。在建立一个镜像后,它被推送到镜像注册表中。镜像进入注册表后,可以从该注册表中提取或下载到任何可以访问该注册表的机器上。每个注册表都需要某种形式的授权来推送镜像,但有些注册表是公开的,这意味着一旦镜像被推送,世界上的任何人都可以拉动并开始运行该镜像。另一些是私有的,也需要授权才能拉取镜像。在这一点上,每个公共云都有注册表作为服务提供,也有开源的注册表服务器,你可以下载并在自己的环境中运行。在你开始设置你的 Kubernetes 集群之前,最好先弄清楚你要在哪里存储你在其中运行的镜像。

一旦你把你的应用打包成容器镜像并推送到注册表,就可以使用该容器来部署应用了,这就是容器编排的作用。

容器编排

当你在某个注册表中存储了一个容器镜像后,你需要运行它来创建一个工作的应用程序。这就是像 Kubernetes 这样的容器编排器的作用。Kuberentes 的工作是将一组提供资源的机器,如 CPU、内存和磁盘,并将它们转化为面向容器的 API,开发人员可以使用它们来部署容器。

Kubernetes API 可以让你声明你想要的世界状态,例如,“我想要这个容器镜像运行,它需要 3 个核心和 10G 的内存才能正常运行”。然后,Kubernetes 系统会审查它的机器群,为该容器镜像找到一个好的运行位置,并在该机器上调度该容器的执行。开发者看到自己的容器镜像在运行,更多的时候,他们不需要关心容器执行的具体位置。

当然,只运行一个容器既没有那么有趣,也没有那么可靠,所以 Kubernetes API 也提供了简单的方法,可以说:“想让这个容器镜像的三个副本在不同的机器上运行,每个机器都有 3 个内核和 10G 的内存。”

但编排系统的意义不仅仅是将容器调度到机器上。除此之外,Kubernetes 编排器还知道如何在这些容器失败时进行愈合。如果你的容器里面的进程崩溃了,Kubernetes 会重新启动它。如果你定义了自定义的健康检查,Kubernetes 可以使用它们来确定你的应用程序是否陷入了死锁,需要重新启动(存活检查),或者它是否应该成为负载均衡服务的一部分(就绪检查)。

说到负载均衡,Kubernetes 还提供了 API 对象,用于定义在这些不同副本之间负载均衡流量的方法。它提供了一种方式来表示:“请创建这个负载均衡器来代表这些运行中的容器”。这些负载均衡器也被赋予了易于发现的名称,这样在集群内将不同的服务链接在一起就很容易了。

Kubernetes 还有执行零宕机推出的对象,以及管理配置、持久卷、秘密等的对象。下面的章节将详细介绍 Kubernetes API 中实现所有这些功能的具体对象。

Kubernetes API

Kubernetes API 是一个基于 HTTP 和 JSON 的 RESTful API,由 API Server 提供。Kubernetes 中的所有组件都是通过 API 进行通信的。这个架构在第 3 章会有更详细的介绍。作为一个开源项目,Kubernetes API 总是在不断发展,但核心对象已经稳定了多年,而且 Kubernetes 社区提供了强大的废弃政策,确保开发者和操作者不必因为系统的每次修订而改变他们正在做的事情。Kubernetes 为 API 提供了一个 OpenAPI 规范,以及大量的各种语言的客户端库

基本对象

Pod

Pod 是 Kubernetes 集群中调度的原子单位。一个 Pod 是由一个或多个正在运行的容器的集合组成。当我们说一个 Pod 是原子的时候,我们的意思是一个 Pod 中的所有容器都能保证落在集群中的同一台机器上。Pod 之间也会共享很多资源。例如,它们都共享相同的网络命名空间,这意味着 Pod 中的每个容器都可以在 localhost 上看到 Pod 中的其他容器。Pod 还共享进程和进程间的通信命名空间,这样不同的容器可以使用共享内存和信令等工具来协调 Pod 中不同进程之间的关系。

这种紧密的分组意味着 Pod 非常适合其容器之间的共生关系,例如主服务容器和后台数据加载容器。一般来说,保持容器映像的分离,可以让不同团队更敏捷地拥有或重用容器映像,但在运行时将它们集中在一个 Pod 中,可以让它们合作运行。

当人们第一次接触 Kubernetes 中的 Pod 时,有时会产生错误的假设。例如,用户看到 Pod 可能会想:“啊,是的,一个前端和一个数据库服务器组成了一个 Pod”。但这通常是错误的粒度水平。要知道为什么,考虑到 Pod 也是扩展和复制的单位,这意味着,如果你把你的前端和你的数据库分组在同一个容器中,你将以与你复制你的前端一样的速度复制你的数据库。你想用这种方式来做事情是不太可能的。

容器也会做一些事情来保持你的应用程序运行。如果容器中的进程崩溃,Kubernetes 会自动重新启动它。Pod 还可以定义应用级的健康检查,可以提供更丰富的、针对应用的方式来决定是否应该自动重启 Pod。

ReplicaSet

当然,如果你部署容器编排器只是为了运行单个容器,那么你可能会把生活变得过于复杂。一般来说,容器编排的主要原因之一是为了更容易地构建复制的可靠系统。虽然单个容器可能会失败,或者可能无法服务于系统的负载,但将一个应用程序复制出来到多个不同的运行容器上,可以极大地降低你的服务在特定时刻完全失败的概率。另外,水平扩展使你能够根据负载来增长你的应用。在 Kubernetes API 中,这种无状态复制由 ReplicaSet 对象处理。ReplicaSet 确保,对于一个给定的 Pod 定义,系统内存在一定数量的副本。实际的复制由 Kubernetes 控制器管理器处理,它创建的 Pod 对象由 Kubernetes 调度器调度。这些架构的细节将在后面的章节中描述。

:::info ReplicaSet 是一个较新的对象。在其 v1 版本时,Kubernetes 有一个名为 ReplicationController 的 API 对象。由于废弃政策,ReplicationController 在 Kubernetes API 中继续存在,但强烈不鼓励使用。 :::

Service

在你可以使用复制集将你的应用复制出去之后,下一个逻辑目标就是创建一个负载平衡器,将流量分散到这些不同的复制体上。为了实现这个目标,Kubernetes 有一个 Service 对象。一个 Service 代表一个 TCP 或 UDP 负载均衡服务。每一个被创建的 Service,无论是 TCP 还是 UDP,都会得到三样东西。

  • 它自己的 IP 地址
  • Kubernetes 集群 DNS 中的一个 DNS 条目
  • 负载平衡规则,将流量代理到实现服务的 Pod 上

当一个服务被创建时,它被分配了一个固定的 IP 地址,这个 IP 地址是虚拟的 — 它不与网络上的任何接口相对应。相反,它被编入网络结构中作为一个负载均衡的 IP 地址。当数据包被发送到该 IP 时,它们会被负载均衡到一组实现服务的 Pod 上。执行的负载均衡可以是循环的,也可以是确定的,基于源和目的 IP 地址元组。

给定这个固定的 IP 地址,就会在 Kubernetes 集群的 DNS 服务器中编程一个 DNS 名称。这个 DNS 地址提供了一个语义名称(例如 frontend),这个名称与 Kubernetes 服务对象的名称相同,它使集群中的其他容器能够发现服务负载均衡器的 IP 地址。

最后,Service 负载均衡被编程到 Kubernetes 集群的网络结构中,这样任何试图与 Service IP 地址对话的容器都会被正确地负载均衡到相应的 Pod 上。网络结构的这种编程是动态的,因此,随着 Pod 因故障或 ReplicaSet 的扩展而来来去去,负载均衡器会不断地重新编程以匹配集群的当前状态。这意味着客户可以依靠连接到服务 IP 地址的连接总是解析到实现该服务的 Pod。

持久化存储和配置

在初步探索 Kubernetes 之后,一个常见的问题是:“我的文件怎么办?” 随着所有这些容器在集群内来来去去,落在不同的机器上,你很难理解应该如何管理你想与容器相关联的文件和存储。幸运的是,Kubernetes 提供了几个不同的 API 对象来帮助你管理文件。

Kubernetes 中引入的第一个存储概念是 Volume,它实际上是 Pod API 的一部分。在一个 Pod 中,你可以定义一组 Volume。每个 Volume 可以是大量不同类型中的一种。目前,你可以创建 10 多种不同类型的 Volumes,包括 NFS、iSCSI、gitRepo、基于云存储的 Volumes 等。

:::info 虽然 Volume 接口最初是通过在 Kubernetes 内部编写代码来实现扩展性的一个点,但不同 Volume 类型的爆炸性增长最终表明这种模式是多么的不可持续。现在,新的 Volume 类型是在 Kubernetes 代码之外开发的,使用的是独立于 Kubernetes 的存储接口 Container Storage Interface(CSI)。 :::

当您将卷添加到您的 Pod 时,您可以选择将其挂载到每个运行容器的任意位置。这使您的运行容器能够访问卷内的存储。不同的容器可以将这些卷挂载在不同的位置,也可以完全忽略卷。

除了基本文件外,还有几种类型的 Kubernetes 对象本身也可以作为 Volume 挂载到你的 Pod 中。其中第一种是 ConfigMap 对象。ConfigMap 代表了一个配置文件的集合。在 Kubernetes 中,你希望对同一个容器镜像有不同的配置。当你将基于 ConfigMap 的 Volume 添加到你的 Pod 中时,ConfigMap 中的文件会显示在你的运行容器的指定目录中。

Kubernetes 使用 Secret 配置类型来处理安全数据,例如数据库密码和证书。在 Volumes 的上下文中,Secret 的工作原理与 ConfigMap 相同。它可以通过 Volume 连接到 Pod 上,并安装到运行容器中使用。

随着时间的推移,使用 Volumes 部署应用程序时发现,Volumes 与 Pod 的紧密绑定实际上是有问题的。例如,当创建一个复制的容器时(通过ReplicaSet),所有的复制体必须使用相同的 Volume。在很多情况下,这是可以接受的,但在某些情况下,你会希望每个复制体使用不同的卷。此外,指定精确的卷类型(例如,Azure 磁盘持久化卷)将您的 Pod 定义绑定到特定环境(在本例中,Microsoft Azure 云),但通常希望拥有一个请求通用存储类型的 Pod 定义(例如,10G 的网络存储),而不指定提供商。为了实现这一点,Kubernetes 引入了 PersistentVolumes 和 PersistentVolumeClaim 的概念。不直接将 Volume 绑定到 Pod 中,而是将 PersistentVolume 创建为一个单独的对象。然后,这个对象通过 PersistentVolumeClaim 向一个特定的 Pod 提出申请,最后通过这个申请挂载到 Pod 中。乍一看,这似乎过于复杂,但 Volume 和 Pod 的抽象化使得前面两个用例所要求的可移植性和自动创建卷的功能得以实现。

使用命名空间和标签组织集群

Kubernetes API 使得在系统中创建大量的对象变得相当容易,但这样的对象集合很容易使管理集群成为一场噩梦。幸运的是,Kubernetes 也有很多对象可以让你更容易管理、查询和推理集群中的对象。

命名空间

组织集群的第一个对象是 Namespace。你可以把 Namespace 看作是类似于 Kubernetes API 对象的文件夹。Namespaces 提供了包含集群中大多数其他对象的目录。命名空间还可以为基于角色的访问控制(RBAC)规则提供一个范围。像文件夹一样,当你删除一个命名空间时,其中的所有对象也会被销毁,所以要小心每个 Kubernetes 集群都有一个名为 default 的内置 Namespace,大多数 Kubernetes 的安装还包括一个名为 kube-system 的 Namespace,集群管理容器就是在这里创建的。

:::info Kubernetes 对象根据是否可以放在 Namespace 中,分为 namespaced 对象和非 namespaced 对象。大多数常见的 Kubernetes API 对象都是 namespaced 对象。但是一些适用于整个集群的对象(比如 Namespace 对象本身,或者集群级的 RBAC),就不是 namespaced 对象。 :::

除了组织 Kubernetes 对象,Namespace 还被放入为 Service 创建的 DNS 名称和提供给容器的 DNS 搜索路径。一个 Service 的完整 DNS 名称是类似 my-service.my-namespace.svc.cluster.local 这样的,这意味着两个不同的 Service 在不同的 Namespace 中最终会有不同的完全合格域名(FQDNs)。此外,每个容器的 DNS 搜索路径都包括 Namespace,因此,对于 foo Namespace 中的容器,frontend 的 DNS 查找将被翻译成 frontend.foo.svc.cluster.internal,而对于 bar Namespace 中的容器,frontend.bar.svc.cluster.local

标签及标签查询

Kubernetes API 中的每个对象都可以有一组任意的标签与之关联。标签是有助于识别对象的字符串键值对。例如,一个标签可能是 role: frontend,这表明该对象是一个前端。这些标签可以用来查询和过滤 API 中的对象。例如,你可以请求 API 服务器为你提供一个标签角色为后端的所有 Pod 的列表。这些请求被称为标签查询或标签选择器。Kubernetes API 中的许多对象使用标签选择器作为识别它们适用的对象集的方式。例如,一个 Pod 可以有一个节点选择器,它标识了该 Pod 可在其上运行的节点集(例如带有 GPU 的节点)。同样,一个服务有一个 Pod 选择器,它标识了该服务应该负载平衡流量的 Pod 集。标签和标签选择器是 Kubernetes 将其对象松散耦合在一起的基本方式。

注释

并不是每一个你想分配给 API 对象的元数据值都是识别信息。有些信息只是关于对象本身的注释。因此每个 Kubernetes API 对象也可以有任意的注释。这些注释可能包括诸如显示在对象旁边的图标,或者改变系统解释对象的方式的修饰符。

通常情况下,Kubernetes 中的实验性或特定于厂商的功能最初是使用注释来实现的,因为它们不是正式的 API 规范的一部分。在这些情况下,注解本身应该带有该功能稳定性的一些概念(例如,beta.kubernetes.io/activate-some-beta-feature)。

进阶概念

当然,简单的、复制的、负载均衡的服务并不是你可能想要在容器中部署的唯一风格的应用。而且,随着 Kubernetes 的发展,它还增加了新的 API 对象,以更好地适应更专业的用例,包括改进的推出、基于 HTTP 的负载均衡和路由以及有状态的工作负载。

Deployment

虽然 ReplicaSet 是运行同一容器映像的多个不同副本的基础,但应用程序不是静态实体。它们会随着开发人员添加新功能和修复错误而不断发展。这意味着向服务推出新代码的行为与复制它以可靠地处理负载一样是一个重要的功能。

部署对象被添加到 Kubernetes API 中,以代表这种从一个版本到另一个版本的安全推出。一个 Deployment 可以持有指向多个 ReplicaSet 的指针,(例如,v1v2),它可以控制从一个 ReplicaSet 到另一个 ReplicaSet 的缓慢和安全迁移。

要了解部署的工作原理,请想象一下,您有一个应用程序,该应用程序部署在名为 rs-v1 的 ReplicaSet 中的三个副本上。当您要求部署推出新映像(v2)时,部署会创建一个带有单个副本的新 ReplicaSet (rs-v2)。部署会等待该副本变得健康,当它变得健康时,部署会将 rs-v1 中的副本数量减少到两个。然后,它将 rs-v2 中的副本数量也增加到两个,并等待 v2 的第二个副本变得健康。这个过程一直持续到 v1 的副本数不多,v2 的健康副本数为三个。

:::info 部署具有大量不同的配置,可以调整这些配置,为应用程序的特定细节提供安全的推出。事实上,在大多数现代集群中,用户只使用部署对象,而不直接管理 ReplicaSet。 :::

Ingress

虽然服务对象提供了一种很好的方式来做简单的 TCP 级负载均衡,但并没有提供一种应用级的方式来做负载均衡和路由。事实上,用户使用容器和 Kubernetes 部署的大多数应用都是基于 HTTP 的 Web 应用。这些由一个了解 HTTP 的负载均衡器提供更好的服务。为了满足这些需求,Kubernetes 中加入了 Ingress API。Ingress 代表一个基于路径和主机的 HTTP 负载均衡器和路由器。当你创建一个 Ingress 对象时,它就像 Service 一样接收一个虚拟 IP 地址,但 Ingress 不是 Service IP 地址和一组 Pod 之间的一对一关系,而是可以使用 HTTP 请求的内容将请求路由到不同的 Service。

为了更清楚地了解 Ingress 的工作原理,想象一下,我们有两个 Kubernetes 服务,分别命名为 “foo” 和 “bar”。每个服务都有自己的 IP 地址,但我们真正想把它们作为同一主机的一部分暴露在互联网上。例如,foo.company.combar.company.com。我们可以通过创建一个 Ingress 对象,并将其 IP 地址与 foo.company.combar.company.com 的 DNS 名关联起来来实现。在 Ingress 对象中,我们还将这两个不同的主机名映射到两个不同的 Kubernetes 服务上。这样,当收到 https://foo.company.com 的请求时,就会被路由到集群中的 “foo” 服务,同样,https://bar.company.com 也会被路由到 “foo” 服务。使用 Ingress,路由可以基于主机或路径,也可以两者兼而有之,所以 https://company.com/bar 也可以路由到 “bar” 服务。

:::info Ingress API 是 Kubernetes 中最解耦、最灵活的 API 之一。默认情况下,虽然 Kubernetes 会存储 Ingress 对象,但在创建 Ingress 对象时不会发生任何事情。相反,当 Ingress 对象被创建时,你还需要在集群中运行一个 Ingress 控制器来采取相应的行动。最流行的 Ingress 控制器之一是 Nginx,但也有许多实现使用其他 HTTP 负载均衡器或使用云或物理负载均衡器 API。 :::

StatefulSet

当水平复制并作为相同的克隆处理时,大多数应用程序都能正常运行。每个复制体都没有独立于任何其他复制体的唯一身份。对于表示这样的应用,Kubernetes ReplicaSet 是一个完美的对象。然而,一些应用,特别是有状态的存储工作负载或碎片化应用,需要在应用中的复制体之间进行更多的区分。虽然可以在 ReplicaSet 的基础上,在应用层添加这种区分,但这样做很复杂,容易出错,而且对最终用户来说也很重复。

为了解决这个问题,Kubernetes 最近引入了 StatefulSet 作为 ReplicaSet 的补充,但适用于更多的有状态工作负载。与 ReplicaSet 一样,StatefulSet 也会创建多个运行在 Kubernetes 集群中的同一个容器镜像的实例,但容器的创建和销毁方式更加确定,每个容器的名称也是如此。

在 ReplicaSe t中,每个被复制的 Pod 都会收到一个涉及随机哈希的名称(例如,frontend-14a2),ReplicaSet 中没有排序的概念。与此相反,对于 StatefulSet,每个复制体都会收到一个单调递增的索引(例如,backed-0backend-1,等等)。

此外,StatefulSet 保证在创建副本一之前,副本零将被创建并成为健康的,以此类推。当结合在一起时,这意味着应用程序可以很容易地使用初始副本(例如,backend-0)作为引导主程序来引导自己。所有后续的副本都可以依赖 backend-0 必须存在的事实。同样,当复制从 StatefulSet中移除时,它们会在最高索引处被移除。如果一个 StatefulSet 从 5 个副本缩减到 4 个副本,那么保证第五个副本就是被移除的那一个。

此外,StatefulSet 会接收 DNS 名称,这样除了可以访问完整的 StatefulSet 外,还可以直接访问每个副本。这使得客户可以轻松地针对分片服务中的特定分片。

批处理工作负载

除了有状态的工作负载外,另一类专门的工作负载是批处理或一次性工作负载。与前面讨论的工作负载相比,这些工作负载并不是不断地服务于流量。相反,它们会执行一些计算,然后在计算完成后被销毁。

在 Kubernetes 中,一个 Job 代表一组需要运行的任务。与 ReplicaSet 和 StatefulSet 一样,Job 的操作方式是创建 Pod,通过运行容器镜像来执行工作。然而,与 ReplicaSet 和 StatefulSet 不同的是,Job 创建的 Pod 只会运行,直到它们完成并退出。一个 Job 包含了它所创建的 Pod 的定义、Job 应该运行的次数以及并行创建的 Pod 的最大数量。例如,一个重复次数为 100 次,最大并行次数为 10 的 Job 将同时运行 10 个 Pod,在旧的 Pod 完成时创建新的 Pod,直到容器有 100 次成功执行。

CronJobs 通过在 Job 对象上添加一个日程表,建立在 Job 对象之上。一个 CronJob 包含了你要创建的 Job 对象的定义,以及该 Job 应该被创建的时间表。

集群守护服务

当人们迁移到 Kubernetes 时,最常见的问题之一是,“我如何运行我的节点代理?” 代理的任务包括入侵检测、日志和监控等。很多人尝试非 Kubernetes 的方法来启用这些代理,比如添加新的 systemd 单元文件或初始化脚本。虽然这些方法可以工作,但它们有几个缺点。第一是 Kubernetes 在核算集群上使用的资源时,并不包括代理的活动。第二是容器镜像和 Kubernetes 用于健康检查、监控等的 API 无法应用到这些代理上。幸运的是,Kubernetes 为用户提供了 DaemonSet API,可以在集群上安装此类代理。一个 DaemonSet 提供了一个 Pod 的模板,它应该在每台机器上运行。当创建一个 DaemonSet 时,Kubernetes 会确保这个 Pod 运行在集群中的每个节点上。如果在以后的某个时刻,增加了一个新的节点,Kubernetes 也会在该节点上创建一个 Pod。虽然默认情况下,Kubernetes 会在集群中的每个节点上放置一个 Pod,但 DaemonSet 也可以提供一个节点选择器标签查询,Kubernetes 只会将该 DaemonSet 的 Pod 放置到符合该标签查询的节点上。

总结

本书的目标是教你如何成功管理 Kubernetes 集群。但要成功地管理任何服务,你需要了解该服务为最终用户提供了什么,以及用户如何使用该服务。在这种情况下,我们正在向开发人员提供一个可靠的 Kubernetes API。开发者则使用这个 API 来成功地构建和部署他们的应用程序。了解 Kubernetes API 的各个部分将使您能够了解您的最终用户,并更好地管理他们日常活动所依赖的系统。本章实际上是对长得多的书中所涉及的主题的简略总结,比如《Kubernetes: Up and Running》,以及 Kubernetes 核心网站上的内容。强烈鼓励有兴趣更深入了解 Kubernetes API 的读者从这些资源中了解更多信息。