kubernetes介绍

文章内容出自《Kubernetes in Action》

本章内容涵盖:

  • 关于应用的开发和部署的方式在近几年的发展趋势
  • 容器如何保障应用间的隔离性,以及减少应用对部署环境的依赖性
  • Docker 容器如何在Kubernetes系统中应用
  • Kubernetes如何提高开发人员和系统管理员 的工作效率

   在过去多数的应用都是大型的单体应用,以单个进程或者几个进程的方式运行于几台服务器之上。这些应用的发布周期长,而且更新也不频繁。每个发布周期结束前,开发者会把应用程序打包交付给运维团队,运维人员再处理部署、监控等事项,并且在硬件发生故障时手动迁移应用。

  今天,大型的单体应用正被逐渐的分解成小的、可独立运行的组件,我们称之为微服务。微服务彼此之间解耦,所以它们可以被独立开发、部署、升级、伸缩。这使得我们可以对每个微服务实现快速迭代,并且迭代速度可以和市场需求变化的速度保持一致。

  但是,随着部署组件的增多和服务器数量的增长,配置、管理并保持系统的正常运行变得越来越困难。如果我们想要有足够高的资源利用率并且降低硬件成本,那么把组件部署在什么地方变得越来越难以决策。手动做所有的事儿,显然不太可行。我们需要一些自动化的措施,包括自动的调度、配置、监管和故障处理。这正是kubernetes的用武之地。

  kubernetes使开发者可以自主部署应用,并且控制部署的频率,完全脱离运维的帮助。kubernetes同时也能让运维团队监控整个系统,并且在硬件故障时重新调度应用。系统管理员的工作重心从监管应用变成了监管kubernetes,以及剩余的系统资源,因为kubernetes会帮助你监管所有的应用。

  Kubernetes 抽象了数据中心的硬件基础设施,使得对外暴露的只是个巨大的资源池。使我们在部署和运行组件时,不用关注底层的服务器。使用kubernetes部署多组件应用时,它会为每个组件都选择一个合适的服务器,部署完后还可以保证每个组件可以轻易的发现其他组件,并实现彼此间的通信。

  所以说使用kubernetes会给大多数场景下的数据中心带来增益,这不仅包括内部部署的数据中心,如果是类似云厂商提供的那种超大型数据中心,这种增益是更加明显的。通过kubernetes,云厂商提供给开发者的是一个可部署且可运行任何类型应用的简易化云平台,而且云厂商的系统管理员可以不用关注这些海量的应用到底是什么。

  随着越来越多的大公司把kubernetes作为他们的运行应用的最佳平台,kubernetes帮助企业实现在云端部署或是内部部署的标准化应用交付方式。

 1.kubernetes系统的需求

  在开始了解kubernetes的细节之前,我们来快速看一下近年来应用程序的开发部署是如何变化的。变化是由两方面导致的,一方面是大型单体应用被拆分为更多的小型微服务,另一方面是应用运行所依赖的基础架构的变化。理解这些变化,有助于我们更好的看待使用kubernetes和容器技术带来的好处。

