Computing In the Clouds

The Cloud concept in a nutshell


February 03, 2009
URL:http://www.drdobbs.com/web-development/computing-in-the-clouds/213000642

Dana and John are Division Scientists at BBN Technologies. They can be contacted at [email protected] and [email protected], respectively.


Normally, clouds on the horizon are harbingers of bad weather. Not this time, however. Developers are likely to welcome the advent of cloud computing with open monitors, because cloud computing quite simply is a way for an application creator to abstract away some of the more challenging aspects of building rich, scalable Internet applications (RIAs). This includes business functionality such as purchase management, services such as databases and security, and critical nonfunctional requirements such as scalability and predictable performance.

Cloud computing offers virtually unlimited, on-demand computing resources. Your applications now live in a new platform -- a computing cloud. In the cloud, your applications take advantage of the seemingly limitless processor cycles, memory storage, and network bandwidth along with extensive software capabilities. Your applications only pay for what they use. Beyond basic computing resources, cloud computing offers a range of application services that form a new platform -- an Internet operating system -- suitable for cost-effective, dynamic, and Internet-scale solutions. An Internet operating system offers the scale and services required to meet the requirements of a dynamic, global, software application.

The basic idea behind cloud computing is similar to how we currently pay for electricity -- you plug it in, it uses electricity, you pay for the electricity it uses. However, electricity is a straightforward commodity, and an Internet operating system is not. Vendors may choose to offer different grades of electricity, different levels of service, and the like. Currently there are no cloud standards that would let you take your application and plug it into a competing cloud offering. Additionally, the nascent nature of cloud computing encourages a level of experimentation by the various vendors to find the right balance of services, performance, and cost. This produces various implementations that offer different types of resources and services and significant changes. But don't let these challenges stop you -- there can be much to gain, even at this early entry point. When was the last time you could instantly grab dozens of computers, play with them, and then discard them for less than one dollar (or even for free)?

Elasticity is the cloud's true virtue. When a cloud-deployed application becomes busy, it merely acquires more resources. Correspondingly, when demand wanes, it drops the extra resources. This is a dramatic difference from the traditional approach. Traditionally, applications acquired the resources necessary to handle the peak. When demand was less than the peak, valuable resources sat idle. When demand exceeded the peak, the application failed, sometimes in ugly ways. We have all experienced the fun of receiving a timeout on a busy site, or programming to handle such situations. The elasticity offers a new, low-cost way to scale your application both up and down while paying for only what the application uses. This is especially beneficial to bursty-types of applications, such as sport sites or fledgling startups. The elasticity also enables new testing and demonstration scenarios. Imagine your demonstration, coolly employing the cloud to simulate millions of users. Imagine test scenarios that stretch the most determined tester. Franz used the cloud to test AllegroGraph, its triple store product. This was done in two days at a cost of less than $200. The promise of cloud computing is all that and more.

In addition to elasticity, cloud computing offers direct connections to valuable software. Clouds can hold more than just your software. They can hold other software including services such as databases but also third-party software. The direct connection overcomes the performance and latency issues that trouble distributed web services. This takes Web 2.0 mashup to a new, lower level where you can interact directly with underlying support abstractions, not simply exposed application layer APIs.

Getting Into the Cloud

Currently, you can create cloud applications through two major implementations:

Let's look at an example in each paradigm. In both cases, we'll note the types of resources your application can acquire, how your application interfaces with the resources, and related resource services your application can use. We also note the costs and risks.

First and foremost, the number one caveat you must consider at the current level of maturity of both approaches is that cloud implementations offer some great benefits, but also risks. Your application depends on the network for the user, and the supporting network infrastructure offered by the provider. If users can't get to the cloud, your application is out of luck. Second, service-level agreements are not always present, so think carefully regarding mission-critical applications. Also, security is always a concern. Additionally, note that AWS and GAE are vastly different in the development and deployment models. The cloud offerings are dramatically different; you will have to use different development styles for each. Finally, examine your support options (what happens when the cloud goes away?).

Amazon Web Services

