When you start with Docker, it seems normal to get inspired by some samples and examples you grabed around the web.
Let’s start, for example, with a simple nodejs project. You grab this nice Dockerfile, and, yes, it works great.
FROM mhart/alpine-node
WORKDIR /src
# Copy your code in the docker image
COPY . /src
# Install your project dependencies
RUN npm install
# Expose the port 3000
EXPOSE 3000
# Set the default command to run when a container starts
CMD ["npm", "start"]
Now, you are working on your source code. And each time you want to build your image to test your code, you have to wait…
Step 1 : FROM mhart/alpine-node
— -> cdbaa8672a25
Step 2 : WORKDIR /src
— -> Using cache
— -> b45c7e778213
Step 3 : COPY . /src
— -> efdeb7ccd271
Removing intermediate container 8658a7345ce0
Step 4 : RUN npm install
— -> Running in f48b8e022460
Removing intermediate container f48b8e022460
Step 5 : EXPOSE 3000
---> Running in 60bccf4e876e
---> d3591a082b67
Removing intermediate container 60bccf4e876e
Step 6 : CMD npm start
---> Running in 9f130513aef6
---> 627434bc0bd3
Removing intermediate container 9f130513aef6
Successfully built 627434bc0bd3
Each build took over a minute on my MacBook Pro… and 90% of this minute is dedicated to the step 4, the npm install.
Here comes three things to know :
Because my code have changed, Docker use his cache before the step 3. After this, docker rebuild intermediates images from step 3 to step 6.
We can see the “Removing intermediate container” logs : docker don’t use his cache for this steps because it depends on the step 3.
To get the best of Docker cache :
Think your Dockerfile like layers of images storted by specialization.
Here is it ! An optimized Dockerfile of my project :
FROM mhart/alpine-node:5.6.0
WORKDIR /src
# Expose the port 3000
EXPOSE 3000
# Set the default command to run when a container starts
CMD ["npm", "start"]
# Install app dependencies
COPY package.json /src
RUN npm install
# Copy your code in the docker image
COPY . /src
Built it again and again after making some changes to our code :
Step 1 : FROM mhart/alpine-node
— -> cdbaa8672a25
Step 2 : WORKDIR /src
— -> Using cache
— -> b45c7e778213
Step 3 : EXPOSE 3000
— -> Using cache
— -> 02992be285db
Step 4 : CMD npm start
— -> Using cache
— -> 4e7f1d140eb1
Step 5 : COPY package.json /src
— -> Using cache
— -> faa22ea65977
Step 6 : RUN npm install
— -> Using cache
— -> ea3b2c32a045
Step 7 : COPY . /src
— -> 45c61b45bb99
Removing intermediate container 871632345f49
Successfully built 45c61b45bb99
Rebuilding this image take only 2 seconds now, is far better than over a minute !!!
We can see the “Using cache” logs.
Steps 1 to 4 are always the same in our project, so cache is always used.
The long npm install task at step 6 is only ran if there is a change on copied package.json file on step 5.
The step 7 is the only step where an image is rebuilt based on previous steps when we make a change on our source code.
We hope this post will help you gain time on your future Docker ready projects.