介绍
通常有许多障碍阻碍您将应用程序轻松地通过开发周期并最终投入生产。 除了开发应用程序以在每个环境中做出适当响应的实际工作之外,您还可能面临跟踪依赖关系、扩展应用程序和更新单个组件而不影响整个应用程序的问题。
Docker 容器化和面向服务的设计试图解决其中的许多问题。 应用程序可以分解为可管理的功能组件,与所有依赖项单独打包,并轻松部署在不规则架构上。 缩放和更新组件也得到了简化。
在本指南中,我们将讨论容器化的好处以及 Docker 如何帮助解决我们上面提到的许多问题。 Docker 是分布式容器部署中的核心组件,可提供轻松的可扩展性和管理。
Linux 容器化简史
容器化和隔离在计算世界中并不是新概念。 十多年来,一些类 Unix 操作系统利用成熟的容器化技术。
在 Linux 中,构成后来容器化技术基础的构建块 LXC 在 2008 年被添加到内核中。 LXC 结合使用内核 cgroups(允许隔离和跟踪资源利用率)和命名空间(允许将组分开,因此它们不能“看到”彼此)来实现轻量级进程隔离。
后来,引入了 Docker,以简化创建和管理容器所需的工具。 它最初使用 LXC 作为其默认执行驱动程序(此后它为此开发了一个名为 libcontainer
的库)。 Docker 虽然没有引入许多新想法,但通过简化流程和标准化界面,使普通开发人员和系统管理员可以使用它们。 它激发了开发人员对 Linux 世界中容器化的新兴趣。
虽然我们将在本文中讨论的一些主题更为笼统,但我们将主要关注 Docker 容器化,因为它具有压倒性的流行度和标准采用率。
容器化带来了什么
容器为开发人员和系统管理员/运营团队带来了许多非常有吸引力的好处。
下面列出了一些最大的好处。
将主机系统从容器化应用程序中抽象出来
容器意味着完全标准化。 这意味着容器使用定义的接口连接到主机和容器外部的任何东西。 容器化应用程序不应依赖或关注有关底层主机资源或架构的详细信息。 这简化了关于操作环境的开发假设。 同样,对于主机来说,每个容器都是一个黑盒子。 它不关心里面的应用程序的细节。
易于扩展
主机系统和容器之间的抽象的好处之一是,如果应用程序设计正确,扩展可以简单直接。 面向服务的设计(稍后讨论)与容器化应用程序相结合,为轻松扩展奠定了基础。
开发人员可以在他们的工作站上运行一些容器,而该系统可以在暂存或测试区域中水平扩展。 当容器投入生产时,它们可以再次向外扩展。
简单的依赖管理和应用程序版本控制
容器允许开发人员将应用程序或应用程序组件与其所有依赖项捆绑为一个单元。 主机系统不必关心运行特定应用程序所需的依赖关系。 只要能运行 Docker,应该能运行所有的 Docker 容器。
这使得依赖管理变得容易,也简化了应用程序版本管理。 主机系统和运营团队不再负责管理应用程序的依赖需求,因为除了对相关容器的依赖之外,它们都应该包含在容器本身中。
极其轻量级、隔离的执行环境
虽然容器没有提供与虚拟化技术相同级别的隔离和资源管理,但它们从权衡中获得的是一个极其轻量级的执行环境。 容器在进程级别被隔离,共享主机的内核。 这意味着容器本身不包含完整的操作系统,导致几乎是即时启动时间。 开发人员可以轻松地从他们的工作站运行数百个容器而不会出现问题。
共享分层
容器在不同意义上是轻量级的,因为它们是在“层”中提交的。 如果多个容器基于同一层,它们可以共享底层而无需重复,从而使后续镜像的磁盘空间利用率非常低。
可组合性和可预测性
Docker 文件允许用户定义创建新容器映像所需的确切操作。 这使您可以像编写代码一样编写执行环境,并在需要时将其存储在版本控制中。 在相同环境中构建的相同 Docker 文件将始终生成相同的容器映像。
使用 Dockerfile 进行可重复、一致的构建
虽然可以使用交互式过程创建容器映像,但一旦知道必要的步骤,最好将配置步骤放在 Dockerfile 中。 Dockerfile 是简单的构建文件,描述了如何从已知的起点创建容器镜像。
Dockerfile 非常有用,而且相当容易掌握。 他们提供的一些好处是:
- 易于版本控制:Dockerfiles 本身可以提交版本控制以跟踪更改并恢复任何错误
- Predicatability:从 Dockerfile 构建镜像有助于消除镜像创建过程中的人为错误。
- Accountability:如果您计划共享您的镜像,提供创建镜像的 Dockerfile 以供其他用户审核过程通常是一个好主意。 它基本上提供了创建映像所采取的步骤的命令历史记录。
- 灵活性:从 Dockerfile 创建图像允许您覆盖提供交互式构建的默认值。 这意味着您不必提供尽可能多的运行时选项来使图像按预期运行。
Dockerfile 是自动化容器镜像构建以建立可重复过程的绝佳工具。
容器化应用的架构
在设计要在容器中部署的应用程序时,首先要关注的领域之一是应用程序的实际架构。 通常,容器化应用程序在实现面向服务的设计时效果最好。
面向服务的应用程序将系统的功能分解为离散的组件,这些组件通过明确定义的接口相互通信。 容器技术本身鼓励这种类型的设计,因为它允许每个组件独立扩展或升级。
实现此类设计的应用程序应具有以下品质:
- 他们不应该关心或依赖主机系统的任何细节
- 每个组件都应该提供一致的 API,消费者可以使用这些 API 来访问服务
- 每个服务都应该在初始配置期间从环境变量中获取线索
- 应用程序数据应存储在容器外部的已安装卷或数据容器中
只要 API 得到维护,这些策略就允许每个组件独立换出或升级。 由于每个组件都可以根据遇到的瓶颈进行扩展,因此它们还有助于专注于水平可扩展性。
每个组件通常可以定义合理的默认值,而不是硬编码特定值。 组件可以使用这些作为备用值,但应该更喜欢它可以从其环境中收集的值。 这通常通过服务发现工具的帮助来完成,组件可以在其启动过程中查询这些工具。
从实际容器中取出配置并将其放入环境中,可以轻松更改应用程序行为,而无需重建容器映像。 它还允许单个设置影响组件的多个实例。 一般来说,面向服务的设计与环境配置策略结合得很好,因为两者都允许更灵活的部署和更直接的扩展。
使用 Docker Registry 进行容器管理
一旦您的应用程序被拆分为功能组件并配置为适当地响应环境中的其他容器和配置标志,下一步通常是通过注册表使您的容器映像可用。 将容器镜像上传到注册表允许 Docker 主机通过简单地知道镜像名称来下拉镜像并启动容器实例。
有多种 Docker 注册表可用于此目的。 有些是公共注册表,任何人都可以查看和使用已提交的图像,而其他注册表是私有的。 可以标记图像,以便它们易于下载或更新。
结论
Docker 提供了分布式容器部署所需的基本构建块。 通过将应用程序组件打包在它们自己的容器中,水平扩展变成了启动或关闭每个组件的多个实例的简单过程。 Docker 提供的工具不仅可以构建容器,还可以管理和与新用户或主机共享它们。
虽然容器化应用程序提供了必要的进程隔离和打包来协助部署,但还有许多其他组件需要在分布式主机集群上充分管理和扩展容器。 在我们的 下一篇指南 中,我们将讨论服务发现和全局分布式配置存储如何为集群容器部署做出贡献。