Cloudfront for Ruby on Rails
Cloudfront for Ruby on Rails
Josh Crews
Buy on Leanpub

CloudFront for Ruby on Rails

Fast site delivery with Amazon Cloudfront for Ruby on Rails apps

joshcrews.com leanpub.com/cloudfront_rails

What is CloudFront?

Amazon’s (Web Services) CDN

What is a CDN?

Content Delivery Network

A network for serving static files fast.

What problems can it solve?

  • Unburden your webserver
  • Lower hosting costs
  • Speed up your site delivery

Unburden your webserver

If a CDN takes over delivering your assets (js, css, image files), then your server only has to deliver the html pages.

ESPECIALLY IMPORTANT FOR HEROKU

Heroku does not cache/proxy your static assets like many other Rails app hosts do. Heroku users will find the steps in this guide the best single step you can take to speed up your app and lower your dyno’s needed

Lower hosting costs (maybe)

You will need less servers, because they don’t have to serve assets.

You will need less bandwidth from your hosting service, because you aren’t serving assets. If your webhost charges less than $0.12 / GB.

Speed up your site delivery

Page responses will be faster from your unburdened server. Assets will be delivered faster from CloudFront’s speed at delivering static content

Why have a fast site?

Visitors expect instant page loads

  • 1 second is tolerable, but slow
  • 2 seconds is uhh…
  • 3 seconds is Back button

Why have a fast site?

  • Facebook pages that are 500ms slower result in a 3% drop-off in traffic, 1000ms is 6% drop-off
  • If Amazon increased page load time by +100ms they lose 1% of sales
  • If Google increased page load by +500 ms they get 25% fewer searches
  • If Yahoo increased page load times by +400ms they see a 5 – 9% drop in full-page traffic
  • If Firefox reduced load times by 2.2 seconds they saw an increase in download conversions by 15.4%

Source: http://www.guypo.com/business/17-statistics-to-sell-web-performance-optimization/

Offload serving assets to CloudFront

  • It’s easy
  • It’s cheap
  • asset_sync no longer necessary

If you are using asset_sync gem

If you have never heard of asset_sync, just skip reading to “Preparing your Rails app for CloudFront”.

The asset_sync gem is no longer necessary to server your assets from CloudFront. asset_sync was awesome, but that was before CloudFront could pull your assets directly off your server. The role asset_sync served was to upload all your assets to an S3 bucket, so that CloudFront could connect to S3 and serve your assets. As CloudFront does not need an S3 bucket to serve assets, asset_sync is no longer needed and adds unnecessary code, app maintenance, and deployment steps.

Preparing your Rails app for CloudFront

Is your Rails app ready to use CloudFront? Here’s the steps:

  • Rails 3.1+
  • serve static assets
  • image_tag
  • image-url

Rails 3.1+

This book assumes at least Rails 3.1, when the asset pipeline is introduced. The asset pipeline makes it super easy to switch the url that all your assets come from with one line of code. If you can’t upgrade, your road to using CloudFront will be harder.

serve static assets

For Rails apps using heroku (a application hosting service)

in config/produciton.rb

1 config.serve_static_assets = true
Why?

Most non-heroku apps are served with Apache or Nginx serving the static assets, so Rails doesn’t even get “turned on” for those files. But on heroku, Rails needs to be “turned on” for handling static files.

image_tag

replace

1 <img src="/assets/logo.png">

with

1 <%= image_tag "logo.png" %>
Why?

image_tag intelligent points the url to an image asset based on the and the asset_host setting.

image_tag will turn <%= image_tag "logo.png" %> into

<img src="/assets/logo.png"

without as asset_host defined. But

<img src="http://asset-server.example.com/assets/logo.png"

if we set asset_host to “http://asset-server.example.com”

image-url

replace

1 background: url(/assets/bg.jpg)

with

1 background: image-url("bg.jpg") 
image-url, rename css to css.scss

