Docker and Java Application examples

docker java application

In this tutorial, we will show you how to Dockerize a Java Application (an executable Jar file).

Tested with

  • Docker 19.03
  • Ubuntu 19
  • Java 8
  • Maven

At the end of the article, we will create an executable Jar file and run inside a docker container.

1. Project Directory

1.1 A standard Maven project structure. See the Dockerfile at the root of the project? We only need this Dockerfile text file to dockerize the Java Application.

project structure

Note The .idea folders and docker-java-app.iml are IDEA configuration files, ignore it.

2. Maven and Java Application

Now, we will create an executable Jar file. This Jar will use jsoup HTML parser to find all links from a URL or web page.

2.1 Project dependencies.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mkyong</groupId>
    <artifactId>docker-java-app</artifactId>
    <version>1.0</version>

    <name>docker-java-app</name>
    <url>https://www.mkyong.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <java.version>1.8</java.version>
        <jsoup.version>1.12.1</jsoup.version>
        <junit.version>5.4.0</junit.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>${jsoup.version}</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>find-links</finalName>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

            <!-- create executable jar -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>com.mkyong.App</mainClass>
                            <classpathPrefix>lib/</classpathPrefix>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <!-- copy dependencies / jars to ${project.build.directory}/lib/ -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                ${project.build.directory}/lib/
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
</project>

2.2 A Simple Java application.

App.java

package com.mkyong;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

public class App {

    public static void main(String[] args) throws IOException {

        if (args.length < 1) {
            System.err.println("[Usage] jar -jar find-links.jar <url>");
            return;
        }

        String url = args[0];

        for (String link : findLinks(url)) {
            System.out.println(link);
        }

    }

    private static Set<String> findLinks(String url) throws IOException {

        Document doc = Jsoup.connect(url)
                .data("query", "Java")
                .userAgent("Mozilla")
                .cookie("auth", "token")
                .timeout(3000)
                .get();

        Set<String> links = new HashSet<>();

        Elements elements = doc.select("a[href]");

        for (Element element : elements) {
            links.add(element.attr("href"));
        }

        return links;
    }
}

3.3 Maven package and run it. It will create an executable jar file at target/find-links.jar, and copy jsoup dependencies to the target/lib folder

Note – Forget about the JUnit 5 dependencies, we are not going to create any unit test.


$ cd ~/projects/docker-java-app

$ mvn package

$ tree
target
    ├── find-links.jar
    ├── lib
    │   ├── apiguardian-api-1.0.0.jar
    │   ├── jsoup-1.12.1.jar
    │   ├── junit-jupiter-api-5.4.0.jar
    │   ├── junit-jupiter-params-5.4.0.jar
    │   ├── junit-platform-commons-1.4.0.jar
    │   └── opentest4j-1.1.1.jar

$ java -jar target/find-links.jar
[Usage] jar -jar find-links.jar <url>

$ java -jar target/find-links.jar https://google.com

https://play.google.com/?hl=en&tab=w8
https://www.google.com/calendar?tab=wc
/intl/en/about.html
https://photos.google.com/?tab=wq&pageId=none
https://drive.google.com/?tab=wo
https://translate.google.com.my/?hl=en&tab=wT
//...

Done, next, we will start this find-links.jar in a docker container.

4. Docker File

A Dockerfile is a text file, contains all the commands to assemble the docker image.

4.1 Review the commands in the Dockerfile, it creates a docker image base on openjdk:8-jdk-alpine, an alpine linux with openjdk-8 installed, changed the working directory with WORKDIR and copy the executable jar and its dependencies to this path /usr/local/runme/, and run the jar file with ENTRYPOINT.

Dockerfile

FROM openjdk:8-jdk-alpine

ARG JAR_FILE=target/find-links.jar
ARG JAR_LIB_FILE=target/lib/

# cd /usr/local/runme
WORKDIR /usr/local/runme

# copy target/find-links.jar /usr/local/runme/app.jar
COPY ${JAR_FILE} app.jar

# copy project dependencies
# cp -rf target/lib/  /usr/local/runme/lib
ADD ${JAR_LIB_FILE} lib/

# java -jar /usr/local/runme/app.jar
ENTRYPOINT ["java","-jar","app.jar"]

Note We can find more base images from docker hub

5. Dockerize Java Application

Now, we need to run docker’s commands to parse the Dockerfile to assemble a docker image.

5.1 Go to the folder containing Dockerfile.


$ cd project

$ pwd
/home/mkyong/projects/docker-java-app

$ ls -lsah

4.0K drwxr-xr-x  5 mkyong mkyong 4.0K Jan  21 18:35 .
4.0K drwxr-xr-x  6 mkyong mkyong 4.0K Jan  20 15:27 ..
4.0K -rw-r--r--  1 mkyong mkyong  352 Jan  21 18:26 Dockerfile
4.0K -rw-r--r--  1 mkyong mkyong 3.3K Jan  21 18:13 pom.xml
4.0K -rw-r--r--  1 mkyong mkyong  267 Jan  21 18:35 readme.md
4.0K drwxr-xr-x  4 mkyong mkyong 4.0K Jan  20 15:27 src
4.0K drwxr-xr-x 10 mkyong mkyong 4.0K Jan  21 18:17 target

