多年前,大多数软件应用程序都是大型的单体,要么作为单个进程运行,要么作为少数服务器上的少量进程运行。这种过时的系统一直延续很久。
它们的发布周期较慢,更新相对较少。
在每个发布周期结束时,开发人员将整个系统打包并交给运维团队,然后运维团队对其进行部署和监控。如果发生硬件故障,运维团队将手动将其迁移到剩余的健康服务器。
但是,随着可部署组件数量的增加和数据中心规模的扩大,配置、管理和保持整个系统平稳运行变得越来越困难。要弄清楚把这些组件放在哪里以实现高资源利用率,从而降低硬件成本要困难得多。手工完成所有这些工作是很辛苦的。我们需要自动化,包括将这些组件自动调度到我们的服务器、自动配置、监督和故障处理。这就是Kubernetes发挥作用的地方。
将应用拆分为微服务
这些问题和其他问题迫使我们开始将复杂的单片应用程序拆分为更小的可独立部署的组件,称为微服务。每个微服务作为一个独立的进程运行(见figure1.1),并通过简单的、定义良好的接口(api)与其他微服务通信。
微服务通过HTTP等同步协议进行通信,它们通常在HTTP上公开RESTful(REpresentational State Transfer)api,或者通过AMQP(Advanced Message Queueing Protocol)等异步协议进行通信。
可扩展的微服务
微服务的扩展不像单片系统那样需要将系统作为一个整体进行扩展,它是在每个服务的基础上完成的,这意味着你可以选择只扩展那些需要更多资源的服务,而让其他服务保持原来的规模。
图1.2显示了一个示例。某些组件被复制并作为部署在不同服务器上的多个流程运行,而其他组件则作为单个应用程序流程运行。
当一个单片应用程序因为其中一个部分不可扩展而无法扩展时,将应用程序拆分为微服务可以让你横向扩展。
部署微服务
与往常一样,微服务也有缺点。当您的系统仅由少量可部署组件组成时,管理这些组件很容易。决定在哪里部署每个组件很简单,因为选择并不多。当这些组件的数量增加时,与部署相关的决策变得越来越困难,因为不仅部署组合的数量增加了,而且组件之间的相互依赖关系的数量也增加了一个更大的因素。
微服务还带来了其他问题,比如难以调试和跟踪执行调用,因为它们跨越多个进程和机器。幸运的是,这些问题现在正在通过Zipkin等分布式跟踪系统得到解决。
了解不同环境需求的差异
微服务架构中的组件不仅是独立部署的,而且也是以这种方式开发的。由于它们的独立性以及通常由独立的团队开发每个组件的事实,没有什么可以阻止每个团队使用不同的库并在需要时替换它们。应用程序组件之间依赖关系的分歧是不可避免的,如Figure1.3所示,其中应用程序需要相同库的不同版本。
部署需要不同版本的共享库和/或需要其他环境细节的动态链接应用程序,对于在生产服务器上部署和管理它们的运维团队来说,可能很快就会成为一场噩梦。在同一台主机上需要部署的组件数量越多,管理它们的所有依赖关系以满足它们的所有需求就越困难。
VM和Container的区别
与VM相比,Container要轻量级得多,这允许您在相同的硬件上运行更多数量的软件组件,主要是因为每个
VM需要运行自己的一组系统进程,除了组件自己的进程所消耗的资源外,这还需要额外的计算资源。
另一方面,Container只不过是在主机操作系统中运行的一个孤立的进程,它只消耗应用程序所消耗的资源,并且没有任何额外进程的开销。
由于VM的开销,您通常会将多个应用程序分组到每个虚拟机中,因为您没有足够的资源将整个虚拟机专用于每个应用程序。
在使用Container时,您可以为每个应用程序使用一个Container,如Figure1.4所示。最终的结果是,您可以在同一台裸机上安装更多的应用程序
当您在一台主机上运行三个vm时,您将有三个完全独立的操作系统运行在相同的裸机硬件上并共享它们。在这些虚拟机下面是主机的操作系统和管理程序,管理程序将物理硬件资源划分为更小的虚拟资源集,这些虚拟资源集可以由每个虚拟机中的操作系统使用VM。在这些虚拟机中运行的应用程序对虚拟机中的来宾操作系统内核执行系统调用,然后内核在主机的物理上执行x86指令
通过虚拟化环境获取CPU。
另一方面,容器都在主机操作系统中运行的完全相同的内核上执行系统调用。这个内核是唯一在主机CPU上执行x86指令的内核。CPU不需要像处理vm那样进行任何类型的虚拟化(参见Figure1.5)。
VM的主要好处是它们提供了完全的隔离,因为每个VM运行自己的Linux内核,而容器都调用相同的内核,这显然会带来安全风险。
如果您的硬件资源有限,那么只有当您希望隔离的进程数量很少时,才可以选择VM。
要在同一台机器上运行更多的隔离进程,容器是更好的选择,因为它们的开销较低。
每个VM运行它自己的一组系统服务,而容器没有,因为它们都运行在相同的操作系统中。
这也意味着要运行容器,不需要启动任何东西,就像vm中的情况一样。
构建、分发和运行docker image
Figure 1.6展示了所有这三个概念以及它们之间的关系。开发人员首先构建image,然后将其推送到注册表。因此,任何可以访问注册中心的人都可以使用该image。然后,他们可以将image拉到运行Docker的任何其他机器上并运行该image。Docker基于镜像创建一个隔离的容器,并运行作为镜像一部分指定的二进制可执行文件。
VM和Docker的区别
Figure 1.7在VM和Docker容器中运行同样的六个应用程序。
无论是在VM中运行还是作为两个独立的容器运行,应用程序A和B都可以访问相同的二进制文件和库。因为两个应用程序都看到相同的文件系统(虚拟机的文件系统)。但是每个容器都有自己独立的文件系统。
应用程序A和应用程序B如何共享相同的文件?
Docker image是由层组成的。不同的镜像可以包含完全相同的层,因为每个Docker镜像都是构建在另一个镜像之上的,两个不同的镜像都可以使用相同的父镜像作为基础。
这加快了image在网络中的分布,因为已经作为第一个image的一部分传输的层在传输另一个image时不需要再次传输。
理论上,容器image可以在任何运行Docker的Linux机器上运行,但是有一点需要注意,即在主机上运行的所有容器都使用主机的Linux内核。如果容器化的应用程序需要特定的内核版本,那么它可能无法在每台机器上运行。如果一台机器运行不同版本的Linux内核,或者没有相同的内核模块可用,则应用程序无法在其上运行。
虽然容器比虚拟机轻量级得多,但它们对在其中运行的应用程序施加了一定的限制。虚拟机没有这样的约束,因为每个虚拟机运行自己的内核。