查看原文
其他

RedHat容器术语实用导论

Go开发大全 2021-01-31

(给Go开发大全加星标)

原文:Scott McCarty,翻译:dockone.io

【导读】 最近几年红帽一直活跃在容器社区内,参与开发和指定标准。本篇文章是红帽容器开发者Scott McCarty(@fatherlinux)2018年发布的一篇文章,其中详细梳理了容器各类概念。



你可能有这样的观点,容器看起来就像是一个已经非常简单的概念,为什么我还需要去了解它相关的术语呢?在我以容器技术传播者身份的工作中,遇到了很多容器术语被滥用的情况,导致大家在掌握容器的道路上走了很多弯路。容器和镜像等术语可以互换使用,但是它们之间存在非常大的概念差异。在容器的世界里,镜像(Repositories)的含义可能跟你想的完全不同。此外,容器化技术的概念比 docker 更大。如果你不熟悉术语,可能很难理解 Docker 和(选择一个你最爱的,CRI-O,rkt,lxc/lxd)之间的区别,或者很难理解开放容器运动(Open Container Initiative)为容器标准化所做的工作。

背景(Background)


开始使用 Linux 容器看起来很简单。只需要几分钟即可安装好,像 docker ,这样的容器引擎,并运行你的第一个命令。在接下来的几分钟内,你可能尝试构建你的第一个容器镜像并打算共享它。接着,你开始构建类生产环境上的容器,并借此熟悉整个流程,(但)渐渐地意识到,你必须了解(容器)背后的许多术语和相关技术。可惜糟糕的是,下面的很多术语可以互换使用...这通常会给新手带来一些混乱。

  • 容器

  • 镜像(Image)

  • 容器镜像(Container Image)

  • 镜像层

  • 镜像仓库(Registry)

  • 镜像(Repository)

  • 标签

  • 基础镜像

  • 平台镜像


了解本技术词典中提供的术语将使你更深入地了解底层技术。这将帮助你和你的团队拥有共同的语言,能提供有关如何更好地实现目标,以及如何设计容器依赖环境的见解。作为工业级和备受广泛关注的社区,这种深入的理解将使我们能够构建新的架构和解决方案。请注意,此技术词典假定读者已经了解如何运行容器。如果你需要的是入门级的介绍,请尝试从红帽的开发人员博客上的 Docker Containers 使用简介开始。

容器 101


理解容器术语,必须精确理解容器的内容。一个容器可以从两方面来看。就像一个普通的 Linux 程序一样,容器有两个状态 - 停止和运行。停止时,容器是保存在磁盘上的一个文件(或一组文件)。这被称为容器镜像或镜像(Repository)。当你键入命令启动容器时,容器引擎将解压所需要的文件和元数据,然后将它们交给 Linux 内核。启动容器与启动正常的 Linux 进程非常像,都需要调用 Linux 内核 API。这个 API 调用通常会启动额外的隔离并装载容器镜像中的文件副本。运行后,容器实际上就是一个 Linux 进程。启动容器的过程以及磁盘上的镜像格式都由标准定义和管理。

当前,有几种相互竞争的容器镜像格式(Docker,Appc,LXD),但业界正在推进开放容器运动(Open Container Initiative),即 OCI。OCI 的范围包括了容器镜像格式规范,该规范定义了容器镜像的磁盘格式,以及定义了诸如硬件体系结构和操作系统(Linux,Widnows 等)的元数据。容器镜像格式的标准化使行业内软件生态系统蓬勃发展 - 不管是个人,还是项目,供应商都能够构建相互兼容的镜像和工具。普通用户希望工具,像签名,扫描,构建,运行,移动和管理容器镜像,可以相互兼容。

还有几个相互竞争的容器化引擎,包括 docker,CRI-O,Railcar,RKT,LXC。这些容器引擎使用容器镜像并将其转换为容器(又叫运行时)。但这些容器化引擎当前都受 OCI 的支配,包括 Container Runtime Specification 和被称为 RunC 的 Reference Runtime Implementation 实现。RunC 是开源的,以社区运营的模式管理,它在创建容器时需要与主机内核通信,已经支持着多种容器引擎。

如果按照 OCI 容器镜像格式规范和容器运行时规范实现的工具,可以保证可跨云提供商,或者有各种内部体系结构的容器平台,容器引擎,并且也保证了工具之间的可移植性。了解容器的命名,容器标准和构建模块的架构,你才可以跟其他架构师顺利地进行交流,并构建可扩展和可被支持的容器化应用程序环境,确保在未来几年内都可高效地运行。

基础词汇(Basic Vocabulary)


  • 容器镜像(Container Image)


容器镜像是一个最简单的定义,它是一个从镜像仓库上拉下来的文件,在启动容器时作为一个安装点在本地使用。容器社区大量使用 “容器镜像” 这个术语,但这个术语有多种含义。Docker,RKT 甚至 LXD,都是基于拉取远程文件并将其作为容器运行的概念。这几种容器引擎以不同的方式处理容器镜像。LXD 提取单个容器镜像(单层),而 docker 和 RKT 使用由多个镜像层(多层)组成并符合 OCI 规范的镜像。