Amazon Web Services (AWS) emerged directly from the challenges in running amazon.com and incorporates an Amazon perspective. AWS offers the cloud at two distinct levels -- raw computing resources and ready-to-go appliances. The former offers traditional computer resources, such as processors, memory, file system/database, and messaging. The ready-to-go appliances build useful applications and/or services on top of the raw resources. Amazon supplies and manages the raw computing resources. Amazon partners typically provide the ready-to-go appliances. AWS charges a la carte for usage of the raw resources -- processor, memory, network traffic, and storage. Partnering appliances are free to attach a surcharge to the resources that implement their offerings. Amazon is actually the enabler. It depends on partners or you to create useful applications. Additionally, third-party solutions have emerged to manage AWS resources directly. At this writing, AWS consists of five primary services:

To access any of these services, you must get an account and activate the services you require. After activation, you can interact with the services via an access key or X.509 certificate. You can obtain an account, access key, and certificate for no cost. Your applications interface with AWS via three primary methods: REST, Query, and SOAP. The REST interface forms a standard HTTP or HTTPS request message containing data within the request body. The Query interface also uses HTTP but relies on simple name and value pair parameters so that basic browsers can perform service operations. The SOAP interface uses XML documents as described in a WSDL. AWS supports these methods as follows:

In addition to these low-level interface methods, there are two other useful interfaces -- the AWS toolkit and higher-level, third-party offerings.

AWS persistence relies on S3. Your account lets you create persistence storage buckets. You are currently allowed 10 buckets and they must be unique throughout all of S3. You can choose to expose the bucket to an Amazon URL, if named accordingly. You can then map this URL to a URL that you control. You can also control where the bucket is physically located.

To illustrate the interfaces, we demonstrate two third-party tools to examine current buckets. JetS3t shows the lower level bucket configuration in Figure 1. You can see we've have declared two buckets -- one that is the default and one using a URL.

[Click image to view at full size]
Figure 1: JetS3t presentation of S3 Topology

Go to pictures.ebmagic.com/SmokingBishopToast.jpg to see a picture linked to a private hostname but stored in S3. Thus, you can make any bucket and/or object addressable. Cockpit mimics a file manager in Figure 2.

[Click image to view at full size]
Figure 2:JungleDisk presentation of S3

This tool makes the buckets look just like a filesystem. You can also see that the object key uses mnemonics that represent the various directories. Cockpit also offers backup utilities to continuously sync up declared directories/files. Thus you can backup your files directly into the cloud for safekeeping. Storage need not use any of the other AWS resources. For example, you could simply use S3 to store all your documents and photos and make them accessible to the Web.

The real heart of AWS is processor access. To get started quickly, you would launch an existing image of an operating system configuration. The Firefox ElasticFox plug-in offers a nice view into what is available (Figure 3).

[Click image to view at full size]
Figure 3: ElasticFox EC2 View.

Figure 3 finds available images that contain the string fedora. There are many available offerings, including those that contain MySQL and Apache. The image ami-25b6534c is selected -- note the visibility. You can limit access to any images that you create. Next, one is selected and launched (Figure 4).

[Click image to view at full size]
Figure 4: Launch EC2 Images.

This presents several options -- the instance type selects the machine size. The user has selected 'small.' You can also set a minimum/maximum number of in stances. This ensures that a certain number of instances are always running and your user base cannot exceed the maximum. This gives you an initial hint at how easy it is to scale an application. You can also set the security, matching key pair, and a location; and create as many different configurations as necessary.

Next, hit the launch button and it's off and running (Figure 5).

[Click image to view at full size]
Figure 5: EC2 running Images.

You now have full access to the machine. For example, you could then use an elastic IP address to attach the image to a specific IP (Figure 6), then map the IP to the domain name through whatever hosting facility you use. Note that you can change the binding between an instance and the IP address.

[Click image to view at full size]
Figure 6: SSH access to running EC2 Images.

Next, you would normally use an ssh client to connect to the instance. Port 22 is open in the default group. Figure 7 shows the ssh connection that lets you change the configuration and download additional files.

[Click image to view at full size]
Figure 7: SSH connection

And there you have it. A fully running image set to a specific hostname and ready to serve up your application from the cloud.

The Amazon toolkit provides the tools to create your own image, upload it into S3, and associate an image ID. As with any object in S3, you can set permissions to allow access to the image.

To allow multiple virtual machines to cooperate with one another, your application can employ the SQS to handle messaging. SQS offers some features beyond normal messaging. Messages, although properly delivered, aren't deleted until specified by the subscribing application. If the message remains undeleted for a specified period of time -- it magically reappears. Thus, if the subscribing application read the message but then failed, the message reappears to be consumed again.

