了解镜像图层
解释
正如您在什么是镜像中所学到的那样,容器镜像由层组成。这些层中的每一个一旦创建,都是不可变的。但是,这实际上意味着什么?如何使用这些层来创建容器可以使用的文件系统?
镜像图层
镜像中的每个图层都包含一组文件系统更改 - 添加、删除或修改。让我们看一个理论镜像:
- 第一层添加基本命令和包管理器,例如 apt。
- 第二层安装 Python 运行时和 pip 以进行依赖项管理。
- 第三个图层复制到应用程序的特定 requirements.txt 文件中。
- 第四层安装该应用程序的特定依赖项。
- 第五层复制应用程序的实际源代码。
此示例可能如下所示:

这是有益的,因为它允许在镜像之间重复使用图层。例如,假设您要创建另一个 Python 应用程序。由于分层,您可以利用相同的 Python 基础。这将使构建速度更快,并减少分发镜像所需的存储和带宽量。镜像分层可能类似于以下内容:

图层允许您通过重用其他镜像的基础层来扩展其镜像,从而允许您仅添加应用程序所需的数据。
堆叠图层
内容可寻址存储和联合文件系统使分层成为可能。虽然这会涉及技术问题,但其工作原理如下:
- 下载每个层后,将其提取到主机文件系统上自己的目录中。
- 当您从镜像运行容器时,将创建一个联合文件系统,其中各层相互堆叠,从而创建一个新的统一视图。
- 当容器启动时,其根目录将设置为此统一目录的位置,使用
chroot
.
创建联合文件系统时,除了镜像层之外,还会专门为正在运行的容器创建一个目录。这允许容器更改文件系统,同时允许原始镜像图层保持不变。这使您能够从同一底层镜像运行多个容器。
试用
在本动手指南中,您将使用docker container commit
命令。请注意,您很少会以这种方式创建镜像,因为您通常会使用 Dockerfile。但是,它使您更容易理解这一切是如何运作的。
创建基础镜像
在第一步中,您将创建自己的基础镜像,然后将其用于以下步骤。
下载并安装Docker 桌面。
在终端中,执行以下命令启动新的容器。
$ docker run --name=base-container -ti ubuntu
下载镜像并启动容器后,您应该会看到一个新的 shell 提示符。这是在你的容器内运行的。它类似于以下内容(容器 ID 会有所不同):
root@d8c5ca119fcd:/#
在容器内,运行以下命令安装 Node.js:
$ apt update && apt install -y nodejs
当此命令运行时,它会在容器内下载并安装 Node。在 union 文件系统的上下文中,这些文件系统更改发生在此容器唯一的目录中。
通过运行以下命令验证是否安装了 Node:
$ node -e 'console.log("Hello world!")'
然后,您应该会在控制台中看到 “Hello world!”。
现在您已经安装了 Node,您可以将所做的更改保存为新的镜像层,您可以从该层启动新容器或构建新镜像。为此,您将使用
docker container commit
命令。在新终端中运行以下命令:$ docker container commit -m "Add node" base-container node-base
使用
docker image history
命令:$ docker image history node-base
您将看到类似于以下内容的输出:
IMAGE CREATED CREATED BY SIZE COMMENT d5c1fca2cdc4 10 seconds ago /bin/bash 126MB Add node 2b7cc08dcdbb 5 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 5 weeks ago /bin/sh -c #(nop) ADD file:07cdbabf782942af0… 69.2MB <missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B <missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B <missing> 5 weeks ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B <missing> 5 weeks ago /bin/sh -c #(nop) ARG RELEASE 0B
请注意顶行的 “Add node” 注释。此层包含您刚刚进行Node.js安装。
要证明您的镜像已安装 Node,您可以使用此新镜像启动新容器:
$ docker run node-base node -e "console.log('Hello again')"
这样,您应该会在终端中获得 “Hello again” 输出,显示 Node 已安装并正常工作。
现在,您已经完成了基础镜像的创建,您可以删除该容器:
$ docker rm -f base-container
基础镜像定义
基础镜像是构建其他镜像的基础。可以将任何镜像用作基础镜像。但是,某些镜像是有意创建的构建块,为应用程序提供基础或起点。
在此示例中,您可能不会部署此
node-base
image,因为它实际上还没有做任何事情。但它是您可以用于其他构建的基础。
构建应用程序镜像
现在,您已拥有基础镜像,可以扩展该镜像以构建其他镜像。
使用新创建的 node-base 镜像启动新容器:
$ docker run --name=app-container -ti node-base
在此容器内,运行以下命令以创建 Node 程序:
$ echo 'console.log("Hello from an app")' > app.js
要运行此 Node 程序,您可以使用以下命令并查看屏幕上打印的消息:
$ node app.js
在另一个终端中,运行以下命令以将此容器的更改保存为新镜像:
$ docker container commit -c "CMD node app.js" -m "Add app" app-container sample-app
此命令不仅会创建一个名为
sample-app
,还会向镜像添加其他配置,以便在启动容器时设置 default 命令。在这种情况下,您将其设置为自动运行node app.js
.在容器外部的终端中,运行以下命令以查看更新的层:
$ docker image history sample-app
然后,您将看到如下所示的输出。请注意,顶层注释有 “Add app” ,下一层有 “Add node”:
IMAGE CREATED CREATED BY SIZE COMMENT c1502e2ec875 About a minute ago /bin/bash 33B Add app 5310da79c50a 4 minutes ago /bin/bash 126MB Add node 2b7cc08dcdbb 5 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 5 weeks ago /bin/sh -c #(nop) ADD file:07cdbabf782942af0… 69.2MB <missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B <missing> 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B <missing> 5 weeks ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B <missing> 5 weeks ago /bin/sh -c #(nop) ARG RELEASE 0B
最后,使用全新的镜像启动新容器。由于您指定了 default 命令,因此可以使用以下命令:
$ docker run sample-app
您应该会看到您的问候语出现在终端中,来自 Node 程序。
现在,您已经完成了容器的使用,您可以使用以下命令将其删除:
$ docker rm -f app-container
其他资源
如果您想更深入地了解所学内容,请查看以下资源:
后续步骤
如前所述,大多数镜像构建不使用docker container commit
.相反,您将使用 Dockerfile 来自动执行这些步骤。