Microservices is an architectural style that structures an application as a collection of loosely coupled services. This enables the continuous delivery/deployment of large, complex applications. It also enables scaling. This tutorial explains how to deploy microservices in a Docker instance.
With rapidly changing technology, the approach towards building Web applications is changing. New styles of development are emerging and old approaches have been adopted in new ways. One such approach that has been rapidly evolving and been widely adopted is the microservices architecture. Another technology in discussion is container technology. Since the release of Docker in 2013, containers have also been adopted quickly.
What are microservices?
Unlike a giant monolithic application trying to provide all the functionality, microservices are small services running as separate processes, where each service is lined up with a specific business capability and only defines the operations necessary to that function. One big single application is broken down into a network of processes. All these services communicate with each other using a well-defined interface such as REST or message queues. Now that we know a little bit about microservices, the next question is, what do they provide in return?
Why use microservices?
Microservices make an application loosely coupled. Therefore, when in need of an upgrade/repair to a microservice, instead of building the entire application, we can just focus on the single service that needs to be upgraded without harming any other services.
When microservices are connected through a well-defined interface, we can use different tools and languages that are best suited for the job. Scaling becomes very easy by adding copies of the microservice that is under load. Since all the microservices are independent of each other, services can continue to work in case one service fails. With microservices, we can have a small team of developers responsible for a specific task.
Why use Docker for microservices?
Docker is a container platform and is designed to simplify the task of creating and running applications by using containers. Everything required to make a piece of software run is packaged into containers. Each microservice can be packaged as a Docker container and each service instance can be deployed as a container. This containerisation of single services makes it very simple to manage and update these services. A few benefits of this approach include:
- Scaling up and down a service can simply be done by adding and removing the number of container instances.
- Containers are extremely fast to build and start. A Docker container starts in a matter of seconds.
- A container imposes limits on the CPU and memory consumed by a service instance which leads to efficient utilisation of resources.
- Greater consistency across different environments is achieved.
Let us look at how to build a simple microservice. This will be a Node.js application that will run within a Docker container. Our app will be a simple ‘Hello world’ app and the source code for it can be downloaded from GitHub.
Note: All the examples in this tutorial are demonstrated on Ubuntu.
The first step is to install Docker. Once Docker is installed, we will pull an Ubuntu image from the Docker hub using the command given below:
[bash$] docker pull library/ubuntu Using default tag: latest latest: Pulling from library/ubuntu 6b683210e931: Pull complete 2710139890b6: Pull complete 73f656ee3a66: Pull complete 0153a4475206: Pull complete ec00daa05fe6: Pull complete 2ebb207dc43f: Pull complete Digest: sha256:8e6b67faf44a036a18b9a3987e826fe3accbd1742e219145a461a3210a9d8142 Status: Downloaded newer image for ubuntu:latest
This will be our base image, and we will install everything required to run our application on this image. Next, we will install Node.js on our image using Dockerfile, which contains commands that will be run when building a container. Here is how our Dockerfile looks:
Dockerfile FROM ubuntu:latest ENV DEBIAN_FRONTEND noninteractive ADD. /home/microservice/ RUN /home/microservice/installScript.sh
This Dockerfile tells Docker to use the newly pulled Ubuntu image — if the image was not already pulled, then it first pulls the Ubuntu image from the registry. The ADD command gives the instruction to copy the contents of the current directory to the directory named ‘microservice’ under /home. The RUN command executes the script named installScript.sh. This file contains the command required to install Node.js.
installScript.sh apt-get update apt-get install nodejs apt-get install npm
The next step is to build the image from the Dockerfile, for which the command is:
[bash$] docker build -t “mservice/base:latest”
When the build successfully completes, a new Docker image is created with the name mservice. To view all the images present on the machine, run the Docker images command. This newly created image has Node.js installed in it and can be used as the base image for all Node.js applications from now on. You can also push the image to Docker-hub.
The next step is to build our app from the mservice/base image. The Dockerfile is as follows:
Dockerfile FROM mservice/base:latest ADD . /home/microservice/app ENTRYPOINT [“/home/microservice/app/boot.sh”]
On the previously created base image, we copy the contents of our app to the directory /home/microservice/app and then execute the file boot.sh, which has one simple command to start our node app. Now that we have built our app within a container, the next step is to run the container. Use the following command to run the app:
docker run -d --name=nodems -p 3000:3000
The -d flag instructs Docker to run the container in the background, –name assigns a name to the container, and the -p flag specifies the host:container port mapping.
We now have our app running within a container, and making a Curl request to localhost:3000 gets the response ‘Hello World!’
The service that we just built looks like any other application running in a container, but the key to building a good microservice is carefully breaking down an application into smaller components. This makes the application easy to maintain. The many advantages of using Docker to build microservices definitely complement this style of development.