The last component is SimpleDB. Instead of employing your own database within your virtual machine, your application can use the SimpleDB for basic test indexing.

One caveat to all this AWS fun: Only S3 and SimpleDB are persistent. If you deactivate a virtual machine or it fails, all of its memory (including allocated disk space) is also deactivated. Therefore, you must move critical information into the SimpleDB or S3.

While you can get the latest pricing at the AWS site, here are some typical costs:

The costs appear nominal but can add up as you take advantage of the operations. For example, a small machine allocated for an entire month would cost over $72. This may exceed the cost from a typical ISP. The secret is in the elasticity. You probably don't need the computer for every hour in a month.

Google Application Engine

While AWS concentrates on computing resources, the Google Application Engine (GAE) focuses on computing capabilities released via the Google SDK. At this writing, GAE is in preview, but the parts of the overall concept already revealed have stunning appeal. What would your life as a developer be like if you could focus 100 percent of your energy on the business end of your RIA, rather than fretting the details of what happens when you actually attract a significant user base? This is the promise of GAE. By leveraging an agile language (Python) and well-known RIA framework (Django), application development is a matter of fast iterative, incremental coding.

GAE is built on a Web 2.0 infrastructure built on a custom (but open) API. When you download the SDK , you get a local webserver environment suitable for code development and light use testing. The SDK:

An Example Application

Architecturally, GAE uses a familiar model-view-controller application structure, with the model and controller code conveniently packaged into the same module. To illustrate, we've created a simple To Do List application called "goalpost". You can see how it operates at goalposter.appspot.com. When you log in, you get a screen that resembles Figure 8.

[Click image to view at full size]
Figure 8: GoalPoster hosted at Google's appspot.

Typing in a text string (for example, Finish Canoe and Kayak article), then hitting Enter adds a new goal embodied in the view as a green button. When users complete a goal, they click its button which turns dark brown. Hitting the Clear button clears the completed goals from the view and from the underlying model. Pretty simple compared to all those Outlook or iCal models, but effective and far more likely to be used.

The Code

What's compelling about GAE is the level of abstraction at which you code. An application's code logic can often be a single Python file, with class definitions for the data model, and individual classes implementing the handlers for the various HTTP POSTs and GETs, as in Listing One.

import cgi
import os 
from datetime import * 
from types import *
from google.appengine.ext.webapp import template
import wsgiref.handlers
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
## global (semi)constants
gDoneString = "*Done*"
gOpenItem = "openItem"
gDoneItem = "doneItem"
class ItemEntry(db.Model):
  author = db.UserProperty()
  contentID = db.StringProperty(multiline=False)
  content = db.StringProperty(multiline=False)
  date = db.DateTimeProperty(auto_now_add=True)
  status = db.StringProperty() 
  representation = db.StringProperty()

class MainPage(webapp.RequestHandler):
  def get(self):
    results = []
    user = users.GetCurrentUser()
    if user:       # users.get_current_user():
      query = db.GqlQuery("SELECT * FROM ItemEntry WHERE author = :1", user ) 
      results = query.fetch(1000)
    if user:
      template_values = {'todoList': results,  
        'auth_url': users.CreateLogoutURL(self.request.uri),
        'auth_text': 'logout','user_email': user.email(),}
    else:
      template_values = {'auth_url': users.CreateLoginURL(self.request.uri),
        'auth_text': 'login', 'user_email': '',}

    path = os.path.join(os.path.dirname(__file__), 'index.html')
    self.response.out.write(template.render(path, template_values))

class HelpPage(webapp.RequestHandler):
  def get(self):
    template_data = {}
    user = users.get_current_user()
    if user:
      template_data = {'auth_url': users.CreateLogoutURL(self.request.uri),
        'auth_text': 'logout','user_email': user.email(),}
    else:
      template_data = {'auth_url': users.CreateLoginURL(self.request.uri),
        'auth_text': 'login','user_email': '',}
    template_path = os.path.join(os.path.dirname(__file__), 'help.html')
    self.response.headers['Content-Type'] = 'text/html'
    self.response.out.write(template.render(template_path, template_data))  

