BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Build a MySQL Spring Boot App Running on WildFly on an Azure VM

Build a MySQL Spring Boot App Running on WildFly on an Azure VM

Key Takeaways

In this post I’ll show you how to:

Recently I was asked to build a demo site that runs on the WildFly application platform and connects to a MySQL database in the cloud, on Microsoft Azure.  The premise seems simple, but the implementation can be tricky, and there is limited documentation on how to set something like this up.  I spent a lot of time working out what needs to be done to make this happen, and I’m sharing the steps here.  

Note that I use Nginx as a reverse proxy server, as it was far easier than converting binaries in the WildFly distribution to use the VM IP address.  Using Nginx, I was able to leave the WildFly distribution binaries as-is using http://127.0.0.1 internally.  This also allows for easier clustering and scaling of WildFly in a cloud environment.   

(Click on the image to enlarge it)

Prerequisites:

Clone the GitHub sample

From the command prompt, navigate to a working directory and clone the sample repository

git clone https://github.com/bbenz/spring-boot-todo

Configure the app to use the MySQL database

Verify your Azure account credentials via the command line

For these steps you’ll need an Azure account.  You can get a Free Trial here. To make sure you’re logged in to your azure account, type az account list.  If you’re not logged in, type az login and follow the prompts.

Create an Azure MySQL database with the Azure CLI

Next up, let’s create an Azure Database for MySQL instance using the Azure CLI.  We will Use the Azure CLI 2.0 in a terminal window to create the resource group and a MySQL instance. 

The Azure Command line interface (CLI) is a great way to leverage the power of Azure from the command line, on Mac, Linux and Windows. It’s POSIX-compatible, written in Python and the open source is available on GitHub.  The CLI runs inside of the terminal windows on Mac and Linux, and on Windows, you can access the CLI vias a command prompt, or via Windows Subsystem for Linux , AKA Bash on Windows.   There’s even an app with CLI capabilities for IOS and Android.

You can also access the Azure CLI as well as many other CLIs with just a browser, from the azure portal or shell.azure.com.  You can find more info on our shell, and updated information on supported partner CLIs here.  Note that all options require a Microsoft Azure account (free trial here).

Log in and create a resource group

Log in to your Azure subscription with the az login command, then follow the on-screen directions. 

Create an Azure resource group.  

Azure resource groups manage Azure services together as a unit. Each resource group must have a location.  To see all possible values you can use for  `--location`, use the az appservice list-locations command.

The following example creates an Azure resource group in the North Europe region.

az group create --name myResourceGroup --location "North Europe"

Create a MySQL server

Create a server in Azure Database for MySQL.  Substitute your own unique MySQL server name where you see the `<mysql_server_name>` placeholder. This name is part of your MySQL server's hostname, `<mysql_server_name>.mysql.database.azure.com`, so it needs to be globally unique. Also substitute `<admin_user>` and `<admin_password>` with your own values:

az mysql server create --name <mysql_server_name> --resource-group myResourceGroup --location "North Europe" --admin-user <admin_user> --admin-password <admin_password>

Configure a MySQL firewall

Create a firewall rule for your MySQL server to allow client connections by using the az mysql server firewall-rule create command.  Here’s an example that creates a firewall rule for a broad range of IP addresses (You will likely want to narrow your actual firewall IP address range): 

az mysql server firewall-rule create --name allIPs --server <mysql_server_name> --resource-group myResourceGroup --start-ip-address 0.0.0.0 --end-ip-address 255.255.255.255

Configure the Azure MySQL database

Connect to the MySQL server using the values you specified previously for `<admin_user>` and `<mysql_server_name>`.

mysql -u <admin_user>@<mysql_server_name> -h <mysql_server_name>.mysql.database.azure.com -P 3306 -p

In the `mysql` prompt, create a database and a table for the to-do items.

CREATE DATABASE tododb;

Create a database user and give it all privileges in the `tododb` database. Replace the placeholders `<Javaapp_user>` and `<Javaapp_password>` with your own unique app name:

CREATE USER '<Javaapp_user>' IDENTIFIED BY '<Javaapp_password>'; 

GRANT ALL PRIVILEGES ON tododb.* TO '<Javaapp_user>';

Exit your server connection by typing `quit`.

Update the values in the application.properties file

Update the following values in src/main/resources/application.properties:

