Unikernels、Docker 以及你为什么应该关注
Docker 最近收购 Unikernel Systems 的举动在微服务领域引起了轰动。与此同时,许多人不知道该如何看待这件事,所以这里快速解释一下为什么这一举动是件好事。
即使您可能没有参与构建或维护基于微服务的软件,但您肯定会使用它。许多流行的网站和服务都由微服务驱动,例如 Netflix、eBay 和 PayPal。微服务架构非常适合云计算和“按需扩展”,因此您肯定会在未来看到更多这样的应用。
更好的微服务工具对开发者来说是好消息,但对用户也有好处。当开发者得到更好的支持时,他们会开发出更好的软件。最终,这意味着更多功能和更少的 bug,对所有人都有利。当然,这是一个相当懒惰的论点。所以,这里更详细地描述一下 Docker 和独内核。
Docker 是一种工具,允许开发者将其软件封装在一个容器中,该容器提供完全可预测的运行时环境。
为了充分理解容器,有必要了解虚拟机。虚拟机几乎正如其名称所示:一种非实际的机器——一种模拟,如果你愿意这么称呼的话。换句话说,它的行为就像一台完整的计算机,包括硬件、文件系统、操作系统、服务和应用软件。由于它是模拟的,因此您可以在同一台机器上运行多个虚拟机。
你为什么要这样做呢?这样做有几个很好的理由。
第一个原因是运行为不同操作系统构建的软件。例如,如果您在 Ubuntu 笔记本电脑上开发 Android 应用程序,则可以使用虚拟机来测试该应用程序是否正常工作。或者,如果您无法让 Windows 程序通过 Wine 运行,则可以在 VirtualBox 中运行 Windows。在这些示例中,虚拟机使您免于切换操作系统或设备的痛苦。
虚拟机已成为高容量企业计算世界中必不可少的工具。在虚拟机普及之前,物理服务器通常会运行单个应用程序或服务,这是一种非常低效的物理资源使用方式。大多数时候,只有一小部分服务器的内存、CPU 和带宽被使用。扩展规模意味着购买新的服务器——而这很昂贵。
虚拟机意味着可以在同一台服务器上同时运行多个服务器。这确保了昂贵的物理资源得到充分利用。
虚拟机也是解决多年来困扰开发者的一个问题的方案:所谓的“在我的机器上可以工作”问题,这个问题发生在开发环境与生产环境不同时。这种情况经常发生。本不应该发生,但它确实发生了。在不同的机器上找到不同版本的软件是很正常的。程序可能对其运行环境非常敏感,尤其是在涉及到它们的依赖项时。库或软件包中的一个微小差异就可能破坏在开发者机器上可以工作的代码。
当然,雇主和客户对“在我的笔记本电脑上可以工作”的论点并不满意。他们希望它在他们的机器上也能工作。
如果开发机器和生产机器使用相同的虚拟机,则代码应该在两种环境中都完美运行。使用虚拟机的抽象,您可以对运行时环境进行大量控制。
虽然虚拟机解决了许多问题,但它们并非没有自身的缺点。首先,存在大量的重复。
想象一下,您在服务器上同时运行两个 CentOS 虚拟机。它们都包含完整的 CentOS 安装,从内核到完整的 GNU 应用程序和实用程序套件、标准服务、语言运行时、软件包和脚本。虚拟机之间唯一的区别是特定的应用程序代码、其数据文件及其依赖项。
容器(例如 Docker)提供了比全功能虚拟机更轻量级的替代方案。在许多方面,它们与虚拟机非常相似。它们为运行代码提供了基本独立的环境。最大的区别在于它们通过共享来减少重复。首先,它们共享宿主环境的 Linux 内核。它们还可以共享操作系统的其余部分。
事实上,除了应用程序代码和数据之外,它们可以共享一切。例如,我可以使用容器在同一台物理机器上运行两个 WordPress 博客。可以将两个容器设置为共享除模板文件、媒体上传和数据库之外的所有内容。
通过一些复杂的文件系统技巧,每个容器都可能“认为”它拥有专用的文件系统。在这里详细描述有点复杂,但请相信我,这非常酷。
与完整的虚拟机相比,容器更轻便,开销更低。Docker 使处理这些容器相对容易,因此开发者和运维人员可以使用相同的代码。而且,容器也非常适合云计算。
那么,微服务和独内核呢?
微服务是一个新概念——或者说是一个旧概念,这取决于您的视角。
这个概念是,与其构建一个庞大的“单体”应用程序,不如将您的应用程序分解为多个服务,这些服务通过消息传递系统(一个定义明确的接口)相互通信。每个微服务都设计有单一的职责。它专注于做好一件简单的事情。
如果您作为经验丰富的 Linux 用户对此感到熟悉,那应该是的。它是 UNIX 哲学的一些主要原则的扩展。程序应该专注于做好一件事并将其做好,并且软件应该由通过定义明确的接口连接的简单部分组成。
微服务通常在它们自己的容器中运行。它们通常通过 TCP 和宿主环境(或可能跨网络)进行通信。
使用微服务构建软件的优势在于代码是松散耦合的。如果您需要修复 bug 或添加功能,您只需要在少数几个地方进行更改。对于单体应用程序,您可能需要更改多个代码片段。
更重要的是,使用微服务架构,您可以扩展那些感到压力的特定微服务。您不必复制整个应用程序。
使用容器来开发和部署微服务架构支持了可扩展性的目标,但也引入了一些缺点。
首先,每个容器消耗的资源都超过了它实际需要的资源。每个容器本质上都是一个完整的 GNU Linux 系统,但每个微服务只使用了底层操作系统的一小部分功能。每个运行的服务都会消耗内存和 CPU 周期,其中许多是完全不必要的。
让我们考虑一下安全外壳 (SSH) 服务。这对于管理员将直接与之交互的微服务可能很有用,但对于暴露简单 TCP 接口的微服务来说,这是一个昂贵的负担。这些微服务不需要许多功能。
效率不是这里唯一关注的问题。当容器共享相同的 Linux 内核时,这为一系列安全漏洞打开了大门。利用内核漏洞的恶意代码可能会影响在同一台机器上运行的其他容器。
Linux 是一个“大杂烩”系统——它包含了大多数多用户环境所需的一切。它拥有人类已知的最深奥的硬件组合的驱动程序。
但在微服务世界中,这种级别的支持绝对是过度的。不需要完整的服务集合,并且宿主虚拟机监控器将公开最少的虚拟设备集,从而消除了对广泛的设备驱动程序集合的需求。即使使用巧妙的容器技巧(例如在容器之间共享文件和代码),仍然存在大量浪费。
独内核是一种更轻量级的替代方案,非常适合微服务。独内核是一个自包含的环境,仅包含微服务运行所需的底层功能。而且,这包括内核功能。
这是可能的,因为该环境使用了“库操作系统”。换句话说,每个内核功能都在一个底层库中实现。当编译微服务代码时,它会与其需要的功能打包在一起,而微服务不使用的通用功能会被剥离掉。
生成的捆绑包比专用虚拟机或容器小得多。独内核无需捆绑 GB 级的通用代码和功能,就可以在几百 KB 中交付完整的微服务。这意味着它们启动和运行速度非常快,并且在同一台物理服务器上可以同时运行更多独内核。
独内核自然也比容器或虚拟机更安全。如果攻击者能够访问容器或虚拟机,他们将拥有整个 Linux 安装来利用。另一方面,独内核只有少数几个功能可以利用,这严重限制了未经授权的用户可以造成的破坏。
独内核很棒,但在过去,开发者很难使用它们。Docker 是一种工具,可以轻松地容器化应用程序和微服务。
Docker 最近收购 Unikernel Systems 意味着它将扩展对独内核的支持,使其更容易在实际的开发和生产环境中使用它们。考虑到独内核为现代架构带来的广泛优势,这是一个令人兴奋的消息。