skip to Main Content

Suppose there is a container image published to a Docker registry, and I want to rebuild the image with modifications while re-using as many of the published layers as possible.

Concretely, suppose the image foo/bar is built from this Dockerfile, and I only want to modify the layer that contains script.sh:

FROM ubuntu:focal
COPY script.sh .

Even though pulling the image downloads the layers for ubuntu:focal, when I re-build my local machine may resolve the ubuntu:focal tag to another version, producing a new image with no common layers with the one I pulled.

                                   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

The desired outcome could possibly be achieved by looking at the pulled layers and tagging the correct one (3b15784) as ubuntu:focal before building. But I’m not sure if Docker exposes enough information to do this in an automatic way.

2

Answers


  1. Chosen as BEST ANSWER

    As a workaround, I could explicitly include the base image's digest as a label on the built image:

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

    Then I would build with:

    # 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
    

    However, a more elegant solution would definitely be welcome, especially one that does not involve any specific assumptions about how the original pulled image was built.


  2. Maybe Docker supports this built in with –cache-from:

    docker pull foo/bar:old
    docker build . 
        --build-arg BUILDKIT_INLINE_CACHE=1 
        --cache-from foo/bar:old
        --tag foo/bar:new
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search