My First Experiment With Docker

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.

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:

$ git clone https://github.com/alexandernsalim/simple-note-application.git

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.

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.

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

The docker-compose.yml file will contain the configuration for all the services in the entire stack. Here’s the docker-compose.yml:

Inside docker-compose.yml what is actually done is:

  • services are used to define service for app-server , app-client , and db.
  • Inside app-server and app-client , build is used to define where to look for the Dockerfile for each of the services.
  • ports is used to define which port is going to be used to forward the exposed port.
  • depends_on is used to define which service needs to be run first before running the service that depends on it.
  • environment is used to define environment variables that are needed by the service.
  • networks is used to define that service on the same networks can communicate with each other.
  • volumes is 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:

Landing Page
List of Notes

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.