For the last few days, I have done an exploration of what is Docker and how I can actually benefit from it. So, what is Docker? Docker is a software platform that enables developers to develop, ship, and run applications anywhere with the help of containers. A Docker container is an instance of a Docker image. Docker image is the building block of a container. Docker image is a read-only template contains a set of instructions needed to create a container that can run on the Docker platform. Docker images can then be shared, shipped, run, and will behave the same regardless of the environment. One super cool thing is that the Docker container is completely isolated and run independently from each other, therefore you can run multiple Docker container with the different configuration at the same time on the same infrastructure. Another super cool thing that I found while experimenting with Docker is Docker compose. Compose is actually a tool for defining and running multi-container Docker applications which is suitable for the use case of a real-world application where interaction with database and any other services is needed.
Now, after all the explanation let’s start discussing what I did for the experiment. The application I built for this experiment is a simple note-taking application that is divided into the client, server, and database application.
Download the application
To shorten this article, I have pushed the application to Github so it can be downloaded. To download the application, simply run the command below:
Inside the downloaded application, there will be two directories which are simple-note-client and simple-note-server. The simple-note-client directory will contain the code and dependency that is needed for the client application and the simple-note-server directory will contain the code and dependency that is needed for the server application.
Defining the Docker image for the server application
To define the image for the server application, we need to make a Dockerfile inside the simple-note-server directory. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. To make the Dockerfile, simply run the command below or just create a new file and name it Dockerfile without any extension.
$ touch Dockerfile
Having the Dockerfile created, let’s define the instructions:
Now we have all the instructions needed to define the Docker image for the server application, but what is actually happened above?
- FROM is the instruction used to define which Docker image is going to be used to built or run the application.
- MAINTAINER is the instruction to add information about the maintainer of the Docker image.
- WORKDIR is the instruction used to define the working directory inside the Docker image.
- ADD is the instruction used to add a file or directory from our local (the host machine) into the Docker image. One thing to mention here is that in the above instruction, I am adding the .jar file which is actually the build result of the application. To build the application as we are using Gradle, follow the command below:
$ ./gradlew build
- EXPOSE is the instruction used to expose a port inside the container into the world outside.
- ENTRYPOINT is the instruction used to configure how the container will be run.
The Dockerfile can then be used to build the image for the application. To build an image for the application, simply run the command below:
$ docker build -t <docker-username>:<image-name> .
In this case, I won’t build the image because in the last part when we are using docker-compose, it will do this building thing for all the services image that is defined in the docker-compose configuration file.
Defining the Docker image for the client application
To define the image for the client application, we will do the same step as before but in the simple-note-client directory. The simple-note-client Dockerfile will then contains the instructions below:
As we can see, some of the instructions above are the same as before but there are new instructions, which is:
- RUN is the instruction used to execute a command inside the Docker image.
- COPY is the instruction used to copy a file or directory from our local (the host machine) into the Docker image.
- CMD is the instruction used to defines the default executable of a Docker image.
Aside from the new instructions, we can also see that there are two
FROM instructions. This approach to making the Dockerfile is called multi-stage build. With multi-stage builds, you use multiple
FROM statements in your Dockerfile. Each
FROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image. The result is a smaller image size with a significant reduction in complexity.
Defining the docker-compose.yml file to run our container
docker-compose.yml file will contain the configuration for all the services in the entire stack. Here’s the
docker-compose.yml what is actually done is:
servicesare used to define service for
buildis used to define where to look for the Dockerfile for each of the services.
portsis used to define which port is going to be used to forward the exposed port.
depends_onis used to define which service needs to be run first before running the service that depends on it.
environmentis used to define environment variables that are needed by the service.
networksis used to define that service on the same networks can communicate with each other.
volumesis used to persist data generated by and used by Docker containers
Run the application
Now, after all the required configuration file is done, we can run the application by moving to the directory which contains the
docker-compose.yml and runs the command below:
$ docker-compose up
After running the command, Docker will start to build all the images of the services and start to run each of them. If the application runs successfully, you can see something similar to this:
After that try to open
localhost:8080/index.html in the web browser and you should see this:
After done this experiment, I found that Docker can be very useful when working with a team of different environments or to simplify the deployment process of an application.
Thank you for spending the time to read this article.