构建容器镜像并重新使用拉取层

Building a container image and re-using pulled layers

假设有一个容器镜像发布到 Docker 注册表,我想通过修改重建镜像,同时尽可能多地重新使用已发布的层。

具体来说,假设图像 foo/bar 是从这个 Docker 文件构建的,我只想修改包含 script.sh:

的图层
FROM ubuntu:focal
COPY script.sh .

即使拉取图像会下载 ubuntu:focal 的层,当我重新构建我的本地机器时可能会将 ubuntu:focal 标记解析为另一个版本,生成一个没有公共层的新图像我拉的那个。

                                   6a9e8d7 <foo/bar:new>
           <foo/bar:old> c7632a5      |
                            |         |
                            +----+----+
                                 |
                              3b15784 <ubuntu:focal (then)>
                                 |
                                ...
           
           
                             DESIRABLE




        <foo/bar:old> c7632a5          48fead0 <foo/bar:new>
                         |                |
                         |                |
<ubuntu:focal (then)> 3b15784          9a634f5 <ubuntu:focal (now)>
                         |                |
                        ...              ...


                            UNDESIRABLE

在构建之前,查看拉出的层并将正确的层 (3b15784) 标记为 ubuntu:focal 可能会达到预期的结果。但我不确定 Docker 是否公开了足够的信息以自动执行此操作。

作为一种解决方法,我可以明确地将基础镜像的摘要作为标签包含在构建的镜像中:

FROM ${UBUNTU_IMAGE_ID:-ubuntu:focal}
COPY script.sh .

然后我将构建:

# First build
UBUNTU_IMAGE_ID=$( \
    docker inspect \
        --format '{{ index .RepoTags 0 }}@{{ index . "Id" }}' \
        ubuntu:focal \
)

# Subsequent builds
docker pull foo/bar:old
UBUNTU_IMAGE_ID=$( \
    docker inspect \
        --format '{{ index .Config.Labels "ubuntu_image_id" }}' \
        foo/bar:old \
)


docker build . \
    --build-arg "UBUNTU_IMAGE_ID=${UBUNTU_IMAGE_ID}" \
    --label "ubuntu_image_id=${UBUNTU_IMAGE_ID}" \
    --tag foo/bar:new

但是,肯定会欢迎更优雅的解决方案,尤其是不涉及有关如何构建原始拉取图像的任何特定假设的解决方案。

也许 Docker 支持内置 --cache-from:

docker pull foo/bar:old
docker build . \
    --build-arg BUILDKIT_INLINE_CACHE=1 \
    --cache-from foo/bar:old
    --tag foo/bar:new