A not so great Dockerfile
Not that long ago, when I needed to dockerize my Node.js application, I used a Dockerfile like the following one.
FROM node:4.4.5 # Copy src files COPY . /app/ # Use /app working directory WORKDIR /app # Expose API port EXPOSE 80 # Build dependencies RUN npm install # Run application CMD ["npm", "start"]
Basically, I tell Docker
- to start from the official Node.js image node:4.4.5
- to copy all the sources of the current directory into the image’s /app directory
- to compile and install all the dependencies
- how to instantiate a container
Why this Dockerfile is not so great ? Let’s put this against a development workflow.
Very often, a developer needs to fix some bugs and add features in his code.
Less often he needs to add new dependencies.
With this Dockerfile, each time a small modification is done in the code, the
COPY . /app/
instruction invalidates the cache and then triggers the compilation of the dependencies (that can take several minutes). We then need to find a way to use the cache for the dependencies if the list has not changed between 2 successive build of the image.
A better Dockerfile
In order to use the cache created for the dependencies, the compilation needs to be done before the copy of the application source code. This implies an additional step in order to compile the dependencies in a temporary folder and then to move then in the application folder.
In order not to have the local node_modules folder override the one created in the image, make sure you have a .dockerignore file containing « node_modules » to this folder is not taken into account in the context sent to the Docker daemon.
.dockerignore can be as minimal as:
A better version of the above Dockerfile is listed below.
FROM mhart/alpine-node:4.4.5 # Copy list of dependencies COPY package.json /tmp/package.json # Install dependencies RUN cd /tmp && npm install # Copy dependencies libraries RUN mkdir /app && cp -a /tmp/node_modules /app/ # Copy source code COPY . /app # Change current dir WORKDIR /app # Expose API port EXPOSE 80 # Run application CMD ["npm", "start"]
Note: in this file we use the mhart/alpine-node:4.4.5 Node.js image, that is much lighter (because it’s based on Linux Alpine) than the previous one (based on Ubuntu…).