Docker

How to build application image with Dockerfile?

Since this is a Java application, I need to use an image that can run a Java application. We need the Dockerfile and to use the OpenJDK base image.

FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

COPY target/*.jar /app/api.jar

EXPOSE 8080

CMD ["java", "-jar", "api.jar"]

This Dockerfile is a recipe for building a Docker image that runs a Java application. Each instruction in the Dockerfile has a specific purpose. Let’s go through it line by line:

FROM eclipse-temurin:17-jre-alpine

  • This line defines the base image for the container, which will be eclipse-temurin:17-jre-alpine.
  • eclipse-temurin is an image based on the OpenJDK Java Runtime Environment (JRE), distributed by the Eclipse Foundation. This image provides the necessary Java infrastructure to run applications.
  • The 17-jre-alpine version indicates that we’re using JRE version 17 (Java 17).

WORKDIR /app

  • Sets the working directory as /app within the container.
  • All subsequent commands will be executed within this directory, and any files copied or created will be placed inside it.
  • Setting the WORKDIR helps maintain organized file structure within the container.

COPY target/*.jar /app/api.jar

  • Copies the .jar file from the local system’s target folder to the container, renaming it as api.jar.
  • This .jar file is the artifact generated by the application’s build, containing all the code necessary for execution.
  • Since the .jar file name can vary, the Dockerfile uses a wildcard *.jar to capture any .jar file within the target folder.

EXPOSE 8080

  • Informs that the application inside the container will listen on port 8080. This line by itself does not open the port but serves to document and assist in mapping the correct port when running the container.
  • During execution (docker run), this port can be exposed to the host, for instance, by using -p 8080:8080 to allow external access.

CMD ["java", "-jar", "api.jar"]

  • Specifies the command that will be executed when the container starts. In this case, the command is:
java -jar api.jar
  • This command starts the Java application by running the previously copied api.jar file.

To create a Docker image with the Dockerfile, you can use the following command:

docker image build -t company .

The docker image build instructs Docker to build an image.

The -t company allows you to name the image, where “company” is the image name. Since no version is specified, Docker will use “latest” by default, meaning that tag will be named latest.

In this case, . indicates the current directory, where the Dockerfile is expected to be located.

Thus, the image is generated using the Dockerfile that was created.

It is necessary to have the .jar file in the target folder. If it doesn’t exist, simply run the following command:

clean package

Result of the image creation when running the command.


To run a container from the created image, simply execute the following docker command:

docker container run --rm -p 8080:8080 company

It will run a container from the company image on port 8080.

The -rm indicates that after stopping the container, it will be automatically removed.

You will likely encounter a scenario where you need to communicate with another container, such as a database container. The application container needs to connect to the database container. Let’s try this scenario using everything we’ve covered so far.

Let’s create a MySQL container for the application, as it is the database I am using.

docker container run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --name company-mysql mysql:8.0.39

Now we can run the application again.

docker container run --rm -p 8080:8080 company

We have the following error:

We couldn’t connect to the database, but why?

This happens because the application container and the database container are on different networks. We need to place both on the same network in order to connect successfully. To resolve this, we first need to create a network.

docker network create --driver bridge company-network

Removing the previously created MySQL container.

docker container rm company-mysql --force --volumes 

Creating a MySQL container on the created network.

docker container run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --network company-network --name company-mysql mysql:8.0.39

Another important thing we must do is parameterize the MySQL connection in the application. In the properties file, where we specify the database host, we cannot leave it fixed as localhost, so I made the following adjustment.

spring.datasource.url=jdbc:mysql://${DATABASE_HOST:localhost}/company?createDatabaseIfNotExist=true&serverTimezone=UTC

If no host value is provided as a parameter, localhost will be used by default.

I will generate a new JAR file to recreate the image.

mvn package
docker image build -t company .

Now, when running the container, we will specify the network and the MySQL database host. Don’t forget to check if the MySQL container is running.

docker container run --rm -p 8080:8080 -e DATABASE_HOST=company-mysql --network company-network company

Now the containers can communicate with each other and we no longer have problems connecting the API to the database, as both are on the same network.

Author

  • Natan Ferreira

    Hello there, I’m Natan Lara Ferreira, Full Stack Developer Java and Angular since 2016. I’m in Open Finance Brazil project using framework Quarkus and Angular since the beginning 2021. I'm a problem solver, critical thinker and team player.