Instructor Demo: Creating Images
In this demo, we'll illustrate:
- How to read each step of the image build output
- How intermediate image layers behave in the cache and as independent images
- What the meanings of 'dangling' and
<missing>image layers are
Understanding Image Build Output
Make a folder
demofor our image demo:PS: node-0 Administrator> mkdir demo ; cd demoIn this folder, create a
Dockerfile:FROM microsoft/windowsservercore SHELL ["powershell", "-Command"] RUN iex (invoke-webrequest https://chocolatey.org/install.ps1 -UseBasicParsing) RUN choco install -y which RUN choco install -y wget RUN choco install -y vimBuild the image from the
Dockerfile:PS: node-0 demo> docker image build -t demo .Examine the output from the build process. The very first line looks like:
Sending build context to Docker daemon 2.048kBHere the Docker daemon is archiving everything at the path specified in the
docker image build command(.or the current directory in this example). This is why we made a fresh directorydemoto build in, so that nothing extra is included in this process.The next two lines look like this:
Step 1/6 : FROM microsoft/windowsservercore ---> 824f26e4c566Do an
image ls:REPOSITORY TAG IMAGE ID CREATED SIZE demo latest 889ea81f9564 13 hours ago 11GB microsoft/windowsservercore latest 824f26e4c566 6 days ago 10.7GBNotice the Image ID for
microsoft/windowsservercorematches that second line in the build output. The build starts from the base image defined in theFROMcommand.The next few lines look like:
Step 2/6 : SHELL powershell -Command ---> Running in c84a289effacThis is the output of the
SHELLcommand,powershell -Command. The lineRunning in c84a289effacspecifies a container that this command is running in, which is spun up based on all previous image layers (just themicrosoft/windowsservercorebase at the moment). Scroll down a bit and you should see something like:---> e573fdd8f035 Removing intermediate container c84a289effacAt the end of this first
SHELLcommand, the temporary containerc84a289effacis saved as an image layere573fdd8f035, and the container is removed. This is the exact same process as when you useddocker container committo save a container as a new image layer, but now running automatically as part of a Dockerfile build.Look at the history of your image:
IMAGE CREATED CREATED BY SIZE 889ea81f9564 14 hours ago powershell -Command choco install -y vim 79.9MB 1852907cf3a0 14 hours ago powershell -Command choco install -y wget 58.1MB 3b8e144326e1 14 hours ago powershell -Command choco install -y which 42.1MB 0d53be0fc9c4 14 hours ago powershell -Command iex (invoke-webrequest... 78.8MB e573fdd8f035 14 hours ago powershell -Command #(nop) SHELL [powersh... 41kB 824f26e4c566 8 days ago Install update 10.0.14393.2430 3.06GB <missing> 20 months ago Apply image 10.0.14393.0 7.68GBAs you can see, the different layers of
democorrespond to a separate line in the Dockerfile and the layers have their own ID. You can see the image layere573fdd8f035committed in the second build step in the list of layers for this image.Look through your build output for where steps 3/6 (installing chocolatey), 4/6 (installing which), 5/6 (installing wget), and 6/6 (installing vim) occur - the same behavior of starting a temporary container based on the previous image layers, running the RUN command, saving the container as a new image layer visible in your docker iamge history output, and deleting the temporary container is visible.
Every layer can be used as you would use any image, which means we can inspect a single layer. Let's inspect the
wgetlayer, which in my case is1852907cf3a0(yours will be different, look at yourdocker image historyoutput):PS: node-0 demo> docker image inspect 1852907cf3a0Let's look for the command associated with this image layer by using
--format:PS: node-0 demo> docker image inspect --format='{{.ContainerConfig.Cmd}}' 1852907cf3a0 [powershell -Command choco install -y wget]We can even start containers based on intermediate image layers; start an interactive container based on the
wgetlayer, and look for whetherwgetandvimare installed:PS: node-0 demo> docker container run -it 1852907cf3a0 powershell PS C:\> which wget C:\ProgramData\chocolatey\bin\wget.exe PS C:\> which vim Not foundwgetis installed in this layer, but sincevimdidn't arrive until the next layer, it's not available here.
Managing Image Layers
Change the last line in the
Dockerfilefrom the last section to installnanoinstead ofvim:FROM microsoft/windowsservercore SHELL ["powershell", "-Command"] RUN iex (invoke-webrequest https://chocolatey.org/install.ps1 -UseBasicParsing) RUN choco install -y which RUN choco install -y wget RUN choco install -y nanoRebuild your image, and list your images again:
PS: node-0 demo> docker image build -t demo . PS: node-0 demo> docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE demo latest b65c04957b1a 3 minutes ago 11GB <none> <none> 889ea81f9564 18 hours ago 11GB microsoft/windowsservercore latest 824f26e4c566 7 days ago 10.7GBWhat is that image named
<none>? Notice the image ID is the same as the old image ID fordemo:latest(see your history output above). The name and tag of an image is just a pointer to the stack of layers that make it up; reuse a name and tag, and you are effectively moving that pointer to a new stack of layers, leaving the old one (the one containing theviminstall in this case) as an untagged or 'dangling' image.Rewrite your
Dockerfileone more time, to combine some of those install steps:FROM microsoft/windowsservercore SHELL ["powershell", "-Command"] RUN iex (invoke-webrequest https://chocolatey.org/install.ps1 -UseBasicParsing) RUN choco install -y which wget nanoRebuild using a
newtag this time, and usedocker image inspectto pull out the size of both this and your previous image, taggedlatest:PS: node-0 demo> docker image build -t demo:new . PS: node-0 demo> docker image inspect --format '{{json .Size}}' demo:latest 11140350009 PS: node-0 demo> docker image inspect --format '{{json .Size}}' demo:new 11058863996Image
demo:newis smaller in size thandemo:latest, even though it contains the exact same software - why?
Conclusion
In this demo, we explored the layered structure of images; each layer is built as a distinct image and can be treated as such, on the host where it was built. This information is preserved on the build host for use in the build cache; build another image based on the same lower layers, and they will be reused to speed up the build process. Notice that the same is not true of downloaded images like microsoft/windowsservercore; intermediate image caches are not downloaded, but rather only the final complete image.