Docker images are great! They are not only portable application containers, they are also building blocks for application stacks. Using a Docker registry or the public Docker index, you can compose setups just by downloading the right Docker image.
But Docker images are not only building blocks for applications, they also use a kind of “build block” themselves: layers. Every Docker image consists of a set of layers which make up the final image.
Layers
Let us consider the following Dockerfile to build a simple Ubuntu image with an Apache installation:
1 2 3 4 |
FROM ubuntu RUN apt-get update RUN apt-get install -y apache2 RUN touch /opt/a.txt |
If we build the image by calling docker build -t test/a .
we get an image called a
, belonging to a repository called test
. We can see the history of your image by calling docker history test/a
:
1 2 3 4 5 6 7 8 |
$ docker history test/a IMAGE CREATED CREATED BY SIZE 4dc359259700 About a minute ago /bin/sh -c touch /opt/a.txt 8 B 9977b78fbad7 About a minute ago /bin/sh -c apt-get install -y apache2 54.17 MB e83b3bf07b42 2 minutes ago /bin/sh -c apt-get update 20.67 MB 9cd978db300e 3 months ago /bin/sh -c #(nop) ADD precise.tar.xz in / 204.4 MB 6170bb7b0ad1 3 months ago /bin/sh -c #(nop) MAINTAINER Tianon Gravi <ad 0 B 511136ea3c5a 10 months ago 0 B |
The final image a
consists of six intermediate images as we can see. The first three layers belongs to the Ubuntu base image and the rest is ours: one layer for every build instruction.
We will see the benefit of this layering if build a slightly different image. Let’s consider this Dockerfile to build nearly the same image (only the text file in the last instruction has a different name):
1 2 3 4 |
FROM ubuntu RUN apt-get update RUN apt-get install -y apache2 RUN touch /opt/b.txt |
When we build this file, the first thing we will notice is that the build is much faster. Since we already created intermediate images for the first three instructions (namely FROM...
, RUN...
and RUN...
), Docker will reuse those layers for the new image. Only the last layer will be created from scratch. The history of this image will look like this:
1 2 3 4 5 6 7 8 |
$ docker history test/b IMAGE CREATED CREATED BY SIZE c0daf4be2ed4 42 seconds ago /bin/sh -c touch /opt/b.txt 8 B 9977b78fbad7 About a minute ago /bin/sh -c apt-get install -y apache2 54.17 MB e83b3bf07b42 3 minutes ago /bin/sh -c apt-get update 20.67 MB 9cd978db300e 3 months ago /bin/sh -c #(nop) ADD precise.tar.xz in / 204.4 MB 6170bb7b0ad1 3 months ago /bin/sh -c #(nop) MAINTAINER Tianon Gravi <ad 0 B 511136ea3c5a 10 months ago 0 B |
As we see, all layers are the same as for image a
, except of the first one where we touch
a different file!
Benefits
Those layers (or intermediate images or whatever you call them) have some benefits. Once we build them, Docker will reuse them for new builds. This makes the builds much faster. This is great for contentious integration, where we want to build an image at the end of each successful build (e.g. in Jenkins). But the build is not only faster, the images are also smaller, since intermediate images are shared between images.
But maybe the best things are rollbacks: since every image contains all of its building steps, we can easily go back to a previous step if we want so. This can be done tagging a certain layer. Let’s take a look at image b
again:
1 2 3 4 5 6 7 8 |
$ docker history test/b IMAGE CREATED CREATED BY SIZE c0daf4be2ed4 3 hours ago /bin/sh -c touch /opt/b.txt 8 B 9977b78fbad7 3 hours ago /bin/sh -c apt-get install -y apache2 54.17 MB e83b3bf07b42 3 hours ago /bin/sh -c apt-get update 20.67 MB 9cd978db300e 3 months ago /bin/sh -c #(nop) ADD precise.tar.xz in / 204.4 MB 6170bb7b0ad1 3 months ago /bin/sh -c #(nop) MAINTAINER Tianon Gravi <ad 0 B 511136ea3c5a 10 months ago 0 B |
If we want to make a rollback and remove the last layer (maybe the file should be called c.txt
instead of b.txt
) we can do so by tagging the layer 9977b78fbad7
:
1 |
$ docker tag 9977b test/b |
Let’s take a look at the new history:
1 2 3 4 5 6 7 |
$ docker history test/b IMAGE CREATED CREATED BY SIZE 9977b78fbad7 3 hours ago /bin/sh -c apt-get install -y apache2 54.17 MB e83b3bf07b42 3 hours ago /bin/sh -c apt-get update 20.67 MB 9cd978db300e 3 months ago /bin/sh -c #(nop) ADD precise.tar.xz in / 204.4 MB 6170bb7b0ad1 3 months ago /bin/sh -c #(nop) MAINTAINER Tianon Gravi <ad 0 B 511136ea3c5a 10 months ago 0 B |
Our last layer is gone and with the layer the text file b.txt
!
Best regards,
Thomas
Simple yet fantastic post about this amazing feature of Docker. Thanks!
Thomas, thank you for such clear explanation.
I know this was just for illustrative purposes; but the Docker best practices guide recommends (quite strongly) that update and install should be in a single line. e.g.
RUN apt-get update && apt-get install -y apache2
See: https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices
Wow. That is so elegant and logical and clearly explained. Brilliantly goes through what could be a complex process and makes it obvious.
great tutorial , Thank you Thomas , nothing can be so simple as this and yet so powerful while explaining the concept.
great work..