Three Tier App for vRealize Automation

One question I’m asked quite a lot is what I use for a 3-tier application when I’m testing things like NSX micro-segmentation with vRealize Automation. The simple answer is that I used to make something up as I went along, deploying components by hand and generally repeating myself a lot. I had some cut/paste commands in my note application that sped things up a little, but nothing that developed. I’ve been meaning to rectify this for a while, and this is the result!

A lot of this is based on the excellent blog posts published on the VMware HOL blog by Doug Baer. Doug wrote five parts on creating his application on Photon OS and they’re well worth a read (start at part 1, here). I have changed a few things for my vRA Three Tier App, and some things are the same:

  • I’m using CentOS7, as that’s what I see out in the wild with customers (RHEL7) and I am most familiar with
  • The app itself is the PHP MySQL CRUD Application from Tutorial Republic
  • The DB tier uses MariaDB (MySQL) not SQLite
  • The App tier is an Apache/PHP server
  • The Web tier is still NGINX as a reverse proxy
  • I am including NSX on-demand load balancers in my blueprint, but you don’t actually need them for single-VM tiers
  • Finally, I want to be able to deploy my 3-tier application using vRA Software Components (though you can also use startup scripts in the customisation spec)

Based on this, my final application will look something like the image below, with clients connecting to the NSX load balancer on HTTPS/443, multiple NGINX reverse proxy servers communicating with the NSX load balancer on HTTP/8080, which is in front of multiple Apache web servers running the PHP application which all talk to the MySQL databased back end over MySQL/3306.

Three Tier App

When in use, the application looks like this:

CentOS 7 Template

At the core of my three tier application is the CentOS 7 template, which has been prepared with open-vm-tools and the vRealize Automation Guest Agent (gugent). The installation was completed by following the Install the Guest Agent on a Linux Reference Machine document. The Guest Agent can be tricky to install, so use the script and validate the downloaded IaaS Manager Service Host certificate.

I also have a software component that performs infrastructure tasks on my CentOS VMs, such as updating when they are first deployed.

Database Server Installer ScriptMariaDB

The Database installer script ( performs the following tasks:

  • Install the mariaDB server
  • Generates a secure password for the application to use
  • Secures the database installation (equivalent to running the “mysql_secure_installation” script) and root account
  • Creates the app database and the app user based on input variables
  • Enables remote connections to the database
  • Opens the firewall for mysql connections
# Install the database
/usr/bin/yum -y install mariadb-server
/usr/bin/systemctl enable mariadb.service
/usr/bin/systemctl start mariadb.service
## Configure the Database
mysql_user_password=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
# Secure the database installation
/usr/bin/mysqladmin -u root password "$mysql_root_password"
/usr/bin/mysql -u root -p"$mysql_root_password" -e "UPDATE mysql.user SET Password=PASSWORD('$mysql_root_password') WHERE User='root'"
/usr/bin/mysql -u root -p"$mysql_root_password" -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '', '::1')"
/usr/bin/mysql -u root -p"$mysql_root_password" -e "DELETE FROM mysql.user WHERE User=''"
/usr/bin/mysql -u root -p"$mysql_root_password" -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%'"
# Add the app user and database
/usr/bin/mysql -u root -p"$mysql_root_password" -e "CREATE DATABASE $mysql_app_database;"
/usr/bin/mysql -u root -p"$mysql_root_password" -e "GRANT ALL PRIVILEGES ON $mysql_app_database.* TO '$mysql_user_name'@'%' IDENTIFIED BY '$mysql_user_password';"
# Flush privileges
/usr/bin/mysql -u root -p"$mysql_root_password" -e "FLUSH PRIVILEGES"
# Enable remote connections
/usr/bin/echo "bind-address = $mysql_bind_address" >> /etc/my.cnf
# Open MySQL firewall for remote connections
/usr/bin/firewall-cmd --zone=public --add-service=mysql --permanent
/usr/bin/firewall-cmd --reload
/usr/bin/systemctl restart mariadb.service

Application Server Installer ScriptApache2

