Getting started with Cloud Foundry using a Node.js and MongoDB application

Cloud Foundry is industry’s first open source Platform as a Service (PaaS). The goal of Cloud Foundry is to remove the obstacles developers face in building, deploying, running and scaling applications and to do it in an open way so that there is no lock-in to frameworks, application services or clouds. Cloud Foundry offers developers –

  • Choice of developer frameworks. Developers can use Spring for Java, Rails and Sinatra for Ruby and Node.js for Javascript. It also supports other JVM based frameworks like Grails. The design of cloudfoundry is such that it will be easy to support other frameworks in the future.
  • Choice of applications infrastructure services. This refers to the data, messaging and web services that are the building blocks of the applications. Currently cloudfoundry supports MySQL, MongoDB and Redis.
  • Choice of clouds to which to deploy applications. Customers need the flexibility in cloud deployment both for the flexibility reasons as well as for financial reasons. Cloud Foundry can be deployed on top of other infrastructure clouds. It can be deployed on top of AWS, Eucalyptus or OpenStack. It can be deployed on different clouds simply by using different command line options.

The following diagram from VMWare nicely illustrates the above mentioned points,

VMWare Cloud Foundry Interfaces

VMWare Cloud Foundry Interfaces

Types of deployments

  • Public clouds -A public cloud PaaS service is available at CloudFoundry.com operated by VMWare. Developers can use this instance of cloudfoundry to play around with it. In addition it also provide a testing ground for new services. Currently developers need to ask for an invite to CloudFoundry.com. As mentioned previously Cloud Foundry will also be available on other public clouds.
  • Private Clouds – VMWare will also offer a commercial version of Cloud Foundry for enterprise customers who want to deploy a PaaS solution in their internal data centers as part of their private clouds. It will support vFabric application services as well as third party services. Because of its support for both private and public cloud it can also help organizations looking to implement the hybrid clouds. A hybrid cloud uses internal data center for normal workload and shifts the work to public cloud in case of any surge in demand.
  • Micro cloud -The cloud can now sit on a developer’s machine as well. With the micro cloud, cloud foundry can run on a single virtual machine behind a firewall. This will allow developers to build and test the applications on their own machines first, before deploying to the public or private clouds. It will also contribute to some cost savings as developers do not need to use public clouds for their testing and their development environment can closely mirror the actual production environment.

Cloud Foundry is released under Apache 2 license. It is hosted on cloudfoundry.org. Its open source nature will help developers avoid the lock-in as well as allow them to extend the platform based on their needs.

Sample Application on Cloud Foundry

This is a very simple app to record the clicks made by a user on a test page. It records the coordinates of the click, the IP address of the user’s machine and the timestamp in GMT. The click data is stored in MongoDB. This data can then be used by a real time analytics application to figure out where the users are clicking on a page and generate heat maps for the page.

In order to create the environment for the app following components need to be installed. I am using Ubuntu 10.04 for this article but most of the instructions should be similar for other operating systems as well.

Installing vmc client

If ruby is not already installed on the system,

sudo apt-get install ruby-full
sudo apt-get install rubygems

Install the vmc client,

sudo gem install vmc

Add the path to $PATH environment variable in your .bashrc file,

export PATH=/var/lib/gems/1.8/bin:$PATH

Set the target to api.cloudfoundry.com so that our app is deployed to cloudfoundry.com. The app can also be deployed on local machine or to Amazon EC2 but for the purpose of this article we will deploy it to cloudfoundry.com. You will need to request for an invite at cloudfoundry.com. It took about a week for me to get the invite.

vmc target api.cloudfoundry.com

Supply the user id and password that you would receive in an email from VMWare, once your invite is approved, in response to the following command,

vmc login

Installing Node.js

As we are going to be using Node.js for this app, we need to install node.js also on our machine.

git clone http://github.com/joyent/node.git
cd node; mkdir ~/local; ./configure --prefix=$HOME/local/node
make
make install

In your .bashrc file, add the following line,

export PATH=$HOME/local/node/bin:$PATH

Also install npm which is a package manager for node as we will be using this to install node modules like mongoose,