从技术上来说,它比镜像仓库上的单个文件复杂得多。当人们使用术语 ”容器镜像“ 时,他们通常意味着镜像(Repository),那些指向多个镜像层,以及能够提供关于这些镜像层额外信息的元数据。

容器镜像的概念隐含了容器镜像格式的概念。

  • 容器镜像格式(Container Image Format)


历史上,每个容器引擎都有自己的容器镜像格式。LXD,RKT 和 Docker 都有自己的镜像格式。有些是由单层构成的,而另外一些由树形结构中的一堆层构成。但今天,几乎所有主流的工具和引擎都已经使用开放容器运动(OCI)定义的格式。该镜像格式定义了容器镜像中的镜像层和元数据格式。本质上,OCI 镜像格式定义了一个由每个镜像层的 tar 文件组成的容器镜像和一个包含元数据的 manifest.json 文件。



开放容器运动(OCI)基于 Docker V2 镜像格式,当前已经成功统一了大多数的容器引擎,云提供商和工具提供商(安全扫描,签名,构建和移动),构建了自己的生态系统。这将有助于保护用户在相关知识和工具,环境上的投资。

  • 容器引擎(Container Engine)


容器引擎本质上是一种软件,它接受用户的请求,包括命令行选项,拉取镜像,并以用户的权限运行容器。大多数容器引擎都是这样做的,包括 docker,RKT,CRI-O 和 LXD。另外,大多数云提供商,服务平台(PaaS)和容器平台都有自己内置的容器引擎,它们使用 Docker 或 OCI 兼容的容器镜像。符合行业标准的容器镜像格式可以在所有这些不同平台之间实现互操作性。

深入一层,大多数容器引擎实际上并不运行容器,它们依赖于 OCI 兼容的运行时,如 RunC。通常,容器运行时负责:

  • 处理用户输入

  • 处理,通常来自容器编排的 API 输入

  • 从镜像仓库拉取容器镜像

  • 使用图形驱动程序(块或文件取决于驱动程序)展开,解压缩,和扩展磁盘上的容器镜像

  • 准备一个容器安装点,通常是写时复制的存储上(取决于驱动程序块或者文件)

  • 准备将传递到容器运行时的元数据,以正确启动容器

  • 使用容器镜像中的一些默认值(例如 Arch X86)

  • 使用用户输入来覆盖镜像中的默认值(例如 CMD, ENTRYPOINT)

  • 使用容器镜像制定的默认值(例如 SECCOM 规则)

  • 调用容器运行时


有关更深入的了解,请参阅理解容器标准。另请参阅容器运行时

  • 容器(Container)


实际上,容器在操作系统中已经存在有一段时间了。容器是容器镜像的运行时实例化。通常通过 clone() 系统调用,而不是 fork() 或者 exec() 创建的标准的 Linux 进程。而且,容器通常通过 cgroups,SELinux 或者 AppArmor 进一步隔离。

  • 容器主机(Container Host)


容器主机是指运行容器程序的系统,通常简称为容器。例如,这可能是在公有云,或者你们数据中的裸机上,运行的 RHEL Atomic 系统。一旦容器镜像(又叫镜像(Repository))从镜像仓库中提取到本地容器主机上,它就被称为在本地的缓存。

确定哪些镜像已经缓存到本地,可以使用以下命令:

[root@rhel7 ~]# docker images -a
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEregistry.access.redhat.com/rhel7 latest 6883d5422f4e 3 weeks ago 201.7 MB


  • 镜像仓库(Registry Server)


镜像仓库本质上是一个花哨的文件服务器,用于存储 docker 镜像。通常,镜像仓库被指定了域名和端口号。docker 生态系统中的大部分有价值的东西都来自于从镜像仓库推送和提取镜像的能力。

当 docker 守护进程没有镜像的本地缓存副本时,它会自动将其从容器指定的路径上提取过来。大多数 Linux 发行版将 docker 守护进程配置为从 docker.io 上提取,但可以在某些 Linux 发行版上自己配置。例如,红帽企业 Linux 被配置为首先从 registry.access.redhat.com 提取镜像,然后才是 docker.io(Docker Hub)。

强调镜像仓库存在隐式的信任是非常重要的。你必须知道你有多少个信任的镜像仓库机构在提供内容,并且你可能想要允许或阻止某些镜像仓库机构。除了安全性外,还有一些其他的问题,例如用户可以访问许可软件和合规性的问题。docker 允许用户提取软件的简介,使得能够确认上游内容的真实性,这非常重要。

在红帽企业 Linux 中,默认的 docker 镜像仓库是可配置的。通过修改配置文件,可以在 RHEL7 和 RHEL7 Atomic 中添加或阻止特定镜像仓库