spring.datasource.url=jdbc:mysql:// >@<mysql_server_name>.mysql.database.azure.com:3306/tododb

spring.datasource.username=adminname@<mysql_server_name>

spring.datasource.password=password

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update

Build and run the sample

Build and run the sample locally using the Maven wrapper included in the repo:

mvn package spring-boot:run

In a browser, open http://localhost:8080 to make sure the app is functional before we send the package to a VM on Azure. 

Configure Maven to generate a .WAR file

To deploy the application to a vm, we need to deploy a .war file using the mvn package command.   Right now maven produces a .jar file, and we need to deploy a .WAR file to Firefly.  It; an easy change – just add this line to your pom.xml:

<packaging>war</packaging>

Now, run mvn clean package from the same location as the pom.xml to  generate a .WAR file in the target directory called todo-app-java-on-azure-1.0-SNAPSHOT.war

Create a Linux VM on Azure

There are several ways to create a new Linux VM on Azure, for this article, we’re going to go with the command line, using the az vm create command.

This command creates a Linux VM with the latest Ubuntu image available on azure:

az vm create -n <vm name> -g <resource group> -l eastus2 --generate-ssh-key --image ubuntults

Open ports 8080 and 9990

Next, open port 8080 and 9990 on the target VM.  Spring Boot uses port 8080 for applications and wildfly uses port 9990 for admin access.  You have to set  priority when you open more than one port on the VM, so set wildfly lower (I used 800) than Spring (which remains at the default 100).  Priority can range from 100 to 4096.

az vm open-port -n <vm name> -g <resource group>  --port 8080

az vm open-port -n <vm name> -g <resource group>  --port 9990 –priority 800

SSH into the VM

Get into the VM with the SSH key you generated as part of the az vm create command. Use the <username> from your VM - your account email to the left of the @ is usually the default.

ssh <username>@< PublicIpAddress>

You should get this response the first time.  Say yes.

The authenticity of host '< PublicIpAddress> (< PublicIpAddress>)' can't be established.

ECDSA key fingerprint is …………

Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added < PublicIpAddress> (ECDSA) to the list of known hosts.

Prep the VM 

Update and Install OpenJDK and nginx

Now that we’re in the VM, let’s make sure that we have the latest Linux available, then install software we’re going to need.  For this demo we’re installing nginx as a reverse proxy and configuring wildfly to run as a service.  We also need Java so we’re installing OpenJDK 8.

Using nginx means that the wildfly server can use it’s configured defaults (127.0.0.1) to run on any VM.  Otherwise we have to configure wildfly with your VM’s public Ip address.   In a clustered or containerized environment, this makes wildfly inflexible without nginx.  For a demo environment, it means we can skip a few configuration steps (there are enough already ).

apt-get update

apt-get install -y openjdk-8*

apt-get install nginx

Install and set up wildfly as a Service

Next up, let’s install and configure wildfly.  There’s no apt-get for wildfly so we need to use wget to retrieve it from the server

wget http://download.jboss.org/wildfly/12.0.0.Final/wildfly-12.0.0.Final.tar.gz

mv wildfly-12.0.0.Final.tar.gz /opt/

cd /opt/ 

 tar -zvxf wildfly-12.0.0.Final.tar.gz

 mv wildfly-12.0.0.Final wildfly

Now that we have wildfly in a location we want it, we need to add a Management user to work with admin tasks:

cd wildfly/bin

./add-user.sh

Here are the prompts you will see and the answers I provided:

What type of user do you wish to add?

 a) Management User (mgmt-users.properties)

 b) Application User (application-users.properties)

(a): a

Enter the details of the new user to add.

Using realm 'ManagementRealm' as discovered from the existing property files.

Username : 

Password : !

What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]:

About to add user 'rhsotdemo1' for realm 'ManagementRealm'

Is this correct yes/no? yes

Is this new user going to be used for one AS process to connect to another AS process?

e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.

yes/no? no

Next up, we will configure a few things in Widfly with information about the VM it is running on:

cd /opt/wildfly/bin/

 vi standalone.conf

Add or edit the following:

JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"

JBOSS_HOME="/opt/wildfly"

Configure wildfly Autostart

Configure wildfly to start automatically when the VM starts (or restarts) by adding these files:

 vi /etc/default/wildfly

Add this to the new file:

WILDFLY_USER="wildfly"

STARTUP_WAIT=180

