Java + Quarkus 2

In this article, we’ll explain how to use Ansible to build and deploy a Quarkus application. Quarkus is an exciting, lightweight Java development framework designed for cloud and Kubernetes deployments, and Red Hat Ansible Automation Platform  is one of the most popular automation tools and a star product from Red Hat.

Set up your Ansible environment

Before discussing how to automate a Quarkus application deployment using Ansible, we need to ensure the prerequisites are in place. First, you have to install Ansible on your development environment. On a Fedora or a Red Hat Enterprise Linux machine, this is achieved easily utilizing the dnf package manager:

$ dnf install ansible-core

The only other requirement is to install the Ansible collection dedicated to Quarkus:

$ ansible-galaxy collection install middleware_automation.quarkus

This is all you need to prepare the Ansible control machine (the name given to the machine executing Ansible).

Generally, the control node is used to set up other systems that are designated under the name targets. For the purpose of this tutorial, and for simplicity's sake, we are going to utilize the same system for both the control node and our (only) target. This will make it easier to reproduce the content of this article on a single development machine.

Note that you don’t need to set up any kind of Java development environment, because the Ansible collection will take care of that.

The Ansible collection dedicated to Quarkus is a community project, and it’s not supported by Red Hat. However, both Quarkus and Ansible are Red Hat products and thus fully supported. The Quarkus collection might be supported at some point in the future, but is not as the time of the writing of this article.

Inventory file

Before we can execute Ansible, we need to provide to the tool an inventory of the targets. There are many ways to achieve that, but the simplest solution for a tutorial such as this one is to write up an inventory file of our own.

As mentioned above, we are going to use the same host for both the controller and the target, so the inventory file has only one host. Here again, for simplicity's sake, this machine is going to be the localhost: 

$ cat inventory
localhost ansible_connection=local

Refer to the Ansible documentation for more information on Ansible inventory.

Build and deploy the app with Ansible

For this demonstration, we are going to utilize one of the sample applications provided as part of the Quarkus quick starts project. We will use Ansible to build and deploy the getting started application.

All we need to provide to Ansible is the application name, repository URL, and the destination folder, where to deploy the application on the target. Because of the directory structure of the Quarkus quick start, containing several projects, we'll also need to specify the directory containing the source code:

$ ansible-playbook -i inventory middleware_automation.quarkus.playbook \
                   -e app_name='optaplanner-quickstart' \
                   -e quarkus_app_source_folder='optaplanner-quickstart' \
                   -e quarkus_path_to_folder_to_deploy=/opt/optplanner \
                   -e quarkus_app_repo_url=''

Below is the output of this command :

PLAY [Build and deploy a Quarkus app using Ansible] ****************************

TASK [Gathering Facts] *********************************************************

ok: [localhost]

TASK [Build the Quarkus from] ***

TASK [middleware_automation.quarkus.quarkus : Ensure required parameters are provided.] ***

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Define path to mvnw script.] *****

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure that builder host localhost has appropriate JDK installed: java-17-openjdk] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Delete previous workdir (if requested).] ***

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure app workdir exists: /tmp/workdir] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Checkout the application source code.] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Build the App using Maven] *******

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Display build application log] ***

skipping: [localhost]

TASK [Deploy Quarkus app on target.] *******************************************

TASK [middleware_automation.quarkus.quarkus : Ensure required parameters are provided.] ***

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure requirements on target system are fullfilled.] ***

included: /root/.ansible/collections/ansible_collections/middleware_automation/quarkus/roles/quarkus/tasks/deploy/prereqs.yml for localhost

TASK [middleware_automation.quarkus.quarkus : Ensure required OpenJDK is installed on target.] ***

skipping: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure Quarkus system group exists on target system] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure Quarkus user exists on target system.] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure deployement directory exits: /opt/optplanner.] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Set Quarkus app source dir (if not defined).] ***

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Deploy application as a systemd service on target system.] ***

included: /root/.ansible/collections/ansible_collections/middleware_automation/quarkus/roles/quarkus/tasks/deploy/service.yml for localhost

TASK [middleware_automation.quarkus.quarkus : Deploy application from  to target system] ***

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Deploy Systemd configuration for Quarkus app] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Perform daemon-reload to ensure the changes are picked up] ***

ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure Quarkus app service is running.] ***

changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure firewalld configuration is appropriate (if requested).] ***

skipping: [localhost]

