1. 概述

随着越来越多的应用通过 Docker 进行部署,了解其生态系统的基本原理变得尤为重要。虽然现有的工具让打包和部署变得简单,但在某些时候,我们可能需要排查部署中的一些问题

其中,镜像对比是调试部署时常见的任务之一。本文将介绍 Docker 镜像的结构,并演示如何查看两个镜像之间的差异。

2. Docker 镜像简介

Docker 镜像是构建容器的蓝图,我们可以将其理解为运行一个应用所需的所有文件和目录的集合,包括操作系统、第三方依赖库以及我们自己的代码。

底层来看,这些镜像本质上就是 tar 文件。构建镜像时,会生成多个层级(layer),每一层都包含一组文件和目录。
通常我们会基于一个已有镜像进行扩展。例如构建一个 Spring Boot 应用,我们可能会基于 OpenJDK 镜像,然后添加自己的 Java 文件(如 fat jar)和依赖库。

最终,所有层级被打包成一个完整的镜像,包含运行应用所需的一切。

3. 查看单个 Docker 镜像

我们可以通过以下命令列出本地镜像:

$ docker image ls
spring-petclinic    2.7.0-SNAPSHOT    0f9d2d05687b   2 months ago    266MB
spring-petclinic    2.6.0-SNAPSHOT    1d79d5bd7779   3 months ago    265MB

然后使用 docker inspect 命令查看某个镜像的详细信息:

$ docker inspect 0f9d2d05687b
[
    {
        "Id": "sha256:0f9d2d05687b8c816cbf54f63cf7e5aa7144d28e1996d468bfaf555a3882610a",
        "RepoTags": [
            "spring-petclinic:2.7.0-SNAPSHOT"
        ],
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 266141567,
        "VirtualSize": 266141567,
        ...
    }
]

该命令展示了镜像的元信息,如创建时间、层级结构等,但不会展示镜像内部的具体内容

要查看内容,可以先将镜像保存为 tar 文件:

$ docker save 0f9d2d05687b > 0f9d2d05687b.tar

再使用 tar 命令查看:

$ tar tvf 0f9d2d05687b.tar
drwxr-xr-x  0 0      0           0 Dec 31  1979 02805fa4a4f35efdcf3804bc1218af1bc22d28ee521cc944cab5cac5dbe5abfe/
-rw-r--r--  0 0      0           3 Dec 31  1979 02805fa4a4f35efdcf3804bc1218af1bc22d28ee521cc944cab5cac5dbe5abfe/VERSION
-rw-r--r--  0 0      0         477 Dec 31  1979 02805fa4a4f35efdcf3804bc1218af1bc22d28ee521cc944cab5cac5dbe5abfe/json
-rw-r--r--  0 0      0        1024 Dec 31  1979 02805fa4a4f35efdcf3804bc1218af1bc22d28ee521cc944cab5cac5dbe5abfe/layer.tar
...

tar 命令可以列出镜像中的文件结构,也可以提取特定文件用于分析。

4. 对比两个 Docker 镜像

前面我们了解了如何查看单个镜像的内容。接下来,我们看看如何对比两个镜像之间的差异

根据我们要比较的内容,可以选择不同的工具:

4.1. container-diff

Google 的 container-diff 是一个专门用于对比镜像的工具,支持多种对比方式(如文件、RPM 包、APT 包等)。

示例命令如下:

$ /usr/local/bin/container-diff diff \
daemon://spring-petclinic:2.6.0-SNAPSHOT \
daemon://spring-petclinic:2.7.0-SNAPSHOT \
--type=file

输出内容分为三部分:

新增文件(Added)

/workspace/BOOT-INF/lib/byte-buddy-1.12.10.jar                                                    3.7M
/workspace/BOOT-INF/lib/classgraph-4.8.139.jar                                                    551.7K
/workspace/BOOT-INF/lib/ehcache-3.10.0.jar                                                        1.7M
/workspace/BOOT-INF/lib/h2-2.1.212.jar                                                            2.4M
/workspace/BOOT-INF/lib/hibernate-core-5.6.9.Final.jar                                            7.1M
/workspace/BOOT-INF/lib/jackson-annotations-2.13.3.jar                                            73.9K

删除文件(Deleted)

These entries have been deleted from spring-petclinic:2.6.0-SNAPSHOT:
FILE                                                                        SIZE
/workspace/BOOT-INF/lib/byte-buddy-1.11.22.jar                              3.5M
/workspace/BOOT-INF/lib/classgraph-4.8.115.jar                              525.4K
/workspace/BOOT-INF/lib/ehcache-3.9.9.jar                                   1.7M
/workspace/BOOT-INF/lib/h2-1.4.200.jar                                      2.2M
/workspace/BOOT-INF/lib/hibernate-core-5.6.7.Final.jar                      7.1M
/workspace/BOOT-INF/lib/jackson-annotations-2.13.2.jar                      73.9K

⚠️ 修改文件(Changed)

These entries have been changed between spring-petclinic:2.6.0-SNAPSHOT and spring-petclinic:2.7.0-SNAPSHOT:
FILE                                                                                                 SIZE1        SIZE2
/layers/config/metadata.toml                                                                         16.6K        1.9K
/workspace/META-INF/maven/org.springframework.samples/spring-petclinic/pom.xml                       13.3K        13.3K
/workspace/BOOT-INF/classes/org/springframework/samples/petclinic/owner/OwnerController.class        7.8K         7.7K
/workspace/org/springframework/boot/loader/ExecutableArchiveLauncher.class                           6.6K         7.5K
...

container-diff 的输出非常清晰,能快速定位到依赖变化和具体代码修改。

4.2. dive

dive 是另一个强大的开源镜像分析工具,它能逐层解析镜像并以文件树的形式展示。

查看单个镜像的结构如下图所示:

dive single image view

虽然 dive 没有原生支持镜像对比功能,但我们可以在两个终端中分别运行 dive 并手动对比,如下图:

dive compare two docker images

这样可以直观看到哪些层级是相同的,哪些是不同的,适合快速分析镜像构建过程中的变化。

5. 总结

本文介绍了 Docker 镜像的结构,并演示了如何查看和对比镜像内容。

  • docker inspectdocker save 可用于查看单个镜像的结构和文件;
  • ✅ 若需对比两个镜像,可使用第三方工具:
    • container-diff:专为镜像对比设计,支持文件、包等多维度对比;
    • dive:适合逐层查看镜像内容,也可通过双窗口方式手动对比差异。

选择合适的工具能大大提升排查镜像问题的效率,避免踩坑。


原始标题:Showing Differences Between Two Docker Images