curl http://npmjs.org/install.sh | sh

Installing MongoDB

We will be using MongoDB as the data store for this application. MongoDB is already bundled with Cloud Foundry as one of the data services provided out of the box. Other data services will also be added in the future, but for now MongoDB is a good choice for the app we are trying to build.

Add the following line to the file /etc/apt/sources.list,

deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen

Run the following commands,

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
sudo apt-get update
sudo apt-get install mongodb-10gen

Installing Mongoose

Mongoose is the utility library for using MongoDB services from Node.js. We will use Mongoose as the ORM layer for MongoDB in our app. It makes the MongoDB access from Node.js applications much cleaner.

First create the directory for the application where will be adding our source files and then use npm to install the mongoose module,

mkdir analytics
cd analytics
npm install mongoose

Installing Fang Of Mongo

For simple MongoDB admin tasks, this is a good tool to look at the data that our application will be storing. Use the following commands to get this tool and run it,

sudo apt-get install python-pymongo
git clone http://github.com/Fiedzia/Fang-of-Mongo.git
cd fangofmongo
python manage.py runserver

You can then visit http://localhost:8000/fangofmango to visit the admin panel for your MongoDB.

The Analytics application

This app uses Node.js to create a web server which will handle different requests coming in from the clients. It will respond to request to three URLs, /test, /store and /view. /test will display a test.html page on which the user can click anywhere to record a click. The code for this page is quite simple,

<script>
    $(document).click(function handleClick(e) {
        $.ajax({
                   url : "/store",
                   type : "POST",
                   data : ({"X" : e.clientX, "Y": e.clientY})
        });
    });
</script>
</head>
<body>
<h1>Click anywhere on this page to record the click</h1>
</body>
</html>

It uses the jQuery library to send an ajax request to our server.

A click handler is registered for the document object which sends an ajax request to our node server whenever the user clicks on the page. It also bundles the X and Y coordinates of the mouse click and sends it to the server as part of the request data. The ajax request is sent to /store URL.

The main component of this application is the Node.js server application which is contained in a single file, node-analytics-server.js. This server handles the request for different URLs. The file starts with the all the required node modules that we will be using for this app,

var http = require('http');
var url = require('url');
var path = require('path');
var fs = require('fs');
var qs = require('querystring');
var mongoose = require('mongoose/');

Other than mongoose, rest of the modules are the standard modules that are bundled with Node.js. For the purpose of this application I am not using any non-builtin modules other than mongoose in order to keep the code straightforward. But modules like express and static-resource can simplify the code for this app.

The following line tries to find out if we are running this app on Cloud Foundry or locally in a non Cloud Foundry environment. In case the application is running in a Cloud Foundry environment, the environment variables VCAP_SERVICES will be set. This variable provides a list of all the bound services for your Cloud Foundry installation.

var boundServices = process.env.VCAP_SERVICES ?
    JSON.parse(process.env.VCAP_SERVICES) : null;

Now we will use mongoose to connect to the MongoDB server. In case we are running in a non Cloud Foundry environment, we can create an analytics database using the fang of mongo tool described above and then connect to it. In case our app is running in a Cloud Foundry environment, the MongoDB instance and credentials to connect to it will be provided by Cloud Foundry. We will use the boundServices variable set earlier to find out the credentials we need to use to connect to the MongoDB instance provided for us.

var credentials = null;
var db = null;

if (boundServices == null) {
    db = mongoose.connect('mongodb://localhost/analytics');
} else {
    credentials = boundServices['mongodb-1.8'][0]['credentials'];
    db = mongoose.createConnection("mongo://"
                             + credentials["username"]
                             + ":" + credentials["password"]
                             + "@" + credentials["hostname"]
                             + ":" + credentials["port"]
                             + "/" + credentials["db"]);
}

We will store the clicks information coming in from the clients in a collection called Click. The code below creates the schema for Click object.

var Click = new mongoose.Schema ({X: Number,
                                  Y: Number,
                                  IPAddress: String,
                                  Timestamp: String});
mongoose.model('Click', Click);
var Click = db.model('Click');

