Docker 映像的工作原理

已完成

回顾一下,我们说过,容器映像是用于分发应用程序的单位。 此外,我们还提到了容器采用开发者团队和运营团队都使用的标准化格式。

现在,我们将探讨在 Docker 中使用的软件、程序包和映像之间的差异。 了解这些概念之间的差异将帮助我们更好地理解 Docker 映像的工作原理。

我们还将简要介绍在主机上运行的操作系统以及在容器中运行的操作系统的角色。

打包到容器中的软件

打包到容器中的软件并不限于开发者生成的应用程序。 我们说的软件,是指在容器中运行的应用程序代码、系统包、二进制文件、库、配置文件和操作系统。

例如,假设我们要开发一个订单跟踪门户,供公司的各个专卖店使用。 我们需要全面地看待将运行 Web 应用的软件。 我们要生成的应用程序是一个 .NET Core MVC 应用,并且我们计划使用 Nginx 作为反向代理服务器在 Ubuntu Linux 上部署应用程序。 所有这些软件组件都属于容器映像。

什么是容器映像?

容器映像是一种包含软件的可移植程序包。 它在运行时便成为了我们的容器。 容器是映像的内存中实例。

容器映像是不可变的。 生成映像后,就无法更改。 更改映像的唯一方法是创建新映像。 此特性可保证在生产环境中使用的映像与在开发环境和 QA 环境中使用的相同。

什么是主机操作系统?

主机操作系统是 Docker 引擎运行于的操作系统。 在 Linux 上运行的 Docker 容器共享主机操作系统内核,只要二进制文件可以直接访问操作系统内核,便无需容器操作系统。

Diagram showing a Docker image with no base OS and the dependency on the host OS Kernel.

但是,Windows 容器需要容器操作系统。 容器依靠操作系统内核来管理服务,例如文件系统、网络管理、进程调度和内存管理。

什么是容器操作系统?

容器操作系统是已打包映像包含的操作系统。 我们可以灵活地在容器中包含不同版本的 Linux 或 Windows 操作系统。 这使我们能够访问特定操作系统功能或安装应用程序可能使用的其他软件。

Diagram showing a Docker image with an Ubuntu base OS and the dependency on the host OS Kernel.

容器操作系统独立于主机操作系统,我们要在这一环境中部署和运行应用程序。 结合映像的不可变性,这种隔离意味着应用程序开发环境和生产环境相同。

在示例中,我们使用 Ubuntu Linux 作为容器操作系统,并且此操作系统不会因开发或生产而改变。 我们所使用的映像始终是同一映像。

什么是可堆叠的统一文件系统 (Unionfs)?

我们使用 Unionfs 创建 Docker 映像。 Unionfs 是一种文件系统,允许以看似合并内容的方式堆叠多个目录(称为分支)。 但是,内容在物理上是保持分开的。 Unionfs 允许在生成文件系统时添加和删除分支。

Diagram showing the stacking of layers in a Docker image created with unionfs.

例如,假设我们要为之前的 Web 应用生成一个映像。 我们将 Ubuntu 发行版作为基础映像叠加到引导文件系统之上。 接下来,我们将安装 Nginx 和 Web 应用。 我们有效地将 Nginx 和 Web 应用叠加到原始 Ubuntu 映像之上。

在通过映像运行容器后,将创建最终的可写层。 但是,当容器被销毁时,此层将不复存在。

什么是基础映像?

基础映像是使用 Docker scratch 映像的映像。 scratch 映像是一种空容器映像,不会创建文件系统层。 此映像假设要运行的应用程序可以直接使用主机操作系统内核。

什么是父级映像?

父级映像是用于创建映像的容器映像。

例如,我们将使用已基于 Ubuntu 的映像,而不是从 scratch 创建映像再安装 Ubuntu。 我们甚至可以使用已安装 Nginx 的映像。 父级映像通常包含一个容器操作系统。

基础映像与父级映像之间的主要区别是什么?

这两种映像类型都可用于创建可重复使用的映像。 但是,基础映像让我们能够更好地控制最终映像的内容。 回想一下前面的内容,映像是不可变的,只能对映像进行添加操作而不能进行删减操作。

