312 lines
17 KiB
HTML
312 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en-us">
|
||
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="description" content="">
|
||
<meta name="author" content="Ryan S. Brown">
|
||
|
||
<title>Interview: Building the Latest Campaign for David Guetta -- Serverless Code</title>
|
||
|
||
<link rel="stylesheet" href="/css/bootstrap-3.3.6.min.css">
|
||
|
||
<link href="/css/all.min.css?h=c3840cffae" rel="stylesheet">
|
||
|
||
|
||
<link href='https://fonts.googleapis.com/css?family=Merriweather:400,400italic,700|Open+Sans:300italic,400italic,600italic,700italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
|
||
|
||
|
||
|
||
<script data-strip>
|
||
var __aerobatic__ = {staticAssetPath: ''};
|
||
</script>
|
||
|
||
</head>
|
||
|
||
<body>
|
||
|
||
|
||
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
|
||
<div class="container-fluid">
|
||
|
||
<div class="navbar-header page-scroll">
|
||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
|
||
<span class="sr-only">Toggle navigation</span>
|
||
<span class="icon-bar"></span>
|
||
<span class="icon-bar"></span>
|
||
<span class="icon-bar"></span>
|
||
</button>
|
||
<a class="navbar-brand" href="https://serverlesscode.com/">Serverless Code</a>
|
||
</div>
|
||
|
||
|
||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||
<ul class="nav navbar-nav navbar-right">
|
||
|
||
<li>
|
||
<a href="/">Home</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/post/">Archives</a>
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
</nav>
|
||
|
||
|
||
|
||
|
||
<header class="intro-header" style="background-image: url('/img/post-bg.jpg')">
|
||
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||
<div class="post-heading">
|
||
<h1>Interview: Building the Latest Campaign for David Guetta</h1>
|
||
<h2 class="subheading">Building an Infinitely Scaleable Online Recording Campaign for David Guetta</h2>
|
||
<span class="meta">Posted by
|
||
<a href="https://twitter.com/ryan_sb">Ryan S. Brown</a>
|
||
on Wed, Jan 13, 2016
|
||
<br />
|
||
In <a href="/categories/interview" >Interview</a>
|
||
|
||
<br />
|
||
Tags: <a href="/tags/lambda" >lambda</a>, <a href="/tags/api-gateway" >api gateway</a>, <a href="/tags/serverless-framework" >serverless framework</a>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
|
||
<article>
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||
<p>Paging David Guetta fans: this week we have an interview with the team that
|
||
built the site behind his latest ad campaign. On the site, fans can record
|
||
themselves singing along to his single, “This One’s For You” and build an album
|
||
cover to go with it.</p>
|
||
<p>Under the hood, the site is built on Lambda, API Gateway, and CloudFront.
|
||
Social campaigns tend to be pretty spiky – when there’s a lot of press a
|
||
stampede of users can bring infrastructure to a crawl if you’re not ready for
|
||
it. The team at <a href="https://parall.ax/">parall.ax</a> chose Lambda because there are no
|
||
long-lived servers, and they could offload all the work of scaling their app up
|
||
and down with demand to Amazon.</p>
|
||
<p>James Hall from <a href="https://parall.ax/">parall.ax</a> is going to tell us how they built an
|
||
internationalized app that can handle any level of demand from nothing in just
|
||
six weeks.</p>
|
||
<h2 id="the-interview">The Interview</h2>
|
||
<!-- raw HTML omitted -->
|
||
<p>Parallax is a digital agency; we provide a range of services including
|
||
application development, security and design services. We have 25 full-time
|
||
employees as well as some external contractors. We specialise in providing
|
||
massively scaleable solutions, with a particular focus on sports, advertising,
|
||
and FMCG (Fast Moving Consumer Goods).</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>The app is part of a huge marketing campaign for David Guetta’s new release
|
||
“This One’s For You” – the official anthem of the UEFA EURO 2016 finals. We’re
|
||
inviting fans – hopefully a million by March – into a virtual recording studio
|
||
and giving them the chance to sing along with Guetta. Not only will some of
|
||
their voices be included on the final song, we also create personalised album
|
||
artwork with their name and favourite team. This can be shared among friends,
|
||
increasing engagement and allowing more fans to become a part of the campaign.
|
||
The whole site has been built in twelve languages, incorporating video content
|
||
of the DJ himself and a chance to win a trip to Paris. You can check it out
|
||
over at <a href="https://thisonesforyou.com/">thisonesforyou.com</a>.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p><img src="/images/guetta_article/image04.png" alt="AWS service architecture diagram" class="img-responsive">
|
||
<em>Image credit: Parallax Agency Ltd.</em></p>
|
||
<p>On the backend we use a variety of technologies to deliver a completely
|
||
scaleable architecture. We use <a href="http://serverless.com">Serverless</a> (formerly JAWS) and CloudFormation to
|
||
orchestrate the entire platform in code.</p>
|
||
<p>Requests are routed via CloudFront, with static assets cached at Edge locations
|
||
nearest to the user requesting them. When the page first loads, everything is
|
||
entirely static. A UUID is generated in the client browser - this is used for
|
||
all future API request and allows us to associate different actions in the page
|
||
together without having to serve cookies. The randomness of this value is
|
||
important, so the library uses timing and crypto functions (where available) to
|
||
derive random seed data.</p>
|
||
<p><img src="/images/guetta_article/image02.png" alt="CloudFormation and S3 integration diagram" class="img-responsive">
|
||
<em>Image credit: Amazon Web Services</em></p>
|
||
<p>The origin of the static assets is a simple S3 bucket. These are uploaded via a
|
||
script on deployment.</p>
|
||
<p>A language detection endpoint then sends back the Accept-Language header and
|
||
also country code that the request was received from. This is using a basic
|
||
Lambda function. Another endpoint adds subscriber data to DynamoDB, as well as
|
||
sending them a welcome email via Amazon SES. To make the recording work, we
|
||
have an endpoint which issues S3 tokens for a path named after the UUID
|
||
provided. Uploads are then directly posted to S3 from the browser.</p>
|
||
<p>The most useful application of Lambda was the image generation endpoint. We
|
||
take an image of the user’s favourite team flag, overlay a picture of Guetta,
|
||
then add their name. This is then uploaded to S3, along with a Facebook and
|
||
Twitter sized graphic. We also upload a static HTML file, which points to this
|
||
unique graphic. This ensures when people share the URL it will display their
|
||
custom artwork. We use Open Graph image tags in the page for this.</p>
|
||
<p>On the frontend, we used Brunch to compile the handlebars templates, compile
|
||
SASS, prefix the CSS and any other build tasks.</p>
|
||
<p>The recording interface itself has three implementations. The “A-grade”
|
||
experience is the WebRTC recorder, which uses the new HTML5 functionality to
|
||
record directly from your microphone. We then have a Flash fallback for the
|
||
same experience, this is for desktop browsers without WebRTC support. Then
|
||
finally we have a file input that prompts users to record on mobile.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>Yes, absolutely. Our regular stack is LAMP, built on top of CloudFront, Elastic
|
||
Load Balancers and EC2 nodes. This would have scaled, but it would have been
|
||
much more difficult to scale as quickly and as simply as it is using a
|
||
Lambda-based architecture. We’d have had to build a queueing system for the
|
||
image generation, then spin up EC2 nodes dedicated to making those images based
|
||
on the throughput.</p>
|
||
<p>Writing a simple Lambda function and letting Amazon do all the hard work seemed
|
||
like the obvious choice.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>The team at Parallax consisted of five people. Tom Faller was the Account
|
||
Manager, responsible for the day-to-day running of the project. I was the
|
||
backend developer creating the Lambda functions and designing the cloud
|
||
architecture. Amit Singh was primarily frontend, but worked on many of the glue
|
||
parts between the interface and the backend JS. Chris Mills was QA and Systems,
|
||
and linked to the Serverless (formerly JAWS) project in the first place. Jamie
|
||
Sefton was another JS developer and worked on integrating compatibility
|
||
fallbacks into the codebase, including a Flash-based and input-based recording
|
||
experience as a fallback for devices that don’t support WebRTC. I’ve had
|
||
experience with Lambda before, but it was new for the other team members.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>I’d say around six or seven weeks in total, though there was plenty of research
|
||
and prototyping done beforehand to arrive at the correct architecture design.
|
||
For the amount of scalability required, it would have taken much longer to
|
||
ensure it was just as robust using our “home turf”.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>The application is deployed from Atlassian Bamboo. The codebase lives in Stash.
|
||
Each and every branch gets its own deployment URL which is posted into our
|
||
company chat once built. This lets us quickly test and decide if changes are
|
||
mergeable.</p>
|
||
<p><img src="/images/guetta_article/image05.png" alt="Lambda deployment bot in action" class="img-responsive">
|
||
<em>Image credit: Parallax Agency Ltd.</em></p>
|
||
<!-- raw HTML omitted -->
|
||
<p>For frontend JS errors we use Bugsnag; for this type of application this is the
|
||
easiest way to see if there’s a problem anywhere in your stack. If error spikes
|
||
are bigger than traffic spikes you know something has gone wonky. We usually
|
||
use NewRelic for the backend, but as all the backend code lives in Lambda
|
||
functions we’ve opted to use CloudWatch.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>Yeah absolutely. Every commit and branch pushed to Stash is deployed to a
|
||
testing environment. For the static assets and frontend JavaScript, this uses
|
||
bamboo, and creates a new folder for every branch and build number. For the
|
||
lambda functions, those were updated to each environment using <code>serverless dash</code>.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>The server Lambda runs on lacks any fonts with Japanese and Chinese characters,
|
||
which is to be expected, but wasn’t something we’d planned for in the
|
||
development. When an operating system renders a particular font and it doesn’t
|
||
contain a particular glyph, it will fallback to the system installed
|
||
multilingual Unicode font. This happens on the web automatically, but inside
|
||
ImageMagick on a bare server this won’t happen. This means we had to ship a
|
||
large Unicode font in our endpoint. We reduced the performance impact of this
|
||
by only routing non-Latin names to the Unicode endpoint.</p>
|
||
<p><img src="/images/guetta_article/image00.png" alt="Helvetica without unicode" class="img-responsive">
|
||
Helvetica, for example, doesn’t include any Asian Glyphs
|
||
<img src="/images/guetta_article/image01.png" alt="Arial with unicode" class="img-responsive">
|
||
But Arial does</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>There’s some issues with multiple branches and the early Serverless (formerly
|
||
JAWS) framework. Say if we’re adding an endpoint in one branch, and another in
|
||
another branch, they can’t both be deployed to the same environment stage. This
|
||
is something we worked around, and we’ll be looking to contribute some helpful
|
||
tools back to Serverless to deal with this.</p>
|
||
<!-- raw HTML omitted -->
|
||
<p>There are a couple of testing tools I’d highly recommend. Cameras and
|
||
microphones behave very differently in emulators - sometimes they’re not even
|
||
simulated at all! This means we had to use real devices for our testing. We
|
||
used a <a href="https://www.vanamco.com/devicelab/">Vanamco Device Lab</a> along with five devices that we consider to
|
||
be a good spread. We use this alongside Ghostlab which allows us to have the
|
||
same page open on all devices and keep them in sync. It includes a web
|
||
inspector to tweak CSS and run debugging JS. Then to increase Android coverage
|
||
we used <a href="http://testdroid.com/">testdroid</a>. This is a brilliant service that allows you to
|
||
use real devices remotely. Open the camera app and you can see inside a data
|
||
centre. I’ve been eagerly awaiting a testdroid engineer to poke his head into a
|
||
rack, but have so far been disappointed!</p>
|
||
<p><img src="/images/guetta_article/image03.png" alt="AWS service architecture diagram" class="img-responsive">
|
||
<em>Testdroid using a Google Nexus 10</em></p>
|
||
<h2 id="wrapping-up">Wrapping Up</h2>
|
||
<p>Thanks again to James Hall for the inside view on CI, mobile testing, and
|
||
Unicode workarounds for all-Lambda backends. To learn more about the
|
||
Serverless Application Framework, check out their <a href="http://serverless.com">site</a> or their
|
||
<a href="https://gitter.im/serverless/serverless">gitter.im chatroom</a>.</p>
|
||
<p><strong>Disclosure:</strong> I have no relationship to Parallax Agency Ltd., but they build
|
||
cool projects and this interview covers just one of them.</p>
|
||
<p>Keep up with future posts via <a href="https://serverlesscode.com/index.xml">RSS</a>. If you have suggestions, questions, or
|
||
comments feel free to email me
|
||
<!-- raw HTML omitted --><a href="mailto:ryan@serverlesscode.com">ryan@serverlesscode.com</a><!-- raw HTML omitted -->.</p>
|
||
|
||
<hr />
|
||
<p>
|
||
<a href="https://twitter.com/share?url=https%3a%2f%2fserverlesscode.com%2fpost%2fdavid-guetta-online-recording-with-lambda%2f&text=New%20ServerlessCode%20article%2c%20%22Interview%3a%20Building%20the%20Latest%20Campaign%20for%20David%20Guetta%22%20via%20%40ryan_sb">Tweet this</a>,
|
||
<a href="https://news.ycombinator.com/submitlink?u=https%3a%2f%2fserverlesscode.com%2fpost%2fdavid-guetta-online-recording-with-lambda%2f&t=Interview%3a%20Building%20the%20Latest%20Campaign%20for%20David%20Guetta">send to Hackernews</a>, or
|
||
<a href="http://www.reddit.com/submit?url=https%3a%2f%2fserverlesscode.com%2fpost%2fdavid-guetta-online-recording-with-lambda%2f&title=Interview%3a%20Building%20the%20Latest%20Campaign%20for%20David%20Guetta%20on%20serverlesscode.com">post on Reddit</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
<hr>
|
||
|
||
<footer>
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||
<ul class="list-inline text-center">
|
||
<li>
|
||
<a href="/index.xml">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fa fa-circle fa-stack-2x"></i>
|
||
<i class="fa fa-rss fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://twitter.com/ryan_sb">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fa fa-circle fa-stack-2x"></i>
|
||
<i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://github.com/ryansb">
|
||
<span class="fa-stack fa-lg">
|
||
<i class="fa fa-circle fa-stack-2x"></i>
|
||
<i class="fa fa-github fa-stack-1x fa-inverse"></i>
|
||
</span>
|
||
</a>
|
||
</li>
|
||
|
||
</ul>
|
||
<p class="copyright text-muted">© 2016 Ryan Scott Brown. Code released under the Apache 2.0 license.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script src="/js/jquery-2.1.1.min.js"></script>
|
||
<script src="/js/bootstrap-3.3.6.min.js"></script>
|
||
<script src="/js/all.min.js?h=664ecf0284"></script>
|
||
<link rel="stylesheet" href="/css/font-awesome-4.5.0.min.css">
|
||
<script>hljs.initHighlightingOnLoad();</script>
|
||
|
||
</body>
|
||
|
||
</html>
|
||
|