Compose a 3-tier Application (CLI)
Objectives
In this tutorial we will show you how to build the component parts of the three-tier app deployed in the previous tutorial and deploy using the AMP CLI.
Pre-requisites
This tutorial assumes you have installed Cloudsoft AMP and created the location for a Ubuntu VM as in the previous tutorial.
Next, navigate to the “CLI Download” using the nine-dots menu in the top right,
and download the “AMP Client CLI” for your OS. Add the downloaded command – br
– to your
path (or, in the following steps, reference it using the full path to its location).
Once downloaded, you need to login using br login http://localhost:8081/ admin password
.
To get help on the available commands, use br --help
.
Instructions
The following blueprint in YAML describes the topology of a three-tier web application.
tosca_definitions_version: tosca_simple_yaml_1_3
metadata:
template_name: io.cloudsoft.3-tier-application-template
template_version: 0.1.0-SNAPSHOT
topology_template:
node_templates:
db-instance:
type: io.cloudsoft.smart-mysql-node
properties:
mysql_creation_script: classpath://io.cloudsoft.brooklyn.tosca13.tosca-3-tier:resources/creation-script-mysql.sql
tomcat-cluster:
type: tosca.entity.DynamicCluster
properties:
cluster.initial.size: 2
dynamiccluster.memberspec:
$brooklyn:entitySpec:
name: Apache Tomcat Member
type: io.cloudsoft.smart-tomcat9-node
brooklyn.config:
tomcat_db_url: { get_attribute: [ db-instance, db_instance_url ] }
tomcat_db_driver: { get_attribute: [ db-instance, db_instance_driver ] }
tomcat_deployment: classpath://io.cloudsoft.brooklyn.tosca13.tosca-3-tier:resources/tosca-hello-world-sql-webapp.war
nginx-host:
type: tosca.nodes.Compute
capabilities:
cloudsoft:
properties:
required.ports: { get_property: [ nginx-instance, app.port ] }
nginx-instance:
type: tosca-nginx-node
requirements:
- host: nginx-host
properties:
nginx.sticky: false
addr.list: { get_attribute: [ tomcat-cluster, addr.list.string ] }
groups:
- add_brooklyn_types:
members: [ tomcat-cluster ]
type: brooklyn.tosca.groups.initializer
properties:
brooklyn.enrichers:
- type: org.apache.brooklyn.enricher.stock.Aggregator
brooklyn.config:
enricher.sourceSensor: $brooklyn:sensor("app.endpoint")
enricher.targetSensor: $brooklyn:sensor("addr.list")
enricher.aggregating.fromMembers: true
- type: org.apache.brooklyn.enricher.stock.Joiner
brooklyn.config:
enricher.joiner.minimum: $brooklyn:config("cluster.initial.size")
enricher.sourceSensor: $brooklyn:sensor("addr.list")
enricher.targetSensor: $brooklyn:sensor("addr.list.string")
enricher.joiner.separator: ","
The blueprint consists of the following items:
- A dynamic cluster managing 2
Apache Tomcat
nodes. MySQL
node, which is configured using SQL DDL script.Nginx
node that acts as a load-balancer for the previous cluster ot Apache Tomcat servers. When this node is created, itsaddr.list
property is initialized with the list of application endpoints of allApache Tomcat
nodes managed by the cluster.
In order to deploy three-tier web application, add Linux (Ubuntu 20.04) location with ID ubuntu20-location
first.
Refer to creating first location for details.
Note, to increase deployment speed, use a Linux VM template that has MySQL 5, Apache Tomcat 9 (unpacked in /usr/local/tomcat
) and Nginx installed.
Create directory tosca-3-tier
and save the content of the blueprint above into file 3-tier-application-template.yaml
which will form tosca-3-tier
bundle with 3-tier web application template:
./tosca-3-tier/3-tier-application-template.yaml
Add the bundle to the catalog:
> cd tosca-3-tier
> br catalog add .
The response from AMP of successfully added bundle to the catalog will look like the following:
Installed AMP catalog bundle io.cloudsoft.3-tier-application-template:0.1.0-SNAPSHOT with ID xp51aywf3u [367]
* io.cloudsoft.3-tier-application-template:0.1.0-SNAPSHOT
Create another blueprint, 3-tier-application.yaml
, with the following content:
tosca_definitions_version: tosca_simple_yaml_1_3
metadata:
template_name: io.cloudsoft.3-tier-application
template_version: 0.1.0-SNAPSHOT
topology_template:
node_templates:
3-tier-application:
type: 'io.cloudsoft.3-tier-application-template:0.1.0-SNAPSHOT'
groups:
- add_brooklyn_types:
members: [io.cloudsoft.3-tier-application]
type: brooklyn.tosca.groups.initializer
properties:
location:
- ubuntu20-location
If needed, change the location name at the last line from ubuntu20-location
to the one you created just before.
Deploy 3-tier-application.yaml
with br
:
> br deploy 3-tier-application.yaml
Id: | dnejwj36fg
Name: | io.cloudsoft.3-tier-application
Status: | In progress
Now inspect status of a deployed application with br
:
> br application
Id | Name | Status | Location
dnejwj36fg | io.cloudsoft.3-tier-application | STARTING | n8k6oojhg8
Status of application changes after deployment is complete:
> br application
Id | Name | Status | Location
dnejwj36fg | io.cloudsoft.3-tier-application | RUNNING | n8k6oojhg8
We will begin with the blueprint for a single mid-tier node:
services:
- type: workflow-software-process
name: Java App
brooklyn.config:
install.workflow:
steps:
- ssh sudo apt install -y zip unzip openjdk-19-jdk-headless
- ssh curl -L "${entity.config.jar_url}" --output "${entity.config.jar_name}"
launch.workflow:
steps:
- ssh nohup java -jar "${entity.config.jar_name}" ${entity.config.extra_args} >> server.log 2>&1 &
- set-sensor main.uri = http://${entity.sensor["host.address"]}:${entity.config["web.port"]}/
stop.workflow:
steps:
- ssh killall java
brooklyn.parameters:
- name: jar_url
description: JAR to install
pinned: true
constraints:
- required
- name: jar_name
default: app.jar
- name: extra_args
- name: web.port
default: 8080
Deploying a standalone mid-tier node
Create a new, empty directory where we will work, either at the command line or in your favorite IDE.
Save the file above as java-app.yaml
in that directory.
This uses the workflow-software-process
entity type which uses workflow to define how to start and stop
a software process. In this example it is installing Java and downloading a JAR file, then launching JAR.
The install.workflow
, customize.workflow
, and launch.workflow
are invoked on deployment and
whenever the start
effector (operation) is triggered. The stop.workflow
is used to stop the process.
The restart
effector will invoke the stop then launch workflow.
The checkRunning.workflow
defines a periodic liveness check; if it fails when AMP expects the entity to be
running (or if there are any problems in the service.problems
map sensor), the entity will show as unhealthy.
This blueprint also defines some parameters.
A jar_url
parameter is required, indicating the JAR to download and run.
Optionally it also accepts parameters for specifying extra CLI args,
configuring the port it listens on,
and choosing a local name to give to the JAR.
Make a copy of this file in java-app-standalone.yaml
and add the following lines at the start of
this new file (replacing aws-ubuntu
by the location ID you created):
location: aws-ubuntu
brooklyn.config:
jar_url: https://github.com/cloudsoft/hello-world-spring-boot/releases/download/1.0.1/hello-world-spring-boot-1.0.1.jar
extra_args: --spring.profiles.active=local
We can now deploy this from the command line with:
br deploy java-app-standalone.yaml
You can observe the activity and sensors in the App Inspector,
and navigate to address shown by the main.uri
sensor, under the Java App, in order to see the web app deployed.
You can also experiment changing the workflows; for example the “stop” step here is crude, and won’t play nice if other Java processes are installed on the same system. You could change the workflow so that it stores the process ID (PID) either as a file on the box or as a sensor in AMP, and so that the “stop” workflow kills based on the PID. Alternatively you could install it as a systemd service and use those commands (which we will do for the DB and nginx).
Making the mid-tier node reusable
The java-app
node is designed to be reusable for different JARs;
this can be facilitated by adding it as a bundle in the AMP catalog.
Catalog bundles are ZIP files with a catalog.bom
descriptor.
Create the following catalog.bom
file in the same folder:
brooklyn.catalog:
bundle: cloudsoft-threetier-example
version: 1.0.0-SNAPSHOT
items:
- id: cloudsoft-threetier-java-midtier
name: Cloudsoft 3T Java App
item: classpath://java-app.yaml
You should now be able to add or update this bundle in the AMP catalog using:
br catalog add .
(The final .
is the path; if running that command from a different directory, replace it with the path
to the folder containing catalog.bom
and java-app.yaml
.)
The blueprint defined in java-app.yaml
is now available as a type cloudsoft-threetier-java-midtier
,
and we can reference this in a blueprint such as the following (either using the Composer UI or
br deploy <file>
):
location: aws-ubuntu
services:
- type: cloudsoft-threetier-java-midtier
brooklyn.config:
jar_url: https://github.com/cloudsoft/hello-world-spring-boot/releases/download/1.0.1/hello-world-spring-boot-1.0.1.jar
extra_args: --spring.profiles.active=local
Defining the LB and DB tiers
Download and save nginx.yaml
and mariadb.yaml
(verify that the files are saved with the .yaml
extension)
which define the load balancer and database nodes.
These use systemd and for the most part are pretty simple, using workflow such as:
install.workflow:
steps:
- ssh sudo apt install -y mariadb-server
- ssh sudo sed -i s/127.0.0.1/0.0.0.0/ /etc/mysql/mariadb.conf.d/50-server.cnf
- ssh sudo service mariadb restart
There a few new features added in these files:
-
The MariaDB entity accepts a
creation_script_sql_url
parameter which will be used to customize the DB after installing it; this is optional, with acondition
specifying thistarget
-
When the MariaDB entity is initialized (before starting) a sensor is added which will periodically retrieve the queries per second:
brooklyn.initializers:
- type: workflow-sensor
brooklyn.config:
name: db.queries_per_sec
steps:
- ssh sudo mysqladmin status | awk '{print $NF}'
- return ${stdout}
period: 10s
- Nginx similarly uses an initializer, to add an effector which will
regenerate-config
based on atarget_ips
config key; it runs a more intricate script to produce the nginx config files pointing at that list of IP addresses
These can be added to our catalog.bom
in a similar fashion, also adding an icon for each:
brooklyn.catalog:
bundle: cloudsoft-threetier-example
version: 1.0.0-SNAPSHOT
items:
- id: cloudsoft-threetier-java-midtier
name: Cloudsoft 3T Java App
iconUrl: classpath://java.png
item: classpath://java-app.yaml
- id: cloudsoft-threetier-nginx
name: Cloudsoft 3T Nginx
iconUrl: classpath://nginx.png
item: classpath://nginx.yaml
- id: cloudsoft-threetier-mariadb
name: Cloudsoft 3T MariaDB
iconUrl: classpath://icons:/db.png
item: classpath://mariadb.yaml
Download and save java.png
and nginx.png
to the same folder,
and the classpath://...
URL format will allow them to be served as icons.
(The classpath://icons:/...
link refers to a pre-installed bundle called icons
which is shipped with AMP
to provide some standard icons such as db.png
, group.png
and server.png
. You could downlaod a MariaDB icon
and reference it instead, of course.)
Now br catalog add .
in the folder to update the bundle.
Because it is a SNAPSHOT
version, you are allowed to update it freely.
When you have a stable version, you may wish to remove the -SNAPSHOT
suffix and catalog add
it,
then increment the version and append -SNAPSHOT
for further development.
You can also have your CI build the ZIP and apply your preferred versioning scheme.
Deploy the Three-tier App
With the above added, you can now deploy the full three-tier app from the command-line.
Create a file threetier.yaml
containing the following, essentially what was created in the composer
in the previous tutorial:
name: Cloudsoft 3T Three Tier Application
services:
- type: cloudsoft-threetier-nginx
brooklyn.config:
target_ips: $brooklyn:component("cluster").attributeWhenReady("children_ips")
target_port: 8080
brooklyn.policies:
- type: workflow-policy
triggers:
- entity: cluster
sensor: children_ips
period: 1d # optionally regenerate daily for good measure
steps:
- invoke-effector regenerate-config
- type: org.apache.brooklyn.entity.group.DynamicCluster
id: cluster
name: Java Cluster
brooklyn.config:
cluster.initial.size: 2
memberSpec:
type: cloudsoft-threetier-java-midtier
brooklyn.config:
jar_url: https://github.com/cloudsoft/hello-world-spring-boot/releases/download/1.0.1/hello-world-spring-boot-1.0.1.jar
extra_args: >
$brooklyn:formatString("--spring.profiles.active=remote --DB_IP='%s' --DB_USER='%s' --DB_PASS='%s'",
$brooklyn:component("db").attributeWhenReady("host.address"),
"brooklyn",
$brooklyn:external("brooklyn-demo-sample","hidden-brooklyn-password"))
brooklyn.policies:
- type: workflow-policy
period: 10s
steps:
- ssh curl -f -LI localhost:8080
- clear-sensor service.problems['curl-check']
on-error:
- set-sensor service.problems['curl-check'] = "curl failed."
brooklyn.enrichers:
- type: org.apache.brooklyn.enricher.stock.Aggregator
id: address_aggregator
brooklyn.config:
enricher.sourceSensor: $brooklyn:sensor("host.address")
enricher.targetSensor: $brooklyn:sensor("children_ips")
enricher.aggregating.fromMembers: true
transformation: list
- type: cloudsoft-threetier-mariadb
id: db
brooklyn.config:
creation_script_sql_url: https://github.com/cloudsoft/hello-world-spring-boot/releases/download/1.0.1/creation-script-mariadb.sql
location: aws-ubuntu
Change the location name on the last line if needed, and then run br deploy threetier.yaml
.
This should create an identical deployment.
Summary
The CLI is an alternative means of managing applications within AMP. It is particularly suitable for automation and useful for developing individual components for use in the Composer. .
Next
- Explore more Tutorials