文章目录
- 第16章 虚拟化
- 16.1 共享资源
- 16.2 虚拟机
- 16.3 虚拟机镜像
- 16.4 容器
- 16.5 容器和虚拟机
- 16.6 容器的可移植性
- 16.7 Pod(容器组)
- 16.8 无服务器架构
- 16.9 小结
- 16.10 扩展阅读
- 16.11 问题讨论
第16章 虚拟化
“虚拟”意味着永远不知道你的下一个字节从哪里来。
—Unknown
在 20 世纪 60 年代,计算领域面临着这样一个难题:如何在一台物理机器上让多个独立的应用程序共享诸如内存、磁盘、I/O 通道和用户输入设备等资源。由于无法共享资源,这意味着在同一时间只能运行一个应用程序。在那个时候,计算机的成本高达数百万美元 (在当时那可是实实在在的一大笔钱)而大多数应用程序通常只使用可用资源的一小部分,大约 10% 左右,所以这种情况对计算成本产生了重大影响。
虚拟机以及后来的容器的出现就是为了解决共享问题。这些虚拟机和容器的目标是将一个应用程序与另一个应用程序隔离开来,同时仍然能够共享资源。隔离使得开发人员能够编写应用程序,就好像他们是唯一使用计算机的人一样,而共享资源则允许多个应用程序同时在计算机上运行。由于应用程序在共享一台具有固定资源集的物理计算机,所以隔离所创造的假象是有限度的。例如,如果一个应用程序消耗了所有的 CPU 资源,那么其他应用程序就无法执行。然而,在大多数情况下,这些机制已经改变了系统和软件架构的面貌。它们从根本上改变了我们构思、部署和支付计算资源的方式。
为什么这个主题会引起架构师的兴趣和关注呢?作为一名架构师,你可能倾向于(或者实际上被要求)使用某种形式的虚拟化来部署你所创建的软件。对于越来越多的应用程序,你将把它们部署到云端(将在 第 17 章 中介绍)并使用容器来实现。此外,在需要部署到专用硬件的情况下,虚拟化允许你在一个比专用硬件更容易访问的环境中进行测试。
本章的目的是介绍在使用虚拟资源时一些最重要的术语、考虑因素和权衡。
16.1 共享资源
出于经济原因,许多组织采用了某种形式的共享资源。这可以极大地降低部署系统的成本。我们通常关心共享的有四种资源:
- 中央处理器(CPU):现代计算机有多个 CPU(并且每个 CPU 可以有多个处理核心)。它们还可能有一个或多个图形处理单元(GPU)或其他专用处理器,例如张量处理单元(TPU)。
- 内存:一台物理计算机有固定数量的物理内存。
- 磁盘存储:磁盘为指令和数据提供持久存储,在计算机重新启动和关闭期间都能保留。一台物理计算机通常有一个或多个连接的磁盘,每个磁盘都有固定的存储容量。磁盘存储可以指旋转的磁性或光学硬盘驱动器设备,也可以指固态磁盘驱动器设备;后者既没有磁盘也没有任何驱动的移动部件。
- 网络连接:如今,每台重要的物理计算机都有一个或多个网络连接,所有消息都通过这些连接传输。
现在我们已经列举了我们想要共享的资源,我们需要考虑如何共享它们,以及如何以足够 “隔离” 的方式进行共享,以便不同的应用程序不知道彼此的存在。
处理器共享是通过线程调度机制实现的。调度程序选择一个执行线程并将其分配给一个可用的处理器,该线程保持控制直到处理器被重新调度。没有应用程序线程可以在不经过调度程序的情况下获得对处理器的控制。当线程让出处理器的控制、固定时间间隔到期或发生中断时,就会进行重新调度。
从历史上看,随着应用程序的增长,所有的代码和数据无法装入物理内存。为了应对这一挑战,开发了虚拟内存技术。内存管理硬件将一个进程的地址空间划分为页面,并根据需要在物理内存和辅助存储之间交换页面。在物理内存中的页面可以立即被访问,其他页面存储在辅助存储器中,直到需要时为止。硬件支持将一个地址空间与另一个地址空间隔离。
磁盘共享和隔离是通过几种机制实现的。首先,只能通过磁盘控制器访问物理磁盘,磁盘控制器确保流向和来自每个线程的数据流按顺序传送。此外,操作系统可以用诸如用户 ID 和组等信息标记正在执行的线程以及磁盘内容(如文件和目录),并通过比较请求访问的线程的标记和磁盘内容来限制可见性或访问。
网络隔离是通过消息的识别来实现的。每个虚拟机(VM)或容器都有一个互联网协议(IP)地址,用于识别发送到该 VM 或容器或从该 VM 或容器发出的消息。本质上,IP 地址用于将响应路由到正确的 VM 或容器。另一种用于发送和接收消息的网络机制依赖于端口的使用。每个针对服务的消息都有一个与之相关联的端口号。一个服务在一个端口上监听,并接收到达服务正在执行的设备上的、指定给该服务正在监听的端口的消息。
16.2 虚拟机
现在我们已经了解了如何将一个应用程序的资源使用与另一个应用程序的资源使用隔离开来,我们可以运用并结合这些机制。“虚拟机” 允许在一台物理计算机中执行多个模拟的或虚拟的计算机。
图 16.1 描绘了几个驻留在一台物理计算机中的虚拟机。物理计算机被称为 “主机”,虚拟机被称为 “客户机”。图 16.1 还展示了一个 “虚拟机管理程序”,它是虚拟机的操作系统。这个虚拟机管理程序直接在物理计算机硬件上运行,通常被称为 “裸机” 或 “类型 1” 虚拟机管理程序。它所托管的虚拟机实现应用程序和服务。裸机虚拟机管理程序通常在数据中心或云中运行。
图 16.1 裸机虚拟机管理程序和虚拟机
图 16.2 描绘了另一种类型的虚拟机管理程序,称为 “托管” 或 “类型 2” 虚拟机管理程序。在这种情况下,虚拟机管理程序作为一种服务在主机操作系统之上运行,而虚拟机管理程序又托管一个或多个虚拟机。托管虚拟机管理程序通常在台式机或笔记本电脑上使用。它们允许开发人员运行和测试与计算机主机操作系统不兼容的应用程序(例如,在 Windows 计算机上运行 Linux 应用程序,或在苹果计算机上运行 Windows 应用程序)。它们还可以用于在开发计算机上复制生产环境,即使操作系统在两者上是相同的。这种方法确保开发环境和生产环境相互匹配。
图 16.2 托管虚拟机管理程序
虚拟机管理程序要求其客户虚拟机使用与底层物理 CPU 相同的指令集 —— 虚拟机管理程序不翻译或模拟指令执行。例如,如果你有一个用于使用 ARM 处理器的移动或嵌入式设备的虚拟机,你不能在使用 x86 处理器的虚拟机管理程序上运行那个虚拟机。另一种与虚拟机管理程序相关的技术支持跨处理器执行;它被称为 “模拟器”。模拟器读取目标或客户处理器的二进制代码,并在主机处理器上模拟客户指令的执行。模拟器通常还模拟客户 I/O 硬件设备。例如,开源的 QEMU 模拟器 1 可以模拟一个完整的 PC 系统,包括 BIOS、x86 处理器和内存、声卡、显卡,甚至软盘驱动器。
托管 / 类型 2 虚拟机管理程序和模拟器允许用户通过主机的屏幕显示、键盘和鼠标 / 触摸板与在虚拟机内部运行的应用程序进行交互。开发桌面应用程序或在专用设备上工作的开发人员,如移动平台或物联网设备,可以使用托管 / 类型 2 虚拟机管理程序和 / 或模拟器作为他们构建 / 测试 / 集成工具链的一部分。
虚拟机管理程序执行两个主要功能:(1)管理在每个虚拟机中运行的代码,(2)管理虚拟机本身。详细说明如下:
-
通过访问虚拟磁盘或网络接口与虚拟机外部进行通信的代码被虚拟机管理程序拦截,并由虚拟机管理程序代表虚拟机执行。这允许虚拟机管理程序标记这些外部请求,以便对这些请求的响应可以路由到正确的虚拟机。
对 I/O 设备或网络的外部请求的响应是一个异步中断。这个中断最初由虚拟机管理程序处理。由于多个虚拟机在一台单一的物理主机上运行,并且每个虚拟机可能都有未完成的 I/O 请求,虚拟机管理程序必须有一种方法将中断转发到正确的虚拟机。这就是前面提到的标记的目的。
-
虚拟机必须被管理。例如,它们必须被创建和销毁,以及其他操作。管理虚拟机是虚拟机管理程序的功能。虚拟机管理程序不会自行决定创建或销毁一个虚拟机,而是根据用户的指令,或者更常见的是根据云基础设施的指令(你将在 第 17 章 中了解更多关于这方面的内容)来行动。创建虚拟机的过程涉及加载一个 “虚拟机镜像”(在下一节中讨论)。
除了创建和销毁虚拟机之外,虚拟机管理程序还对它们进行监控。健康检查和资源使用情况是监控的一部分。虚拟机管理程序也位于虚拟机的防御安全边界内,作为对攻击的防御。
最后,虚拟机管理程序负责确保虚拟机不超过其资源使用限制。每个虚拟机在 CPU 利用率、内存、磁盘和网络 I/O 带宽方面都有限制。在启动虚拟机之前,虚拟机管理程序首先确保有足够的物理资源可满足该虚拟机的需求,然后在虚拟机运行时执行这些限制。
虚拟机的启动方式与裸机物理机的启动方式相同。当机器开始执行时,它会自动从磁盘存储(无论是计算机内部的还是通过网络连接的)读取一个特殊的程序,称为引导加载程序。引导加载程序将操作系统代码从磁盘读入内存,然后将执行转移到操作系统。对于物理计算机,在加电过程中建立与磁盘驱动器的连接。对于虚拟机,在虚拟机管理程序启动虚拟机时建立与磁盘驱动器的连接。“虚拟机镜像” 部分将更详细地讨论这个过程。
从虚拟机内部的操作系统和软件服务的角度来看,似乎软件是在裸机物理机内部执行。虚拟机提供一个 CPU、内存、I/O 设备和一个网络连接。
鉴于它必须解决的许多问题,虚拟机管理程序是一个复杂的软件。虚拟机的一个问题是虚拟化所需的共享和隔离所带来的开销。也就是说,与直接在裸机物理机上运行相比,服务在虚拟机上运行会慢多少?这个问题的答案很复杂:它取决于服务的特性和所使用的虚拟化技术。例如,执行更多磁盘和网络 I/O 的服务比不共享这些主机资源的服务产生更多的开销。虚拟化技术一直在不断改进,但据微软报告,其 Hyper-V 虚拟机管理程序的开销约为 10%。2
虚拟机对架构师有两个主要影响:
- 性能:虚拟化会带来性能成本。虽然类型 1 虚拟机管理程序只带来适度的性能损失,但类型 2 虚拟机管理程序可能会带来显著更大的开销。
- 关注点分离:虚拟化允许架构师将运行时资源视为商品,将供应和部署决策推迟到另一个人或组织。
16.3 虚拟机镜像
我们将启动虚拟机的磁盘存储内容称为 “虚拟机镜像”。这个镜像包含代表构成我们将运行的软件(即操作系统和服务)的指令和数据的位。这些位根据你的操作系统使用的文件系统组织成文件和目录。镜像还包含存储在其预定位置的引导加载程序。
有三种方法可以用来创建新的虚拟机镜像:
- 你可以找到一台已经在运行你想要的软件的机器,并对该机器内存中的位进行快照复制。
- 你可以从一个现有的镜像开始,并添加额外的软件。
- 你可以从头开始创建一个镜像。在这里,你首先获取你选择的操作系统的安装介质。你从安装介质启动你的新机器,它会格式化机器的磁盘驱动器,将操作系统复制到驱动器上,并在预定位置添加引导加载程序。
对于前两种方法,有机器镜像存储库可用(通常包含开源软件),提供各种只有操作系统内核的最小镜像、其他包含完整应用程序的镜像以及介于两者之间的所有镜像。这些高效的起点可以支持你快速尝试一个新的软件包或程序。
然而,当你下载并运行一个不是你(或你的组织)创建的镜像时,可能会出现一些问题:
- 你无法控制操作系统和软件的版本。
- 镜像可能包含有漏洞的软件或没有安全配置的软件;更糟糕的是,镜像可能包含恶意软件。
虚拟机镜像的其他重要方面是:
- 这些镜像非常大,所以通过网络传输它们可能非常慢。
- 一个镜像与它的所有依赖项捆绑在一起。
- 你可以在你的开发计算机上构建一个虚拟机镜像,然后将其部署到云中。
- 你可能希望向虚拟机添加你自己的服务。
虽然在创建镜像时你可以很容易地安装服务,但这会导致每个服务的每个版本都有一个独特的镜像。除了存储成本之外,这种镜像的扩散变得难以跟踪和管理。因此,通常的做法是创建只包含操作系统和其他基本程序的镜像,然后在虚拟机启动后向这些镜像添加服务,这个过程称为配置。
16.4 容器
虚拟机解决了共享资源和保持隔离的问题。然而,虚拟机镜像可能很大,在网络上传输虚拟机镜像很耗时。假设你有一个 8GB 的虚拟机镜像。你希望将其从网络上的一个位置移动到另一个位置。理论上,在每秒 1Gb 的网络上,这将需要 64 秒。然而,实际上 1Gbps 的网络运行效率约为 35%。因此,在现实世界中传输一个 8GB 的虚拟机镜像将需要超过 3 分钟。虽然你可以采用一些技术来减少这个传输时间,但结果仍然是以分钟为单位测量的时间。在镜像传输后,虚拟机必须启动操作系统并启动你的服务,这还需要更多的时间。
“容器” 是一种在减少镜像传输时间和启动时间的同时保持虚拟化大部分优势的机制。与虚拟机和虚拟机镜像一样,容器被打包成可执行的容器镜像进行传输。(然而,在实践中并不总是遵循这个术语。)
重新审视 图 16.1,我们看到虚拟机在虚拟机管理程序的控制下在虚拟化硬件上执行。在 图 16.3 中,我们看到几个容器在 “容器运行时引擎” 的控制下运行,而容器运行时引擎又在一个固定的操作系统之上运行。容器运行时引擎充当一个虚拟化的操作系统。就像物理主机上的所有虚拟机共享相同的底层物理硬件一样,主机内的所有容器通过运行时引擎(并且通过操作系统,它们共享相同的底层物理硬件)共享相同的操作系统内核。操作系统可以加载到裸机物理机或虚拟机上。
图 16.3 在虚拟机管理程序(或裸机)之上的操作系统之上的容器运行时引擎之上的容器
虚拟机是通过找到一个有足够未使用资源来支持一个额外虚拟机的物理机来分配的。从概念上讲,这是通过查询虚拟机管理程序以找到一个有空闲容量的来实现的。容器是通过找到一个有足够未使用资源来支持一个额外容器的容器运行时引擎来分配的。这可能反过来需要创建一个额外的虚拟机来支持一个额外的容器运行时引擎。图 16.3 描绘了在虚拟机管理程序控制下的虚拟机中的操作系统上运行的容器运行时引擎上运行的容器。
这种操作系统的共享代表了在传输镜像时性能提升的一个来源。只要目标机器上有一个标准的容器运行时引擎在运行(如今所有的容器运行时引擎都是按照标准构建的),就没有必要将操作系统作为容器镜像的一部分进行传输。
性能提升的第二个来源是在容器镜像中使用 “层”。(注意,容器层与我们在 第 1 章 中介绍的模块结构中的层概念不同。)为了更好地理解容器层,我们将描述容器镜像是如何构建的。在这种情况下,我们将说明构建一个用于运行 “LAMP 栈” 的容器,并以层的方式构建镜像。(LAMP—— 代表 Linux、Apache、MySQL 和 PHP—— 是一种广泛用于构建 Web 应用程序的栈。)
使用 LAMP 栈构建镜像的过程如下:
- 创建一个包含 Linux 发行版的容器镜像。(这个镜像可以使用容器管理系统从库中下载。)
- 一旦你创建了镜像并将其标识为一个镜像,就执行它(即实例化它)。
- 使用那个容器加载服务 —— 在我们的例子中是 Apache,使用 Linux 的功能。
- 退出容器并通知容器管理系统这是第二个镜像。
- 执行这个第二个镜像并加载 MySQL。
- 退出容器并给这个第三个镜像一个名称。
- 再重复这个过程一次并加载 PHP。现在你有了第四个容器镜像;这个映像包含整个 LAMP 栈。
因为这个镜像是逐步创建的,并且你告诉容器管理系统将每个步骤都作为一个镜像,所以容器管理系统认为最终的镜像由 “层” 组成。
现在你可以将 LAMP 栈容器镜像移动到不同的位置用于生产。初始移动需要移动栈的所有元素。然而,假设你将 PHP 更新到一个较新的版本并将这个修订后的栈投入生产(前面过程中的步骤 7)。容器管理系统知道只有 PHP 被修订了,只移动映像的 PHP 层。这节省了移动栈的其余部分所涉及的工作量。由于在镜像中更改软件组件比初始镜像创建要频繁得多,将容器的新版本投入生产比使用虚拟机要快得多。加载虚拟机需要几分钟的时间量级,而加载容器的新版本需要微秒或毫秒的时间量级。请注意,这个过程仅适用于栈的最上层。例如,如果你想用一个较新的版本更新 MySQL,你将需要执行前面列表中的步骤 5 到 7。
你可以创建一个包含创建容器镜像步骤的脚本并将其存储在一个文件中。这个文件特定于你用于创建容器镜像的工具。这样的文件允许你指定哪些软件片段要被加载到容器中并作为镜像保存。对规范文件使用版本控制确保你的团队的每个成员都可以创建一个相同的容器镜像,并根据需要修改规范文件。将这些脚本视为代码带来了许多优势:这些脚本可以被有意识地设计、测试、进行配置控制、审查、记录和共享。
16.5 容器和虚拟机
在虚拟机中交付服务与在容器中交付服务之间有哪些权衡?
正如我们前面提到的,虚拟机对物理硬件(CPU、磁盘、内存和网络)进行虚拟化。在虚拟机上运行的软件包括一个完整的操作系统,并且你几乎可以在虚拟机中运行任何操作系统。你也几乎可以在虚拟机中运行任何程序(除非它必须直接与物理硬件交互),这在处理遗留软件或购买的软件时很重要。拥有完整的操作系统还允许你在同一个虚拟机中运行多个服务 —— 当服务紧密耦合或共享大型数据集时,或者如果你想利用在同一虚拟机环境中运行的服务之间可用的高效内部服务通信和协调时,这是一个理想的结果。虚拟机管理程序确保操作系统启动,监控其执行,并在操作系统崩溃时重新启动它。
容器实例共享一个操作系统。操作系统必须与容器运行时引擎兼容,这限制了可以在容器上运行的软件。容器运行时引擎启动、监控并重新启动在容器中运行的服务。这个引擎通常在一个容器实例中只启动和监控一个程序。如果那个程序正常完成并退出,该容器的执行就会结束。出于这个原因,容器通常只运行一个服务(尽管该服务可以是多线程的)。此外,使用容器的一个好处是容器映像的大小很小,只包括支持我们想要运行的服务所必需的那些程序和库。容器中的多个服务可能会使映像大小膨胀,增加容器启动时间和运行时内存占用。正如我们很快将看到的,我们可以将运行相关服务的容器实例分组,以便它们在同一台物理机器上执行并可以高效地通信。一些容器运行时引擎甚至允许一个组内的容器共享内存和诸如信号量之类的协调机制。
虚拟机和容器之间的其他差异如下:
- 虚拟机可以运行任何操作系统,而目前容器仅限于 Linux、Windows 或 iOS。
- 虚拟机中的服务通过操作系统功能启动、停止和暂停,而容器中的服务通过容器运行时引擎功能启动和停止。
- 虚拟机在其中运行的服务终止后仍然存在;容器则不是。
- 在使用容器时存在一些对端口使用的限制,而在使用虚拟机时不存在这些限制。
16.6 容器的可移植性
我们已经介绍了容器与之交互的容器运行时管理器的概念。有几家供应商提供容器运行时引擎,最著名的是 Docker、containerd 和 Mesos。这些供应商中的每一个都有一个容器运行时引擎,该引擎提供创建容器映像以及分配和执行容器实例的功能。容器运行时引擎和容器之间的接口已由开放容器倡议(Open Container Initiative)标准化,这使得由一个供应商的软件包(例如 Docker)创建的容器能够在另一个供应商(例如 containerd)提供的容器运行时引擎上执行。
这意味着你可以在自己的开发计算机上开发一个容器,将其部署到生产计算机上,并使其在那里执行。当然,每种情况下可用的资源会有所不同,所以部署仍然不是一件轻而易举的事。如果你将所有资源指定为配置参数,那么将容器迁移到生产环境的过程就会简化。
16.7 Pod(容器组)
Kubernetes 是用于部署、管理和扩展容器的开源编排软件。它的层级结构中还有一个元素:Pod(容器组)。一个 “Pod” 是一组相关的容器。在 Kubernetes 中,节点(硬件或虚拟机)包含 Pod,Pod 包含容器,如 图 16.4 所示。Pod 中的容器共享一个 IP 地址和端口空间以接收来自其他服务的请求。它们可以使用进程间通信(IPC)机制(如信号量或共享内存)相互通信,并且它们可以共享在 Pod 生命周期内存在的临时存储卷。它们具有相同的生命周期 ——Pod 中的容器会一起被分配和释放。例如,在 第 9 章 中讨论的服务网格通常被打包为一个 Pod。
图 16.4 包含多个 Pod(容器组)的节点,每个 Pod 又包含多个容器
Pod(容器组)的目的是降低紧密相关的容器之间的通信成本。在 图 16.4 中,如果容器 1 和容器 2 频繁通信,它们作为一个 Pod(容器组)进行部署(从而被分配到同一个虚拟机上)这一事实,使得它们能够使用比消息传递更快的通信机制。
16.8 无服务器架构
回想一下,分配虚拟机首先要找到一台有足够空闲容量的物理机,然后将虚拟机映像加载到该物理机中。因此,这些物理计算机构成了一个可从中分配资源的资源池。现在假设你希望将容器分配到容器运行时引擎中,而不是将虚拟机分配到物理机中。也就是说,你有一个容器运行时引擎池,容器被分配到这个池中。
容器的加载时间非常短 —— 冷启动只需几秒,重新分配只需几毫秒。现在让我们更进一步。由于虚拟机的分配和加载相对耗时,可能需要数分钟来加载和启动实例,所以即使在请求之间存在空闲时间,你通常也会让虚拟机实例保持运行。相比之下,由于将容器分配到容器运行时引擎的速度很快,就没有必要让容器一直运行。我们可以为每个请求重新分配一个新的容器实例。当你的服务完成一个请求的处理时,它不会循环回去接收另一个请求,而是直接退出,容器停止运行并被释放。
这种系统设计方法被称为 “无服务器架构”—— 尽管实际上并非没有服务器。存在托管容器运行时引擎的服务器,但由于它们是根据每个请求动态分配的,所以服务器和容器运行时引擎体现在基础设施中。作为开发人员,你不需要负责分配或释放它们。支持这种功能的云服务提供商特性被称为函数即服务(FaaS)。
响应单个请求进行动态分配和释放的一个结果是,这些短生命周期的容器无法维护任何状态:容器必须是无状态的。在无服务器架构中,任何用于协调的所需状态必须存储在云提供商提供的基础设施服务中,或者作为参数传递。
云提供商对 FaaS 特性施加了一些实际限制。首先,提供商提供的基础容器镜像选择有限,这限制了你的编程语言选项和库依赖关系。这样做是为了减少容器加载时间 —— 你的服务被限制为在提供商的基础映像层之上的一个薄镜像层。下一个限制是,当你的容器首次被分配和加载时的 “冷启动” 时间可能长达数秒。后续请求几乎可以即时处理,因为你的容器镜像被缓存在某个节点上。最后,对一个请求的执行时间有限制 —— 你的服务必须在提供商的时间限制内处理请求并退出,否则将被终止。云提供商这样做是出于经济原因,以便能够针对 FaaS 定制价格(相较于其他运行容器的方式),并确保没有 FaaS 用户过度消耗资源池。一些无服务器系统的设计者花费大量精力来绕过或克服这些限制 —— 例如,预启动服务以避免冷启动延迟、发送虚拟请求以保持服务在缓存中,以及将请求从一个服务派生或链接到另一个服务以延长有效执行时间。
16.9 小结
虚拟化对软件和系统架构师来说是一个福音,因为它为联网(通常是基于网络的)服务提供了高效、经济的分配平台。硬件虚拟化允许创建多个共享同一台物理机的虚拟机。它在这样做的同时确保了 CPU、内存、磁盘存储和网络的隔离。因此,物理机的资源可以在多个虚拟机之间共享,同时将一个组织必须购买或租用的物理机数量降至最低。
虚拟机镜像是加载到虚拟机中以使其能够执行的一组比特。虚拟机镜像可以通过各种供应技术创建,包括使用操作系统功能或加载预先创建的镜像。
容器是一种对操作系统进行虚拟化的打包机制。如果有兼容的容器运行时引擎可用,容器就可以从一个环境迁移到另一个环境。容器运行时引擎的接口已经标准化。
将多个容器放入一个 Pod(容器组)意味着它们将一起被分配,并且容器之间的任何通信都可以快速完成。
无服务器架构允许容器快速实例化,并将分配和释放的责任转移到云提供商的基础设施上。
16.10 扩展阅读
本章的材料取自《软件工程师的部署与运维》[Bass 19],在该书中你能找到更详细的论述。
维基百科始终是查找协议、容器运行时引擎和无服务器架构的最新详细信息的好去处。
16.11 问题讨论
1. 使用 Docker 创建一个 LAMP 容器。将你创建的容器镜像大小与你在互联网上找到的一个进行比较。差异的来源是什么?在什么情况下,这会成为你作为架构师所担心的问题?
2. 容器管理系统如何知道只有一层被更改,从而只需传输一层?
3. 我们已经关注了在虚拟机管理程序上同时运行的虚拟机之间的隔离。虚拟机可能会关闭并停止执行,新的虚拟机可能会启动。虚拟机管理程序如何在不同时间运行的虚拟机之间保持隔离或防止泄漏?提示:考虑内存、磁盘、虚拟 MAC 地址和 IP 地址的管理。
4. 将哪些服务集组合成一个 Pod(就像服务网格那样)是合理的?为什么?
5. 与容器相关的安全问题有哪些?你将如何缓解这些问题?
6. 在嵌入式系统中使用虚拟化技术会带来哪些问题?
7. 使用虚拟机、容器和 Pod 可以避免哪些类型的集成和部署错误?哪些类型不能避免?
qemu.org ↩︎
https://docs.microsoft.com/en-us/biztalk/technical-guides/system-resource-costs-on-hyper-v ↩︎