bash vi /etc/sysconfig/docker

在 RHEL 7 和 RHEL 7 Atomic 中,红帽的镜像仓库是开箱即用的:

bash ADD_REGISTRY='--add-registry registry.access.redhat.com'

为了安全起见,阻止 DockerHub 等公共 Docker 镜像仓库可能很有用:

bashBLOCK_REGISTRY='--block-registry'

红帽还提供带有 Openshift 容器平台集成的镜像仓库,带 Quay Enterprise 的独立企业镜像仓库,以及 Quay.io 上的基于云,公共和私有镜像仓库。


  • 容器编排(Container Orchestration)


通常,团队从安装容器主机开始,然后拉取一些容器镜像。接着,它们开始构建一些新的容器镜像,并将它们推送到镜像仓库,以与他们的团队中的其他人员共享。过了一段时间后,他们想将几个容器连接在一起并将他们作为一个整体的单元进行部署。最后,在某个时候,他们希望将该单元推送到生产环境中(Dev/QA/Prod)。这个流程就是容器编排产生的需求。

一个容器编排实际上做了两件事:

1、在一组计算机中动态调度容器,实现工作负载,这通常称为分布式计算。

2、提供标准化的应用程序定义文件(kube yaml,docker compose 等)。

以上两个特性提供了很多功能:

允许应用程序中的容器完全分开安排。这是有意义的,如果:

  • 允许使用大容量主机集群

  • 单个容器允许失败(进程挂起,内存不足(OOM))

  • 容器主机失败(磁盘,网络,重新启动)

  • 容器引擎失败(异常退出,重启)

  • 单个容器需要伸缩


将相同应用程序的新实例部署到新环境中很容易。在云计算的世界里,你可能想要这样做的原因有很多,其中包括:
- 在运行容器协调器的开发人员的笔记本电脑上
- 在私有名称空间中的共享开发环境中
- 在内部公共名称空间的共享开发环境中进行可视化开发和测试
- 内部质量保证(QA)
- 在云中动态配置,取消配置,的负载测试环境中
- 在重要的环境下测试与生产环境的兼容性
- 在生产环境中
- 在灾难恢复环境中
- 在已升级的容器主机上,容器引擎或容器协调器的新的生产环境中
- 在具有相同版本的容器主机,容器引擎和容器编排的新生产环境中,新的地理位置(APAC,EMEA 等)

社区和供应商正在开发容器调度程序。从历史上看,Swarm,Mesos 和 Kubernetes 是三大巨头,但最近甚至 Docker 和 Mesosphere 都宣布支持 Kubernetes -- 几乎所有主流的云服务提供商都是如此。与之前的 Linux 相似,Kubernetes 已经成为容器业务流程中的事实标准。如果你正在看容器编排,红帽建议你关注下我们发布的 OpenShift。

高级词汇(Advanced Vocabulary)


  • 容器运行时(Container Runtime)


容器运行时通常在容器引擎中使用了较低级别的组件,但也可以手动进行测试。开放容器运动(OCI)运行时标准参考了 RunC 的实现。这是使用最广泛的容器运行时,但还有其他 OCI 兼容运行时,如 railcar 和 katacontainers。Docker,CRI-O 和许多其他依赖于 runc 的容器引擎。

容器运行时负责:

  • 使用容器引擎提供的容器装载点(也可以是用于测试的普通目录)

  • 使用容器引擎提供的容器元数据(可以是手动构建的用于测试的 config.json)

  • 与内核通信以启动容器化进程(克隆系统调用)

  • 设置 cgroups

  • 设置 SELinux 策略

  • 设置 App Armor 规则

  • 为了提供一些历史记录,当 Docker 引擎首次创建时,它依靠 LXC 作为容器运行时。后来,Docker 团队开发了自己的 libcontainer 库来启动容器。这个库是用 Golang 写的,并且被编译成当前的 Docker 引擎。最后,当创建 OCI 时,Docker 捐赠了 libcontainer 代码并将其转换为独立运行的程序 runc。现在,runc 作为实现时的参考,并被其他容器引擎(如 CRI-O)使用。在底层,无论容器引擎是哪一个,它提供了一致启动容器的能力。Runc 是一个非常简洁的应用程序,只需要提供一个挂载点(目录)和元数据(config.json)给它。有关 runc 的更多信息,请参阅本教程。


有关更深入的了解,请参阅理解容器标准。另请参阅容器运行时

  • 镜像层(Image Layer)


镜像(Repository)通常被称为镜像或容器镜像,但实际上他们是由一个或多个镜像层组成的。镜像(Repository)中的镜像层以父 - 子关系连接在一起。每个镜像层代表其本身和父层之间的变化。