在 Windows 上,只能创建基于 Windows 基础容器映像的容器映像。 Microsoft 提供并服务这些 Windows 基础容器映像。

什么是 Dockerfile?

Dockerfile 是一种文本文件,其中包含用于生成和运行 Docker 映像的说明。 已定义映像的以下方面:

  • 用于创建新映像的基础映像或父级映像
  • 用于更新基础操作系统和安装其他软件的命令
  • 要包含的生成项目,例如开发的应用程序
  • 要公开的服务,例如存储和网络配置
  • 要在启动容器时运行的命令

让我们将这些方面一一对应到示例 Dockerfile。 假设我们要为 ASP.NET Core 网站创建 Docker 映像。 Dockerfile 可能与以下示例类似:

# Step 1: Specify the parent image for the new image
FROM ubuntu:18.04

# Step 2: Update OS packages and install additional software
RUN apt -y update &&  apt install -y wget nginx software-properties-common apt-transport-https \
	&& wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
	&& dpkg -i packages-microsoft-prod.deb \
	&& add-apt-repository universe \
	&& apt -y update \
	&& apt install -y dotnet-sdk-3.0

# Step 3: Configure Nginx environment
CMD service nginx start

# Step 4: Configure Nginx environment
COPY ./default /etc/nginx/sites-available/default

# STEP 5: Configure work directory
WORKDIR /app

# STEP 6: Copy website code to container
COPY ./website/. .

# STEP 7: Configure network requirements
EXPOSE 80:8080

# STEP 8: Define the entry point of the process that runs in the container
ENTRYPOINT ["dotnet", "website.dll"]

我们不会在此介绍 Dockerfile 文件规范,也不会详细解释先前示例中的每个命令。 但是,请注意,此文件中有多个命令可用于操纵映像结构。 例如,COPY 命令将内容从本地计算机上的特定文件夹复制到要生成的容器映像。

回忆一下,之前我们提到过,Docker 映像使用 unionfs。 在生成最终的容器映像时,这些步骤中的每一步都会创建一个缓存的容器映像。 这些临时映像会叠加在上一个映像之上,并在所有步骤完成后显示为单个映像。

最后,请注意最后一个步骤,即步骤 8。 文件中的 ENTRYPOINT 表示从映像运行容器后将执行的进程。 如果没有要执行的 ENTRYPOINT 或进程,Docker 将理解为容器无需执行任何操作,且容器将退出。

如何管理 Docker 映像

Docker 映像是最初存储在电脑上的大型文件,需要使用工具来管理这些文件。

可通过 Docker CLI 和 Docker Desktop 来管理映像,包括生成、列出、删除和运行映像。 我们使用 docker 客户端来管理 Docker 映像。 客户端不直接执行命令,它会将所有查询发送到 dockerd 守护程序。

我们不会在这里介绍所有客户端命令和命令标志,但将介绍一些常用命令。 本模块末尾的“了解详细信息”部分包括 Docker 文档的链接,其中详细介绍了所有命令和命令标志。

如何生成映像

我们使用 docker build 命令生成 Docker 映像。 假设我们使用之前的 Dockerfile 定义来生成映像。 以下示例演示了 build 命令:

docker build -t temp-ubuntu .

下面是生成命令生成的输出结果:

Sending build context to Docker daemon  4.69MB
Step 1/8 : FROM ubuntu:18.04
 ---> a2a15febcdf3
Step 2/8 : RUN apt -y update && apt install -y wget nginx software-properties-common apt-transport-https && wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && dpkg -i packages-microsoft-prod.deb && add-apt-repository universe && apt -y update && apt install -y dotnet-sdk-3.0
 ---> Using cache
 ---> feb452bac55a
Step 3/8 : CMD service nginx start
 ---> Using cache
 ---> ce3fd40bd13c
Step 4/8 : COPY ./default /etc/nginx/sites-available/default
 ---> 97ff0c042b03
Step 5/8 : WORKDIR /app
 ---> Running in 883f8dc5dcce
Removing intermediate container 883f8dc5dcce
 ---> 6e36758d40b1
Step 6/8 : COPY ./website/. .
 ---> bfe84cc406a4
Step 7/8 : EXPOSE 80:8080
 ---> Running in b611a87425f2
