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