下面,我们将检查本地容器主机上的镜像(Repository)的镜像层。从 Docker 1.7 版本之后,就没有本地工具可以检查本地镜像(Repository)中的镜像层(有用于容器镜像仓库的工具)。在 Dockviz 工具的帮助下,你可以快速检查所有的镜像层。请注意,每个镜像层都有标签和唯一通用标识符(UUID)。下面的命令将返回缩写版本的 UUID,这些版本通常具有足够的唯一性,可在单台计算机上使用。如果你需要完整的 UUID,请使用 -no-trunc 选项。

docker run --rm --privileged -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz images -t
├─2332d8973c93 Virtual Size: 187.7 MB │ └─ea358092da77 Virtual Size: 187.9 MB │ └─a467a7c6794f Virtual Size: 187.9 MB │ └─ca4d7b1b9a51 Virtual Size: 187.9 MB │ └─4084976dd96d Virtual Size: 384.2 MB │ └─943128b20e28 Virtual Size: 386.7 MB │ └─db20cc018f56 Virtual Size: 386.7 MB │ └─45b3c59b9130 Virtual Size: 398.2 MB │ └─91275de1a5d7 Virtual Size: 422.8 MB │ └─e7a97058d51f Virtual Size: 422.8 MB │ └─d5c963edfcb2 Virtual Size: 422.8 MB │ └─5cfc0ce98e02 Virtual Size: 422.8 MB │ └─7728f71a4bcd Virtual Size: 422.8 MB │                             └─0542f67da01b Virtual Size: 422.8 MB Tags: docker.io/registry:latest


注意,“docker.io/registry“ 镜像(Repository)实际上由许多镜像层组成。更重要的是,请注意,用户可能会根据这些镜像层中的任何一层 ”运行“ 容器。以下命令完全有效,但不保证已经过测试或实际上可以正常工作。通常,镜像构建器将为你使用的特定镜像层添加标签(为其添加名称):

bash docker run -it 45b3c59b9130 bash

镜像(Repository)以这种方式构建:每当镜像构建器创建新的镜像时,差异就会被保存为一个镜像层。在镜像仓库中创建新的镜像层有两种主要方式。首先,如果手动构建镜像层,每个 “提交” 都会创建一个新的镜像层。如果镜像构建器使用 Dockerfile 构建镜像,则文件中的每个指令都会创建一个新的镜像层。如果可以查看容器中层与层之间的修改将非常有用。

  • 标签(Tag)


即使用户可以指定安装的容器,并从容器的任何镜像层开始,他们也不一定要这样做。当镜像构建器创建新的容器镜像时,他们通常被标记为最佳的镜像层。这些被称为标签,是容器镜像构建者与容器镜像消费者进行通信的工具,哪些镜像层最适合使用。通常,标签用于指定镜像仓库中的软件版本。这只是惯例 - 实际上,OCI 或任何其他标准都没有要求只能使用哪些标签,这可能会导致用户滥用。要小心这一点,因为它可能会在开发,运营和架构团队中造成很多混乱,因此如果你将其用于软件版本以外的任何其他应用程序中,请将其记录下来。


有一个特殊的标签 - latest - 通常指向镜像(Repository)中最新版本软件的镜像层。这个特殊的标签仍然是一个镜像层,就像任何其他标签一样,因此也可能会被滥用。

要远程查看容器镜像中可用的标签,请运行下面的命令(jq 程序使输出更加容易阅读):

curl -s registry.access.redhat.com/v1/repositories/rhel7/tags | jq {"7.0-21": "e1f5733f050b2488a17b7630cb038bfbea8b7bdfa9bdfb99e63a33117e28d02f","7.0-23": "bef54b8f8a2fdd221734f1da404d4c0a7d07ee9169b1443a338ab54236c8c91a","7.0-27": "8e6704f39a3d4a0c82ec7262ad683a9d1d9a281e3c1ebbb64c045b9af39b3940","7.1-11": "d0a516b529ab1adda28429cae5985cab9db93bfd8d301b3a94d22299af72914b","7.1-12": "275be1d3d0709a06ff1ae38d0d5402bc8f0eeac44812e5ec1df4a9e99214eb9a","7.1-16": "82ad5fa11820c2889c60f7f748d67aab04400700c581843db0d1e68735327443","7.1-24": "c4f590bbcbe329a77c00fea33a3a960063072041489012061ec3a134baba50d6","7.1-4": "10acc31def5d6f249b548e01e8ffbaccfd61af0240c17315a7ad393d022c5ca2","7.1-6": "65de4a13fc7cf28b4376e65efa31c5c3805e18da4eb01ad0c8b8801f4a10bc16","7.1-9": "e3c92c6cff3543d19d0c9a24c72cd3840f8ba3ee00357f997b786e8939efef2f","7.2": "6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e","7.2-2": "58958c7fafb7e1a71650bc7bdbb9f5fd634f3545b00ec7d390b2075db511327d","7.2-35": "6883d5422f4ec2810e1312c0e3e5a902142e2a8185cd3a1124b459a7c38dc55b","7.2-38": "6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e","latest": "6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e"}


  • 镜像(Repository)