The Application installer script ( performs the following tasks

  • Install Apache2, PHP and various PHP libraries required for the application
  • Download the App files and place them in the default web server location
  • Replace the MySQL connection parameters in the configuration file
  • Download the SQL file and create the app database table
  • Configure Apache to listen on port 8080
  • Open the firewall port
  • Configure SELinux to allow the web server to connect to remote databases

The PHP and SQL files that are downloaded are on my GitHub repository – I’d always suggest reviewing them before downloading and executing!

# Install Apache and PHP
/usr/bin/yum -y install mysql httpd php php-mysqlnd php-common php-gd php-xml php-mbstring php-mcrypt php-xmlrpc unzip wget
# Download and install Demo app
/usr/bin/wget -O /var/www/html/config.php
/usr/bin/wget -O /var/www/html/create.php
/usr/bin/wget -O /var/www/html/delete.php
/usr/bin/wget -O /var/www/html/error.php
/usr/bin/wget -O /var/www/html/index.php
/usr/bin/wget -O /var/www/html/read.php
/usr/bin/wget -O /var/www/html/update.php
/usr/bin/sed -i "s@DBName@$demo_app_mysql_database@" /var/www/html/config.php
/usr/bin/sed -i "s@DBUser@$demo_app_mysql_user@" /var/www/html/config.php
/usr/bin/sed -i "s@DBPassword@$demo_app_mysql_password@" /var/www/html/config.php
/usr/bin/sed -i "s@DBServer@$demo_app_mysql_server@" /var/www/html/config.php
/usr/bin/sed -i "s@HOSTNAME@$hostname@" /var/www/html/index.php
/usr/bin/wget -O /tmp/employees.sql
/usr/bin/mysql -u "$demo_app_mysql_user" -p"$demo_app_mysql_password" demo -h $demo_app_mysql_server < /tmp/employees.sql
# Configure and start Apache
/usr/bin/sed -i "/^<Directory \"\/var\/www\/html\">/,/^<\/Directory>/{s/AllowOverride None/AllowOverride All/g}" /etc/httpd/conf/httpd.conf
/usr/bin/sed -i "s@Listen 80@Listen 8080@" /etc/httpd/conf/httpd.conf
/usr/bin/systemctl enable httpd.service
/usr/bin/systemctl start httpd.service
# Open http firewall for remote connections
/usr/bin/firewall-cmd --permanent --zone=public --add-port=8080/tcp
/usr/bin/firewall-cmd --reload
# Configure SELinux to allow remote DB connection
/usr/sbin/setsebool httpd_can_network_connect_db on -P

Web Server Installer ScriptNGINX

The Web installer script ( performs the following tasks

  • Install the EPEL repository
  • Install NGINX
  • Download and configure the proxy configuration for NGINX
  • Download the SSL configuration and create an SSL certificate
  • Open the firewall port
  • Configure SELinux to allow the web server to connect to remote servers
# Add epel-release
/usr/bin/yum -y install epel-release
# Install nginx
/usr/bin/yum -y install nginx
# Download the reverse proxy configuration
/usr/bin/wget -O /etc/nginx/conf.d/proxy.conf
/usr/bin/sed -i "s@SERVERNAME@$web_server_name@" /etc/nginx/conf.d/proxy.conf
/usr/bin/sed -i "s@APPTIER@$app_server_name@" /etc/nginx/conf.d/proxy.conf
# Create the SSL folder
/usr/bin/mkdir /etc/nginx/ssl
# Download the proxy SSL conf
/usr/bin/wget -O /etc/nginx/ssl/proxy.conf
/usr/bin/sed -i "s@WEBSERVERNAME@$web_server_name@" /etc/nginx/ssl/proxy.conf
# Generate SSL keys
/usr/bin/openssl req -x509 -nodes -days 1825 -newkey rsa:2048 -keyout /etc/nginx/ssl/proxy.key -out /etc/nginx/ssl/proxy.pem -config /etc/nginx/ssl/proxy.conf
# Open http firewall for remote connections
/usr/bin/firewall-cmd --permanent --zone=public --add-service=https
/usr/bin/firewall-cmd --reload
# Configure SELinux
/usr/sbin/setsebool -P httpd_can_network_connect true
# Start and enable nginx
/usr/bin/systemctl start nginx
/usr/bin/systemctl enable nginx

The two configuration files that are downloaded are available from my GitHub repository, one is a configuration file for the NGINX reverse proxy, which is placed in the /etc/nginx/conf.d folder. I don’t make any changes to the default NGINX configuration.

server {
      listen 443;
      server_name SERVERNAME;

      ssl on;
      ssl_certificate     /etc/nginx/ssl/proxy.pem;
      ssl_certificate_key /etc/nginx/ssl/proxy.key;

      ssl_session_cache shared:SSL:1m;
      ssl_session_timeout 2m;

      location / {
         proxy_pass http://APPTIER:8080;
         proxy_set_header Host $host;
         proxy_redirect off;
         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;

The second configuration file is a template for openSSL to generate an SSL certificate for the NGINX reverse proxy.

distinguished_name = req_distinguished_name
x509_extensions = v3_req
default_md = sha256
prompt = no
C = GB
ST = West Sussex
L = Horsham
O = DefinIT
OU = Lab
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

Creating the vRealize Automation Software Components

The next step is to create three Software Components, one for each tier of the application.

Demo App Database

The Database Script has five properties, four of which will be inputs for the end user, one of which is computed.

  • mysql_app_database – the name of the MySQL database to create
  • mysql_root_password – the password to set for the MySQL root user
  • mysql_bind_address – the address MySQL should listen on (this is bound to the database server’s IP in the blueprint later)
  • mysql_user_password – this is set as a Computed Value because the script auto-generates a password. Setting it to a Computed Value lets us bind to the property later in the blueprint to pass to other components)
  • mysql_user_name – the name of the MySQL user to be created for the app

Demo App Database Properties

Demo App Application

The application tier install component has four inputs, which will be directly mapped to the properties for the database component

  • demo_app_mysql_server – the IP address of the MySQL database server
  • demo_app_mysql_user – the MySQL user created for the application
  • demp_app_mysql_password – the generated password for the MySQL user
  • demo_app_mysql_database – the name of the MySQL databsae

demo app application

Demo App Web

The web tier install component has two inputs for the script, which can be either mapped to the tier server IP addresses (for single node deploys) or to the load balancer IP addresses (for multi-node deploys).

  • web_server_name – the name or IP of the web tier (server or load balancer)
  • app_server_name – the DNS name or IP of the app tier (server or load balancer)

Demo App Web Server

Creating the vRealize Automation Blueprints

Now that all of the building blocks are in place, I can create the Blueprints that will tie it all together.

For the multi-node deployment, the blueprint looks like this (running from left to right):

  • An on-demand routed network (though any network will be fine)
  • web_tier_lb – an on-demand load balancer to front the web_tier
  • web_tier – a vSphere Machine using my CentOS7 template, configured as 1-3 VMs
  • CentOS7_OS_Config – configures and updates my template, not strictly required!
  • Demo_App_Web – the Demo App Web software component
  • app_tier_lb – another on-demand load balancer to front the app_tier
  • app_tier – a vSphere Machine using my CentOS7 template, configured as 1-3 VMs
  • CentOS7_OS_Config – configures and updates my template, not strictly required!
  • Demo_App_Application – the Demo App Application software component
  • db_tier – a vSphere Machine using my CentOS7 template, configured as 1 VM only
  • CentOS7_OS_Config – configures and updates my template, not strictly required!
  • Demo_App_Database – the Demo App Database software component

multi node blueprint

Note the lines depicting build dependencies – the Web depends on the App, which depends on the DB.

The custom property mapping for the database tier lets the user specify the database name, root password and app user name in the request (Show in Request, Overridable) while providing some default values. The MySQL bind address is bound to the _resource~db_tier~ip_address property, which will be the IP address vRA assigns to the database server.

Database custom property mapping

All of the custom properties for the application tier are bound to property values from the database tier, so that the PHP file is updated with the database server configuration (server, database, user and password). These properties are hidden from the user as they’re bound.

App Tier Properties

The two inputs for the web tier custom properties are mapped to the load balancer IP addresses for the web and app tier load balancers.

Web Tier Properties

To create a single-node blueprint of the application there’s only one difference – the web tier properties are mapped directly to the web_tier and app_tier IP addresses.

Single Node App

Deploying and testing the three tier Demo App

Once the blueprint has been published and assigned to a catalog, service and entitlement, it’s time to deploy and test the app! Below you can see the deployment form, and the configuration of the database install script.

Deploying the three-tier Demo App

The deployed blueprint looks something like this – you can see that the web tier load balancer has an IP address of

Deployed blueprint

And when I hit the load balancer IP, the application loads!

Working application

And finally…

All of the vRA components have been exported using CloudClient and are also on the GitHub repo if you want to import them. Since this post is getting pretty long, I’ll draw it to a close here – hopefully this is of some use to you – please feel free to leave me a comment or ping me on twitter if you have any questions!