rename styles.css to styles.css.scss anywhere `image-url is used

Why?

url(/assets/bg.jpg) hardcodes that background image to a relative path, and we need the path to bg.jpg to change to CloudFront in production.

image-url is not valid css, but it is valid scss.

What is scss?

SCSS is a “CSS with bonus features” language that compiles down to CSS. Rails will automatically compile styles.css.scss into styles.css. One of the features of scss is that it will take

image-url("bg.jpg") and create url("http://asset-server.example.com/assets/bg.jpg") in your final css based on the value of asset_host in your config/(development|test|production).rb file.

All valid css files are valid scss files. So you can add .scss to any css file, and

What about sass?

If you are using sass, great, image-url works there too.

font-url

If you have font files in your app/assets/fonts directory, you can use font-url inplace of url in scss files and Rails will build the full path to your font files automatically.

Rails app now prepared for CloudFront

test it out (development)

Your site should still look right on localhost:3000

test it out

run

rake assets:precompile RAILS_ENV=production

(You are seeing if any errors are thrown)

That step will write a bunch of files to public/assets in your project directory. Unless you are intentially precompiling assets locally before every deploy to production, delete that folder.

Deploy your app

Deploy your app to production (or staging)

Does your site look right? If so, you are ready to go.

Setup CloudFront

  • signup http://aws.amazon.com
  • create a CloudFront distribution
Sign up for AWS

http://aws.amazon.com

You will have to put down a credit card.

I recommend you do not use your existing Amazon consumer/shopping account. Keep AWS and Amazon shopping separate.

Free AWS allowances

As of writing (June 5, 2014) AWS offers free tiers for many of their services. For CloudFront they offer 2,000,000 requests (2mil hits to your hosted files) and 50 GB out (~ 500,000 hits to a 100kb image) for new users.

Pricing

Is about $0.12 / GB (as of 2014/06/15). That’s cheap for a CDN. $1 should buy you 80,000 hits to a 100kb image.

I can’t guarantee this in 100% of situations, but the pricing should be no-brainer because every hit to a file on CloudFront is a hit that your webserver didn’t have to process, and your webserver didn’t have to pay for bandwidth to serve.

Create CloudFront Distribution

Create Distribution

Create Distribution

Choose ‘Web’

Choose Web

Choose Web

Set Origin Domain Name as your site

Set Origin Domain Name as your site

Press ‘Create Distribution’

Note your CloudFront Distribution URL

Looks like dxxxxxxxxxxxx.CloudFront.net

Finding new distribution URL

Finding new distribution URL

… takes ~5 minutes to Deploy

The “Status” column will update to “Deployed” on that screen when it’s ready.

Update your asset_host

update config/production.rb

config.action_controller.asset_host = "http://YOUR-CLOUD-FRONT-SUBDOMAIN.CloudFront.net"

Bonus: Update your asset_host for https

update config/production.rb

1 config.action_controller.asset_host = ->(source, request = nil, *_){
2   if request && request.ssl?
3     "https://YOUR-CLOUD-FRONT-SUBDOMAIN.CloudFront.net"
4   else 
5     "http://YOUR-CLOUD-FRONT-SUBDOMAIN.CloudFront.net"
6   end
7 }

Deploy to production

Your assets (css/js/images) should be coming from CloudFront

Understanding what is CloudFront doing

CloudFront serving assets

CloudFront serving assets

How CloudFront works

  1. Your app points to http://WHATEVER.cloudfront.net/assets/logo.png for the logo image (and css, js, all images)
  2. A visitor requests assets/logo.png from CloudFront.
  3. If they are the first request for that file in their geographic region, CloudFront will forward that request to your server, serve the file to the visitor, and the save a cached copy
  4. Future requests CloudFront will serve it’s cached copy (for 1 year by default)

Updating files on CloudFront

So what if your logo changes? Or more likely your css and js files?

Rails handles this automatically!

asset fingerprints

When Rails precompiles assets, it creates a ‘fingerprint’ for each asset. When that asset changes, the fingerprint changes.

So the link to your logo is not /assets/logo.png, it’s more like

/assets/logo-67268e8d2b9f2b27e2f141d68ca42478.png

When your app deploy again to production, visitors will request the new file, and because that new file is not cached on CloudFront, CloudFront will retrieve the new file from the server.

Bonus: faster deliver times

Your assets will be served by edge locations worldwide

CloudFront Locations in North America as of 2014-06-15

CloudFront Locations in North America as of 2014-06-15

Imagine a site visitor from northern California

Site visitor in California

Site visitor in California

(Without a CDN / CloudFront)

In this example, your app server is in northern Virginia.

For all files served by your server, the request and response travels the country.

Site visit without CDN crossing country

Site visit without CDN crossing country

With CloudFront installed, all your static files get served from an Edge Location in San Francisco.

Site visit with CDN goes to closest CloudFront location

Site visit with CDN goes to closest CloudFront location

All CloudFront locations

CloudFront has locations in the United States, Europe, South Asia, East Asia, South America and Australia.

http://aws.amazon.com/cloudfront/details/#Detailed_Description

The farther away your visitor, the more CloudFront helps site speed

Imagine a visitor to your site/app on mobile in India. The request is going to have to travel along way to get your page, but all the other files on the page can come from a CloudFront edge location in India. Your site will be faster than if all css/js/images had to come from the United States.