译者注:镜像(Repository)可理解为包含多个版本的容器镜像,它们拥有相同的镜像名,但是有不同的版本(Tag),各个版本之间共享一些镜像层,它们在存储时会被检查与这个镜像的镜像层是否相同,如果相同,则实际上只是给相同的镜像层打上一个新的,另外的,标签名。

在使用 docker 命令时,镜像(Repository)可以通过命令行指定的,不需要整个镜像名。在下面的命令中,“rhel7” 就是镜像(Repository)。

bash docker pull rhel7

这实际上将自动解释为:

bash docker pull registry.access.redhat.com/rhel7:latest

这可能会让人感到困惑,许多人将其称为镜像(Repository)或者容器镜像。实际上,docker image 这个子命令是用来列出本地可用的镜像(Repository)。从概念上讲,这些镜像(Repository)可以被认为是容器镜像,但重要的是要认识到这些容器镜像实际上由镜像层组成,并且涉及到文件清单(manifest.json)中包含的元数据

docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEregistry.access.redhat.com/rhel7 latest 6883d5422f4e 4 weeks ago 201.7 MBregistry.access.redhat.com/rhel latest 6883d5422f4e 4 weeks ago 201.7 MBregistry.access.redhat.com/rhel6 latest 05c3d56ba777 4 weeks ago 166.1 MBregistry.access.redhat.com/rhel6/rhel latest 05c3d56ba777 4 weeks ago 166.1 MB...

当我们在命令行上指定容器镜像时,容器引擎正在为你做一些额外的工作。在这种情况下,docker 守护程序(不是客户端工具)配置要搜索的服务器列表。在我们上面的示例中,守护进程将在每个配置的服务器上搜索 “rhel7“ 镜像(Repository)。

在上面的命令中,只指定镜像(Repository)的名称,但也可以使用 docker 客户端指定完整的 URL。为了突出这一点,我们先剖析一个完整的 URL。

经常看到另外一种指定的方法

EGISTRY/NAMESPACE/REPOSITORY[:TAG]

完整的 URL 由标准服务器名称,名字空间和可选标签组成。实际上有很多关于如何指定 URL 的排列方式,并且在你探索 Docker 生态系统时,你会发现很多部分是可选的。下面的命令都是有效的,并且都会拉取同一个容器镜像:


bash docker pull registry.access.redhat.com/rhel7/rhel:latestdocker pull registry.access.redhat.com/rhel7/rheldocker pull registry.access.redhat.com/rhel7docker pull rhel7/rhel:latest


  • 命名空间(Namespace)


命名空间是用于分隔容器镜像组的工具。在公共 DockerHub 上,命名空间通常是共享镜像的人的用户名,但也可以是组名或逻辑名。

红帽根据 Red Hat Federated Registry 服务器上列出的产品,使用命名空间分隔容器镜像组。下面是 registry.access.redhat.com 返回的一些示例结果。注意,最后的结果实际上是在另一个容器镜像仓库上列出的。这是因为红帽公司也会搜索我们的合作伙伴的容器镜像仓库:

registry.access.redhat.com/rhel7/rhel registry.access.redhat.com/openshift3/mongodb-24-rhel7registry.access.redhat.com/rhscl/mongodb-26-rhel7registry.access.redhat.com/rhscl_beta/mongodb-26-rhel7registry-mariadbcorp.rhcloud.com/rhel7/mariadb-enterprise-server:10.0


请注意,有时不需要指定完整的 URL。在这种情况下,给定名称空间有一个默认的镜像(Repository)。如果用户指定 fedora 命名空间,则默认镜像(Repository)中的最新标记将被拉取到本地服务器上。因此,运行下面的命令基本上都是同样的结果,越往下越具体

bash docker pull fedoradocker pull docker.io/fedoradocker pull docker.io/library/fedora:latest


  • 内核命名空间(Kernel Namespace)


内核命名空间与我们在讨论的镜像(Repository)和镜像仓库时提到的命名空间不同。在讨论容器时,内核命名空间可能是最重要的数据结构了,因为他们启动了今天我们所知的所有容器。内核命名空间使每个容器都拥有自己的挂载点,网络接口,用户标识符,进程标识符等。

当你在 Bash 终端输入一个命令并敲入会车时,Bash 向内核发出一个请求,使用 exec() 系统调用的一个版本创建一个正常的 Linux 进程。容器是特殊的,因为当你向 docker 这样的容器引擎发送请求时,docker 守护进程向内核发出一个请求,以使用不同的系统调用 clone() 创建一个容器化的进程。这个 clone() 系统调用是特殊的,因为它可以用它自己的虚拟挂载点,进程 ID,用户 ID,网络接口,主机名等创建一个进程。

虽然在技术上,Linux 中没有单一的数据结构代表一个容器,但内核命名空间和 clone() 系统调用已经尽可能接近我们所需。


  • 图驱动程序(Graph Driver)