1.1  从单体应用到微服务

  单体应用由很多个组件组成,这些组件紧密的耦合在一起,由于它们在同一个OS进程中运行,所以在开发、部署、管理的时候必须以同一个实体进行。对单体应用来说,即使是某个组件中一个小的修改,都需要重新部署整个应用。组件之间缺乏严格的边界定义,相互依赖,日积月累导致系统的复杂度提升,整体质量也急剧恶化。

  运行一个单体应用,通常需要一台能为整个应用提供足够资源的高性能服务器。为了应对不断增长的系统负荷,我们需要通过增加CPU、内存或其他系统资源的方式来对服务器做垂直扩展,或者增加更多的跑这些应用程序的服务器的做水平扩展 。垂直扩展不需要应用程序做任何变化,但是成本很快会越来越高,并且通常还会有瓶颈。如果是水平扩展,就可能需要应用程序代码做比较大的改动,有时候甚至是不可行的,比如系统的一些组件非常难于甚至不太可能去做水平扩展(像关系型数据库)。如果单体应用的任何一个部分不能扩展,那么整个应用就不能扩展,除非我们想办法把它拆分开。

  将应用拆分为多个微服务

  这些问题迫使我们将复杂的大型单体应用,拆分为小的可独立部署的微服务组件。每个微服务以独立的进程(见图1.1)运行,并通过简单且定义良好的接口(api)与其他微服务通信。

    服务之间可以通过类似 HTTP 这样的同步协议通信,或者通过像 AMQP 这样的异步协议通信。这些协议能够被大多数开发者所理解,并且并不局限于某种编程语言。这意味着任何一个微服务都可以用最适合的开发语言来实现。

  因为每个微服务都是独立的进程,提供相对静态的API ,所以独立开发和部署单个微服务成了可能。只要API不变或者向前兼容,改动一个微服务,并不会要求对其他微服务进行改动或者重新部署。

  微服务的扩容

  面向单体系统,扩容针对的是整个系统,而面向微服务架构,扩容却只需要针对单个服务,这意味着你可以选择仅扩容那些需要更多资源的服务,而保持其他的服务仍然维持在原来的规模。如图1.2所示,三种组件都被复制了多个,并以多进程的方式部署在不同的服务器上,而另外的组件只能以单体进程应用运行。当单体应用因为其中一部分无法扩容而整体被限制扩容时,可以把应用拆分成多个微服务,将那些能进行扩容的组件进行水平扩展,不能进行扩容的组件进行垂直扩展。

    部署微服务

  当然了微服务也有缺点。若你的系统仅包含少许可部署的组件, 管理那些组件是简单的。决定每个组件部署在哪儿是不重要的,因为没有那么多选择。但是当组件数量增加时,部署相关的决定就变得越来越困难。因为不仅组件部署的组合数在增加,而且组件之间依赖的组合数也在以更大的因素增加。

   微服务以团队形式完成工作,所以需要找到彼此进行交流。部署微服务时,部署者需要正确地配置所有服务来使其作为一个单一系统正常工作,随着微服务的数量不断增加,配置工作变得冗杂且易错,特别是当考虑服务器宕机时,运维需要如何做的时候。

   微服务还带来其他问题,比如因为跨了多个进程和机器,使得调试代码和定位异常调用变得困难。还好这些问题现在己经被诸如 Zipkin 这样的分布式定位系统解决。

  环境需求的差异

  正如已经提到的,一个微服务架构中的组件不仅被独立部署,也被独立开发。 因为它们的独立性,出现不同的团队开发不同的组件是很正常的事,每个团队都有可能使用不同的库并在需求升级时替换它。如图 1.3 所示,因为组件之间依赖的差异性,应用程序需要同一个库的不同版本是不可避免的。

    在部署动态请求的应用需要不同版本的共享库或者需要其他特殊环境,在生产服务器部署并管理这种应用很快会成为运维团队的噩梦。需要在同一个主机上部署的组件数量越大,满足这些组件的所有需求就越难。

1.2  为应用程序提供一个一致的环境

  不管你同时开发和部署多少个独立组件,开发和运维团队总是需要解决的一个最大的问题是程序运行环境的差异性,这种巨大差异不仅存在于开发环境与生产环境之间,甚至存在于各个生产机器之间。另外一个无法避免的事实是生产机器的环境会随着时间的推移而变化。

  这些差异性存在于从硬件到操作系统再到每台机器的可用库上。生产环境是由运维团队管理的,而开发者一般只关心之间的开发环境。这两拨人对系统管理的理解程度是不同的,这个理解偏差导致两个环境的系统有较大的差异,系统管理员更重视保持系统更新最近的安全补丁,而大多数开发者则并不太关心。

  生产系统可能要运行多个开发者或者开发团队的应用 ,而对于开发者的电脑来说就不是这个情况了 。一个生产系统必须给所有它需要承载的应用提供合适的环境, 尽管这些应用可能需要不同的甚至带有冲突的版本库。

  为了减少仅会在生产环境才暴露的问题,最理想的做法是让应用在开发和生产 阶段可以运行在完全一样的环境下,它们有完全一样的操作系统、库、系统配置、网络环境和其他所有的条件。 你也不想让这个环境随时间推移而改变。 如果可能,你想要确保在一台服务器上部署新的应用时,不会影响到服务器上已有的应用。

1.3 迈向持续交付:DevOps和NoOps

  在最近几年中,我们看到了应用在开发流程和生产运维流程中的变化。在过去,开发团队的任务是创建应用并交付给运维团队,然后运维团队部署应用并使它运行。 但是现在,公司都意识到,让同一个团队参与应用的开发、部署 、运维的整个生命周期更好。这意味着开发者、QA 和运维团队彼此之间的合作需要贯穿整个流程。这种实践被称为 DevOps

  带来的好处

  让开发者更快的在生产环境中运行应用,能够使他们对用户的需求和问题,以及运维团队维护应用所面临的困难,有一个更好的理解。应用程序开发者现在更趋向于将应用尽快地发布上线,通过收集用户的反馈对应用做进一步开发。

  为了频繁地发布应用,就需要简化你的部署流程。理想的状态是开发人员能够自己部署应用上线,而不需要交付给运维人员操作。但是,部署应用 往往需要具备对数据中心底层设备和硬件架构的理解。开发人员却通常不知道或者不想知道这些细节。

  让开发和运维做他们擅长的

  成功运行应用并服务于客户,这是开发和运维共同的目标,但他们也有着不同的个人目标和驱动因素。

  开发者热衷于创造新的功能和提升用户体验,通常不想成为确保底层操作系统已经更新所有安全补丁的那些人,他们只想把那些事留给运维人员。

  运维团队负责管理生产部署流程及应用所在的硬件设备。他们关系系统安全、使用率,以及其他对于开发者来说优先级不高的东西。但运维人员不想处理各个应用组件之间依赖关系,也不想考虑底层操作系统或者基础设施的改变会怎样影响到应用程序, 但是他们却不得不关注这些事情。

  理想情况是,开发者是部署程序本身,不需要知道硬件基础设施的任何情况 也不需要和运维团队交涉,这被叫作 NoOps。很明显,你仍然需要有一些人来关心 硬件基础设施,但这些人不需要再处理应用程序的独特性。

  正如你所看到的,kubernetes 能让我们实现所有这些想法。通过对实际硬件做抽象,然后将自身暴露成一个平台,用于部署和运行应用程序。它允许开发者自己配置和部署应用程序,而不需要系统管理员的任何帮助,让系统管理员聚焦于保持底层基础设施运转正常的同时,不需要关注实际运行在平台上的应用程序。

