容器是当今 IT 运营的关键组成部分。容器镜像包含一个打包的应用程序,以及它的依赖项,和关于启动时运行什么进程的信息。
您可以通过提供一组特殊格式的指令来创建容器镜像,可以作为提交到注册表或 Dockerfile。例如,这个 Dockerfile 为 PHP Web 应用程序创建一个容器
FROM registry.access.redhat.com/ubi8/ubi:8.1
RUN yum --disableplugin=subscription-manager -y module enable php:7.3 \
&& yum --disableplugin=subscription-manager -y install httpd php \
&& yum --disableplugin=subscription-manager clean all
ADD index.php /var/www/html
RUN sed -i 's/Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf \
&& sed -i 's/listen.acl_users = apache,nginx/listen.acl_users =/' /etc/php-fpm.d/www.conf \
&& mkdir /run/php-fpm \
&& chgrp -R 0 /var/log/httpd /var/run/httpd /run/php-fpm \
&& chmod -R g=u /var/log/httpd /var/run/httpd /run/php-fpm
EXPOSE 8080
USER 1001
CMD php-fpm & httpd -D FOREGROUND
此文件中的每个指令都会向容器镜像添加一个层。每个层仅添加与其下方层的差异,然后,所有这些层堆叠在一起形成一个只读容器镜像。
这是如何工作的?
您需要了解一些关于容器镜像的知识,并且按这个顺序理解这些概念非常重要
- 联合文件系统
- 写时复制
- Overlay 文件系统
- 快照器
联合文件系统 (Aufs)
联合文件系统 (UnionFS) 内置于 Linux 内核中,它允许将一个文件系统的内容与另一个文件系统的内容合并,同时保持“物理”内容分离。 结果是一个统一的文件系统,即使数据实际上是按分支结构组织的。
这里的想法是,如果您有多个具有一些相同数据的镜像,则无需再次复制此数据,而是通过使用称为层的东西来共享它。

图片 CC BY-SA opensource.com
每个层都是一个文件系统,可以在多个容器之间共享,例如,httpd 基础层是官方的 Apache 镜像,可以跨任意数量的容器使用。 想象一下我们节省了多少磁盘空间,因为我们对所有容器都使用了相同的基本层。
这些镜像层始终是只读的,但是当我们从此镜像创建一个新容器时,我们在其顶部添加一个薄的可写层。 这个可写层是您为每个容器创建/修改/删除或进行其他更改的地方。
写时复制
当您启动一个容器时,它看起来好像容器拥有自己的整个文件系统。 这意味着您在系统中运行的每个容器都需要其自身的文件系统副本。 这不会占用大量磁盘空间,并且还需要花费大量时间来启动容器吗? 不会——因为每个容器都不需要其自身的文件系统副本!
容器和镜像使用写时复制机制来实现此目的。 写时复制策略不是复制文件,而是将相同的数据实例共享给多个进程,并且仅在进程需要修改或写入数据时才复制。 所有其他进程将继续使用原始数据。 在正在运行的容器中执行任何写入操作之前,要修改的文件的副本将放置在容器的可写层上。 这就是写入发生的地方。 现在您知道为什么它被称为写时复制了。
此策略优化了镜像磁盘空间使用率和容器启动时间的性能,并与 UnionFS 协同工作。
Overlay 文件系统
Overlay 位于现有文件系统的顶部,组合了一个上层和下层目录树,并将它们呈现为单个目录。 这些目录称为层。 下层保持不变。 每个层仅添加与其下方层的差异(在计算术语中称为diff),并且此统一过程称为联合挂载。
最底层的目录或镜像层称为lowerdir,上层目录称为upperdir。 最终的 overlayed 或统一层称为 merged。

图片 CC BY-SA opensource.com
常用术语包括这些层定义
- 基础层是文件系统文件所在的位置。 就容器镜像而言,此层将是您的基础镜像。
- Overlay 层通常称为容器层,因为对正在运行的容器所做的所有更改(如添加、删除或修改文件)都将写入此可写层。 对此层所做的所有更改都存储在下一层中,并且是基础层和 Diff 层的联合视图。
- Diff 层包含在 Overlay 层中所做的所有更改。 如果您写入的内容已在基础层中,则 overlay 文件系统会将文件复制到 Diff 层并进行您打算写入的修改。 这称为写时复制。
快照器
容器可以使用层和图形驱动程序构建、管理和分发更改作为其容器文件系统的一部分。 但是使用图形驱动程序非常复杂且容易出错。 快照器与图形驱动程序不同,因为它们不了解镜像或容器。
快照器的工作方式与 Git 非常相似,例如具有树的概念,以及跟踪每次提交对树的更改。 快照表示文件系统状态。 快照使用一组目录具有父子关系。 可以在父级及其快照之间获取 diff 以创建层。
快照器提供了一个 API,用于分配、快照和挂载抽象的、分层的文件系统。
总结
您现在对容器镜像是什么以及它们的分层方法如何使容器可移植有了很好的了解。 接下来,我将介绍容器运行时和内部结构。
本文基于 techbeatly 文章,并已获得许可进行改编。
评论已关闭。