当终端用户指定要运行的容器镜像的标签时 - 默认情况下,用的是最新的标签 - 图形驱动程序将解压缩所有依赖的镜像层,以构建所选标签中的数据。图形驱动程序是将镜像(Repository)中必要的镜像层映射到本地存储的软件。容器镜像层可以使用像 Overlay2 这样的驱动程序映射到一个目录,或者使用 Device Mapper 之类的驱动程序在块存储中映射到一个目录。驱动程序包括:aufs,devicemapper,btrfs,zfs 和 overlayfs。

当容器启动时,镜像层以只读内核命名空间装入。容器镜像中的镜像层始终安装为只读,但默认情况下,还会设置单独的写时复制层。这允许容器化过程在容器内写入数据。写入数据时,它将存储在底层主机上的写时复制层中。可以通过使用诸如 -readonly 之类的选项来禁用该写入时复制层。

docker 守护进程拥有它自己的一套图形驱动程序,还有其他一些开源库,它们提供图形驱动程序,如用于像 CRI-O,Skopeo 和其他容器引擎这样的工具的容器/镜像。

通过 docker info 命令可以知道你正在使用哪个图形驱动程序:

[root@rhel7 ~]# docker info
...Storage Driver: devicemapperPool Name: docker-253:1-884266-poolPool Blocksize: 65.54 kBBacking Filesystem: xfsData file: /dev/loop0Metadata file: /dev/loop1Data Space Used: 3.037 GBData Space Total: 107.4 GBData Space Available: 2.56 GBMetadata Space Used: 2.707 MBMetadata Space Total: 2.147 GBMetadata Space Available: 2.145 GBUdev Sync Supported: trueDeferred Removal Enabled: falseData loop file: /var/lib/docker/devicemapper/devicemapper/dataMetadata loop file: /var/lib/docker/devicemapper/devicemapper/metadataLibrary Version: 1.02.107-RHEL7 (2015-10-14)


容器使用例子(Container Use Cases)


容器设计模式形成了很多种类。由于容器是容器镜像的运行时版本,因此它的构建方式与其运行方式密切相关。

某些容器镜像被设计成受限运行,而其他容器镜像可能要求更高,需要类似根权限等。有许多维度可以评估模式,并且用户通常会在同一个容器镜像/容器中看到多个模式或使用案例。

本节将深入探讨用户使用容器解决的一些常见例子。

  • 应用容器(Application Containers)


应用容器是最受欢迎的容器形式。这些是开发人员和应用程序所有者所关心的。应用程序容器包含开发人员写的处理代码。他们还可能包括诸如 MySQL,Apache,MongoDB 和 Node.js 之类的东西。

已经有很好的应用容器构建生态系统。像 Software Collections 这样的项目正在提供安全和可支持呃应用程序容器镜像,以便与红帽企业 Linux 一起使用。于此同时,红帽社区成员正在推动一些优秀的顶尖应用容器。

红帽认为应用程序容器通常不需要特殊的权限来运行他们的工作。也就是说,生产容器环境通常需要的只是非特权应用程序容器来提供支持服务。


  • 操作系统容器(Operating System Containers)


操作系统容器是指更容易虚拟成完整的操作系统的容器。操作系统容器仍然共享一个主机内核,但运行一个完整的 init 系统,使他们能够轻松地运行多个进程。LXC 和 LXD 是操作系统容器的例子,因为他们看起来很像一个完整的虚拟机。

也可以使用 Docker/OCI 容器来近似操作系统容器,但需要在容器内运行 systemd。这允许最终用户像通常那样安装软件,并将容器看作更像完整的操作系统。

bash yum install mysqlbash systemctl enable mysql

这使迁移现有应用程序变得更加容易。红帽正在努力通过使 systemd 在容器内运行并通过加工管理来使操作系统容器变得更容易实现。尽管许多用户尚未准备好采用微服务,但他们仍然可以从采用基于镜像的容器作为软件交付模式中获益。


  • 小容器(Pet Containers)


尽管红帽肯定会推荐,支持并鼓励使用云主机模式来进行新应用的开发,但实际上并非所有现有的应用程序都会被重写以利用新模式。许多现有的应用程序都是独一无二的,其中有一种,通常被称为 “小程序” 的应用程序。专门为了处理这些小应用而构建的容器有时被称为小容器。

小容器为用户提供了依赖于镜像仓库,容器镜像和用于基础设施的标准容器主机标准化容器基础架构的便携性和便利性,并为了容器内的传统环境提供灵活性。这个想法使得更容易集成现有的应用程序,例如在容器中使用 systemd。目标是重用现有的自动化,安装程序和工具来轻松创建一个可以马上运行的容器镜像。

  • 超级特权容器(Super Privileged Containers)


在专用容器主机(如红帽推出的企业级 Linux Atomic 主机)上构建基础架构时,系统管理员仍需要执行管理任务。无论是与分布式系统(如 Kubernetes 还是 OpenShift)或独立容器主机一起使用,超级特权容器(SPC)是一个强大的工具。SPC 甚至可以执行诸如加载专用内核模块的操作,比如使用 systemtap。