2.  介绍容器技术

  在上一节中,罗列了一个不全面的开发和运维团队如今所面临的问题列表,尽管你有很多解决这些问题的方式,但本书将关注如何用 Kubernetes 解决。

  Kubernetes 使用 Linux 容器技术来提供应用的隔离,所以在钻研 Kubernetes 之前, 需要通过熟悉容器的基本知识来更加深入地理解 Kubernetes ,包括认识到存在的容器技术分支,诸如 Docker 或者 rkt。

2.1 什么是容器

  在1.1 节中,我们看到在同一台机器上运行的不同组件需要不同的、可能存在冲突的依赖库版本,或者是其他的不同环境需求。

  当一个应用程序仅由较少数量的大组件构成时,完全可以接受给每个组件分配专用的虚拟机,以及通过给每个组件提供自己的操作系统实例来隔离它们的环境。但是当这些组件开始变小而且数量开始增长时,如果你不想浪费硬件资源,又想持续的压低硬件成本,那就不能给每个应用组件都配置一个虚拟机了。但是这还不仅仅是浪费硬件资源,还因为每个虚拟机都需要被独立配置和管理,随着虚拟机数量的增加还导致了人力资源的浪费,因为这增加了运维的工作负担。

  用Linux容器技术隔离组件

  开发者不再使用虚拟机来隔离每个微服务的环境(进程),而是正在转向Linux容器技术。容器允许你在同一台机器上运行多个服务,不仅给每个服务提供不同的环境,而且还将它们互相隔离。容器类似虚拟机,但开销要小很多。

  一个容器里运行的进程实际上是运行在宿主机的操作系统上,就像操作系统上其他进程一样(不同于虚拟机,进程是运行在虚拟机操作系统上)。但在容器里的进程仍然是和其他进程隔离的。对于容器内进程本身而言,就好像它是这个机器或者说操作系统上唯一的一个进程一样。

  比较虚拟机和容器

  和虚拟机比较,容器更加轻量级,它允许在相同的硬件上运行更多数量的组件。主要是因为每个虚拟机需要运行自己的一组虚拟机系统进程,这就产生了除应用进程消耗以外的额外计算资源损耗。

