2014-08-21

Brooklyn and Node.JS Applications

This post shows how Apache Brooklyn can be used to deploy and manage Node.JS applications. This feature was requested in JIRA issue BROOKLYN-12 and implemented in pull request #94.

We are going to create a simple Todo List application, using the code hosted on GitHub at nodejs-todo. This application uses a Redis store to keep track of the list items, and the Express JavaScript framework as the front-end. The original application was written by Amir Rajan, and is part of his Node.JS by Example samples.

So, to deploy this application in Brooklyn, we need to create a Node.JS service, running the code from the GitHub repository. This will need to be configured with the required dependencies, which should be installed by npm. Since the application uses Redis, we will also need a RedisStore node, and some way of telling the app to connect to it. Fortunately, the application has been written to use Heroku's Redis To Go add-on. We just set the REDISTOGO_URL environment variable to the URL of our store, using the host and port sensors from the entity. Due to changes in the latest Express release I have had to modify the application code in my fork because of deprecated dependencies. This also affects the list of dependencies to be installed by npm, and these are listed in the nodePackages configuration.

id: nodejs-todo-application
name: "Node.JS Todo Application"
origin: "https://github.com/amirrajan/nodejs-todo/"
locations:
- jclouds:softlayer:ams01
services:
- serviceType: brooklyn.entity.nosql.redis.RedisStore
  id: redis
  name: "Redis"
- serviceType: brooklyn.entity.webapp.nodejs.NodeJsWebAppService
  id: nodejs
  name: "Node.JS"
  brooklyn.config:
    gitRepoUrl:
      "https://github.com/grkvlt/nodejs-todo/"
    appFileName: server.js
    appName: nodejs-todo
    nodePackages:
    - express
    - ejs
    - jasmine-node
    - underscore
    - method-override
    - cookie-parser
    - express-session
    - body-parser
    - cookie-session
    - redis
    - redis-url
    - connect
    env:
      REDISTOGO_URL: >
        $brooklyn:formatString("redis://%s:%d/",
        component("redis").attributeWhenReady("host.name"),
        component("redis").attributeWhenReady("redis.port"))
    launch.latch: $brooklyn:component("redis").attributeWhenReady("service.isUp")

This blueprint file is available in the Brooklyn repository, in the examples/simple-web-cluster project as nodejs-todo.yaml. As you can see, it defines two services - RedisStore and NodeJsWebAppService. The Node.JS service is configured with the gitRepoUrl pointing to the forked repository with the application code, and the appFileName is used to set the filename passed to the node command to start the application. The env configuration sets the location of the store. This is done using attributeWhenReady calls to retrieve the required sensor values from the Redis entity when they are available. Finally, we use launch.latch to make the Node.JS service wait until Redis has started before actually launching the application.

To deploy the application, just paste the YAML blueprint into the YAML tab of the Create Application window in the Brooklyn console UI. We use Softlayer as the target location in this example, but you can use any configured location, as described in the Getting Started guide. Once the machines have been provisioned and the entities have started up, you should see something similar to the screenshot above. To access the application itself, open the link given in the webapp.url sensor. You should see a Todo Redis page, like the screenshot below.

To deploy your own Node.JS applications, use the YAML blueprint as a guide, and modify the configuration as required. You can also view the source code for the NodeJsWebAppService entity, to see the rest of the available configuration keys. More detailed documentation is available, and further information can be found at the main Brooklyn site or on GitHub.

ADDENDUM These YAML blueprints will also run on Clocker giving you a simple way of containerizing your application. If you modify the locations list to point at the name of an already running Docker Cloud, for example my-docker-cloud then the Node.JS and Redis services will each start in their own Docker containers. The REDISTOGO_URL will need to be changed to use the forwarded Docker port instead, so replace the configuration for the env entry with the following snipped.

    env:
      REDISTOGO_URL: >
        $brooklyn:formatString("redis://%s/",
        component("redis").attributeWhenReady("mapped.redis.port"))

To find out more about Clocker, see my earlier post Implementing a Docker Cloud with Apache Brooklyn here, or Creating a Docker Cloud with Apache Brooklyn at Cloudsoft. There is also an excellent Getting Started video from Andrea.