Creating Node(react) Container Images using Dockerfile

Okay. My first experience with Dockerfile wasn’t all that pleasant. But every dark cloud has a silver lining🙂. It made me more curious about them and led me to practice/self study more. The source code used in this post is here. If you want to learn more on how this react app works as it is(without using containers) you can check out this post.

Let’s talk Dockerfile

Below is the Dockerfile that we will peruse.

FROM node:12-alpine
COPY package.json ./
COPY yarn.lock ./
RUN npm install [email protected]
RUN yarn install
COPY . .
CMD ["yarn","start"]

Why 12-alpine?

The answer to that is two fold. Firstly, alpine images are smaller in size(some of them are like 1/10th the size of an original image.). In our case, I tried creating an Dockerfile based image using a vanilla node image. This resulted in an image that is just over 900MB. When we use an alpine image here, the resulting image is a little over 400MB. Which is like half the size. Once we upload this image to Dockerhub, the size reduces to be over 300MB. Larger image sizes means, longer time to build and it takes up more space in the container registry. So as long as we are getting the same functionality, we can go for an alpine version.

The second part to the same question above is on why the version 12 of node is being used. This is because it is one of the few versions that support the node-sass package in our package.json file. While we are at it, let’s look at why the node-sass package is installed separately before the npm install command. Again, that is because of a version conflict and not running as it is will result in an error. You can read more about this on stackoverflow. Gopal’s comment in there is what helped me on this.

Why so many Copy commands?

Why not just Copy . . ? (Which would copy everything). Yes we can just use only Copy . . and that will copy over the yarn.lock and package.json as well. The reason why we write this separately is so that if there were no changes in the yarn.lock and package.json files (No changes in the dependencies), then docker will use the cache and the build will be much faster. This means docker wouldn’t have to build the image from scratch every time we do a build. Suppose we do have only just Copy . ., then even if we make a slight change in the repo, it will have to build the image from scratch(Not taking advantage of the cache).

What else?

Another important thing is sequence. The Copy . . needs to be under the yarn install command. If the Copy . . command sits above the yarn install command, then it would defeat the purpose of having separate copy commands. (Caching wouldn’t work and the builds will take longer time suppose there is a slight change in anything that is copied over.)

Well that is it for this short blog. Pretty sure there can be more improvements to the Dockerfile. I will try to keep you posted if I find anything. Next time I hope to write another short blog on docker-compose. Until next time 👋