反之,一个容器仅仅是运行在宿主机上的一个被隔离的单独进程,只消耗应用容器消耗的资源,没有其他的资源开销。

  因为虚拟机的额外开销,导致没有足够的资源给每个应用开一个专用的虚拟机,最终只能将多个应用分组塞进每个虚拟机中。但是当使用容器时,能够让每个应用都有一个容器。最终结果就是可以在同一台裸机上运行更多的应用程序。正如图 1.4 所示。

    虚拟化:当你在一台主机上运行三个虚拟机的时候,你拥有了三个完全分离的操作系统,它们运行并共享一台裸机 。在那些虚拟机之下是宿主机的操作系统与一个管理程序, 它将物理硬件资源分成较小部分的虚拟硬件资源,从而被每个虚拟机里的操作系统使用。运行在那些虚拟机里的应用程序会执行虚拟机操作系统的系统调用,然后虚拟机内核会通过管理程序在宿主机上的物理来CPU 执行 x86 指令。 

  注意 存在两种类型的管理程序。 第一种类型的管理程序不会使用宿主机OS(像VMware Esxi),而第二种类型的会。(像KVM)

  容器:多个容器则会完全执行运行在宿主机上的同一个内核的系统调用,此内核是唯一一个在宿主机操作系统上执行x86指令的内核。CPU也不需要做任何对虚拟机做的那样虚拟化。

    虚拟机的主要好处是它们提供完全隔离的环境,因为每个虚拟机运行在它自己的linux内核上,而容器都是调用的同一个内核,这自然会有安全隐患。假设你的硬件资源有限,如果你只有少量进程需要被隔离的时候,可以选择虚拟机;但为了在同一台机器上运行大量被隔离的进程,容器因它的低消耗而成为一个更好的选择。强调一下,每个虚拟机都运行着它自己的一组系统服务,而容器则没有,因为容器是运行在宿主机操作系统上。所以容器也不会像虚拟机那样还需要开机,它只是一个进程可以很快被启动。

  容器实现隔离机制介绍

  就像如上所说的一样,你会好奇如果多个进程运行在同一个OS上,那容器是如果隔离它们的。有两个机制组成:第一个是Linux命名空间,它使每个进程只看到它自己的系统视图(文件、进程、网络接口、主机名等);第二个是Linux控制组(CGroup),它限制了进程能使用的资源量( CPU 、内存、网络带宽等)。

  使用Linux命名空间隔离进程

  默认情况下,每个Linux系统最初仅有一个命名空间。所有系统资源(诸如文件系统、用户 ID 、网络接口等)属于这一个命名空间 。但是你能创建额外的命名空间,以及在它们之间组织资源。 对于 一个进程,可以在其中一个命名空间中运行它。进程将只能看到同一个命名空间下的资源。 当然,会存在多种类型的多个命名空间, 所以一个进程不单单只属于某一个命名空间,而属于每个类型的一个命名空间。

  存在以下类型的命名空间:

  • Mount (mnt)
  • Process ID ( pid ) 
  • Network (net)
  • Inter-process communicaion ( ipd)
  • UTS
  • User ID ( user)

  每种命名空间被用来隔离一组特定的资源。例如, UTS 命名空间决定了运行在命名空间里的进程能看见哪些主机名和域名。通过分派两个不同的UTS 命名空间给一对进程,能使它们看见不同的本地主机名。 换句话说,这两个进程就好像正在两个不同的机器上运行一样,至少就主机名而言是这样的。

  同样地, 一个进程属于什么 Network 命名空间决定了运行在进程里的应用程序能看见什么网络接口。 每个网络接口属于一个命名空间,但是可以从一个命名空间移到另一个。每个容器都使用它自己的网络命名空间,因此每个容器仅能看见它自己的一组网络接口。

  现在你应该已经了解命名空间是如何隔离容器中运行的应用的。

  限制进程的可用资源

  另外的隔离性就是限制容器能使用的系统资源。这通过 cgroups 来实现。 cgroups是一个 Linux 内核功能, 它被用来限制一个进程或者一组进程的资源使用。 一个进程的资源 (CPU 、内存、网络带宽等)使用量不能超出被分配的量。这种方式下,进程不能过分使用为其他进程保留的资源,这和进程运行在不同的机器上是类似的。

