Key Takeaways
In this post I’ll show you how to:
- Retrieve a simple Spring Boot Java App from GitHub
- Connect it to the Azure Database for MySQL Service
- Set up a Linux VM running WildFly on Microsoft Azure
- Deploy the sample app to the WildFly server.
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:
- An Azure account (Free Trial here)
- A Git Client
- The Azure CLI 2.0
- Java 7 JDK or above
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.