在构建用于运行容器的基础架构中,管理员很可能需要 SPC 来执行监视,备份等操作。认识到 SPC 与主机内核之间通常存在更紧密耦合的实际情况很重要,因此管理员需要选择坚如磐石的容器主机并对其进行标准化,特别是在大型集群/分布式环境中,否则将很难排除故障。然后需要在 SPC 中选择与主机内核兼容的用户空间。

  • 工具和操作系统软件(Tools & Operating System Software)


Linux 发行版一直为用户提供系统软件,例如 Rsyslogd,SSSD,sadc 等。历史上,这些系统软件是通过 RPM 或 DEB 软件包安装。但随着容器作为包装格式的出现,通过容器镜像安装系统软件变得既方便又容易。红帽为 RedHat 虚拟化工具,rsyslog,sssd 和 sadc 等产品提供了一些预先打包的容器。

容器体系结构(Architecture of Containers)


随着越来越多的人使用容器提供的软件,新的设计模式正在形成。红帽工程正在利用并推动社区中的许多类似的模式。本节的目标是帮助突出并定义其中的一些模式。

容器保存在磁盘上的方式(即其镜像格式)可能会对运行方式产生巨大的影响。例如,设计用于运行 sssd 的容器需要在运行时拥有特殊权限,否则无法完成其工作。下面是容器社区中形成的模式的部分列表:

  • 应用镜像(Application Images)


这些镜像是终端用户使用的。其中包括数据库和 Web 服务器,应用程序和服务总线。这些可以在内部构件或从 ISV 交付给客户。通常最终用户会调查并关心用于创建独立镜像的位数。独立的镜像是最简单的镜像小号,但最难设计,构建和打补丁。

  • 基础镜像(Base Images)


基础镜像是最简单的镜像类型之一,但你会发现其有很多定义。有时用户会参考企业标准来构建,甚至会拿应用镜像作为 ”基础镜像“。从技术上说,这不是基础镜像。这些是中间镜像。

简而言之,基础镜像是没有父层的镜像。通常,基本镜像包含操作系统的全新副本。基本镜像通常包括安装软件包所需的工具(yum,rpm,apt-get,dnf,microdnf),并随着时间的推移对镜像进行更新。虽然基础镜像可以 “手工制作”,但实际上他们通常由开源项目(如 Debian,Fedora 或 CentOS)和供应商(如 红帽)制作和发布。基础镜像的出处对于安全至关重要。简而言之,基础镜像的唯一目的是为创建衍生镜像提供一个开始的位置。在使用 dockerfile 时,你正要使用的基础镜像是非常明确的:

FROM registry.access.redhat.com/rhel7-atomic


  • 编译镜像(Builder Images)


这些是容器镜像的一种特殊形式,它以后代的形式生成应用程序容器镜像。他们包括除开发者的源代码之外的所有内容。编译镜像包括操作系统库,语言的运行时,中间件以及源到镜像的工具。

当构建容器镜像运行时,它会注入开发者源代码并生成一个即可运行的后代应用程序容器镜像。这个新创建的应用容器镜像可以在开发或生产环境中运行。

例如,如果开发人员拥有 PHP 代码,并且他们希望在容器中运行该代码,则他们可以使用 PHP 编译镜像来生成可立即运行的应用程序容器镜像。开发人员通过存储代码的 GitHub URL,而编译镜像则使他们完成剩余的工作。编译容器的输出是一个应用程序容器镜像,其中包括红帽企业版 Linux,来自软件集合的 PHP 以及开发人员的代码,可以随时运行。

编译镜像提供了一种强大的方式,可以快速轻松地从代码到容器,一切从可信组件中构建而成。

  • 容器组件(Containerized Components)


容器意味着被部署的是一个很大的软件系统,而且它不是单独部署的。两大趋势推动了这一现象。

首先,微服务正在推动最佳组件的使用 - 这也推动了将更多组件组合在一起以构建单一应用程序的使用。容器化组件满足了更快更简单地部署大量复杂软件的需求。这些组件中的每一个都可以有不同的版本,而容器镜像可以帮助实现这一点。诸如 Kubernetes/OpenShift 部署 yaml/json,开放服务代理,OpenShift 模版和 Helm Charts 等应用程序定义都可以在更高层次上定义应用程序。

其次,并不是所有的软件都容易部署为容器。有时候,只容纳一些容易移动到容器或为整个项目提供更多价值的组件是有意义的。对于多服务应用程序,某些服务可能作为容器进行部署,而其他服务则可能通过传统方法进行部署,例如 RPM 或安装程序脚本 - 请参阅小容器。但是,其他组件可能很难放入容器,因为他们耦合得太紧而无法分离,需要访问特殊硬件,或者需要较底层的内核 API 等。在较大的应用程序中,可能会有部分应用程序可以容器化。容器化组件代表了可以容器化的组件。容器化组件旨在作为特定应用程序的一部分运行,而不是单独运行。了解容器化组件并非设计为独立运行这一点很重要。他们为更大的软件提供了价值,但他们为自身提供的价值很小。

