原文:https://www.jeremyjordan.me/kubernetes/(博客园团队推荐的)node
这篇博客文章将对Kubernetes进行介绍,以便您了解该工具背后的动机,含义以及使用方式。在后续文章中,我将讨论如何使用更具体的(数据科学)示例来利用Kubernetes加强数据科学工做负载。可是,这有助于您首先了解基本原理-这是本文的重点。设计模式
先决条件:我将假设您熟悉Docker等容器技术。若是您没有构建和运行容器映像的经验,建议您先熟悉以后,在继续阅读本文服务器
这是咱们将在本文中讨论的内容。网络
Kubernetes一般被描述为容器编排平台。为了理解确切的含义,它有助于从新审视容器的用途,缺乏的内容以及Kubernetes如何填补这一空白。架构
注意:您还将看到Kubernetes其简称numeronym,K8S。这意味着同一件事,只是更容易键入。app
为何咱们喜欢容器?容器提供了一种轻量级的机制来隔离应用程序的环境。对于给定的应用程序,咱们能够指定要安装的系统配置和库,而没必要担忧与可能在同一台物理计算机上运行的其余应用程序产生冲突。咱们将每一个应用程序封装为容器映像(container image)能够在任何机器上可靠地执行*(只要它可以运行容器映像),从而为咱们提供了可移植性,以实现从开发到部署的平稳过渡。此外,因为每一个应用程序都是独立的,无需担忧环境冲突,所以将多个工做负载放置在同一台物理计算机上并实现更高的资源(内存和CPU)利用率更加容易-最终下降了成本。框架
缺乏的东西?可是,若是您的容器死了怎么办?甚至更糟的是,若是运行您的容器的计算机发生故障,会发生什么?容器没有提供容错(fault tolerance)解决方案。或者,若是您有多个须要通讯的容器,该如何在容器之间实现联网?当您旋转单个容器时,此变化如何?容器网络(networking )很容易变成一团糟。最后,假设您的生产环境由多台机器组成-您如何决定使用哪台机器来运行容器?机器学习
Kubernetes做为容器编排平台。咱们可使用容器编排平台解决上述许多问题。分布式
乐团的负责人拥有音乐表演的愿景,并与音乐家沟通,以协调他们我的的乐器演奏,以实现整体愿景。做为系统的架构师,您的工做只是简单地创做音乐(指定要运行的容器),而后将控制权移交给乐团总监(容器编排平台)以实现该愿景。微服务
容器编排平台管理单个容器的整个生命周期,根据须要扩展和关闭资源。若是某个容器意外关闭,编排平台将经过在其位置启动另外一个容器来做出反应。
最重要的是,编排平台为应用程序之间的通讯提供了一种机制,即便底层的单个容器被建立和销毁也是如此。
最后,在给定(1)一组要运行的容器工做负载和(2)集群上的一组计算机的状况下,容器协调器将检查每一个容器并肯定最佳的计算机来调度该工做负载。要了解为何这颇有价值,请观看Kelsey Hightower(17:47-20:55)使用俄罗斯方块示例游戏来讲明自动化部署和容器编排之间的区别。
如今咱们大体了解了容器编排的动机,让咱们花一些时间来讨论Kubernetes背后的动机设计原则。它有助于理解这些原理,以便您能够按预期使用该工具。
也许Kubernetes中最重要的设计原则是,咱们仅定义系统的指望状态,并让Kubernetes自动化工做以确保系统的实际状态反映这些指望。这使您免于在大多数事物损坏时进行修复的责任;你只需说明你的系统是什么应该看起来像一个理想的状态。Kubernetes将检测到系统的实际状态什么时候不符合这些指望,它将表明您进行干预以解决问题。这使咱们的系统可以自我修复并对问题作出反应,而无需人工干预。
系统的“状态”由一组对象定义。每一个Kubernetes对象具备(1)一个规范在其中提供所指望的状态和(2)的状态反映了对象的当前状态。Kubernetes维护全部对象规范的列表,并不断轮询每一个对象,以确保其状态与规范相等。若是对象无响应,Kubernetes将启动一个新版原本替换它。若是对象的状态偏离了规范,Kubernetes将发出必要的命令以将该对象驱动回到其所需状态。
对于必定的操做规模,有必要将您的应用程序设计为分布式系统。Kubernetes旨在为此类分布式系统提供基础设施层,产生干净的抽象以在一组机器(统称为集群)之上构建应用程序。更具体地说,Kubernetes提供了一个用于与该集群交互的统一界面,所以您没必要担忧与每台机器进行单独通讯。
容器开发一般建议单一关注。结果,开发容器化应用程序很是适合微服务架构设计模式,该模式建议“将软件应用程序设计为可独立部署的服务套件”。
Kubernetes中提供的抽象天然支持分离服务的思想,该服务能够独立缩放和更新。这些服务在逻辑上是分开的,并经过定义良好的API进行通讯。这种逻辑上的分离使团队能够更快地将更改部署到生产中,由于每一个服务均可以在独立的发布周期内运行(前提是他们遵照现有的API合约)。
为了从容器和容器编排中得到最大收益,您应该部署不可变的基础结构。这是否是应该登陆到计算机上的容器以进行更改(例如,更新库),而是应该构建新的容器映像,部署新版本并终止旧版本。在项目的生命周期(开发->测试->生产)中跨环境过渡时,您应该使用相同的容器映像,而且只能修改容器映像外部的配置(例如,经过安装配置文件)。
这一点很是重要,由于容器被设计为短暂的,随时能够被另外一个容器实例替换。若是您的原始容器处于突变状态(例如,手动配置),可是因为运行情况检查失败而被关闭,则在其位置旋转的新容器不会反映这些手动更改,并可能破坏您的应用程序。
当您维护不可变的基础结构时,将应用程序回滚到之前的状态(例如,若是发生错误)也变得更加容易-您能够简单地更新配置以使用较旧的容器映像。
以前,我提到过,咱们经过Kubernetes 对象的集合描述了系统的指望状态。到目前为止,咱们对Kubernetes的讨论还相对抽象和高层次。在本节中,咱们将经过覆盖Kubernetes中可用的基本对象,深刻探讨有关如何在Kubernetes上部署应用程序的更多细节。
可使用YAML或JSON文件定义Kubernetes对象。这些定义对象的文件一般称为清单(manifests)。将这些清单保留在版本控制的存储库中是一个好习惯,该存储库可做为有关集群上正在运行哪些对象的惟一事实来源。
pod对象是Kubernetes的基本构建块,由一个或多个(紧密相关的)的容器,一个共享的网络层,和共享文件系统的卷。与容器相似,Pods被设计为短暂的-不会指望特定的单个POD会长期存在。
一般,您不会在清单中显式建立Pod对象,由于使用更高级的组件来为您管理Pod对象一般更简单。
部署方式
一个部署对象包括由模板和副本数量(模板的多少副本,咱们要运行)定义的pods的集合。您能够为副本数设置特定的值,也可使用单独的Kubernetes资源(例如,水平Pod自动缩放器)根据系统指标(例如CPU利用率)来控制副本数。
注意:Deployment对象的控制器实际上在内部建立了另外一个对象ReplicaSet。可是,这是做为用户从您那里抽象出来的。
虽然您不能依赖任何一个Pod来无限期地运行,可是您能够依靠集群将始终尝试使n个Pod可用的事实(其中n由您指定的副本数定义)。 若是咱们有一个部署的副本数为10的Deployment,而且其中3个Pod因机器故障而崩溃,那么将安排另外3个Pod在群集中的另外一台计算机上运行。所以,Deployment最适合无状态应用程序,在这些应用程序中Pod能够随时更换而不会损坏。
如下YAML文件提供了有关如何定义Deployment对象的带注释的示例。在此示例中,咱们要运行一个容器的10个实例,该实例经过REST接口提供ML模型。
注意:为了让Kubernetes知道此工做负载可能有多计算密集型,咱们还应该在Pod模板规范中提供资源限制。
部署还容许咱们指定当咱们有新版本的容器映像时咱们但愿如何推出更新;这篇博客文章很好地概述了您的不一样选择。若是咱们想覆盖默认值,咱们将strategy
在object下包含一个附加字段spec
。Kubernetes将确保正常关闭运行旧容器映像的Pod并启动运行新容器映像的新Pod。
服务
Kubernetes中的每一个Pod都分配有一个惟一的IP地址,咱们能够用来与之通讯。可是,因为Pod是短暂的,所以很难将流量发送到所需的容器。例如,让咱们考虑上面的“部署”,其中有10个Pod运行一个容器,经过REST为机器学习模型提供服务。若是做为部署的一部分运行的Pod集合能够随时更改,咱们如何与服务器可靠地通讯?这是服务对象输入图片的地方。Kubernetes服务为您提供了一个稳定的端点,即便因为更新,扩展和故障致使确切的基础Pod发生变化,它也能够用于将流量引导到所需的Pod。服务根据标签知道应将流量发送到哪一个Pod (键值对),咱们在Pod元数据中定义。
注意:这篇博客文章很好地解释了如何实际路由流量。
在此示例中,咱们的服务使用标签将流量发送到全部健康的Pod app="ml-model"
。
如下YAML文件提供了一个示例,说明了咱们如何围绕早期的Deployment示例包装Service。
Ingress
尽管“服务”使咱们能够在稳定的终结点后面公开应用程序,但该终结点仅可用于内部群集通讯。若是咱们想将应用程序暴露给集群外部的流量,则须要定义一个Ingress对象。
这种方法的好处在于,您能够选择公开哪些服务。例如,假设除了咱们的机器学习模型服务外,咱们还有一个UI,该UI利用了模型的预测做为大型应用程序的一部分。咱们可能选择仅使UI可用于公共流量,从而阻止用户直接查询服务模型服务。
如下YAML文件为上述示例定义了一个Ingress对象,使UI能够公开访问。
到目前为止,我已经描述过的Kubernetes对象能够组成可靠的,长期运行的服务。相反,当您要执行离散任务时,Job对象颇有用。例如,假设咱们想根据前一天收集的信息天天从新训练模型。天天,咱们都但愿启动一个容器来执行预约义的工做负载(例如train.py
脚本),而后在培训结束时关闭它。乔布斯为咱们提供了作到这一点的能力!若是因为某种缘由咱们的容器在完成脚本以前崩溃了,Kubernetes将经过在其位置启动一个新Pod来完成工做来作出反应。对于Job对象,对象的“所需状态”是做业的完成。
如下YAML定义了一个用于训练机器学习模型的示例Job(假设在中定义了训练代码train.py
)。
注意:此做业规范将仅执行一次训练。若是咱们想天天执行此做业,则能够定义一个CronJob对象。
上面讨论的对象固然不是Kubernetes中可用资源类型的详尽列表。在部署应用程序时,您可能会发现有用的其余一些对象包括:
Volume:用于管理安装在Pod上的目录
Secret:用于存储敏感凭证
NameSpace:用于分隔群集上的资源
ConfigMap:用于指定要做为文件挂载的应用程序配置值
HorizontalPodAutoscaler:用于基于现有Pod的当前资源利用率扩展部署
StatefulSet:与Deployment相似,但适用于须要运行有状态应用程序的状况
怎么样?Kubernetes control plane(控制平面)。
至此,您可能想知道Kubernetes如何可以采用咱们全部的对象规范并在集群上实际执行这些工做负载。在本节中,咱们将讨论组成Kubernetes 控制平面的组件,这些组件控制如何在集群上执行,监视和维护工做负载。
在深刻研究以前,重要的是区分集群上的两类计算机:
一个master node主节点包含了大部分,这使得咱们的控制平面,咱们将在下面讨论的组件。在大多数中等大小的集群中,您只有一个主节点,尽管能够有多个主节点来实现高可用性。若是您使用云提供商的托管Kubernetes服务,则它们一般会抽象化主节点,而您没必要进行管理或为此付费。
一个worker node工做节点是实际运行咱们的应用程序工做负载的机器。能够针对集群上的不一样类型的工做负载量身定制多种不一样的计算机类型。例如,您可能具备一些GPU优化的节点以进行更快的模型训练,而后使用CPU优化的节点进行服务。定义对象规格时,能够指定有关将工做负载分配给哪一种机器的首选项。
如今,让咱们深刻了解主节点上的主要组件。与Kubernetes通讯以提供新的或更新的对象规范时,您正在与API服务器进行通讯。
更具体地说,API服务器验证更新对象的请求,并充当有关集群当前状态的问题的统一接口。可是,集群的状态存储在etcd(分布式键值存储)中。咱们将使用etcd来保存有关如下信息:集群配置,对象规范,对象状态,集群上的节点以及分配对象在哪些节点上运行。
注意:etcd是咱们控制平面中惟一的有状态组件,全部其余组件都是无状态的。
说到应该在哪里运行对象,调度程序scheduler 负责肯定这一点!调度程序将询问API服务器(而后将与etcd通讯)还没有分配给计算机的对象。而后,调度程序将肯定这些对象应分配给哪些机器,并将回复API服务器以反映此分配(该分配将传播到etcd)。
咱们将在本文中讨论的主节点上的最后一个组件是controller-manager,它经过API服务器监视集群的状态,以查看集群的当前状态是否符合咱们的指望状态。若是实际状态与咱们的指望状态不一样,则控制器管理器将经过API服务器进行更改,以尝试将集群驱动到指望状态。控制器管理器由一组控制器controllers定义,每一个负责管理集群上特定资源类型的对象。在很是高的级别上,控制器将监视存储在etcd中的特定资源类型(例如,部署),并为应运行的Pod建立规范以实现对象的所需状态。而后,控制者有责任确保这些吊舱在运行时保持健康,并在须要时关闭。
总结到目前为止咱们所涵盖的内容...
接下来,让咱们讨论在工做程序节点上运行的控制平面组件。咱们的工做程序节点上可用的大多数资源都花在了运行咱们的实际应用程序上,可是咱们的节点确实须要知道他们应该运行哪些Pod,以及如何与其余计算机上的Pod通讯。咱们将讨论的控制平面的两个最后组成部分刚好涵盖了这两个方面。
该kubelet做为一个节点的“代理人”,其与API服务器进行通讯,以查看哪些容器工做量被分配到节点。而后,它负责旋转Pod以运行这些分配的工做负载。当节点首次加入集群时,kubelet负责向API服务器宣布节点的存在,以便调度程序能够为其分配容器。
最后,kube-proxy使容器可以跨集群上的各个节点相互通讯。该组件处理全部网络问题,例如如何将流量转发到适当的Pod。
但愿到这一点,您应该可以开始了解Kubernetes集群中事物的运行方式。全部组件都经过API服务器进行交互,咱们将群集的状态存储在etcd中。有多种组件(经过API服务器)写入etcd,以对集群进行更改,而且集群上的节点(经过API服务器)侦听etcd,以查看其应运行的Pod。
整个系统的设计使故障对整个群集的影响最小。例如,若是咱们的主节点发生故障,那么咱们的应用程序都不会当即受到影响;在新的主节点上线以前,咱们将没法对集群进行任何进一步的更改。
与每项新技术同样,你会花费一些时间,您了解它是如何工做的,以及它如何应用于您正在构建的应用程序时。问“我真的须要Kubernetes吗?”是一个合理的问题。所以,我将尝试提供一些答案可能为否的示例状况。
您能够在单台计算机上运行工做负载。(Kubernetes能够看做是构建分布式系统的平台,可是若是不须要,则不该构建分布式系统!)
您的计算需求很少。(在本例中,用于编排框架的计算相对较高!)
您不须要高可用性,而且能够容忍停机时间。
您不会想到对已部署的服务进行大量更改。
您已经拥有一个满意的有效工具栈。
您拥有一个单体架构,不打算将其分红微服务。(这能够回到本来打算使用的工具的状态。)
您阅读了这篇文章,并认为“这很复杂”而不是“这颇有用”。
参考:
朱莉娅·埃文斯(Julia Evans)-Kubernetes很酷的缘由(https://jvns.ca/blog/2017/10/05/reasons-kubernetes-is-cool/)
朱莉娅·埃文斯(Julia Evans)-我对Kubernetes的一些了解(Julia的杂志对个人视觉解释Kubernetes控制平面有很大的启发)(https://jvns.ca/blog/2017/06/04/learning-about-kubernetes/)