class ToDo(webapp.RequestHandler):
  def post(self):
    itemEntry = ItemEntry()

    if users.get_current_user():
      itemEntry.author = users.get_current_user()
    itemEntry.content = self.request.get('description')
    d = datetime.now()
    itemEntry.date = d
    itemEntry.contentID = d.__str__().replace(" ", "^")
    itemEntry.status = d.ctime()[4:10]
    itemEntry.representation = gOpenItem
    itemEntry.put()
    self.redirect('/')
     
class MarkDone(webapp.RequestHandler):
  def post(self):
    which = self.request.get('item')
    q = db.GqlQuery("SELECT * FROM ItemEntry WHERE contentID =:1, ORDER DESC by ItemEntry.status" , which) 
    item = q.get() 
    if type(item) is NoneType: #  no specific button pushed
      q = db.GqlQuery("SELECT * FROM ItemEntry WHERE status =:1", gDoneString)
      results = q.fetch(1000)
      for result in results:
        result.delete()
    else:   # the user pressed one button only, so change its state
      if item.representation == gOpenItem:
        item.status = gDoneString # "*Done*" 
        item.representation = gDoneItem
      else:
        d = datetime.now()
        item.date = d
        item.contentID = d.__str__().replace(" ", "^")
        item.status = d.ctime()[4:10]
        item.representation = gOpenItem
      db.put(item) 
    self.redirect('/')  

def main():
  application = webapp.WSGIApplication([('/', MainPage),('/todo', ToDo), ('/help', HelpPage),('/markDone', MarkDone)],debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
  main() 

Listing One

Examining Listing One, notice that there is a class (ItemEntry) whose parent class is db.model and another three descended from webapp.RequestHandler. These latter three correspond to HTTP get and post handlers, which are associated with Web 2.0 URL reference style invocations via the two lines configuring, then running the application:


application = 
  webapp.WSGIApplication([('/', 
  MainPage),('/todo', ToDo), 
  ('/help, HelpPage) ('/markDone', 
  MarkDone)],debug=True)
  wsgiref.handlers.CGIHandler().
  run(application)

The first line connects the three controller functions to the three classes in Listing One. The second starts the application itself. When the user enters a new goal, the browser shifts control to the ToDo controller function. The controller classes implement get or post functions; this too should be familiar to you. ToDo and MarkDone both transfer control using a redirect to '/' which is handled by the MainPage class.

Using Django Templating

In MainPage, the controller code then does a little behind the scenes AJAXian magic to correctly return the page, as seen by the user, replacing the parts of the page's DOM without refreshing the entire page. By convention, the default application view is contained in index.html; see Listing Two.

<html>
  <head>
    <link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
  </head>
  <body>
     <div id="head">
         <h1><a href="/">GoalPoster</a>: The No Nonsense Personal Goal Manager</h1>
        <div id="topnav">
          {% if user_email %}
          <strong>{{ user_email }}</strong> | 
          {% endif %}
          <a href="/help">help</a> |
          <a href="{{ auth_url }}">{{ auth_text }}</a>
     </div>
    <div id="banner"> 
      <img src="/static/images/Goalpost-Hockey.gif" />   
    </div>
    {% if user_email %}   
    <div id="goals">
    <form action="/markDone" method="post">    
    {% for entry in todoList %}
      <div id="content">
      <p><button id="{{entry.contentID}}" value="{{entry.contentID}}" name="item" 
             class="{{entry.representation}}">{{ entry.status }}  | 
             {{ entry.content|escape }}</button></p>
      </div>
    {% endfor %}
    <input type="submit" value="Clear">
    </form>
    </div>  
    <hr/>
    <form action="/todo" method="post">  
      <div><input type="text" class="content" name="description"></input></div>
    </form>
    {% else %}
    <center><h1><a href="{{ auth_url }}">Login to use GoalPoster</a></h1></center> 
    {% endif %} 
</body>
</html>
An interesting code snippet from index.html is this one:
{% for entry in todoList %}
  <div id="content">
    <button id="{{entry.contentID}}" value="{{entry.contentID}}" name="item" class="{{entry.representation}}">{{ entry.status }}  | {{ entry.content|escape }}</button>
  </div>
{% endfor %}

Listing Two

An interesting code snippet from index.html is:

% for entry in todoList %} <div id="content"> <button id="{ { entry.contentID} } " value="{ { entry.contentID} } " name="item" class="{ { entry.representation} } "> { { entry.status } } | { { entry.content|escape } } </button> </div> { % endfor %}