With all the global variables set property, next we will create the http server that will handle all the requests coming in from the client. We use the url module to parse the url being requested.

server = http.createServer(function(req, res) {
        var pathname = url.parse(req.url).pathname;

When the user requests for /test, we read the test.html stored in the file system and just spit it out to the browser. We use the filesystem and path modules to read the html files from the file system.

var fullpath = path.join(process.cwd(), pathname + ".html");

path.exists(fullpath, function(exists) {
    if (!exists) {
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.end("<h1>Page not found</h1>");
        return;
    }

    fs.readFile(fullpath, "binary", function(err, data) {
        if (err) {
            res.writeHead(500, {'Content-Type': 'text/plain'});
            res.end("<h1>Page cannot be read</h1>");
            return;
        }

        res.writeHead(200, {'Content-Type' : 'text/html'});
        res.write(data, "binary");
        res.end();
    });
});

When the user clicks anywhere on the test.html page, it generates an ajax request to /store. This is handled by the following code in the node.js server. It makes use of the querystring module to parse the post data sent by the browser. Once we have all the data that we want to store in hand, we create a Click object and save it to the database.

if (pathname == "/store") {
    res.writeHead(200, {'Content-Type' : 'text/html'});
    var requestData = '';
    req.on('data', function (data) {
        requestData += data;
    });

    req.on('end', function () {
        var POST = qs.parse(requestData);
        var clientIPAddress = '';
        clientIPAddress = req.headers['x-forwarded-for'];
         if (clientIPAddress === undefined) {
             clientIPAddress = req.connection.remoteAddress;
         }

         var click = new Click ({X : POST.X, Y: POST.Y,
                                 IPAddress: clientIPAddress,
                                 Timestamp: new Date().toUTCString()});
         click.save();
     });

     res.end();
     return;
 }

In a non Cloud Foundry environment, we can look at the data stored by this app using the fang of mongo tool. The data is stored in the click collection and looks something like below,

Fang of Mongo Panel

In Cloud Foundry environment, currently there is no way to directly access the data services like MongoDB in order to look at the data. So in order to verify that the data that we are storing looks ok, our node.js server also handles the /view request. The handler for /view reads all the data stored in the click collection and simply dumps the JSON array containing all the records.

if (pathname == "/view") {
    res.writeHead(200, {'Content-Type':'text/html'});
    Click.find({}, function (err, docs) {
        if (err) {
            res.write(err);
        } else {
            res.write(JSON.stringify(docs));
        }
        res.end();
   });
   return;
}

The data dumped by the /view action looks something like below.

Click data dump

Click data dump

Next we call the listen method of the server so that the server is listening on port 8001 in the non cloudfoundry environment. In cloudfoundry environment, we will be using the port provided to us as part of the environment variables, VMC_APP_PORT, rather than any hard coded port number.

server.listen(process.env.VMC_APP_PORT || 8001);

Deployment

Once the application is ready, we can first test it locally without the cloudfoundry environment to make sure that the app is working properly. Start the node.js server using,

node node-analytics-server.js

Then visit the url http://localhost:8001/test and click anywhere on the page.

You can use either fang of mongo tool to look at the data stored by this app or visit the url http://localhost:8001/view to look at the dump of the data that was stored.

Once we verify that the app is working properly in the non cloudfoundry environment, it is time to deploy it to cloudfoundry.com.

For this we need to create a package_info.json file which will detail the dependencies for this node.js application. The content of this file for this app are,

{
"name":"gganalytics",
  "version":"0.0.1",
  "dependencies":{
    "mongoose":""
  }
}

Next we push the app to cloudfoundry.com using,

vmc push gganalytics

It will ask a series of questions for which we safely can accept the default choices. It will automatically try to guess the type of application that is being pushed. You will also need to mention that the MongoDB service needs to be bound to this app. If everything goes well, then you can visit gganalytics.cloudfoundry.com/test to store the clicks and gganalytics.cloudfoundry.com/view to view the click data stored in the mongodb.

vmc is a nice command line tool for managing the cloudfoundry applications. There are some other useful commands that can be used.

  • vmc update – Whenever you make some change in the app and want to push the latest code to the cloudfoundry.com, you can use this command.
  • vmc logs and vmc crashlogs – This will display the logs for the app. You can use console.log statements in your node.js app to do some logging in your apps. vmc crashlogs will show any reasons for the fatal errors encountered by your application.
  • vmc list – Displays the list of apps and their health.
  • vmc info - Displays more detailed information about your Cloud Foundry environment.
  • vmc stats – Displays the resournce consumption of the app.
  • vmc instances n – This can be used to scale up or scale down the instances used by this application.

We can also run the app locally on the Cloud Foundry Micro Cloud to test the installation on a VM.

As you may have found out from this post that it is quite easy to get started with the Cloud Foundry platform. It represents a major step forward in the cloud frameworks and can prove very beneficial for developers trying to avoid vendor lock-in and also wanting to use a more open and extensible cloud application environment. There are still some kinks to be worked out but overall it is a very positive development for the cloud application developers.

Related Posts

  1. Getting NodeJS to work in a Windows environment
  2. Case Study: Using MongoDb for an E-Commerce Platform
  3. ZendCon 2011 – Introducing Zend Developer Cloud, Zend Studio 9 and Zend Framework 2
  4. User-based cloud computing
  5. Introducing NodeJS

Categories: Development

Gaurav Gehlot

Technical Architect

I have worked in multiple domains like finance, internet, small business accounting, manufacturing, ERP etc. using almost all possible technologies in my career spanning across India, Europe and USA. Currently my areas of interests include cloud applications, browser technologies, nosql databases and android development. I love spending my free time with my 5 and 2 year old daughters.
  • http://twitter.com/vikalpsahni vikalp sahni

    interesting.

  • ggehlot

    @twitter-21069044:disqus Yes, on the surface it is quite similar to the GAE offering from Google. But for me the major difference is that it is an open platform. So I can go and add a different data service in case one of the existing one does not fulfill my need. But I cannot do this on GAE. The other difference is that Cloud Foundry can run on my local machine, in a private data center or on EC2. With GAE, I am limited to running my app on their infrastructure only.

  • Rich

    Great post!  Is the source code available for this example?

  • Ggehlot

    Hi Rich,
    You can send me an email at ggehlot AT mihinternet DOT com. I will send you the source code.
    Thanks,
    Gaurav

  • Ahmed Kamal

    Cloud foundry is indeed very interesting, probably once an open PaaS environment wins the minds and hearts of developers, IaaS will become less important as the real developer focus will be on PaaS. I work as the Ubuntu cloud community manager, and at Ubuntu we’ve done some really interesting work for deploying and managing Cloud Foundry on the cloud (and even physical servers) through a tool that we call ensemble (http://ensemble.ubuntu.com)

    Checkout how to deploy manage and scale-up a multimachine cloud foundry deployment in 10 mins on ec2! Here is a very detailed article no how to get this done
    http://cloud.ubuntu.com/2011/09/from-zero-to-drawbridge-via-ubuntu-server-ensemble-and-cloudfoundry-in-less-than-10-minutes/

  • http://mrdanadams.com/2012/node-js-paas-hosting-services/ Node.js PaaS hosting services | Mister Dan Adams

    [...] purpose cloud hosting platform, does support Node.js and Joyent is the community leader for it. This post has a nice tutorial on getting [...]

  • http://blog.newrelic.com/2012/02/17/node-js-asp-net-sinatra-rails-java-the-list-goes-on-removing-the-os-barrier-with-paas-part-3-14159265/ Node.js, ASP.NET, & Sinatra, Rails, Java, The List Goes On: Removing the OS Barrier with PaaS Part 3.14159265 | New Relic blog

    [...] Intro, Part 5 with Node.js (video) * Deploying a Node.js Application with NPM Dependencies * Getting Started with Cloud Foundry Using a Node.js and Mongo DB ApplicationSinatra / Rails Sinatra is a lean and mean framework for serving up Ruby web applications. It [...]

blog comments powered by Disqus