WildFly Java Microservice - PART 1: Docker Image

In this guide, you will learn how to create and run a Docker Image containing a Jakarta REST service implemented using WildFly.

Prerequisites

To complete this guide, you need:

  • Roughly 10 minutes

  • JDK 11+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.9+

  • Install Docker or Podman

Maven Project

Create a simple Jakarta EE application Maven Project containing a Jakarta REST service using the org.wildfly.archetype:wildfly-getting-started-archetype archetype:

mvn archetype:generate \
-DarchetypeGroupId=org.wildfly.archetype \
-DarchetypeArtifactId=wildfly-getting-started-archetype
Note
See Getting Started with WildFly for the details

pom.xml

We need to add some features that will be required when, in the next guide, we will run our application on Kubernetes; specifically, we need to expose Liveness, Readiness and Startup Probes; we can add these features thanks to the wildfly-cloud-galleon-pack.

Update the pom.xml file and add the wildfly-cloud-galleon-pack to the wildfly-maven-plugin configuration like in the following:

    <plugin>
        <groupId>org.wildfly.plugins</groupId>
        <artifactId>wildfly-maven-plugin</artifactId>
        <version>5.0.0.Final</version>
        <configuration>
            <feature-packs>
                <feature-pack>
                    <location>org.wildfly:wildfly-galleon-pack:32.0.0.Final</location>
                </feature-pack>
                <feature-pack>
                    <location>org.wildfly.cloud:wildfly-cloud-galleon-pack:7.0.0.Final</location>
                </feature-pack>
            </feature-packs>
            <layers>
                <layer>cloud-server</layer>
            </layers>
        </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>package</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

Build the application

$ mvn clean package
...
[INFO] Copy deployment /home/tborgato/Documents/WildFly-Mini-Serie/getting-started/target/ROOT.war to /home/tborgato/Documents/WildFly-Mini-Serie/getting-started/target/server/standalone/deployments/ROOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.478 s
[INFO] Finished at: 2024-04-12T17:35:46+02:00
[INFO] ------------------------------------------------------------------------

Start the application

$ ./target/server/bin/standalone.sh
...
17:36:25,755 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-3) WFLYUT0006: Undertow HTTP listener default listening on 0.0.0.0:8080
...
17:36:25,967 INFO  [org.jboss.weld.deployer] (MSC service thread 1-2) WFLYWELD0003: Processing weld deployment ROOT.war
...
17:36:26,835 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 62) RESTEASY002225: Deploying jakarta.ws.rs.core.Application: class org.wildfly.examples.GettingStartedApplication
17:36:26,859 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 62) WFLYUT0021: Registered web context: '/' for server 'default-server'
17:36:26,883 INFO  [org.jboss.as.server] (ServerService Thread Pool -- 35) WFLYSRV0010: Deployed "ROOT.war" (runtime-name : "ROOT.war")
17:36:26,901 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0212: Resuming server
17:36:26,904 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
17:36:26,904 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0054: Admin console is not enabled
17:36:26,904 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 31.0.0.Final (WildFly Core 23.0.1.Final) started in 2175ms - Started 280 of 378 services (149 services are lazy, passive or on-demand) - Server configuration file in use: standalone.xml

As you can see, the application started, and it’s ready to serve requests on port 8080.

Check the application

Go to this URL:

either in your browser or using a utility like curl:

$ curl http://127.0.0.1:8080/hello/pippo
Hello 'pippo'.

Check Liveness, Readiness and Startup Probes

Go to these URLs:

either in your browser, or using a utility like curl:

$ curl http://127.0.0.1:9990/health/live
{"status":"UP","checks":[{"name":"empty-liveness-checks","status":"UP"}]}
$ curl http://127.0.0.1:9990/health/ready
{"status":"UP","checks":[{"name":"suspend-state","status":"UP","data":{"value":"RUNNING"}},{"name":"server-state","status":"UP","data":{"value":"running"}},{"name":"deployments-status","status":"UP","data":{"ROOT.war":"OK"}},{"name":"boot-errors","status":"UP"},{"name":"ready-deployment.ROOT.war","status":"UP"}]}
$ curl http://127.0.0.1:9990/health/started
{"status":"UP","checks":[{"name":"started-deployment.ROOT.war","status":"UP"}]}

Everything works fine!

Let’s move on and package our application in a Docker Image …​

Docker Image

Dockerfile

We will use the Dockerfile from examples/docker-build/Dockerfile because it is crafted specifically for WildFly.

Create a file named Dockerfile in the same directory as the pom.xml file, with the following content:

ARG runtime_image=quay.io/wildfly/wildfly-runtime:latest
FROM ${runtime_image}
COPY --chown=jboss:root target/server $JBOSS_HOME
RUN chmod -R ug+rwX $JBOSS_HOME

Build the Docker Image

Build the Docker Image my-jaxrs-app:latest with the following command:

$ podman build -t my-jaxrs-app:latest .
STEP 1/3: FROM quay.io/wildfly/wildfly-runtime:latest
STEP 2/3: COPY --chown=jboss:root target/server $JBOSS_HOME
-→ cf1b99511a9b
STEP 3/3: RUN chmod -R ug+rwX $JBOSS_HOME
COMMIT my-jaxrs-app:latest
-→ e1ab6e80ed20
Successfully tagged localhost/my-jaxrs-app:latest
e1ab6e80ed20c3619a7e859f03c71f33b79a4d292f971ed83e7484f4779121d8
Note
As you can see, we used Podman, but you can also use Docker, depending on your preference and on which one you have installed on your PC; to switch to Docker, replace podman with docker in the previous command (and in the ones that will follow).

Run the Docker Image

To check that our brand-new my-jaxrs-app:latest Docker Image works as expected, run the following command:

$ podman run --rm -p 8080:8080 -p 9990:9990 \
  --name=my-jaxrs-app \
  my-jaxrs-app
…​
16:14:49,477 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-5) WFLYUT0006: Undertow HTTP listener default listening on 0.0.0.0:8080
…​
16:14:49,677 INFO  [org.jboss.weld.deployer] (MSC service thread 1-4) WFLYWELD0003: Processing weld deployment ROOT.war
…​
16:14:50,403 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool — 61) RESTEASY002225: Deploying jakarta.ws.rs.core.Application: class org.wildfly.examples.GettingStartedApplication
16:14:50,426 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool — 61) WFLYUT0021: Registered web context: '/' for server 'default-server'
16:14:50,451 INFO  [org.jboss.as.server] (ServerService Thread Pool — 35) WFLYSRV0010: Deployed "ROOT.war" (runtime-name : "ROOT.war")
16:14:50,459 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0212: Resuming server
16:14:50,460 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://0.0.0.0:9990/management
16:14:50,460 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0054: Admin console is not enabled
16:14:50,461 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 31.0.0.Final (WildFly Core 23.0.1.Final) started in 1101ms - Started 280 of 378 services (149 services are lazy, passive or on-demand) - Server configuration file in use: standalone.xml

As you can see, the application started, and it’s ready to serve requests on port 8080.

Note
we explicitly mapped port 8080 in the container to port 8080 on the HOST (your PC); we also mapped port 9990 in the container to port 9990 on the HOST to expose the Liveness, Readiness and Startup Probes.

Check the application

Repeat the checks in Check the application.

Note
the only difference here, is that the response is served by the application running inside our my-jaxrs-app:latest Docker Image

Check Liveness, Readiness and Startup Probes

Note
here too, the only difference, is that the responses are served by the application running inside our my-jaxrs-app:latest Docker Image

Stop the Docker container

Stop the running container:

$ podman stop my-jaxrs-app
< Back to Guides