「CONTAINER-IMAGE」- 镜像 | Image

该笔记将记录:什么是镜像,以及镜像的管理,镜像的创建,存储,修改,共享等方面的内容;

分层文件系统

容器镜像基于分层文件系统的概念,其中每个层都包含应用程序的一部分或一个组件。

镜像(image)由多个文件系统叠加而成:

bootfs,引导文件系统,位于最底端,像 Linux/Unix 的引导文件系统。用户不会和该引导文件系统有什么交互。当容器启动时,容器会被移动到内存中,而 bootfs 会被卸载,以释放由 initrd 磁盘镜像使用的内存;

rootfs,第二层文件系统,位于 bootfs 之上。rootfs 可以是多种操作系统,例如 Debain、Ubuntu 等等。rootfs 永远是只读状态,不同于传统的 Linux 引导过程(在传统的 Linux 引导过程中,rootfs 先以只读方式加载,引导结束并完成完整性检查后,会进入读写模式);

只读文件系统,位于 rootfs 之上。通过“联合挂载”(union mount)技术,在 rootfs 上挂载更多的只读文件系统。什么是联合挂载呢?联合挂载同时加载多个文件系统,但是整体上看只呈现一个文件系统。联合挂载会将各层文件系统叠加到一起,这样最终的文件系统会包含所有底层文件系统的文件和目录;

在 Docker 中,将这些文件系统称为镜像;

一个镜像可以放到另一个镜像的顶部。位于下面的镜像成为父镜像,最底层的镜像称之为基础镜像。当从镜像中启动容器时,Docker 会在镜像的最顶层加载一个读写文件系统,我们运行的程序都是在这个读写文件系统中执行的;

在 Docker 中,使用到了一种写时复制机制。在初始化时,读写层是空的。当文件系统发生修改时,所有的变化都存在与该读写层中。比如修改一个文件,该文件会被从只读层复制到读写层,在读写层进行修改。但是该文件的只读版本依旧存在,但是它被读写层中的该文件隐藏,正是因为使用了联合挂载技术。上面的这些层共同构成了镜像。

镜像名称

docker pull | Docker Documentation
Where can I find the sha256 code of a docker image? – Stack Overflow

[registry-hostname/][username/]repository-name[:tag][@digest]

registry-hostname/ | 主机名,可选

  • 作用:指定镜像所在的 Docker Registry 地址,默认是 docker.io(即 Docker Hub)。

username/ | 用户名或组织名,可选

  • 在 Docker Hub 或其他 Registry 中,表示镜像所属的用户或组织。如果省略,Docker 会使用 library/(官方镜像)。

repository-name | 仓库名称,必选

  • 镜像的基本名称,通常代表软件或应用名称。

tag | 标签,可选

  • 标识镜像的版本,默认是 latest。
  • 最大长度:未知
  • 允许字符:

digest | 摘要 | DIGEST

  • 作用:基于镜像内容的唯一哈希值(SHA256),比 tag 更精确,确保拉取的是特定构建。digest 是镜像的唯一身份,不会被修改(而 tag 会被修改),通过 digest 能够获取确定版本的镜像;
  • 但是,当 docker tag 后,不同仓库地址的 Digest 不同;

常见问题

关于父镜像与父镜像层

Is docker inspect -f ‘{{.Parent}}’ a safe way to get the base image ID?

命令 docker image inspect b7b28af77ffe --format '{{.Parent}}‘ 输出确实是父镜像,但是这个“父镜像”是“父镜像层”,而非 Dockerfile 中 FROM 引用的镜像层(除非 Dockerfile 中只有一个 FROM 指令);

待办事项

WIP ! Docker 是如何存储镜像的,以及镜像的大小
About images, containers, and storage drivers
Docker Image Size – Does It Matter?
moby/daemon/graphdriver/driver.go
Where are Docker images stored on the host machine?

镜像信息 | docker image inspect …

Changing the IMAGE ID of a docker image?
DeepSeek / 解释 docker image inspect 输出中,各个参数的含义