5.2 Let build a docker image with sudo docker build -t name:tag .


$ pwd
/home/mkyong/projects/docker-java-app

$ sudo docker build -t docker-java:1.0 .

[sudo] password for mkyong:
Sending build context to Docker daemon  1.206MB
Step 1/7 : FROM openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/7 : ARG JAR_FILE=target/find-links.jar
 ---> Running in 8eaabda7c1ce
Removing intermediate container 8eaabda7c1ce
 ---> 50eedc2911be
Step 3/7 : ARG JAR_LIB_FILE=target/lib/
 ---> Running in 7a6c30690589
Removing intermediate container 7a6c30690589
 ---> 053f8ebac2a0
Step 4/7 : WORKDIR /usr/local/runme
 ---> Running in 91ab9f0f41d3
Removing intermediate container 91ab9f0f41d3
 ---> eadff9da3d5d
Step 5/7 : COPY ${JAR_FILE} app.jar
 ---> f52d6a8440c8
Step 6/7 : ADD ${JAR_LIB_FILE} lib/
 ---> fd693617a411
Step 7/7 : ENTRYPOINT ["java","-jar","app.jar"]
 ---> Running in 1dce276e93b3
Removing intermediate container 1dce276e93b3
 ---> 94b4a465425f
Successfully built 94b4a465425f
Successfully tagged docker-java:1.0

5.3 Start a docker container, run the app.jar file during startup. When the Jar file finished running, exit the docker container.


$ sudo docker run -t docker-java:1.0 https://google.com

https://play.google.com/?hl=en&tab=w8
https://www.google.com/calendar?tab=wc
/intl/en/about.html
https://photos.google.com/?tab=wq&pageId=none
https://drive.google.com/?tab=wo
https://translate.google.com.my/?hl=en&tab=wT
https://maps.google.com.my/maps?hl=en&tab=wl
https://books.google.com.my/bkshp?hl=en&tab=wp
https://mail.google.com/mail/?tab=wm
//...

Done, we dockerize a Java application.

6. FAQs

6.1 How to access the docker container’s file system or shell, for debugging?
A: Comment the ENTRYPOINT in Dockerfile to avoid the docker container to run the jar file during startup.

Dockerfile

#...

# cd /usr/local/runme
WORKDIR /usr/local/runme

#...

# java -jar /usr/local/runme/app.jar
# ENTRYPOINT ["java","-jar","app.jar"]

Rebuild the docker image.


$ sudo docker build -t docker-java:1.0 .

Rerun it, since we defined WORKDIR in the Dockerfile file, the docker container will start a shell and change the directory to /usr/local/runme, start debugging here.


# -i
$ sudo docker run -it docker-java:1.0

# This is docker container shell!
/usr/local/runme # ls -lsah
total 16
     4 drwxr-xr-x    1 root     root        4.0K Jan 21 10:38 .
     4 drwxr-xr-x    1 root     root        4.0K Jan 21 10:38 ..
     4 -rw-r--r--    1 root     root        3.4K Jan 21 10:17 app.jar
     4 drwxr-xr-x    2 root     root        4.0K Jan 21 10:38 lib

/usr/local/runme # cd lib
/usr/local/runme/lib # ls -lsah
     total 1128
          4 drwxr-xr-x    2 root     root        4.0K Jan 21 10:38 .
          4 drwxr-xr-x    1 root     root        4.0K Jan 21 10:38 ..
          4 -rw-r--r--    1 root     root        2.1K Jan 15 10:29 apiguardian-api-1.0.0.jar
        388 -rw-r--r--    1 root     root      387.8K Jan 15 10:28 jsoup-1.12.1.jar
        132 -rw-r--r--    1 root     root      131.0K Jan 15 10:29 junit-jupiter-api-5.4.0.jar
        500 -rw-r--r--    1 root     root      499.9K Jan 15 10:29 junit-jupiter-params-5.4.0.jar
         88 -rw-r--r--    1 root     root       87.2K Jan 15 10:29 junit-platform-commons-1.4.0.jar
          8 -rw-r--r--    1 root     root        7.0K Jan 15 10:29 opentest4j-1.1.1.jar

/usr/local/runme/lib # which java
/usr/bin/java

/usr/local/runme/lib # java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0)
OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)

Note If the docker container is running, we can access it directly with this command


$ sudo docker exec -ti container_name /bin/bash
$ sudo docker exec -ti container_name /bin/sh

6.2 How to ship the docker image for deployment?
A: The faster way is to push the image to the docker hub and pull it from the server.


# client
# tag
$ sudo docker image tag docker-java:1.0 mkyong2002/docker-java:1.0
# push
$ sudo docker image push mkyong2002/docker-java:1.0

# server
$ sudo docker pull mkyong2002/docker-java:1.0

Note
You may like this Docker + Spring Boot examples

Download Source Code

$ git clone https://github.com/mkyong/docker-java
$ cd docker-java-app
$ mvn package
$ java -jar target/find-links.jar https://google.com

//dockerize
// create a docker image
$ sudo docker build -t docker-java:1.0 .
// run it
$ sudo docker run -t docker-java:1.0 https://google.com

References

About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

avatar