2.2  Docker容器平台介绍

  尽管容器技术已经出现很久了,却是随着Docker容器平台的出现而变得广为人知。Docker 是第一个使容器能在不同机器之间移植的系统。它不仅简化了打包应用的流程 ,也简化了打包应用的库和依赖,甚至整个操作系统的文件系统能被打包成一个简单可移植的包,这个包可以被用来在任何其他运行 Docker 的机器上使用。

  当你用Docker运行一个被打包的应用程序时,它能看见你捆绑的文件系统的内容,不管运行在开发机器还是生产机器上,它都能看见相同的文件,即使生产机器运行的是完全不同的操作系统。应用程序不会关心它所在服务器上的任何东西,所以也不需要关心生产服务器上是否安装了和你开发机完全相同的一组库。

  例如,如果你用RHEL系统的文件打包了你的应用程序,不管是在装有 Fedora 开发机上运行它,还是在装有 Debian 或者其他 inux 发行版的服务器上运行它,应用程序都认为它运行在RHEL中, 只是内核可能不同。

  与虚拟机的镜像类似(在虚拟机中安装操作系统得到一个虚拟机镜像,再将应用打包到镜像里,通过分发整个虚拟机镜像包到主机,使应用程序能够运行起来),Docker也能够达到相同的效果,但不是使用虚拟机实现应用隔离,而是使用前面提到的Linux容器技术来达到和虚拟机相同级别的隔离。容器也不会使用庞大的虚拟机镜像,它用的是体积较小容器镜像。

  差别: Docker 容器镜像和虚拟机镜像的一个很大的不同,那就是容器镜像是由多层构成,它能在多个镜像之间共享使用,如果有一个已经被下载的容器镜像包含了后面下载镜像的某些层,那么后面下载的镜像就无须再下载这些层。

  Docker 的概念

  Docker是一个打包、分发和运行应用程序的平台。它允许你将应用和应用依赖的环境打包在一起。Docker使得传输这个包到一个中央仓库成为可能,然后这个包就能被分发到任何运行 Docker 的机器上,在那儿被执行(大部分情况是这样的,但也不都是,后面作解释)

  三个主要概念组成了这种情形:

  • 镜像——Docker镜像里包含了你打包的应用程序及其所所依赖的环境。它包含应用程序可用的文件系统和其他元数据,如镜像运行时的可执行文件路径。
  • 镜像仓库——Docker 镜像仓库用于存放 Docker 镜像,以及促进不同人和不同电脑之间共享这些镜像 。当你编译你的镜像时,要么可以在编译它的电脑上运行,要么可以先上传镜像到一个镜像仓库,然后下载到另外一台电脑上并运行它。某些仓库是公开的,允许所有人从中拉取镜像,同时也有 些是私有的,仅部分人和机器可接入。
  • 容器——Docker 容器通常是一个 Linux 容器,它基于 Docker 镜像被创建 。一个运行中的容器其实是一个运行在 Docker宿主机上的进程,但它和主机以及所有运行在主机上的其他进程都是隔离的 这个进程也是资源受限的,意味着只能访问和使用分配给它的资源( CPU 、内存等)

  构建、分发和运行 Dcoker 镜像

  图1.6 显示了这三个概念以及它们之间的关系。开发人员首先构建一个镜像, 然后把镜像推到镜像仓库中。 因此,任何可以访问镜像仓库的人都可以使用该镜像。 然后,他们可以将镜像拉取到任何运行着 Docker 的机器上并运行镜像。 Docker会基于镜像创建一个独立的容器,并运行二进制可执行文件指定其作为镜像的一部分。

   对比虚拟机与 Docker 容器

   由上文可知, Linux 容器和虚拟机的确有相像之处,但容器更轻量级。现在让我们看一下 Docker 容器和虚拟机的具体比较(以及 Docker 镜像和虚拟机镜像的比较)。如图例1.7所示,相同的6个应用分别运行在虚拟机上和使用Docker容器运行。

    你会注意到应用A和应用B无论是运行在虚拟机上还是作为两个分离容器运行时都可以访问相同的二进制和库。在虚拟机里,这是显然的,因为两个应用都看到相同的文件系统。但是我们知道每个容器都有自己隔离的文件系统,那应用A和应用B是如何共享文件的呢?

  镜像层

  前面已经说过 Docker 镜像由多层构成。不同镜像可能包含完全相同的层,因为这些 Docker 镜像都是基于另一个镜像之上构建的,不同的镜像都能使用相同的父镜像作为它们的基础镜像。这提升了镜像在网络上的分发效率,当传输某个镜像时, 因为相同的层已经被之前的镜像传输,那么这些层就不需要再被传输。

   层不仅使分发更高效,也有助于减少镜像的存储空间 。每一层仅被存一次,当基于相同基础层的镜像被创建成两个容器时,它们就能够读相同的文件。但是如果其中一个容器写入某些文件,另外一个是无法看见文件变更的。因此,即使它们共享文件,仍然彼此隔离。这是因为容器镜像层是只读的。容器运行时,一个新的可写层在镜像层之上被创建。容器中进程写入位于底层的一个文件时,此文件的一个拷贝在顶层被创建,进程写的是此拷贝。

   容器镜像可移植性的限制

  理论上,一个容器镜像能够运行在任何一个运行着docker的机器上。但有一个小警告,那就是所有运行容器都共享着同一个主机内核。如果一个容器应用需要一个特定的内核版本,那么它是不可能在任意机器上运行的。

  虽然容器相比虚拟机轻量很多,但也给运行其上的应用带来一些局限性。虚拟机没有这些局限性,因为每个虚拟机都运行着自己的内核。

  还不光是内核的问题。一个在特定硬件架构上编译的容器化应用,只能在有相同硬件架构的机器上运行。不能将一个x86架构编译的应用容器化后,运行在ARM架构的机器上,你仍然需要虚拟机来解决这件事。

