Dockerfile Tip , 1 You Won’t Forget

There are lots of Dockerfile tips and best practices out there but the problem is, who can remember them all ! In the end, all that matters is building a Docker Image as fast as possible and keeping it lean and mean. I’m going to cover 1 easy to remember Dockerfile tip that will serve you well going forward.

Dockerfile Tip on Caching – The 1 Strike Rule!

As expected, when building your Docker Image the very first time, there is no caching taking place. However on subsequent re-builds Docker verifies each line in your Dockerfile against its cache. When a cache hit occurs, that layer is re-used and we can achieve faster Docker Image builds.

The problem is that it can be downright tricky to take full advantage of the Docker cache even if your writing some really simple Dockerfiles (as I’ll show you).

So what’s the one Strike rule? Once Docker comes across a line in your Dockerfile that invalidates the cache, it will stop verifying the cache from that point on. There’s no going back. Strike one … your out of the cache game!

All subsequent Dockerfile lines will have a new image layer created in order to build yet another new Docker Image. Obviously this slows down the build process and is to be avoided when possible.

The lesson to be learnt here … You have to know when its best to strike out to take full advantage of the cache game.  No one wants to strike out on the first throw! Let’s play.

Let’s go to Bat

Here’s a simple Dockerfile  example to bring home the point …

FROM anapsix/alpine-java:jdk8

COPY file1.txt /home
VOLUME /home
EXPOSE 80
LABEL com.mvpjava.project.version="0.0.1"
LABEL maintainer="MVP Java"

Build the Docker Image via “docker build -t demo .” for the first time …

docker build -t demo .
Sending build context to Docker daemon 4.096 kB
Step 1/6 : FROM anapsix/alpine-java:jdk8
 ---> ed55c27d366d
Step 2/6 : COPY file1.txt /home
 ---> 539a456b8f7b
Removing intermediate container 9923a61118c5
Step 3/6 : VOLUME /home
 ---> Running in 0cd144c441bb
 ---> f0b6226027f4
Removing intermediate container 0cd144c441bb
Step 4/6 : EXPOSE 80
 ---> Running in 7333e886bc1e
 ---> 744e2f79b45c
Removing intermediate container 7333e886bc1e
Step 5/6 : LABEL com.mvpjava.project.verion "0.0.1"
 ---> Running in 762896922e8e
 ---> 620bc042769e
Removing intermediate container 762896922e8e
Step 6/6 : LABEL maintainer "MVP Java"
 ---> Running in e33b40d692cb
 ---> bd32deb80926
Removing intermediate container e33b40d692cb
Successfully built bd32deb80926

As expected, we did not benefit from caching since it was our first time. If we don’t change anything and re-run the build, we should benefit from re-using the cached image layers …

docker build -t demo .
Sending build context to Docker daemon 4.096 kB
Step 1/6 : FROM anapsix/alpine-java:jdk8
 ---> ed55c27d366d
Step 2/6 : COPY file1.txt /home
 ---> Using cache
 ---> daf12ebeb748
Step 3/6 : VOLUME /home
 ---> Using cache
 ---> 415ddd681b9f
Step 4/6 : EXPOSE 80
 ---> Using cache
 ---> b09cfb968d82
Step 5/6 : LABEL com.mvpjava.project.verion "0.0.1"
 ---> Using cache
 ---> 155848fcdd08
Step 6/6 : LABEL maintainer "MVP Java"
 ---> Using cache
 ---> 72a55db09590
Successfully built 72a55db09590

Good, all green! So what happens when we make a modification now? Let’s say file1.txt gets modified (we add a line) and then we re-build the Docker Image. Let’s see …

The Big Strike-out!

docker build -t demo .
Sending build context to Docker daemon 4.096 kB
Step 1/6 : FROM anapsix/alpine-java:jdk8
 ---> ed55c27d366d
Step 2/6 : COPY file1.txt /home
 ---> 9bd7edf98d81
Removing intermediate container 8ec29258bc3e
Step 3/6 : VOLUME /home
 ---> Running in 4220618616b1
 ---> 099fc2676cbc
Removing intermediate container 4220618616b1
Step 4/6 : EXPOSE 80
 ---> Running in 1cf68a8ab00e
 ---> c0caf94b3cf0
Removing intermediate container 1cf68a8ab00e
Step 5/6 : LABEL com.mvpjava.project.verion "0.0.1"
 ---> Running in f1ebee8d1ab1
 ---> dddd167b0edc
Removing intermediate container f1ebee8d1ab1
Step 6/6 : LABEL maintainer "MVP Java"
 ---> Running in 4b8685344a8f
 ---> 7b44735a2833
Removing intermediate container 4b8685344a8f
Successfully built 7b44735a2833

Wow, No caching occurred what so ever! In fact, Docker didn’t even re-use the image layers in the cache for the lines that didn’t change under the line “COPY file1.txt /home“. So what happened?

As soon as you invalidate the cache due to introducing a modification (our file1.txt in this case), Docker throws its hands up in the air and just gives up even trying to look any further from that point on, hence you lose all caching benefits. Is there anything that can be done to get around this?

Keep Swinging Until You Can’t Swing No More

You can get around this by putting the Dockerfile commands that are not likely to change on the top in order to benefit from caching and place the commands that will eventually invoke a cache invalidation after (not always obvious). This way you’ll use the Cache as long as possible until you eventually strike out; which you want to do as late as possible.

Let’s do just that …

  • Modify file1.txt again  (add another line)
  • Move the line “COPY file1.txt /home” command to the end of the Dockerfile.
  • Re-build once again

Here’s the output …

docker build -t demo .
Sending build context to Docker daemon 4.096 kB
Step 1/6 : FROM anapsix/alpine-java:jdk8
 ---> ed55c27d366d
Step 2/6 : VOLUME /home
 ---> Using cache
 ---> 307692a18920
Step 3/6 : EXPOSE 80
 ---> Using cache
 ---> ada2bd8273c2
Step 4/6 : LABEL com.mvpjava.project.verion "0.0.1"
 ---> Using cache
 ---> b1fc86f6a04b
Step 5/6 : LABEL maintainer "MVP Java"
 ---> Using cache
 ---> 2e9b6a95d30f
Step 6/6 : COPY file1.txt /home
 ---> e8548f23fa3c
Removing intermediate container b907eba6172b
Successfully built e8548f23fa3c

Notice that we re-used the cached layers this time all the way down until we struck out on Step 6/6. That’s playing it smart! We stayed in the cache game as long as we could just by moving that line to be bottom.

Now I know that the Dockerfile example above is simple but that’s the point  🙂 It’s the principle that you have to remember no matter how complicated the Dockerfile is.

There are more gotchas out there but I’ll keep this in line with the title of the post, mainly 1 tip!  It’s short, to the point, easy to remember and will serve you well going forward.

For a full list of Dockerfile best practices, check out the official Docker link Best practices for writing Dockerfiles

Leave a Reply

Your email address will not be published.