例如,当 OpenShift Enterprise 3.0 发布时,大多数核心代码都是使用 RPM 部署的,但安装管理员将路由器和注册表部署为容器后。随着 OpenShift 3.1 的发布,向安装程序添加了一个选项,将主节点,openvswitch 和 etcd 组件部署为容器 - 安装后,管理员可以选择将 elasticsearch, fluented 和 kibana 部署为容器。

虽然 OpenShift 安装程序仍然对服务器的文件系统有做修改,但现在可以使用容器镜像来安装所有主要的软件组件。这些容器化组件的含义是,举个例子,内置在 OpenShift 中的 etcd 镜像的一个实例应该永远不会用于存储面向客户的应用程序代码的数据,因为它是一个容器化组件,旨在作为 OpenShift 的一部分运行容器化平台。

随着 OpenShift 的最新发布,越来越多的容器化组件出现了这样的趋势。容器化组件模式变得越来越普遍,其他软件供应商看到了作为容器化组件部署的优势。

  • 部署者的镜像(Deployer Images)


部署者镜像是一种特殊的容器,在运行时部署或管理其他的容器。该模式支持复杂的部署技术,例如强制执行容器的启动顺序,或者优先运行的逻辑,例如填充模式或数据。

例如,“镜像/容器类型“ 模式用于在 OpenShift 中部署日志记录和度量。通过容器部署这些组件,OpenShift 工程团队可以控制不同组件的启动顺序,并确保它们全部启动并一起运行。

  • 中间镜像(Intermediate Images)


中间镜像是依赖于基础镜像的任何容器镜像。通常情况下,核心构建,中间件和语言运行时在基础镜像的 “顶部” 构建为层。这些镜像在另一个镜像的 FROM 指令中被引用。这些镜像不是单独使用的,它们通常作为构建独立镜像的构建块。

通常有不同的专家团队拥有不同的镜像层。系统管理员可能拥有核心构建层,而 “开发人员” 可能拥有中间件层。中间镜像被构建成供其他团队构建镜像使用,但有时也可以单独运行,特别是用于测试。

  • 联合的容器镜像(Intermodal Container Images)


多式联合的容器镜像,类似于联合的容器镜像,是具有混合结构的镜像。例如,可以通过两种方式使用许多红帽软件集合镜像。首先,它们可以用作简单的应用程序容器,运行完整的 Ruby on Rails 和 Apache 服务器。其次,它们可以用作 OpenShift 容器平台内部的构建者镜像。在这种情况下,包含 Ruby on Rails,Apache 的输出子镜像以及源镜像处理的应用程序代码在构建阶段被指向。

通过一个容器镜像解决了两个业务问题,多式联合的模式变得越来越普遍。

  • 系统容器(System Containers)


当系统软件作为容器分发时,通常需要运行超级特权。为了简化部署,并允许这些容器在容器运行时或编排之前启动,红帽开发了一种名为系统容器的特殊容器模式。系统容器在引导过程的早期就开始,并且依赖于原子命令和 systemd 独立于任何容器运行时或编排而被启动。红帽为许多软件提供系统容器,包括 rsyslog,驾驶舱,etcd 和 flanneld。未来,红帽将扩大名单。

这种设计模式将使管理员更容易以模块化方式将这些服务添加到红帽企业 Linux 和原子主机。

结论


容器非常容易使用,但是在构建生产容器环境时,它会改变幕后的复杂性。为了能够讨论体系结构,以及如何建立自己的环境。共享镜像名是很重要的。当你更深入地构建你的环境时,有很多坑。我们先给你留下一些印象。

人们通常可以互换地使用容器镜像和镜像(Repository),而 docker 子命名不会区分镜像和镜像(Repository)。这些命名非常易于使用,但是一旦体系结构讨论开始,了解镜像(Repository)实际上是中央数据结构很重要。

误解命名空间,镜像(Repository),镜像层和标签之间的区别也很容易。其中每个都有构建的目的。尽管不同的供应商和用户将它们用于不同的目的,但它们是我们工具箱中的工具。

本文的目标是让你掌握专业术语,以便可以创建更复杂的体系结构。例如,假设你刚刚负责构建基于角色的基础架构,该基础架构可以根据业务规则来限制哪些命名空间,仓库以及哪些镜像层和标签可以被推入和拉出。最后,请记住,构建容器镜像将对如何运行(编排,特权等)产生深远影响。


 - EOF -

推荐阅读(点击标题可打开)

1、图解 Goroutine 与抢占机制

2、浅析 k8s 容器运行时演进

3、构建现代化的无服务 Go 应用

如果觉得本文不错,欢迎转发推荐给更多人。



分享、点赞和在看

支持我们分享更多好文章,谢谢!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存