2.3  rkt——一个Docker的替代方案

  Docker 是第一个使容器成为主流的容器平台。 Docker 本身并不提供进程隔离, 实际上容器隔离是在 Linux内核之上使用诸如 Linux 名空间和 cgroups 之类的内核特性完成的。 Docker 仅简化了这些特性的使用。

  在Docker成功后,开放容器计划(OCI)就开始围绕容器格式和运行时创建了开放工业标准。Docker是计划的一部分,rkt则是另外一个Linux容器引擎。

  和Docker一样,rkt也是一个运行容器的平台,它强调安全性、可构建性并遵从开放标准。 它使用 OCI 容器镜像,甚至可以运行常规的 Docker 容器镜像。

  这本书只集中于使用 Docker 作为 Kubernetes 的容器,因为它是 Kubernetes最初唯一支持的容器类型 。现在kubernetes也开始支持rkt以及其他容器类型。

  我们不应该错误的认为kubernetes是一个专为Docker容器涉及的容器编排系统。实际上,在阅读这本书的过程中,你将会认识到 Kubernetes 的核心远不止是编排容器。容器恰好是在不同集群节点上运行应用的最佳方式。 有了这些识,终于可以深入探讨本书所讲的核心内容——kubernetes了。

 3  Kubernetes介绍

  我们已经展示了,随着系统可部署组件的数量增长,把它们都管理起来会变得 越来越困难。需要一个更好的方式来部署和管理这些组件,并支持基础设施的全球性伸缩,谷歌可能是第一个意识到这一点的公司。谷歌等全球少数几个公司运行着成千上万的服务器,而且在如此海量规模下,不得不处理部署管理的问题。这推动着他们找出解决方案使成千上万组件的管理变得有效且成本低廉。

 3.1  初衷

  这些年来,谷歌开发出了一个叫Borg的内部系统(后来还有一个新系统叫Omega),应用开发者和系统管理员管理那些数以千计的应用程序和服务都受益于它的帮助。除了简化开发和管理,它也帮助他们获得了更高的基础设施利用率,在你的组织如此庞大时,这很重要。当你运行成千上万台机器时,哪怕一丁点的利用率提升也意味着节约了数百万美元,所以,开发这个系统的动机是显而易见的。

  在保守Borg和Omega秘密数十年之后,2014年,谷歌开放了Kubemnetes,一个基于Borg、Omega及其他谷歌内部系统实践的开源系统。

 3.2  深入浅出的了解kubernetes

  Kubernetes 是一个软件系统,它允许你在其上很容易地部署和管理容器化的应用。它依赖于 Linux 容器的特性来运行异构应用,而无须知道这些应用的内部详情,也不需要手动将这些应用部署到每台机器。因为这些应用运行在容器里,它们不会影响运行在同一台服务器上的其他应用 ,当你是为完全不同的组织机构运行应用时, 这就很关键了 。这对于云供应商来说是至关重要的,因为它们在追求高硬件可用率的同时也必须保障所承载应用的完全隔离。

  Kubernetes 使你在数以千计的电脑节点上运行软件时就像所有这些节点是单个大节点一样。它将底层基础设施抽象,这样做同时简化了应用的开发、部署,以及对开发和运维团队的管理。

  通过 Kubernetes 部署应用程序时,你的集群包含多少节点都是一样的 。集群规模不会造成什么差异性,额外的集群节点只是代表了更多可用来部署应用的资源。

  Kubernetes的核心功能

  图1.8展示了一幅最简单的 Kubernetes 系统图 。整个系统由一个主节点和若干个工作节点组成。 开发者把一个应用列表提交到主节点, Kubernetes 会将它们部署到集群的工作节点。组件被部署在哪个节点对于开发者和系统管理员来说都不用关心。

     开发者还能指定一些应用必须在一起运行,Kubernetes将会在一个工作节点上部署这些应用。其他的应用可以被分散部署到集群中,但不管在哪儿,它们都能以相同的方式互相通信。

  帮助开发者聚焦核心应用功能

  Kubernetes可以被当做一个集群系统来看待。它降低了开发者不得不在应用里实现的一些与基础设施相关服务的心智负担。他们现在依赖于Kubernetes来提供这些服务,包括服务发现、扩容、负载均衡、自恢复,设置领导者选举。开发者因此能集中精力实现应用本身的功能而不用浪费时间思索怎样集成应用与基础设施。

  帮助运维团队获取更高的资源利用率

  Kubernetes将你的容器化应用运行在集群的某个地方,井提供信息给应用组件来发现彼此并保证它们的运行。因为你的应用程序不关心它运行在哪个节点上,Kubernetes能在任何时间迁移应用并通过混合和匹配应用来获得比手动调度高很的资源利用率。

 3.3  Kubernetes集群架构

  我们已经通过上帝视角看到了Kubernetes的架构,现在我们在近距离看一下Kubernetes集群的组成。在硬件级别,一个Kubernetes集群由很多节点组成,这些节点被分为以下两种类型:

  • 主节点,承载着Kubernetes控制和管理整个集群系统的控制面板
  • 工作节点,运行用户实际部署的应用

