Friday, January 07, 2011

Creating a Custom Origin Server for Amazon CloudFront

At the time of writing, tool support is limited to the REST API? The intention with this piece of work was to take content being served by our origin server; e.g. http://example.com/images/foo.png; and serve it via Amazon CloudFront on http://cdn.example.com/images/foo.png.

  1. Download cfcurl.pl.
  2. Get any dependencies from CPAN (the cfcurl.pl script tells you how to do that in case you aren't sure).
  3. Create $HOME/.aws-secrets and chmod 600.
    $ cat /Users/jabley/.aws-secrets
    %awsSecretAccessKeys = (
    # my personal account
    'james-personal' => {
    id => 'foo',
    key => 'bar',
    },

    # my corporate account
    'james-work' => {
    id => 'AWS-ID',
    key => 'AWS-Secret-Key',
    },
    );
  4. Create a file with the request data
    $ cat create-distribution.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/">
    <CustomOrigin>
    <DNSName>example.com</DNSName>
    <OriginProtocolPolicy>http-only</OriginProtocolPolicy>
    </CustomOrigin>
    <CallerReference>20110106103700</CallerReference>
    <CNAME>cdn.example.com</CNAME>
    <Comment>example.com CloudFront CDN</Comment>
    <Enabled>true</Enabled>
    <Logging>
    <Bucket>accesslogs-example.com.s3.amazonaws.com</Bucket>
    <Prefix>cdn.example.com/</Prefix>
    </Logging>
    </DistributionConfig>
  5. POST the file
       perl cfcurl.pl --keyname james-work -- -X POST -H "Content-Type: text/xml;charset=utf-8" --upload-file \
    create-distribution.xml https://cloudfront.amazonaws.com/2010-11-01/distribution
  6. Poll to see when it has finished creating the distribution:
       perl cfcurl.pl --keyname james-work -- https://cloudfront.amazonaws.com/2008-06-30/distribution
  7. Configure DNS so that cdn.example.com is a CNAME for the DomainName value of your newly created CloudFront Distribution.

You should now be able to request a resource using the new CDN name:

$ curl -v -s "http://cdn.example.com/images/foo.png" -o /dev/null
* About to connect() to cdn.example.com port 80 (#0)
* Trying 192.168.1.1... connected
* Connected to cdn.example.com (192.168.1.1) port 80 (#0)
> GET /images/foo.png HTTP/1.1
> Host: cdn.example.com
> Accept: */*
> User-Agent: curl
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Thu, 06 Jan 2011 18:51:35 GMT
< Server: Apache/2.2.3 (Red Hat)
< Content-Length: 1233
< Cache-Control: max-age=86400
< Content-Type: image/png
< Age: 13311
< X-Cache: Hit from cloudfront
< X-Amz-Cf-Id: b769b423c54e2ffb0a6fb60369e2e0f7b103251ef3e2c549084fb4abe4ef9a236052f8eec40b3a14,80416de274eb8ee87c21ee41c863f9f6f9ef1c251823d9c12c46ab13dc33759dcbb04175b7d4a5a7
< Via: 1.0 83eb7919a5076e946a3a2d59d7f4415b.cloudfront.net:11180 (CloudFront), 1.0 26fb80d2abd86d7f52358cd1c2efd787.cloudfront.net:11180 (CloudFront)
< Connection: close
<
{ [data not shown]
* Closing connection #0

Note that Amazon CloudFront doesn't support query strings on resources, so you might need to use some Apache mod_rewrite stuff.

2 comments:

Jeff said...

Howdy James.

Thanks a million for this post - I was struggling with how to format and post the XML request to the CloudFront API, and the perl library did the trick.

In case you're interested, I wrote up a post detailing how I used this tip plus a few other tricks to add a CloudFront CDN layer on top of a Rails stack: http://stdout.wooswiff.com/2011/01/no-fuss-amazon-cloudfront-cdn-for-your.html.

Thanks again!

abautu said...

Hi James,

Thanks for the tip. It worked and save me a few hours to code a similar tool myself.

Cheers,
Andrei