PLAY RECAP *********************************************************************

localhost              : ok=19   changed=8 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0  

As you can see, the Ansible collection for Quarkus does all the heavy lifting for us: its content takes care of checking out the source code from GitHub and builds the application. It also ensures the system used for this step has the required OpenJDK installed on the target machine.

Once the application is successfully built, the collection takes care of the deployment. Here again, it checks that the appropriate OpenJDK is available on the target system. Then, it verifies that the required user and group exist on the target and if not, creates them. This is recommended mostly to be able to run the Quarkus application with a regular user, rather than with the root account.

With those requirements in place, the jars produced during the build phase are copied over to the target, along with the required configuration for the application integration into systemd as a service. Any change to the systemd configuration requires reloading its daemon, which the collection ensures will happen whenever it is needed. With all of that in place, the collection starts the service itself.

Validate the execution results 

Let’s take a minute to verify that all went well and that the service is indeed running:

# systemctl status optaplanner-quickstart.service
● optaplanner-quickstart.service - A Quarkus service named optaplanner-quickstart
   Loaded: loaded (/usr/lib/systemd/system/optaplanner-quickstart.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2023-04-26 09:40:13 UTC; 3h 19min ago
 Main PID: 934 (java)
   CGroup: /system.slice/optaplanner-quickstart.service
        └─934 /usr/bin/java -jar /opt/optplanner/quarkus-run.jar

Apr 26 09:40:13 be44b3acb1f3 systemd[1]: Started A Quarkus service named optaplanner-quickstart.
Apr 26 09:40:14 be44b3acb1f3 java[934]: __  ____  __  _____   ___  __ ____  ______
Apr 26 09:40:14 be44b3acb1f3 java[934]:  --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
Apr 26 09:40:14 be44b3acb1f3 java[934]:  -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
Apr 26 09:40:14 be44b3acb1f3 java[934]: --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
Apr 26 09:40:14 be44b3acb1f3 java[934]: 2023-04-26 09:40:14,843 INFO  [io.quarkus] (main) optaplanner-quickstart 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.16.6.Final) started in 1.468s. Listening on:
Apr 26 09:40:14 be44b3acb1f3 java[934]: 2023-04-26 09:40:14,848 INFO  [io.quarkus] (main) Profile prod activated.
Apr 26 09:40:14 be44b3acb1f3 java[934]: 2023-04-26 09:40:14,848 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, hibernate-orm-rest-data-panache, jdbc-h2, narayana-jta, optaplanner, optaplanner-jackson, resteasy-reactive, resteasy-reactive-jackson, resteasy-reactive-links, smallrye-context-propagation, vertx, webjars-locator]

Having the service running is certainly good, but it does not guarantee by itself that the application is available. To double-check, we can simply confirm the accessibility of the application by connecting to it:

# curl -I http://localhost:8080/
HTTP/1.1 200 OK
accept-ranges: bytes
content-length: 8533
cache-control: public, immutable, max-age=86400
last-modified: Wed, 26 Apr 2023 10:00:18 GMT
date: Wed, 26 Apr 2023 13:00:19 GMT

Writing up a playbook

The default playbook provided with the Ansible collection for Quarkus is quite handy and allows you to bootstrap your automation with a single command. However, most likely, you’ll need to write your own playbook so you can add automation required around the deployment of your Quarkus app.

Here is the content of the playbook provided with the collection that you can simply use as a base for your own:

- name: "Build and deploy a Quarkus app using Ansible"
  hosts: all
  gather_facts: false
    quarkus_app_repo_url: ''
    app_name: optaplanner-quickstart'
    quarkus_app_source_folder: 'optaplanner-quickstart'
    quarkus_path_to_folder_to_deploy: '/opt/optaplanner'

    - name: "Build the Quarkus from {{ quarkus_app_repo_url }}."
        name: quarkus
        tasks_from: build.yml


    - name: "Deploy Quarkus app on target."
        name: quarkus
        tasks_from: deploy.yml

To run this playbook, you again use the ansible-playbook command, but providing the path to the playbook:

$ ansible-playbook -i inventory playbook.yml


Thanks to the Ansible collection for Quarkus, the work needed to automate the deployment of a Quarkus application is minimal. The collection takes care of most of the heavy lifting and allows its user to focus on the automation needs specific to their application and business needs.

Explore other Ansible tutorials on Red Hat Developer:

Last updated: August 14, 2023