图1.9 展示了运行在这两组节点上的组件,接下来进一步解释。

    控制面板

  控制面板用于控制集群并使它工作。它包含多个组件,组件可以运行在单个主节点上或者通过副本分别部署在多个主节点以确保高可用性。 这些组件是:

  • Kubernetes API 服务器,你和其他控制面板组件都要通过它通信
  • Scheduler  ,它负责调度你的应用(为应用分配工作节点)
  • Controller Manager,它负责执行集群级别的功能,如复制组件、持续跟踪工作节点、处理节点故障等
  • etcd,一个可靠的分布式数据存储,可以持久化存储集群配置

  工作节点

  工作节点是运行容器化应用的机器。运行、监控和管理应用服务的任务是由以下组件完成的:

  • Docker、rkt或其他的容器类型
  • Kubelet,它负责与API Server通信,并管理它所在节点上的容器
  • Kubernetes Service Proxy(kube-proxy),它负责组件之间的负载均衡网络流量

  我们将在11章中详细解释所有的这些组件。笔者不喜欢先解释事物是如何工作的,然后再解释它的功能并教人们如何使用它。 就像学习开车,你不想知道引擎盖下是什么,你首先想要学习怎样从A点开到B点 。只有在你学会了如何做到这一点后,你才会对汽车如何使这成为可能产生兴趣。毕竟,知道引擎盖下面是什么,可能在有一天它抛锚后你被困在路边时,会帮助你让车再次移动。

  3.4  在Kubernetes中运行应用

  为了在Kubernetes中运行应用,首先需要将应用打包进一个或多个容器镜像, 再将那些镜像推送到镜像仓库,然后将应用的描述(manifest)发布到 Kubernetes API 服务器。

  该描述文件包括了像容器镜像或者包含应用程序组件的容器镜像、这些组件如何相互关联,以及哪些组件需要同时运行在同一个节点上和哪些组件不需要同时运行等信息。此外,该描述还包括哪些组件为内部或外部客户提供服务且应该通过单个 IP 地址暴露 ,并使其他组件可以发现。

  描述信息怎样成为一个运行的容器

  当API服务器处理应用的描述时,Scheduler调度指定组的容器到可用的工作节点上,调度是基于描述信息中所需的计算资源,以及每个节点未分配的资源来决定的。然后,那些节点上的kubelet指示容器运行时(如Docker)拉取所需的镜像并运行容器。

  仔细看图 1.10 以更好地理解如何在 Kubernetes 中部署应用程序。应用描述符列 出了四个容器,并将它们分为三组(这些集合被称为 pod ,我们将在第3章中解释它们是什么)。前两个pod只包含一个容器,而最后一个pod包含两个。这意味着两个容器都需要协作运行,不应该相互隔离。在每 pod 旁边,还可以看到一个数字,表示需要并行运行的每个pod 副本数量。在向 Kubernetes 提交描述符之后,它将把每个pod的指定副本数量调度到可用的工作节点上。节点上的 Kubelet将告知 Docker 从镜像仓库中拉取容器镜像并运行容器。

  保持容器运行

  一旦应用程序运行起来, Kubernetes 就会不断地确认应用程序的部署状态始终与你提供的描述相匹配。 例如,如果你指出你需要运行五个web 服务器实例,那么 Kubernetes 总是保持正好运行五个实例。如果实例之一停止了正常工作,比如当进程崩溃或停止响应时, Kubernetes 将自动重启它。

  同理,如果整个工作节点死亡或无法访问, Kubernetes 将会为故障节点上运行的所有容器选择新节点,并在新选择的节点上运行它们。

    扩展副本数量

  当应用程序运行时,可以决定要增加或减少副本量 ,而 Kubernetes 将分别增加副本或停止多余的副本。甚至可以把决定最佳副本数目的工作交给 Kubernetes。 它可以根据实时指标(如 CPU 负载、内存消耗、每秒查询或应用程序公开的任何其他指标)自动调整副本数。

  命中移动目标

  我们已经说过,Kubernetes可能需要在集群中迁移你的容器。当它们运行的节点失败时,或者为了给其他容器腾出地方而从节点移除时,就会发生这种情况。如果容器向运行在集群中的其他容器或者外部客户端提供服务,那么当容器在集群内频繁调度时,它们该如何正确使用这个容器?当这些容器被复制并分布在整个集群中时,客户端如何连接到提供服务的容器呢?

  为了让客户能够轻松地找到提供特定服务的容器,可以告诉Kubernetes哪些容 器提供相同的服务,而Kubernetes将通过一个静态 IP 地址暴露所有容器,并将该地址暴露给集群中运行的所有应用程序。这是通过环境变量完成的,但是客户端也可以通过良好的DNS查找服务IP。kube-proxy将确保到服务的连接可以跨容器实现负载均衡。服务的IP保持不变,因此客户端始终可以连接到它的容器,即使容器在集群中移动。