[
    {
        "Id": "sha256:09508d094ad5c8eec96b8aeba128d4d86c55c741fbe189e5873befdd665eb159",
        "RepoTags": [
            "dreamacro/clash:v1.18.0"
        ],
        "RepoDigests": [
            "dreamacro/clash@sha256:8d10815afb37cc49d262f64e82d9346b15ea889b2ba7fd566c1369a2630b807c"
        ],
        "Parent": "",
        "Comment": "buildkit.dockerfile.v0",
        "Created": "2023-08-17T14:47:27.620903309Z",
        "DockerVersion": "",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": null,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": [
                "/clash"
            ],
            "OnBuild": null,
            "Labels": {
                "org.opencontainers.image.source": "https://github.com/Dreamacro/clash"
            }
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 24589149,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/c42c25da8cf3ce31954eddf4163c00302deefe79027ecb86e2fc8e0ea065a207/diff:/var/lib/docker/overlay2/9915f00caf7f29a60b1863ce8b66a2f1a1a77900c29db181bad23ec5cf24edeb/diff:/var/lib/docker/overlay2/0b0be3b5e72d5e032e9b6228afa866efa45b6ed3c77e64ccc343ff96630d4509/diff:/var/lib/docker/overlay2/0e5f4984de2fe946debcb160ae94c67881a1b6dcb21a930daf5cb774a57cdbb9/diff",
                "MergedDir": "/var/lib/docker/overlay2/64095f9eea7b63c2d5da891cd3ac19e8ca371a48d72b8629f8909e46874d1f61/merged",
                "UpperDir": "/var/lib/docker/overlay2/64095f9eea7b63c2d5da891cd3ac19e8ca371a48d72b8629f8909e46874d1f61/diff",
                "WorkDir": "/var/lib/docker/overlay2/64095f9eea7b63c2d5da891cd3ac19e8ca371a48d72b8629f8909e46874d1f61/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:4693057ce2364720d39e57e85a5b8e0bd9ac3573716237736d6470ec5b7b7230",
                "sha256:c11d3c6d457e9d188582ed01ec27ba02aea5bd476727bafd1e9a04a6bf00dcb6",
                "sha256:c7df344fa7e00b73d82f3cf8a3e147ed4d417c96d5a469abadb0f64856f8ddfb",
                "sha256:391f1b610f5899799dcf2804d2d67f70be8a69d536ff06c70f10b72b3cf12ffd",
                "sha256:8594e1b784e95f6cffae70fc430bc7a15f32b56488e9f78e0ce6a368cc60c066"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

The image id is calculated from the content of the image, you can’t customize it.

基本信息

  • Id: 镜像的唯一标识符(SHA256哈希值)。Id 是一个关键字段,表示 Docker 镜像的唯一标识符。
    • 它基于镜像的配置和文件系统内容计算得出,确保不同内容的镜像具有不同的 ID。
    • 修改镜像的任意文件或配置(如 ENV、CMD)都会生成新的 Id。我们通常使用 ID 来唯一标识一个镜像。Id 与镜像的文件系统层(Layers)关联(见 RootFS.Layers 字段),用于存储和复用镜像层。
  • RepoTags: 镜像的仓库标签列表(如 [“nginx:latest”, “myrepo/nginx:v1”])
  • RepoDigests: 镜像的摘要列表(如 [“nginx@sha256:…”])

构建信息

  • Parent: 父镜像的ID(如果是基于其他镜像构建的)
  • Comment: 构建镜像时添加的注释
  • Created: 镜像创建时间(ISO 8601格式)
  • Container: 构建此镜像的容器ID
  • DockerVersion: 构建此镜像时使用的Docker版本
  • Author: 镜像作者

配置信息

  • ContainerConfig: 构建容器时的配置(等同于 docker run 时的参数):Hostname, Domainname, User, Env, Cmd, Image, Volumes, WorkingDir, Entrypoint, Labels 等
  • Config: 镜像的运行时配置(会覆盖ContainerConfig中的设置)

系统信息

  • Architecture: 目标架构(如 amd64, arm64 等)
  • Os: 操作系统(如 linux, windows)
  • Size: 镜像的磁盘大小(字节)
  • VirtualSize: 包含所有层的总大小(字节)

存储驱动信息

  • GraphDriver: 使用的存储驱动信息。Name: 驱动名称(如 overlay2);Data: 驱动特定数据(如 lowerdir, upperdir, mergeddir 等)

文件系统信息

  • RootFS: 根文件系统信息。Type: 通常是 layers;Layers: 组成镜像的各层ID列表(SHA256哈希值);

元数据

  • Metadata: 镜像的元数据。LastTagTime: 最后一次打标签的时间(ISO 8601格式)