cloudsoft.io

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, its addr.list property is initialized with the list of application endpoints of all Apache 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 a condition specifying this target

  • 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 a target_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