Tutorial: Running WildFly and Jakarta EE in a Kubernetes Environment

In this short tutorial, I’ll explain how to set up and customize a WildFly application server operating in standalone mode to run a Jakarta EE application in a container-based environment orchestrated by Kubernetes.

WildFly provides a modern application framework that simplifies development of web applications and microservices. It is Jakarta EE 8 platform compatible and web profile compatible. It also includes Eclipse MicroProfile version 3.3, the latest available version. All WildFly runtime services minimize heap allocation and applications start very fast with a minimum of memory.

Start WildFly in a Docker Container

WildFly provides an official container image that is well-documented and provides the latest WildFly containers. To start WildFly in a Docker container, run the command shown below.

$ docker run -it jboss/wildfly

The Docker image is based on OpenJDK 11, which is optimized to run in container environments such as Kubernetes. Docker Hub includes documentation about the various ways you can build your own images. I describe one such approach here.

Build Your Custom Docker Image

To build a WildFly 20 Docker container with your own Jakarta EE application, set up a new Dockerfile and copy your application into the deployment directory, as shown below.


FROM jboss/wildfly
ADD your-awesome-app.war /opt/jboss/wildfly/standalone/deployments/

WildFly automatically deploys your application each time the server starts.

Customize the Configuration

In cases where you need additional configurations, such as security realms or database connection pools, to run the application, you can customize the main configuration file — standalone.xml — or add modules, such as Java Database Connectivity (JDBC) drivers.

Below, I show how to add a PostgreSQL JDBC driver to your WildFly Server. The ability to separate external resources, such as database connections, from your application is one of Jakarta EE’s strengths.

The following directory tree shows a custom configuration that adds a PostgreSQL JDBC driver as a module and a custom standalone.xml file.

wildfly
      ├── modules
      │   ├── org
      │   │   └── postgresql
      │   │       └── main
      │   │       	  ├── module.xml
      │   │       	  └── postgresql-42.2.5.jar
      └── standalone.xml

Configure the JDBC Drive Module and Database Pool

To configure the JDBC driver module and a JDBC database pool, copy the standalone.xml file from a running WildFly container, then add the configuration shown below into the <datasource> section. This adds the new postgreSQL JDBC driver module and a generic database pool that can be configured in Kubernetes using environment variables.


.....
...
<datasource jta="true" jndi-name="java:/jdbc/office"
    pool-name="office" enabled="true" use-ccm="true">
    <connection-url>${env.POSTGRES_CONNECTION}</connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <driver>postgresql</driver>
    <security>
        <user-name>${env.POSTGRES_USER}</user-name>
        <password>${env.POSTGRES_PASSWORD}</password>
    </security>
</datasource>
<drivers>
    <driver name="h2" module="com.h2database.h2">
        <xa-datasource-class>org.h2.jdbcx.JdbcDataSource
        </xa-datasource-class>
    </driver>
    <driver name="postgresql" module="org.postgresql">
        <driver-class>org.postgresql.Driver</driver-class>
    </driver>
</drivers>
...
.....

Build Your Custom Docker Image

Now you can add the new configuration files into your Dockerfile and build your new customized Docker image, as shown below.


FROM jboss/wildfly:20.0.1.Final
 
COPY ./wildfly/modules/ /opt/jboss/wildfly/modules/
COPY ./wildfly/standalone.xml /opt/jboss/wildfly/standalone/configuration/
 
# Deploy artefact
ADD your-awesome-app.war /opt/jboss/wildfly/standalone/deployments/

If you provide a custom configuration, make sure your Docker image is based on a WildFly version that matches your standalone.xml and module configuration. Otherwise, a newer WildFly container may not start with your configuration.

Run the Container in Kubernetes

If you don’t already have a Kubernetes environment, the open source project Imixs-Cloud provides an easy way to set up a self-managed Kubernetes cluster.

The example below shows a YAML Ain't Markup Language (YAML) deployment file for the custom WildFly image.


apiVersion: apps/v1
kind: Deployment
metadata:
  name: awesome-app
  labels:
    app: awesome-app
