Kubernetes 是一个功能强大的开源系统,最初由 Google 开发,并得到云原生计算基金会 (CNCF) 的支持,用于在集群环境中管理容器化应用程序。它旨在提供更好的方法来管理跨不同基础设施的相关分布式组件和服务。要了解有关 Kubernetes 的更多信息,请浏览下面的指南。如果您正在寻找托管 Kubernetes 托管服务,查看我们为增长而构建的简单、托管 Kubernetes 服务.
在本指南中,我们将讨论什么是 Kubernetes,以及 Kubernetes 的一些基本概念。我们将讨论系统的架构、它解决的问题以及它用于处理容器化部署和扩展的模型。
库伯内斯从根本上讲,它是一个用于跨机器集群运行和协调容器化应用程序的系统。它是一个旨在使用提供可预测性、可扩展性和高可用性的方法来完全管理容器化应用程序和服务的生命周期的平台。
作为 Kubernetes 用户,您可以定义应用程序应如何运行以及它们应如何与其他应用程序或外部世界交互。您可以向上或向下扩展服务、执行平稳的滚动更新以及在不同版本的应用程序之间切换流量以测试功能或回滚有问题的部署。 Kubernetes 提供接口和可组合平台原语,使您能够定义和管理具有高度灵活性、强大功能和可靠性的应用程序。
要了解 Kubernetes 如何提供这些功能,了解它的高层次设计和组织方式会很有帮助。 Kubernetes 可以被视为一个分层构建的系统,每个较高层都抽象了较低级别中的复杂性。
在其基础上,Kubernetes 使用共享网络将各个物理机或虚拟机聚集到一个集群中,以便在每个服务器(无论是物理机还是虚拟机)之间进行通信。该 Kubernetes 集群是配置所有 Kubernetes 组件、功能和工作负载的物理平台。
Kubernetes 集群中的每台机器在 Kubernetes 生态系统中都被赋予了一个角色。一台服务器(或高可用性部署中的一小组)充当master服务器。该服务器充当集群的网关和大脑,通过向用户和客户端公开 Kubernetes API、检查其他服务器的运行状况、决定如何最好地分割和分配工作(称为“调度”)以及编排其他组件之间的通信(有时称为容器编排)。主服务器充当与集群的主要联系点,负责 Kubernetes 提供的大部分集中式逻辑。
集群中的其他机器被指定为nodes:负责使用本地和外部资源接受和运行工作负载的服务器。为了帮助实现隔离、管理和灵活性,Kubernetes 在以下位置运行应用程序和服务:容器,所以每个节点都需要配备一个容器运行时(如Docker或rkt)。节点从主服务器接收工作指令,并相应地创建或销毁容器,调整网络规则以适当地路由和转发流量。
如上所述,应用程序和服务本身在容器内的集群上运行。底层组件确保应用程序的所需状态与集群的实际状态相匹配。用户通过直接与主 Kubernetes API 服务器通信或与客户端和库通信来与集群进行交互。要启动应用程序或服务,需要以 JSON 或 YAML 形式提交声明性计划,定义要创建的内容以及如何管理它。然后,主服务器接受该计划,并通过检查系统的需求和当前状态来找出如何在基础设施上运行它。这组按照指定计划运行的用户定义的应用程序代表了 Kubernetes 的最后一层。
如上所述,主服务器充当 Kubernetes 集群的主要控制平面。它作为管理员和用户的主要联系点,并且还为相对不复杂的工作节点提供了许多集群范围的系统。总体而言,主服务器上的组件协同工作来接受用户请求,确定调度工作负载容器的最佳方式,对客户端和节点进行身份验证,调整集群范围的网络,以及管理扩展和运行状况检查职责。
这些组件可以安装在单台计算机上,也可以分布在多个服务器上。在本节中,我们将了解与 Kubernetes 集群主服务器相关的每个单独组件。
Kubernetes 运行所需的基本组件之一是全局可用的配置存储。这etcd project由 CoreOS(操作系统)团队开发的,是一个轻量级的分布式键值存储,可以配置为跨多个节点。
Kubernetes 使用etcd
存储可由集群中每个节点访问的配置数据。这可用于服务发现,并可帮助组件根据最新信息配置或重新配置自身。它还通过领导者选举和分布式锁定等功能帮助维护集群状态。通过提供简单的 HTTP/JSON API,用于设置或检索值的界面非常简单。
与控制平面中的大多数其他组件一样,etcd
可以在单个主服务器上配置,或者在生产场景中分布在多台机器上。唯一的要求是每台 Kubernetes 机器都可以通过网络访问它。
最重要的主服务之一是 API 服务器。这是整个集群的主要管理点,因为它允许用户配置 Kubernetes 的工作负载和组织单位。它还负责确保etcd
存储和部署的容器的服务细节是一致的。它充当各个组件之间的桥梁,以维护集群健康状况并传播信息和命令。
API 服务器实现了 RESTful 接口,这意味着许多不同的工具和库可以轻松与其通信。名为 kubectl 的客户端可作为从本地计算机与 Kubernetes 集群交互的默认方法。
控制器管理器是一个具有许多职责的通用服务。它主要管理不同的控制器,以调节集群的状态、管理工作负载生命周期并执行例行任务。例如,复制控制器确保为 Pod 定义的副本(相同副本)数量与集群上当前部署的数量相匹配。这些操作的详细信息被写入etcd
,控制器管理器通过 API 服务器监视更改。
当发现变化时,控制器读取新信息并执行满足所需状态的过程。这可能涉及向上或向下扩展应用程序、调整端点等。
真正将工作负载分配给集群中特定节点的进程是调度程序。该服务读取工作负载的操作要求,分析当前的基础设施环境,并将工作放置在可接受的一个或多个节点上。
调度程序负责跟踪每个主机上的可用容量,以确保调度的工作负载不会超出可用资源。调度程序必须了解总容量以及已分配给每台服务器上现有工作负载的资源。
Kubernetes 可以部署在许多不同的环境中,并且可以与各种基础设施提供商交互,以了解和管理集群中资源的状态。虽然 Kubernetes 使用可附加存储和负载均衡器等资源的通用表示形式,但它需要一种方法将这些资源映射到非同质云提供商提供的实际资源。
云控制器管理器充当粘合剂,允许 Kubernetes 与具有不同功能、特性和 API 的提供商进行交互,同时在内部维护相对通用的构造。这使得 Kubernetes 能够根据从云提供商收集的信息更新其状态信息,根据系统需要的变化调整云资源,并创建和使用额外的云服务来满足提交到集群的工作要求。
在 Kubernetes 中,通过运行容器来执行工作的服务器被称为nodes。节点服务器有一些与主组件通信、配置容器网络以及运行分配给它们的实际工作负载所必需的要求。
每个节点必须拥有的第一个组件是容器运行时。通常,通过安装和运行来满足此要求Docker,但替代品如rkt and runc也可用。
容器运行时负责启动和管理容器、封装在相对隔离但轻量级运行环境中的应用程序。集群上的每个工作单元在其基本级别上都是作为必须部署的一个或多个容器来实现的。每个节点上的容器运行时是最终运行提交到集群的工作负载中定义的容器的组件。
每个节点与集群组的主要联系点是一个名为kubelet。该服务负责与控制平面服务之间转发信息,并与控制平面服务进行交互。etcd
存储以读取配置详细信息或写入新值。
The kubelet
服务与主组件通信以向集群进行身份验证并接收命令和工作。工作以以下形式收到:manifest它定义了工作负载和操作参数。这kubelet
然后,进程承担维护节点服务器上的工作状态的责任。它控制容器运行时以根据需要启动或销毁容器。
为了管理各个主机子网划分并使服务可供其他组件使用,需要使用一个名为的小型代理服务kube代理在每个节点服务器上运行。此进程将请求转发到正确的容器,可以进行原始负载平衡,并且通常负责确保网络环境可预测且可访问,但在适当的情况下进行隔离。
虽然容器是用于部署容器化应用程序的底层机制,但 Kubernetes 在容器接口上使用额外的抽象层来提供扩展、弹性和生命周期管理功能。用户不是直接管理容器,而是定义由 Kubernetes 对象模型提供的各种原语组成的实例并与之交互。我们将在下面介绍可用于定义这些工作负载的不同类型的对象。
A pod是 Kubernetes 处理的最基本的单元。容器本身并未分配给主机。相反,一个或多个紧密耦合的容器被封装在一个称为 Pod 的对象中。
Pod 通常表示应作为单个应用程序进行控制的容器。 Pod 由紧密运行的容器组成,共享生命周期,并且应始终调度在同一节点上。它们完全作为一个单元进行管理,并共享其环境、卷和 IP 空间。尽管 Pod 是容器化实现的,但您通常应该将 Pod 视为单个整体应用程序,以便最好地概念化集群将如何管理 Pod 的资源和调度。
通常,Pod 由一个满足工作负载一般用途的主容器和一些可选的帮助密切相关任务的辅助容器组成。这些程序受益于在自己的容器中运行和管理,但与主应用程序紧密相关。例如,一个 Pod 可能有一个运行主应用程序服务器的容器和一个辅助容器,当在外部存储库中检测到更改时,该容器会将文件拉取到共享文件系统。通常不鼓励在 Pod 级别上进行水平扩展,因为还有其他更适合该任务的更高级别对象。
一般来说,用户不应该自己管理 Pod,因为它们不提供应用程序中通常需要的一些功能(例如复杂的生命周期管理和扩展)。相反,我们鼓励用户使用更高级别的对象,这些对象使用 Pod 或 Pod 模板作为基本组件,但实现附加功能。
通常,在使用 Kubernetes 时,您不是使用单个 Pod,而是管理一组相同的复制 Pod。它们是根据 Pod 模板创建的,可以通过称为复制控制器和复制集的控制器进行水平扩展。
A 复制控制器是一个对象,它定义 Pod 模板和控制参数,以通过增加或减少运行副本的数量来水平扩展 Pod 的相同副本。这是在 Kubernetes 内分配负载并提高本机可用性的简单方法。复制控制器知道如何根据需要创建新的 Pod,因为复制控制器配置中嵌入了与 Pod 定义非常相似的模板。
复制控制器负责确保集群中部署的 Pod 数量与其配置中的 Pod 数量相匹配。如果 Pod 或底层主机发生故障,控制器将启动新的 Pod 进行补偿。如果控制器配置中的副本数量发生变化,控制器将启动或终止容器以匹配所需的数量。复制控制器还可以执行滚动更新,将一组 Pod 逐个滚动到新版本,从而最大限度地减少对应用程序可用性的影响。
复制集是复制控制器设计的迭代,在控制器如何识别其要管理的 Pod 方面具有更大的灵活性。复制集开始取代复制控制器,因为它们具有更强大的副本选择功能,但它们无法像复制控制器那样进行滚动更新以将后端循环到新版本。相反,复制集应该在提供该功能的附加、更高级别的单元内部使用。
与 Pod 一样,复制控制器和复制集很少是您直接使用的单元。虽然它们基于 Pod 设计来添加水平扩展和可靠性保证,但它们缺乏更复杂对象中的一些细粒度生命周期管理功能。
部署是直接创建和管理的最常见的工作负载之一。部署使用复制集作为构建块,为组合添加灵活的生命周期管理功能。
虽然使用复制集构建的部署可能看起来重复了复制控制器提供的功能,但部署解决了滚动更新实现中存在的许多痛点。当使用复制控制器更新应用程序时,用户需要提交新的复制控制器的计划来替换当前的控制器。使用复制控制器时,跟踪历史记录、更新期间从网络故障中恢复以及回滚错误更改等任务要么很困难,要么由用户负责。
部署是一个高级对象,旨在简化复制 Pod 的生命周期管理。通过更改配置可以轻松修改部署,Kubernetes 将调整副本集、管理不同应用程序版本之间的转换,并可选择自动维护事件历史记录和撤消功能。由于这些功能,部署可能是您最常使用的 Kubernetes 对象类型。
有状态集是专门的 Pod 控制器,提供排序和唯一性保证。主要是当您有与部署顺序、持久数据或稳定网络相关的特殊要求时,它们用于进行更细粒度的控制。例如,有状态集通常与面向数据的应用程序(例如数据库)相关联,即使重新安排到新节点,也需要访问相同的卷。
有状态集通过为每个 Pod 创建一个基于数字的唯一名称来提供稳定的网络标识符,即使 Pod 需要移动到另一个节点,该名称也将持续存在。同样,当需要重新调度时,持久存储卷可以通过 Pod 进行传输。即使在删除 Pod 后,卷仍会保留,以防止意外数据丢失。
在部署或调整规模时,有状态集根据其名称中的编号标识符执行操作。这可以提供更好的可预测性和对执行顺序的控制,这在某些情况下很有用。
守护进程集是 Pod 控制器的另一种特殊形式,它在集群中的每个节点(或子集,如果指定)上运行 Pod 的副本。当部署有助于执行维护并为 Kubernetes 节点本身提供服务的 Pod 时,这通常很有用。
例如,收集和转发日志、聚合指标以及运行增强节点本身功能的服务都是守护进程集的热门候选。由于守护进程集通常提供基本服务并且整个队列都需要它们,因此它们可以绕过阻止其他控制器将 Pod 分配给某些主机的 Pod 调度限制。例如,由于其独特的职责,主服务器经常被配置为无法进行正常的 Pod 调度,但守护程序集能够逐个 Pod 地覆盖限制,以确保基本服务正在运行。
到目前为止,我们描述的工作负载都假设有一个长期运行的、类似服务的生命周期。 Kubernetes 使用称为jobs提供更加基于任务的工作流程,其中正在运行的容器在完成工作后预计会在一段时间后成功退出。如果您需要执行一次性或批处理而不是运行连续的服务,则作业非常有用。
建立在就业基础上的是计划任务。和传统的一样cron
Linux 和类 Unix 系统上按计划执行脚本的守护进程,Kubernetes 中的 cron 作业提供了一个使用调度组件运行作业的接口。 Cron 作业可用于安排作业在将来执行或定期重复执行。 Kubernetes cron 作业基本上是经典 cron 行为的重新实现,使用集群作为平台而不是单个操作系统。
除了可以在集群上运行的工作负载之外,Kubernetes 还提供了许多其他抽象来帮助您管理应用程序、控制网络和实现持久性。我们将在这里讨论一些更常见的示例。
到目前为止,我们一直在传统的类 Unix 意义上使用术语“服务”:表示长期运行的进程,通常与网络连接,能够响应请求。然而,在 Kubernetes 中,service是充当基本内部负载均衡器和 pod 大使的组件。服务将执行相同功能的 Pod 逻辑集合组合在一起,将它们呈现为单个实体。
这允许您部署一个可以跟踪并路由到特定类型的所有后端容器的服务。内部消费者只需要了解服务提供的稳定端点。同时,服务抽象允许您根据需要扩展或替换后端工作单元。无论其路由到的 Pod 发生什么变化,服务的 IP 地址都保持稳定。通过部署服务,您可以轻松获得可发现性并简化容器设计。
每当您需要向另一个应用程序或外部使用者提供对一个或多个 Pod 的访问时,您都应该配置一项服务。例如,如果您有一组运行可通过互联网访问的 Web 服务器的 pod,则服务将提供必要的抽象。同样,如果您的 Web 服务器需要存储和检索数据,您可能需要配置内部服务以允许它们访问您的数据库 Pod。
尽管默认情况下,服务只能使用内部可路由的 IP 地址来使用,但可以通过选择多种策略之一来使它们在集群外部可用。这NodePort配置的工作原理是在每个节点的外部网络接口上打开一个静态端口。到外部端口的流量将使用内部集群 IP 服务自动路由到适当的 Pod。
或者,负载均衡器服务类型创建一个外部负载均衡器,以使用云提供商的 Kubernetes 负载均衡器集成路由到服务。云控制器管理器将创建适当的资源并使用内部服务服务地址对其进行配置。
在许多容器化环境中,可靠地共享数据并保证容器重新启动之间的可用性是一个挑战。容器运行时通常提供某种机制将存储附加到容器,该存储在容器的生命周期之外持续存在,但实现通常缺乏灵活性。
为了解决这个问题,Kubernetes 使用自己的volumes允许 pod 内的所有容器共享数据并保持可用直到 pod 终止的抽象。这意味着紧密耦合的 Pod 可以轻松共享文件,而无需复杂的外部机制。 Pod 内的容器故障不会影响对共享文件的访问。一旦 Pod 终止,共享卷就会被销毁,因此对于真正持久化的数据来说,这并不是一个好的解决方案。
持久卷是一种用于抽象更强大的存储的机制,该存储与 Pod 生命周期无关。相反,它们允许管理员为集群配置存储资源,用户可以为他们正在运行的 Pod 请求和声明这些资源。一旦 pod 使用完持久卷,该卷的回收策略将决定是否保留该卷,直到手动删除或立即与数据一起删除。持久数据可用于防止基于节点的故障,并分配比本地可用的存储量更大的存储量。
与其他概念相关但在其他概念之外的 Kubernetes 组织抽象是标签。 AlabelKubernetes 中的语义标签可以附加到 Kubernetes 对象上,将它们标记为组的一部分。然后,当针对不同实例进行管理或路由时,可以选择这些。例如,每个基于控制器的对象都使用标签来标识它们应该操作的 Pod。服务使用标签来了解它们应将请求路由到的后端 Pod。
标签以简单的键值对形式给出。每个单元可以有多个标签,但每个单元的每个键只能有一个条目。通常,“名称”键用作通用标识符,但您还可以根据其他标准(例如开发阶段、公共可访问性、应用程序版本等)对对象进行分类。
注释是一种类似的机制,允许您将任意键值信息附加到对象。虽然标签应用于语义信息,有助于将 pod 与选择标准相匹配,但注释的格式更加自由,并且可以包含较少的结构化数据。一般来说,注释是一种向对象添加丰富元数据的方法,这对选择目的没有帮助。
Kubernetes 是一个令人兴奋的项目,它允许用户在高度抽象的平台上运行可扩展、高度可用的容器化工作负载。虽然 Kubernetes 的架构和内部组件集乍一看似乎令人畏惧,但它们的强大功能、灵活性和强大的功能集在开源世界和云原生开发中是无与伦比的。通过了解基本构建块如何组合在一起,您可以开始设计系统,充分利用平台的功能来大规模运行和管理工作负载,从而构建令人惊叹的云原生应用程序。