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-nodeWORKDIR /src# Copy your code in the docker imageCOPY . /src# Install your project dependenciesRUN npm install# Expose the port 3000EXPOSE 3000# Set the default command to run when a container startsCMD ["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— -> cdbaa8672a25Step 2 : WORKDIR /src— -> Using cache— -> b45c7e778213Step 3 : COPY . /src— -> efdeb7ccd271Removing intermediate container 8658a7345ce0Step 4 : RUN npm install— -> Running in f48b8e022460Removing intermediate container f48b8e022460Step 5 : EXPOSE 3000---> Running in 60bccf4e876e---> d3591a082b67Removing intermediate container 60bccf4e876eStep 6 : CMD npm start---> Running in 9f130513aef6---> 627434bc0bd3Removing intermediate container 9f130513aef6Successfully 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 :
- Docker build your image step by step.
- Each step is dependent to the previous one.
- Each step is cached by Docker
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.
- First, i need a node ready image :
- Second, i need a node image which working dir is /src
- Then, i need a node ready image which working dir is /src and listening on port 3000.
- Then, i need a node ready image which working dir is /src and listening on port 3000 and running npm start as default command.
- I need this previous image but with the node dependencies i specified in my package.json
- Last step is the most specified step of this image, i want my current source code on it
Here is it ! An optimized Dockerfile of my project :
FROM mhart/alpine-node:5.6.0WORKDIR /src# Expose the port 3000EXPOSE 3000# Set the default command to run when a container startsCMD ["npm", "start"]# Install app dependenciesCOPY package.json /srcRUN npm install# Copy your code in the docker imageCOPY . /src
Built it again and again after making some changes to our code :
Step 1 : FROM mhart/alpine-node— -> cdbaa8672a25Step 2 : WORKDIR /src— -> Using cache— -> b45c7e778213Step 3 : EXPOSE 3000— -> Using cache— -> 02992be285dbStep 4 : CMD npm start— -> Using cache— -> 4e7f1d140eb1Step 5 : COPY package.json /src— -> Using cache— -> faa22ea65977Step 6 : RUN npm install— -> Using cache— -> ea3b2c32a045Step 7 : COPY . /src— -> 45c61b45bb99Removing intermediate container 871632345f49Successfully 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.