Clusters and Policies

Now let’s bring the concept of the “cluster” back in. We could wrap our appserver in the same DynamicCluster we used earlier, although then we’d need to define and configure the load balancer. But another blueprint, the ControlledDynamicWebAppCluster, does this for us. It takes the same dynamiccluster.memberspec, so we can build a fully functional elastic 3-tier deployment of our hello-world-sql application as follows:

name: appserver-clustered-w-db
services:
- type: org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster
  brooklyn.config:
    cluster.initial.size: 2
    dynamiccluster.memberspec:
      $brooklyn:entitySpec:
        type: org.apache.brooklyn.entity.webapp.jboss.JBoss7Server
        brooklyn.config:
          wars.root: http://search.maven.org/remotecontent?filepath=org/apache/brooklyn/example/brooklyn-example-hello-world-sql-webapp/0.8.0-incubating/brooklyn-example-hello-world-sql-webapp-0.8.0-incubating.war
          http.port: 8080+
          java.sysprops:
            brooklyn.example.db.url:
              $brooklyn:formatString:
                - jdbc:%s%s?user=%s\\&password=%s
                - $brooklyn:component("db").attributeWhenReady("datastore.url")
                - visitors
                - brooklyn
                - $brooklyn:external("brooklyn-demo-sample", "hidden-brooklyn-password")
- type: org.apache.brooklyn.entity.database.mysql.MySqlNode
  id: db
  name: DB HelloWorld Visitors
  brooklyn.config:
    creation.script.password: $brooklyn:external("brooklyn-demo-sample", "hidden-brooklyn-password")
    datastore.creation.script.url: https://github.com/apache/brooklyn-library/blob/master/examples/simple-web-cluster/src/main/resources/visitors-creation-script.sql

This sets up Nginx as the controller by default, but that can be configured using the controllerSpec key. This uses the same externalized config as in other examples to hide the password.

JBoss is actually the default appserver in the ControlledDynamicWebAppCluster, so because brooklyn.config keys in Brooklyn are inherited by default, the same blueprint can be expressed more concisely as:

name: appserver-clustered-w-db-concise
services:
- type: org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster
  brooklyn.config:
    cluster.initial.size: 2
    wars.root: http://search.maven.org/remotecontent?filepath=org/apache/brooklyn/example/brooklyn-example-hello-world-sql-webapp/0.8.0-incubating/brooklyn-example-hello-world-sql-webapp-0.8.0-incubating.war
    http.port: 8080+
    java.sysprops: 
      brooklyn.example.db.url: $brooklyn:formatString("jdbc:%s%s?user=%s\\&password=%s", component("db").attributeWhenReady("datastore.url"), "visitors", "brooklyn", $brooklyn:external("brooklyn-demo-sample", "hidden-brooklyn-password"))
- type: org.apache.brooklyn.entity.database.mysql.MySqlNode
  id: db
  name: DB HelloWorld Visitors
  brooklyn.config:
    creation.script.password: $brooklyn:external("brooklyn-demo-sample", "hidden-brooklyn-password")
    datastore.creation.script.url: https://github.com/apache/brooklyn-library/blob/master/examples/simple-web-cluster/src/main/resources/visitors-creation-script.sql

The other nicety supplied by the ControlledDynamicWebAppCluster blueprint is that it aggregates sensors from the appserver, so we have access to things like webapp.reqs.perSec.windowed.perNode. These are convenient for plugging in to policies! We can set up our blueprint to do autoscaling based on requests per second (keeping it in the range 10..100, with a maximum of 5 appserver nodes) as follows:

name: appserver-w-policy
services:
- type: org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster
  brooklyn.config:
    cluster.initial.size: 1
    dynamiccluster.memberspec:
      $brooklyn:entitySpec:
        type: org.apache.brooklyn.entity.webapp.jboss.JBoss7Server
        brooklyn.config:
          wars.root: http://search.maven.org/remotecontent?filepath=org/apache/brooklyn/example/brooklyn-example-hello-world-sql-webapp/0.8.0-incubating/brooklyn-example-hello-world-sql-webapp-0.8.0-incubating.war
          http.port: 8080+
          java.sysprops:
            brooklyn.example.db.url:
              $brooklyn:formatString:
              - jdbc:%s%s?user=%s\\&password=%s
              - $brooklyn:component("db").attributeWhenReady("datastore.url")
              - visitors
              - brooklyn
              - $brooklyn:external("brooklyn-demo-sample", "hidden-brooklyn-password")
  brooklyn.policies:
  - type: org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy
    brooklyn.config:
      metric: $brooklyn:sensor("brooklyn.entity.webapp.DynamicWebAppCluster", "webapp.reqs.perSec.windowed.perNode")
      metricLowerBound: 10
      metricUpperBound: 100
      minPoolSize: 1
      maxPoolSize: 5
- type: org.apache.brooklyn.entity.database.mysql.MySqlNode
  id: db
  name: DB HelloWorld Visitors
  brooklyn.config:
    creation.script.password: $brooklyn:external("brooklyn-demo-sample", "hidden-brooklyn-password")
    datastore.creation.script.url: https://github.com/apache/brooklyn-library/raw/master/examples/simple-web-cluster/src/main/resources/visitors-creation-script.sql

Use your favorite load-generation tool (jmeter is one good example) to send a huge volume of requests against the server and see the policies kick in to resize it.