Archive for June, 2012

Deploying HTML5 apps on CloudFront with efficient invalidation requests

Friday, June 8th, 2012

So you decided to build your next web app/site using nothing but HTML5 and Javascript.
No server side processing for anything related to UI.

This means you will be coding a lot of JavaScript.

Wouldn’t it be nice to put all that static HTML and JS on your CloudFront CDN and not deal with web servers?

Once you deploy it’s nice, but Amazon Cloudfront is not meant for you to be invalidating objects all the time, they actually charge you $0.005 per each invalidated file.

They tell you to version your files, but if you’re going for a no webserver based deployment, 100% cloudfront deployment, you can’t play around with url rewrites or redirects, so you’ll want to invalidate the files that have changed.

Here’s a script to invalidate only the files that have changed.

Our configuration
Cloudfront pointed to a custom origin.

The custom origin web server Document Root points to
/var/www/mysite/production

‘production’ is a symlink pointing to a folder inside /var/www/mysite, in there we have copies of the entire website saved under version numbers, for example

/var/www/mysite/1.0.1
/var/www/mysite/1.0.2
/var/www/mysite/1.0.3 -> production/
/var/www/mysite/1.0.4

What our script does, is it uses rsync to compare the files that changed between the current production directory (in this case 1.0.3 with the next version 1.0.4), and dump the new files on a temporary folder /var/www/mysite/tmp

We then create a list with all the files in the /tmp folder, and for each 1,000 files that have changed we send Cloudfront an invalidation request and move the ‘production’ symlink to the new version.

Instead of invalidating every file (which would be costly), we just invalidate what changed, and we don’t have to change any of our code to reflect version numbers.

All of this is handled by command line tools that let us do this in one step (one step deployment)

Here’s some of the python code we use to get the list of files that changed in order to build the invalidation request with the minimum files necessary so Jeff Bezos doesn’t take all our money.

def getFilesToInvalidate(config, newVersion):
    '''Compares the files in the new version folder with what's currently in production.                                                                                                                                                                                      
    returns a list of the files that changed or got removed.'''
    checkKey(config,'website.exports.path')

    oldFiles = config['website.exports.path'] + os.path.sep + 'production'
    diffFiles = config['website.exports.path'] + os.path.sep + 'diff'

    #the ../NEW_VERSION is important, the command needs relative paths to work
    #rsync -av --compare-dest=../NEW_VERSION /path/to/OLD_VERSION /path/to/TEMP_CHANGED                                                                                                                                                   
    rmDiffCmd = 'rm -fr ' + diffFiles
    cmd = 'rsync -av --compare-dest=../%s %s/ %s' % (newVersion,
                                                    oldFiles,
                                                    diffFiles)
    print cmd
    os.system(rmDiffCmd)
    os.system(cmd)
    diffList = getFilesInPath(diffFiles)
    result = []
    for i in xrange(len(diffList)):
        if diffList[i].find('#')!=-1:
            continue
        result.append(diffList[i].replace(diffFiles,''))
    os.system(rmDiffCmd)

    return result

Then use the ‘result’ list to create your CloudFront invalidation request.

Screw server side processing to render web pages, clients can do it now with client-side templating and javascript.
And oh Amazon, you guys need to step up your CloudFront Console features, it’d be awesome to invalidate paths and what not, it’s ultimately about what the customer needs.

Here’s toast to a faster cloud, cheers.




  • Categories

  • June 2013
  • May 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • October 2012
  • September 2012
  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
  • March 2010
  • February 2010
  • January 2010
  • December 2009
  • October 2009
  • September 2009
  • July 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007
  • February 2007
  • January 2007
  • December 2006
  • November 2006
  • October 2006
  • September 2006
  • August 2006
  • July 2006
  • June 2006
  • May 2006
  • April 2006
  • March 2006
  • February 2006
  • January 2006
  • December 2005
  • November 2005
  • October 2005
  • September 2005
  • August 2005
  • July 2005
  • June 2005
  • May 2005
  • April 2005
  • March 2005
  • February 2005
  • January 2005
  • December 2004
  • November 2004
  • October 2004