3.5  使用Kubernetes的好处

  如果在所有服务器上部署了kubernetes ,那么运维团队就不需要再部署应用程序。因为容器化的应用程序已经包含了运行所需的所有内容,系统管理员不需要安装任何东西来部署和运行应用程序。在任何部署Kubernetes 的节点上, Kubernetes 可以在不需要系统管理员任何帮助的情况下立即运行应用程序。

  简化应用程序部署

  由于Kubernetes将其所有工作节点公开为一个部署平台,因此应用程序开发人员可以自己开始部署应用程序,不需要了解组成集群的服务器。

  实际上,现在所有节点都是一组等待应用程序使用它们的计算资源。开发人员 通常不关心应用程序运行在哪个服务器上,只要服务器能够为应用程序提供足够的 系统资源即可。

  在某些情况下,开发人员确实也关心应用程序应该运行在哪种硬件上。 如果节点是异构的,那么你将会发现你希望某些应用程序在具有特定功能的节点上运行,并在其他的节点上运行其他应用程序。例如,你的一个应用程序可能需要在具有SSD的机器上运行而不是HDD 的机器上,而其他应用程序在 HDDs 上运行即可。 在这种情况下,你显然希望确保特定的应用程序总是被调度到有 SSD 的节点上。

  在不使用 Kubernetes 的情况下,系统管理员需要为应用选择一个具有 SSD 的特定节点, 并在那里部署应用程序。但是当使用 kubernetes时,与其选择应用程序应该运行在某一特定节点上,不如告诉 Kubernetes 只在具有 SSD 的节点中进行选择,你将在第3章学到如何做到这一点。

  更好地利用硬件

  通过在服务器上装配Kubernetes,,并使用它运行应用程序而不是手动运行它们, 你己经将应用程序与基础设施分离开来。当你高速Kubernetes运行你的应用程序时, 其实在让它根据应用程序的资源需求描述和每个节点上的可用资源选择最合适的节点来运行你的应用程序。

  通过使用容器,不再用把这个应用绑定到一个特定的集群节点,而允许应用程序在任何时候都在集群中自由迁移,所以在集群上运行的不同应用程序组件可以被混合和匹配来紧密打包到集群节点。这将确保节点的硬件资源得到尽可能好的利用。

  可以随时在集群中移动应用程序的能力,使得Kubernetes可以比人工更好的利用基础设施。人类不擅长寻找最优的组合,尤其是当所有选项的数量都很大的时候, 比如当你有许多应用程序组件和许多服务器节点时,所有的组件可以部署在所有的节点上。显然,计算机可以比人类更好、更快地完成这项工作。

  健康检查和自修复

  在服务器发生故障时,拥有一个允许在任何时候跨集群迁移应用程序的系统也很有价值。随着集群大小的增加,你将更频繁地处理出现故障的计算机组件。

  Kubernetes监控你的应用程序组件和它们运行的节点,并在节点出现故障时自动将它们重新调度到其他节点,这使运维团队不必手动迁移应用程序组件,并允许团队立即专注于修复节点本身,并将其修好送回到可用的硬件资源池中,而不是将重点放在重新定位应用程序上。

  如果你的基础设施有足够的备用资源来允许正常的系统运行,即使故障节点没有恢复,运维团队甚至不需要立即对故障做出反应,比如在凌晨3点。运维可以睡得很香,等到正常的工作时间再去处理失败的节点。

  自动扩容(对容器和节点做自动的扩容缩容)

  使用 Kubernetes 来管理部署的应用程序,也意味着运维团队不需要不断地监控单个应用程序的负载,以对突发负载峰值做出反应。如前所述,可以告诉 Kubernetes 监视应用程序使用资源,并不断调整每个应用程序的运行实例数量。

  如果 Kubernetes运行在云基础设施上,在这些基础设施中,添加额外的节点就像通过云供应商的 API 请求它一样简单, 那么 Kubernetes甚至可以根据部署的应用程序的需要自动地将整个集群规模放大或缩小。

   简化应用部署

  前一节中描述的特性主要对运维团队有利 。但是开发人员呢? Kubernetes 是否也给他带来什么好处?这毋庸置疑。

  如果你回过头来看看,应用程序开发和生产流程中都运行在同一个环境中,这对发现 bug 有很大的影响。我们都同意越早发现一个bug修复它就越容易, 修复它需要的工作量也就越少 。由于是在开发阶段就修复 bug ,所以这意味着他们的工作量减少了。

  还有一个事实是,开发人员不需要实现他们通常会实现的特性。这包括在集群应用中发现服务和对端。这是由 Kubernetes来完成的而不是应用。通常,应用程序只需要查找某些环境变量或执行DNS 查询。如果这还不够,应用程序可以直接查询 Kubernetes API Server 以获取该信息和其他信息。像这样查询 Kubernetes API 服务器, 甚至可以让开发人员不必实现诸如复杂的集群 leader 选举机制。

    作为最后一个关于 Kubernetes 带来什么的例子,还需要考虑到开发者们的信心增加。当他们知道,新版本的应用将会被推出时 Kubernetes可以自动检测一个应用的新版本是否有问题,如果是则立即停止其滚动更新,这种信心的增强通常会加速应用程序的持续交付,这对整个组织都有好处。

4 本章小结

  在这个介绍性章节中,你已经看到了近年来应用程序的变化,以及它们现在如何变得更难部署和管理。我们已经介绍了 Kubernetes ,并展示了它如何与 Docker 或其他容器平台一起帮助部署和管理应用程序及其运行的基础设施。你己经学到了:

  • 单体应用程序更容易部署,但随着时间的推移更难维护,并且有时难以扩展。
  • 基于微服务的应用程序体系结构使每个组件的开发更容易,但是很难配置和部署它们作为单个系统工作。
  • Linux 容器提供的好处与虚拟机差不多,但它们轻量许多 ,并且允许更好地 利用硬件。
  • 通过允许更简单快捷地将容器化应用和其操作系统环境一起管理,Docker 进了现有的 Linux 容器技术。
  • Kubernetes 将整个数据中心暴露为用于运行应用程序的单个计算资源。
  • 开发人员可以通过 Kubernetes部署应用程序,而无须系统管理员的帮助。
  • 通过让 Kubernetes 自动地处理故障节点 ,系统管理员可以睡得更好。

  在下一章中,你将通过构建一个应用程序并在 Docker和Kubernetes中运行它, 来上手实践。

posted @ 2020-03-07 16:55  天乐_技术博客  阅读(546)  评论(0编辑  收藏  举报