spec:
  replicas: 1
  selector:
    matchLabels:
  	app: awesome-app
  strategy:
	type: Recreate
  template:
	metadata:
  	labels:
    	app: awesome-app
	spec:
  	containers:
  	- name: awesome-app
    	image: registry.foo.com/library/awesome-app:latest
    	env:
    	- name: POSTGRES_CONNECTION
      	value: jdbc:postgresql://postgre/my-db
    	- name: POSTGRES_PASSWORD
      	value: mypassword
    	- name: POSTGRES_USER
   	   value: myuser
    	ports:
          - name: web
        	containerPort: 8080
    	resources:
      	requests:
        	memory: "512Mi"
      	limits:
        	memory: "1Gi"
  	restartPolicy: Always

Note that the database pool is configured using the environment variables POSTGRES_CONNECTION, POSTGRES_PASSWORD, and POSTGRES_USER as defined in the WildFly standard.xml file. This means our container is still independent from a specific database instance and can be connected to different environments.

Set Memory Limits

As previously noted, OpenJDK 11 is optimized to run in containers. Use the memory limits defined in the deployment object to define how Kubernetes should manage container memory.

In the example below, the container starts with 512 MB of memory and can use a maximum of 1 GB. The heap size is automatically controlled by OpenJDK.


resources:
      	requests:
        	memory: "512Mi"
      	limits:
        	memory: "1Gi"

In most cases, no additional configuration is needed. However, if you want to configure factors such as the ratio of how OpenJDK uses available memory for heap, you can include additional java virtual machine (JVM) options, such as the one shown in the example below.


- name: JAVA_OPTS
  value: "-XX:MaxRAMPercentage=75.0"

WildFly automatically uses this new Java option on startup. You can easily verify the settings in the container log file. Depending on your application, other options may be useful as well.

Configure the Liveness Probe and Health Check

With Eclipse MicroProfile, WildFly provides various metrics and a Health API out of the box.

The Eclipse MicroProfile Health API can be used for a liveness probe in Kubernetes. The MicroProfile health check specification defines three HTTP endpoints:

  • */health to test the liveness and the readiness of the runtime.
  • */health/live to test the liveness of the runtime.
  • */health/ready to test the readiness of the runtime.

The health check HTTP endpoints are accessible through WildFly HTTP management interface port 9990. To activate the management interface, change the default command to start WildFly at the end of your Dockerfile, as shown in the example below.


# Run with management interface
CMD ["/opt/jboss/WildFly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0"]

This command publishes port 9990. With the additional configuration shown below, and the corresponding service configuration, you can activate the liveness probe in Kubernetes YAML deployment files.

In the example below, Kubernetes checks the WildFly server for liveness every five seconds after a start delay of 60 seconds, which is sufficient for a complete startup.


livenessProbe:
      	httpGet:
        	path: /health
        	port: 9990
      	initialDelaySeconds: 60
      	periodSeconds: 5

Note that you can also use the Eclipse MicroProfile Health API to add application-specific health checks and use them in your custom liveness probe. For example, you can implement a check to verify the database is up and running and in a consistent state.

Monitor and Analyze Application Metrics

WildFly also provides a large number of server metrics based on the Eclipse MicroProfile Metrics API. These metrics can be monitored using the Prometheus system monitoring and alerting toolkit and the Grafana analytics platform. Of course, you can easily add your own application metrics too, as shown in the example below.

http://your-server:9990/metrics

Monitor and Manage a Running Instance

WildFly provides a convenient web interface to monitor and manage a running instance. This interface is accessible through port 9990 but is protected by default.

To access the WildFly web interface, use Secure Shell (SSH) to access a running WildFly container and activate the admin user, as shown in the example below.

$ ./WildFly/bin/add-user.sh

You will be prompted to add a new management user with a password. Complete this step, then log in to the web interface, as shown in the example below.

http://your-server:9990

WildFly is now ready to run in container-based environments, such as Kubernetes.

With the latest Docker image and Eclipse MicroProfile, you can configure WildFly in various ways and integrate it smoothly into a microservices architecture.

Learn More

To learn more about Jakarta EE, click here.

To learn more about Eclipse MicroProfile, click here.

About the Author

Ralph Soika

Ralph Soika

Ralph Soika works at Imixs Software Solutions GmbH. He is the project lead of the open source project Imixs-Workflow, as well as a committer and project lead on the Eclipse Business Process Model and Notation (BPMN2) project.