Removing intermediate container b611a87425f2
 ---> 209b54a9567f
Step 8/8 : ENTRYPOINT ["dotnet", "website.dll"]
 ---> Running in ea2efbc6c375
Removing intermediate container ea2efbc6c375
 ---> f982892ea056
Successfully built f982892ea056
Successfully tagged temp-ubuntu:latest

如果不理解以上输出结果,请不要担心。 但是,请注意输出中列出的步骤。 执行每个步骤时,都会向正在生成的映像添加一个新层。

另请注意,我们将执行多个命令以安装软件和管理配置。 例如,在步骤 2 中,我们将运行 apt -y updateapt install -y 命令来更新操作系统。 这些命令在为该步骤创建的运行容器中执行。 命令运行后,将删除中间容器。 基础缓存映像保存在生成主机上,并且不会自动删除。 此优化可确保以后的生成可重复使用这些映像来缩短生成时间。

什么是映像标记?

映像标记是用于对映像进行版本设置的文本字符串。

在之前的示例生成中,请注意最后一个生成消息,该消息显示“已成功标记 temp-ubuntu: latest”。 生成映像时,我们使用 -t 命令标志来命名和标记(可选)映像。 在我们的示例中,我们使用 -t temp-ubuntu 命名映像,而生成的映像名称标记为 temp-ubuntu: latest。 如果未指定标记,将向映像应用 latest 标记。

单个映像可以分配有多个标记。 按照约定,最新版本的映像分配有 latest 标记以及描述其映像版本号的标记。 当发布新版本的映像时,可以重新分配 latest 标记以引用新映像。

对于 Windows,Microsoft 不提供具有最新标记的基础容器映像。 对于 Windows 基础容器映像,必须指定要使用的标记。 例如,Server Core 的 Windows 基本容器映像为 mcr.microsoft.com/windows/servercore。 其标记包括 ltsc2016ltsc2019ltsc2022

再提供一个示例。 假设要使用 .NET Core 示例 Docker 映像。 这里有四个平台版本可供选择:

  • mcr.microsoft.com/dotnet/core/samples:dotnetapp

  • mcr.microsoft.com/dotnet/core/samples:aspnetapp

  • mcr.microsoft.com/dotnet/core/samples:wcfservice

  • mcr.microsoft.com/dotnet/core/samples:wcfclient

在先前的映像列表中,可以看到 Microsoft 提供了多个 .NET Core 示例。 标记指定了映像引用的示例:ASP.NET、WCF 服务等。

如何列出映像

Docker 软件会在计算机上自动配置本地映像注册表。 可以使用 docker images 命令查看此注册表中的映像。

docker images

输出如下所示:

REPOSITORY          TAG                     IMAGE ID            CREATED                     SIZE
tmp-ubuntu          latest             f89469694960        14 minutes ago         1.69GB
tmp-ubuntu          version-1.0        f89469694960        14 minutes ago         1.69GB
ubuntu              18.04                   a2a15febcdf3        5 weeks ago            64.2MB

请注意如何使用“名称”、“标记”以及“映像 ID”列出映像。 回忆一下,我们可以向一个映像应用多个标签。 前面的输出显示了一个示例:即使映像名称不同,但我们可以看到 ID 是相同的。

当映像的名称或标记可能有歧义时,映像 ID 是用于标识和管理映像的有用方法。

如何删除映像

可以使用 docker rmi 命令从本地 docker 注册表中删除映像。 如果需要节省容器主机磁盘上的空间,这将非常有用,因为容器映像层会增加总可用空间。

指定要删除的映像的名称或 ID。 此示例使用映像名称删除示例 Web 应用的映像:

docker rmi temp-ubuntu:version-1.0

如果容器仍在使用映像,则无法删除该映像。 docker rmi 命令返回了一条错误消息,其中列出了依赖该映像的容器。

我们已探讨了 Docker 映像的基础知识、如何管理这些映像以及如何从映像运行容器。 接下来,我们将介绍如何管理容器。

知识检查

1.

Docker Desktop 是一种用于构建和共享容器化应用和微服务的应用,可以在以下哪个操作系统上使用?

2.

以下哪一项是用于重新生成容器映像的正确 Docker 命令?

3.

以下哪一句是容器映像的最恰当描述?