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

    I am a seasoned Full Stack Software Developer with 8+ years of experience, including 6+ years specializing in Java with Spring and Quarkus. My core expertise lies in developing robust RESTful APIs integrated with Cosmos Db, MySQL, and cloud platforms like Azure and AWS. I have extensive experience designing and implementing microservices architectures, ensuring performance and reliability for high-traffic systems. In addition to backend development, I have experience with Angular to build user-friendly interfaces, leveraging my postgraduate degree in frontend web development to deliver seamless and responsive user experiences. My dedication to clean and secure code led me to present best practices to my company and clients, using tools like Sonar to ensure code quality and security. I am a critical thinker, problem solver, and team player, thriving in collaborative environments while tackling complex challenges. Beyond development, I share knowledge through my blog, NatanCode, where I write about Java, Spring, Quarkus, databases, and frontend development. My passion for learning and delivering innovative solutions drives me to excel in every project I undertake.