11 mins
Apr 03, 2024
We all have heard about the container and the benefits they provide. Till now I was convinced that this should be used in the backend only and not frontend. The main reason was that all frameworks like Svelte, ReactJS, Angular, and VueJS convert the code into normal Javascript files and these files can be executed by your browser standalone without even needing a server. In this article, we will discuss two scenarios that compelled me to use containers in the frontend as well. Also, we will see an example of how to containerize a frontend application.
In a real-life scenario, it is common to see a dedicated frontend developer working on multiple projects in a day. So, let’s imagine you are working on three different projects and you got a brand new laptop. Below are the steps that I presume you would do:
1. Clone repositories
2. Install dependencies (node_modules)
Then try to run the project and you get the error that node version that you are using for one project is not supported for another project. So what do you do in this case? Either upgrade or downgrade the version or have two Virtual Machines (VM) running two different versions of node. But running a gigantic VM will take a hit on your hardware — unless you are using the cloud resources.
This scenario is similar to the previous one but with a small difference. Here, the impact is on deployment and not development. As a developer, you might not care how your application would be deployed and whereas it is a worry for your DevOps team and not yours.
Generally, small organizations have on-premise servers to serve all the projects and on average we assume there must be at least 15–20 projects. So chances of having conflicts in different versions of dependencies — be it nodejs or anything else — would be more here when compared to the previous one. Consider you are in hurry to deploy your application to showcase the demo to your client and stumble upon this issue of different versions, what would you do?
1. Will you uninstall the existing version? Of course not. You cannot do that else rest of the applications will break.
2. Will you set up a new server to host all apps using the same version of node on one server? But it is a costlier option.
This is where containers shine. Though the containerization concept exists since a long time, you might not have paid attention to it as you didn’t need it till now. Let me show you how to containerize your frontend application.
We will need Docker Desktop to build images and run the containers. You can download it from docker website. Just make sure you have enough RAM and disk space on your machine.
If you are using a Windows machine then you can run Windows Containers and Linux Containers as well. The reason for that is now Windows has support for mini Linux called WSL (Windows Subsystem for Linux). This is damn good if you want to run bash scripts. But in our case, we will use it to run Linux Containers. When you restart your machine after installing Docker Desktop, you might be prompted to install WSL. You will be redirected over Install Linux on Windows with WSL page which will take you to the 4th step of the documentation. To ensure all went well, just open your command prompt and type docker and if your result looks as shown below then it means you are good to go.
Like we push our code on GitHub so that anyone can download the source code, the same way we have DockerHub to save our images so that anyone can download the image and create docker instances from it. Then log in using the same id on your Docker Desktop.
This is the file where you define what contents would be added to your image. This file is NOT an image. Image is built from this file by docker using the build command.
Through this file, we are giving instructions to docker about how to construct the image.
Though this is not mandatory, I would recommend you to have this file since it will speed up the image build process and also keep the image lean by excluding unnecessary code/dependencies. It is just like .gitignore. In other words, it won’t send node_modules to Docker daemon.
Here demo-app:dev is the <image name>:<tag>. You can give any name and tag. It must be followed by . (dot) which means that the path of the Docker build context and Dockerfile is the current folder. The process will take 2–3 minutes to complete.
To verify whether the image has been created successfully or not, you can docker images command and you will see the image with its name and size.
What is the difference between an image and a container? If you come from the OOPS world, then the below analogy will help you.
Class -> Image
Object -> Container
You can create as many containers as you want from a single image.
Let’s break the above command:
Currently, if you open localhost:3001 then you will see your app running in development mode. So, if you make any changes in your code, it will reflect those changes as well. This is possible due to the 7th step we performed above.
You can also run the above container in detached mode. Just add -d flag in the above command. When you do this, you will see that your command prompt is not waiting for any inputs unlike in the previous case.
If you want to stop your container then just use docker stop <container id>.
In the above steps, we saw how to create the container for the development environment. It won’t help you if you want to create a production build. For that, we need to create a separate file Dockerfile.prod. You can create different files for UAT, Staging, etc environments.
As it is a production build, we need some servers to serve our application. Here we are using nginx.
Now build the image using the below command:
docker build -f Dockerfile.prod -t demo-app:prod .
Once you have the image ready, spin up the container using the following command and access the application at localhost:3002.
docker run -it — rm -p 3002:80 demo-app:prod
What if you want to share the images you just created with your teammates? Simple, just push them to Docker Hub. Login to hub.docker.com with the id you created in Step 2.
Create a repository where you can store all your images.
To push your local image, you first need to name it using one of the below methods:
docker build -t <hub-user>/<repo-name>[:<tag>]
docker tag <existing-image> <hub-user>/<repo-name>[:<tag>]
We will use the second method as we already have one. Use the below command:
docker tag demo-app:prod amitvchaudhary/reactapps:demo-app-prod
Now you will see two images as shown below.
Push the newly created image.
Format: docker push <hub-user>/<repo-name>:<tag>
Command: docker push amitvchaudhary/reactapps:demo-app-prod
You will be asked to login if you haven’t. Your image will be uploaded successfully and will appear on Docker Hub.
Anyone who wishes to download the above image just needs to pull it using this command: docker pull amitvchaudhary/reactapps:demo-app-prod
Containers don’t just help in making deployment easy but you can take advantage of scalability. In the next article, we will see how to use Kubernetes to manage multiple containers.