If you have worked in Rails, TurboGears, Django, or the like, this is probably familiar. Essentially, this segment contains a code directive that the underlying GAE code resolves prior to display. When the GAE code encounters The { %...%} pairs, signal the controller method to interpret the text between delimiters as Python code. Controller class methods return multiple values as a Python dictionary. Wherever it encounters variables appearing between pairs of curly braces, the underlying Django engine substitutes values from the dictionary into the correct template slots in index.html before writing to the browser via self.response.out.write(template.render(template_path, template_data)). The HTML page slots to be filled in are shown bolded in Listing Two. Notice how they match the bolded dictionary references in Listing Two.

Configuration and Testing

Thus far, you've seen the model and controller code expressed in goalpost.py and the view, expressed in index.html. These are specified in the configuration file, (by convention called 'app.yaml') necessary for local testing or for web deployment. YAML was chosen for its succinctness and overall simplicity of form. As in most matters, GAE code and configuration philosophy follows that of Rails: 'convention over configuration', and 'principle of least surprise.' Files exist in standard directory structure and have names as expected by the application runner.

The configuration for goalpost is in Listing Three. Although for this application the configuration specifics are simple, potential configuration details can grow in detail to encompass numerous aspects of a large-scale application.


application: goalposter
version: 1
runtime: python
api_version: 1

handlers:
- url: /stylesheets
  static_dir: stylesheets
- url: /static/images
  static_dir: static/images
  mime_type: image/png,image/gif 
- url: /.*
  script: goalpost.py

Listing Three

Locally, an application may be exercised from the command line using the dev_appserver supplied with the SDK. The general invocation signature of dev_app server is:


dev_appserver.py [options] <application root>

where a number of options are possible. Thus, a typical terminal window invocation for the sample application would be:


python2.5  /usr/local/bin/dev_appserver.py   -- debug  -- port=8080   GoalPost/

You can also use the GoogleApp Engine Launcher application manager on Mac OS/X to give you a boost up in creating and managing a new application or datastore if you're command-line averse; however, most developers find their favorite editor and the command-line tools sufficient. The rather remarkable aspect of writing a GAE-based app is how succinct both code and the configuration can be.

Registration and Upload

Once you've satisfactorily tested your application locally, you can register and upload all required files. Registration is a one-step process in which you supply a mobile phone number to which a registration number may be sent. After you receive a registration code, you can manage deployment and observe the application's performance and resource consumption using the handy administration console at appengine.google.com/dashboard.

If you want to have Google host your application you need to supply an application name, which is prepended to the domain appspot.com. For the sample application the name goalposter was used; thus the application resides at goalposter.appspot.com, as in Figure 8.

Clearly, GAE lets you write applications at a very high level of abstraction, where underlying machine architectures, memory and disk limits tend to disappear; further, there are no 'seams' visible to either application developers or end users. A typical application can rely on there being enough resources to support its needs almost regardless of the number of concurrent users.

BigTable

One reason for the scalability of GAE is the ability to support distributed data storage for an extremely large corpus delivered through Google's 'BigTable' architecture. Google asserts BigTable has the ability to deliver high-performance interaction data in the range of several hundred terabytes. BigTable is essentially a large flat space that provides object-relational semantics does not really implement a full relational model. Although BigTable applications such as goalposter use what appears to be a classic object-relational model, the underlying mechanisms for access, data versioning, and garbage collection share little with 'real' RDBs such as MySQL.

Even so, both object.method (for example, itemEntry.put() in Listing One) semantics and a more general call semantic resembling classic embedded SQL (the GqlQuery queries in Listing One) are implemented. If you understand either or both models from traditional programming or full scope styles such as Rails or TurboGears, you already understand enough to get started writing your first GAE cloud-hosted application.

Summary

One thing of which we can be certain as developers is that cloud computing is going to expand in overall level of acceptance in the enterprise. The concept will continue to mature and gain both features and developers. As this article goes to press, Amazon has just announced CloudFront, a content delivery/content management service based on other Amazon services such as S3. Google continues to mature and update its GAE capabilities. Even Microsoft has decided to get its cloud on with a service platform called 'Azure. ' The weather out there for us developers is becoming at the same time both more cloudy and brighter.

Acknowledgement

Thanks to Prakash Manghawani for his help on this article.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.