文章目录
- 前言
- 背景
- 基本术语
- 容器镜像
- 容器镜像格式
- 容器引擎
- 容器
- 容器主机
- 注册中心
- 容器编排
- 进阶术语
- 容器运行时
- 镜像层
- 标签
- 存储库
- 名称空间
- 参考
前言
本文内容翻译自参考文献。
背景
要理解容器术语,重要的是要精确地理解容器是什么。容器实际上是两个不同的东西。像普通的Linux程序一样,容器实际上有两种状态——休眠和运行。在休眠状态下,容器是保存在磁盘上的一个文件(或一组文件)。这称为容器镜像或容器存储库。当您输入启动容器的命令时,容器引擎将解包所需的文件和元数据,然后将它们交给Linux内核。启动容器与启动普通Linux进程非常相似,需要通过API调用Liunx内核。这个API调用通常会启动额外的隔离并挂载容器镜像中文件的一个副本。一旦运行,容器就只是一个Linux进程。启动容器的过程,以及磁盘上的镜像格式,都由标准定义和管理。
有几种相互竞争的容器镜像格式(Docker, Appc, LXD),但业界正在推进开放容器倡议下的标准,有时简称为开放容器或OCI。OCI的范围包括一个容器镜像格式规范,它定义了容器镜像的磁盘格式以及元数据,元数据定义了硬件架构和操作系统(Linux、Windows等)。标准的容器镜像格式使软件生态系统蓬勃发展——不同的独立贡献者、项目和供应商能够构建可互操作的镜像和工具。用户需要签名、扫描、构建、运行、移动和管理容器镜像的工具之间的互操作性。
也有几个竞争的容器引擎,包括Docker, CRI-O, Railcar, RKT, LXC。这些容器引擎获取容器镜像并将其转换为容器(也就是正在运行的进程)。这是由OCI的范围控制的,其中包括容器运行时规范和称为RunC的参考运行时实现。此参考实现是开源的,由社区开发模型管理,并且通常被许多容器引擎用于在创建容器时与主机内核通信。
针对OCI容器镜像格式规范和容器运行时规范的工具确保了容器平台、容器引擎和云提供商和内部架构的支持工具的广泛生态系统之间的可移植性。了解容器的术语、容器标准和构建块的体系结构,将确保您能够与其他架构师进行沟通,以构建可扩展和可支持的容器化应用程序和环境,以便在未来几年高效地运行容器。
基本术语
容器镜像
从最简单的定义来看,容器镜像是一个从注册中心拉下的文件,在启动容器时用作本地挂载点。容器社区经常使用“容器镜像”,但是这种命名法可能会让人很困惑。Docker、RKT甚至LXD都是基于提取远程文件并将其作为容器运行的概念进行操作的。这些技术都以不同的方式处理容器镜像。LXD提取单个容器镜像(单层),而Docker和RKT使用基于OCI的镜像,这些镜像可以由多层组成。
从技术上讲,它比注册表服务器上的单个文件复杂得多。当人们使用术语“容器镜像”时,他们通常指的是Repository,指的是多个容器镜像层的一束,以及提供有关这些层的额外信息的元数据。
容器镜像的概念中隐含着容器镜像格式的概念。
容器镜像格式
历史上,每个容器引擎都有自己的容器镜像格式。LXD、RKT和Docker都有自己的镜像格式。有些是由一层组成的,而有些是由树结构中的一堆层组成的。今天,几乎所有的主要工具和引擎都转向了开放容器倡议(Open Container Initiative, OCI)定义的格式。这种镜像格式定义了容器镜像中的层和元数据。本质上,OCI镜像格式定义了由每个层的tar文件和清单组成的容器镜像。包含元数据的Json文件。
开放容器倡议(Open Container Initiative, OCI)最初基于Docker V2镜像格式,已经成功地统一了容器引擎、云提供商和工具提供商(安全扫描、签名、构建和移动)的广泛生态系统。这将有助于保护用户在他们的环境中投资知识和工具。
容器引擎
容器引擎是一个接受用户请求(包括命令行选项)、提取镜像并从最终用户的角度运行容器的软件。容器引擎有很多,包括docker、RKT、CRI-O和LXD。此外,许多云提供商、平台即服务(PaaS)和容器平台都有自己内置的容器引擎,它们使用Docker或OCI兼容的容器镜像。拥有行业标准的容器镜像格式允许所有这些不同平台之间的互操作性。
再深入一层,大多数容器引擎实际上并不运行容器,它们依赖于像runc这样兼容OCI的运行时。通常,容器引擎负责:
- 处理用户输入
- 通过API处理输入,通常来自容器编排器
- 从注册中心服务器提取容器镜像
- 使用图形驱动程序(取决于驱动程序的块或文件)在磁盘上解压和扩展容器镜像
- 准备容器挂载点,通常在写时复制存储上(同样是块或文件,取决于驱动程序)
- 准备将传递给容器的元数据,以便正确启动容器
- 使用容器镜像中的一些默认值(例如archx86)
- 使用用户输入来覆盖容器镜像中的默认值(例如CMD, ENTRYPOINT)
- 使用容器镜像指定的默认值(例如SECCOM规则)
- 调用容器运行时
容器
容器在操作系统中已经存在了相当长的时间。容器是容器镜像的运行时实例化。容器是一个标准的Linux进程,通常通过clone()系统调用而不是fork()或exec()创建。此外,容器通常通过使用cgroups、SELinux或AppArmor进一步隔离。
容器主机
容器主机是运行容器化进程的系统。例如,它可以是在VM中运行的RHEL Atomic Host,作为公共云中的实例,或者在数据中心的裸机上运行。一旦容器镜像(又名存储库)从Registry Server拉到本地容器主机,就说它在本地缓存中。
可以使用以下命令确定哪些存储库被同步到本地缓存:
[root@rhel7 ~]# docker images -aREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
registry.access.redhat.com/rhel7 latest 6883d5422f4e 3 weeks ago 201.7 MB
注册中心
注册服务器本质上是一个奇特的文件服务器,用于存储Docker存储库。通常,注册服务器被指定为普通DNS名称和可选的连接端口号。Docker生态系统的大部分价值来自于从注册服务器推送和拉取存储库的能力。
当Docker守护进程没有存储库的本地缓存副本时,它将自动从注册表服务器提取该副本。大多数Linux发行版都配置了从Docker中提取的Docker守护进程。但是它在一些Linux发行版上是可配置的。例如,Red Hat Enterprise Linux被配置为先从registry.access.redhat.com获取存储库,然后再尝试docker。io (Docker Hub)。
必须强调的是,注册中心服务器中存在隐式信任。您必须确定您对注册表提供的内容的信任程度,并且您可能希望允许或阻止某些注册表。除了安全性之外,还有其他问题,例如用户是否有权访问许可软件和遵从性问题。docker允许用户拉取软件的简单性使得信任上游内容变得至关重要。
在Red Hat Enterprise Linux中,默认的docker注册表是可配置的。通过修改配置文件,可以在RHEL7和RHEL7 Atomic中添加或阻止特定的注册表服务器:
vi /etc/sysconfig/docker
在RHEL7和RHEL7 Atomic中,Red Hat的注册表服务器是开箱即用的:
ADD_REGISTRY='--add-registry registry.access.redhat.com'
出于安全考虑,阻止公共docker存储库(如DockerHub)可能会很有用:
# BLOCK_REGISTRY='--block-registry'
容器编排
团队通常从安装容器主机开始,然后拉取一些容器镜像。然后,他们继续构建一些新的容器镜像,并将它们推送到注册表服务器上,以便与团队中的其他人共享。过了一段时间后,他们希望将几个容器连接在一起,并将它们作为一个单元部署。最后,在某种程度上,他们希望将该单元推进到通向生产的管道(Dev/QA/Prod)中。这是实现需要编制的途径。
容器编排器实际上做两件事:
- 动态调度计算机集群中的容器工作负载。这通常被称为分布式计算。
- 提供标准化的应用程序定义文件(kube yaml、docker compose等)
以上两个特性提供了许多功能:
- 允许应用程序中的容器完全单独调度。这在以下情况下很有用:
- 允许使用大型容器主机集群
- 单个容器失败(进程挂起,内存不足)
- 容器主机故障(磁盘、网络、重启)
- 容器引擎失败(损坏、重新启动)
- 单个容器需要按比例放大或缩小
- 将相同应用程序的新实例部署到新环境中很容易。在云原生世界或传统世界中,您可能需要这样做的原因有很多,包括:
- 在带有容器编排器的开发人员笔记本电脑上,
- 在私有命名空间中的共享开发环境中运行,
- 在内部公共命名空间中的共享开发环境中运行,以便在质量保证(QA)环境中进行可见性和测试,
- 在云上动态配置和卸载的负载测试环境中,
- 在新的生产环境中测试与生产环境的兼容性,在灾难恢复环境中测试与生产环境的兼容性
- 在新的生产环境中升级了容器主机、容器引擎或容器编排器的环境,该环境具有新的地理位置(APAC、EMEA等)中的容器主机、容器引擎和容器编排器的相同版本。
社区和供应商正在开发许多容器调度器。从历史上看,Swarm、Mesos和Kubernetes是三巨头,但最近就连Docker和Mesosphere也宣布支持Kubernetes——几乎所有主要的云服务提供商都宣布支持Kubernetes。Kubernetes已经成为容器编排的事实上的标准,类似于之前的Linux。如果您正在考虑容器编排,红帽推荐我们的企业发行版OpenShift
进阶术语
容器运行时
容器运行时是通常在容器引擎中使用的较低层组件,但也可以手工用于测试。开放容器计划(OCI)运行时标准参考实现是runc。这是最广泛使用的容器运行时,但是还有其他兼容OCI的运行时,如crun、railcar和katcontainers。Docker、CRI-O和许多其他容器引擎都依赖于runc。容器运行时负责:
- 使用容器引擎提供的容器挂载点(也可以是用于测试的普通目录)
- 使用容器引擎提供的容器元数据(也可以是手工配置)
- 与内核通信以启动容器化进程(克隆系统调用)
- 设置群组
- 设置SELinux策略
- 设置App Armor规则
为了提供一些历史信息,当Docker引擎第一次创建时,它依赖于LXC作为容器运行时。后来,Docker团队开发了自己的库libcontainer来启动容器。这个库是用Golang编写的,并编译到原始的Docker引擎中。最后,创建OCI时,Docker提供了libcontainer代码,并将其转换为名为runc的独立实用程序。现在,runc是参考实现,并被其他容器引擎(如CRI-O)使用。在最低级别,这提供了一致启动容器的能力,无论容器引擎是什么。Runc是一个非常简洁的实用程序,需要提供挂载点(目录)和元数据(config.json)。有关runc的更多信息,请参阅本教程。
镜像层
存储库通常被称为镜像或容器镜像,但实际上它们由一个或多个层组成。存储库中的镜像层以父子关系连接在一起。每个镜像层表示自身和父层之间的变化。
下面,我们将检查本地容器主机上存储库的层。自Docker 1.7以来,没有本地工具来检查本地存储库中的镜像((有用于在线注册的工具)。在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的存储库实际上是由许多镜像层组成的。更重要的是,请注意,用户可能基于这些层中的任何一层“运行”容器。下面的命令是完全有效的,尽管不能保证已经测试过,甚至不能保证正确地工作。通常,一个镜像生成器会标记(创建一个名称)你应该使用的特定图层:
docker run -it 45b3c59b9130 bash
储存库是以这种方式构造的,因为每当镜像构建器创建新镜像时,差异都会保存为一个层。在存储库中创建新层主要有两种方式。首先,如果手动构建惊险,每次“提交”都会创建一个新层。如果镜像构建器正在用Dockerfile构建一个镜像,那么文件中的每个指令都会创建一个新层。了解每个层之间容器存储库中发生了哪些变化是很有用的。
标签
即使用户可以指定容器装载并从存储库中的任何层开始,他们也不一定要这样做。当镜像构建器创建新的存储库时,他们通常会标记要使用的最佳镜像层。这些被称为标签(tag),是容器镜像构建器与容器镜像使用者沟通的工具,用于告知哪个层最好使用。通常,标记用于指定存储库中的软件版本。这只是约定——实际上,OCI或任何其他标准都强制要求可以使用哪些标签,并且它们可以被滥用于用户想要的任何东西。要小心这样做,因为它会给开发、运维和架构团队带来很多困惑,所以如果你将它用于软件版本以外的任何事情,请做好文档。
有一个特殊的标签——latest——它通常指向存储库中包含最新版本软件的层。这个特殊的标签仍然只是指向一个镜像层,像任何其他标签一样,所以它可以被滥用。
要远程查看存储库中可用的标记,请运行以下命令(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"}
存储库
当使用docker命令时,存储库是在命令行上指定的,而不是镜像。在下面的命令中,“rhel7”是存储库。
docker pull rhel7
这实际上会自动扩展为:
docker pull registry.access.redhat.com/rhel7:latest
这可能令人困惑,许多人将其称为镜像或容器镜像。实际上,docker images子命令用于列出本地可用的存储库。从概念上讲,这些存储库可以被认为是容器镜像,但重要的是要意识到这些存储库实际上是由层组成的,并在一个称为manifest (manifest.json)的文件中包含元数据:
docker imagesREPOSITORY 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”存储库。
在上面的命令中,只指定了存储库名称,但是也可以使用docker客户端指定完整的URL。为了强调这一点,让我们从剖析一个完整的URL开始。
你经常看到的另一种指定方式是:
REGISTRY/NAMESPACE/REPOSITORY[:TAG]
完整的URL由标准服务器名称、名称空间和可选的标记组成。实际上,指定URL的方式有很多种,当您探索docker生态系统时,您会发现许多部分是可选的。下面的命令都是有效的,并且都是同一存储库的一些排列:
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
名称空间
名称空间是用于分隔存储库组的工具。在公共DockerHub上,名称空间通常是共享镜像的人的用户名,但也可以是组名或逻辑名。
Red Hat使用名称空间来根据Red Hat Federated Registry服务器上列出的产品分离存储库组。以下是registry.access.redhat.com返回的一些示例结果。注意,最后一个结果实际上列在另一个注册表服务器上。这是因为Red Hat也会在我们合作伙伴的注册服务器上列出存储库:
registry.access.redhat.com/rhel7/rhel
registry.access.redhat.com/openshift3/mongodb-24-rhel7
registry.access.redhat.com/rhscl/mongodb-26-rhel7
registry.access.redhat.com/rhscl_beta/mongodb-26-rhel7
registry-mariadbcorp.rhcloud.com/rhel7/mariadb-enterprise-server:10.0
注意,有时不需要指定完整的URL。在这种情况下,给定的名称空间有一个默认存储库。如果用户只指定fedora名称空间,则默认存储库中的最新标记将被拉到本地服务器。因此,运行以下命令本质上是相同的,每一个都更具体:
docker pull fedora
docker pull docker.io/fedora
docker pull docker.io/library/fedora:latest
参考
- https://developers.redhat.com/blog/2018/02/22/container-terminology-practical-introduction#container_host
- https://opencontainers.org/about/overview/
- https://github.com/opencontainers/image-spec/blob/main/manifest.md
- https://github.com/opencontainers/image-spec/blob/main/layer.md
- https://github.com/opencontainers/image-spec/blob/main/config.md