SHUTDOWN_WAIT=30

WILDFLY_CONFIG=standalone.xml

WILDFLY_MODE=standalone

WILDFLY_BIND=0.0.0.0

Save that file, then create this file:

 vi /opt/wildfly/bin/launch.sh

Add this to the new file:

#!/bin/sh

if [ "x$WILDFLY_HOME" = "x" ]; then

    WILDFLY_HOME=/opt/wildfly

fi

if [ "x$1" = "xdomain" ]; then

    echo 'Starting wildfly in domain mode.'

    $WILDFLY_HOME/bin/domain.sh -c $2 -b $3

else

    echo 'Starting wildfly in standalone mode.'

    $WILDFLY_HOME/bin/standalone.sh -c $2 -b $3

fi

Save that file:

Make the startup script executable:

 chmod 755 /opt/wildfly/bin/launch.sh

Next, create a systemd init file:

 vi /etc/systemd/system/wildfly.service

Add this to the new file:

[Unit]

Description=The wildfly Application Server

After=syslog.target network.target

Before=nginx.service

[Service]

Environment=LAUNCH_JBOSS_IN_BACKGROUND=1

EnvironmentFile=/etc/default/wildfly

User=wildfly

LimitNOFILE=102642

PIDFile=/var/run/wildfly/wildfly.pid

ExecStart=/opt/wildfly/bin/launch.sh $WILDFLY_MODE $WILDFLY_CONFIG $WILDFLY_BIND

StandardOutput=null

[Install]

WantedBy=multi-user.target

Save that file 

and set the ownership of files and directories:

chown wildfly:wildfly -R /opt/wildfly/

systemctl enable wildfly

We also need to disable nginx listening for port 80 (wildfly will take care of this):

vi /etc/nginx/sites-enabled/default

Comment out: 

#listen [::]:80 default_server;

Next up, we need to create a new nginx server block for accessing wildfly using nginx as a reverse proxy:

vi /etc/nginx/sites-available/wildfly

Add this to the new File:

upstream wildfly {

    server 127.0.0.1:8080;

}

server {

    listen      80;

    server_name your-domain.com;

    access_log  /var/log/nginx/wildfly.access.log;

    error_log   /var/log/nginx/wildfly.error.log;

    proxy_buffers 16 64k;

    proxy_buffer_size 128k;

location / {

        proxy_pass  http://wildfly;

        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;

        proxy_redirect off;

        proxy_set_header    Host            $host;

        proxy_set_header    X-Real-IP       $remote_addr;

        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header    X-Forwarded-Proto https;

    }

}

From the command line, create a symbolic link between nginx and wildfly, then start the wildfly service and enable wildfly to start on boot with nginx:

ln -s /etc/nginx/sites-available/wildfly /etc/nginx/sites-enabled/

systemctl enable nginx

systemctl start nginx.service

groupadd -r wildfly

useradd -r -g wildfly -d /opt/wildfly -s /sbin/nologin wildfly

systemctl daemon-reload

systemctl start wildfly

Pro Tip – if you need to change any of the above, restart nginx and wildfly with this command:

systemctl restart nginx.service

Deploy the .WAR file to the wildfly server

You’re now ready to transfer the .WAR file to the VM.  The easiest way is scp (secure copy).  

scp /mnt/c/GitHub/todo-app-java-on-azure/target/todo-app-java-on-azure-1.0-SNAPSHOT.war <username>@< PublicIpAddress>:~/

Use the mv command to relocate the file to the /opt/wildfly/standalone/deployments/ folder, where wildfly will start it automatically for you.  Then access the app via the following URL:

http://<VMPublicIpAddress>

If you made it this far, thanks and congratulations for making it all the way though!  You know have a Java application running on a wildfly server on a Linux VM in the cloud and accessing a MySQL database on Azure.  There have been a lot of configuration steps, but once the initial environment has been established, it's quite easy to set up a deployment pipeline to get new versions of the code and DB pushed to the Azure environment

As always, we value your feedback so please let us know what you think!  

About the Author

Brian Benz is a Senior Cloud Developer Advocate at Microsoft, helping Java developers to get the most out of Azure. Before Joining Microsoft, he was a solution architect, consultant, developer, author and presenter at IBM, Deloitte, and other companies. Find him on Twitter @bbenz.

Rate this Article

Adoption
Style

BT