Lightning Fast Deployment of
Your Rails-Backed Javascript App
Luke Melia, Yapp Labs
RailsConf Chicago
April 22nd, 2014
1
Slide 2
Slide 2 text
About this Rubyist
2
Slide 3
Slide 3 text
Based in New York & Seattle
3
Yapp Labs
!
Ember.js
Consulting & Training
Slide 4
Slide 4 text
4
Deploying my app
was driving me nuts!
Slide 5
Slide 5 text
5
Javascript App
JSON API
T&Cs page
Home page
Our app consisted of:
Slide 6
Slide 6 text
6
Our app consisted of:
Javascript App
JSON API
T&Cs page
Home page
Slide 7
Slide 7 text
7
Javascript App
JSON API
T&Cs page
Home page
Got changes?
Build and deploy everything!
Slide 8
Slide 8 text
Q: How long
does it take
to deploy a
Rails app?
8
Slide 9
Slide 9 text
Install dependencies. Boot app.
A: It takes at
least a few
minutes
to deploy a
Rails app. 9
Transfer lots of files.
Slide 10
Slide 10 text
10
Javascript App
JSON API
T&Cs page
Home page
I went days without deploying
anything but Javascript changes.
Slide 11
Slide 11 text
11
Javascript App
JSON API
T&Cs page
Home page
And waiting 5 minutes each time
I deployed static JS changes!
Slide 12
Slide 12 text
12
Javascript App
JSON API
T&Cs page
Home page
I wasn’t just annoying myself.
Our users had “hiccups” each deploy.
Slide 13
Slide 13 text
13
Javascript App
JSON API
T&Cs page
Home page
I wasn’t just annoying myself.
Our users had “hiccups? each deploy.
Slide 14
Slide 14 text
14
Slide 15
Slide 15 text
Downtime and other hiccups
during deploys
If your Rails app takes several seconds to boot,
no requests are getting served during that time.
Under high load and most architectures, those waiting
requests are queuing up, one behind the other.
End result: users can experience your site as non-
responsive / down while you are deploying and
a few seconds after.
15
Slide 16
Slide 16 text
Downtime and other hiccups
during deploys
Heroku has an experimental solution:
heroku labs:enable preboot
Starts up new servers (dynos) and then switches traffic
over after 3 minutes
16
Slide 17
Slide 17 text
Downtime and other hiccups
during deploys
Puma and Unicorn have facilities to restart one worker
at a time via signals sent to the master process.
HAProxy is another tool that can be useful.
Out of scope: zero-downtime migrations (find me later)
17
Slide 18
Slide 18 text
Downtime and other hiccups
during deploys
Issues with static assets and achieving
zero-downtime deploys are not often
discussed.
So let’s discuss them.
18
20
Initial request
Request: /index.html
Response: text/html
Asset files are typically “fingerprinted” and served with fingerprint-based
filenames and far future expires headers.
So this HTML response might contain:
Slide 21
Slide 21 text
21
Page is parsed, and then a short time later…
Request: /assets/app-abc123.js
Response: text/javascript
Slide 22
Slide 22 text
22
But during deployments, this can break down
index.html
/assets/app-abc123.js
index.html
/assets/app-def456.js
Request: /index.html
Response: text/html
HTML response contains:
Slide 23
Slide 23 text
23
…when traffic starts routing to the new app
index.html
/assets/app-abc123.js
index.html
/assets/app-def456.js
Request: /assets/app-abc123.js
Response: 404 Not Found
Slide 24
Slide 24 text
24
Slide 25
Slide 25 text
Hiccup-free: thinking about keeping
static assets working during deploys
Both old and new versions of
assets need to be available for
at least a few minutes during a
deploy.
25
Slide 26
Slide 26 text
Hiccup-free: thinking about keeping
static assets working during deploys
We could figure out how to do
this on our app servers, or
move assets elsewhere…
26
Slide 27
Slide 27 text
Hiccup-free: thinking about keeping
static assets working during deploys
If our static assets aren’t served
off of our app servers, we don’t
need to deploy asset-only
changes there, right?
27
Slide 28
Slide 28 text
28
Sketching out the idea
Static assets server
Rails server
Deploy Rails
app code
Dev or CI
Deploy
JS, CSS, images
What about the
HTML page?
Slide 29
Slide 29 text
Deployment & serving strategy:
factors to consider about HTML page
Points to fingerprinted JS/CSS but is not
fingerprinted itself
Contains JS URLs and code to boot JS app and
load CSS in the right order
Good place to provide environment-specific
configuration to Javascript
29
Slide 30
Slide 30 text
Deployment & serving strategy:
factors to consider about HTML page
When on the same domain as API, avoids CORS
complexity
Caching should be minimal to none in order to
allow for updates that take effect quickly
30
Slide 31
Slide 31 text
Conclusion about deploying and serving
HTML page
HTML Page should be managed and deployed as
part of static asset deployment process
HTML Page should be served by Rails, but
updates should not require re-deploying the
Rails app or restarting the Rails server
31
Slide 32
Slide 32 text
32
Sketching out the idea, II
Static assets server
Rails server
Deploy Rails
app code
Dev or CI
Deploy
JS, CSS, images
API requests
dynamic Rails pages
HTML for JS App
JS for JS App
CSS, Images for JS App
Deploy HTML?
Slide 33
Slide 33 text
33
Sketching out the idea, II
Rails servers
Dev or CI
Deploy HTML
Deploy HTML to filesystem of each server?
No, because disk is ephemeral in many deployment environments.
Slide 34
Slide 34 text
34
Sketching out the idea, II
Rails servers
Dev or CI
Deploy HTML
Deploy HTML to S3 and read from Rails servers?
Better, but S3 reads can be slow and we want this page fast.
Read
Slide 35
Slide 35 text
35
Sketching out the idea, II
Rails servers
Dev or CI
Deploy HTML
Deploy HTML to Redis and read from Rails servers?
Persistent, fast, and already in my environment. Yes!
Read
Slide 36
Slide 36 text
Deploy into redis. Serve out of redis via
Rails controller. 36
Slide 37
Slide 37 text
Deploy into redis. Serve out of redis via
Rails controller. 37
Slide 38
Slide 38 text
38
Refining the approach
Static assets server
Rails server
Deploy Rails
app code
Dev or CI
Deploy
JS, CSS, images
API requests
dynamic Rails pages
HTML for JS App
JS for JS App
CSS, Images for JS App
Deploy HTML
Slide 39
Slide 39 text
39
Refining the approach
Static assets server
(AWS S3)
Rails server
Dev or CI
Deploy
JS, CSS, images
(additive)
API requests
dynamic Rails pages
HTML for JS App
JS for JS App
CSS, Images for JS App
Deploy HTML
AWS Cloudfront
Deploy Rails
app code
Slide 40
Slide 40 text
Differential, additive deploy to S3
S3 can be slow for getting a list of files
Instead, generate a manifest file of current assets.
Compare them against the remote manifest and upload
only what is missing. Then update the remote manifest
with the difference. (Leave purging as TODO.)
https://github.com/yappbox/embarista/blob/master/lib/
embarista/s3sync.rb
40
Slide 41
Slide 41 text
Repository management
This architecture paves the way to manage your
Javascript app in one SCM repository, and your Rails
app in the other.
Each can be deployed and versioned independently.
Thinking of your Javascript app as a independent
client of your API works well!
41
Slide 42
Slide 42 text
Build tools for your Javascript app
With separate repositories and deployment
paths, you are free to choose best of breed
build tooling for your Javascript app.
Javascript build tools are seeing the most
attention and innovation right now.
42
Slide 43
Slide 43 text
43
Refining the approach
Static assets server
(AWS S3)
Rails server
Dev or CI
Deploy
JS, CSS, images
(additive)
API requests
dynamic Rails pages
HTML for JS App
JS for JS App
CSS, Images for JS App
Deploy HTML
AWS Cloudfront
Deploy Rails
app code
Slide 44
Slide 44 text
44
How fast is this in the real world?
Xfer HTML
2.38s
Xfer Assets
1.01s Build
6.55s
9.94 seconds
(real-world example project;
your results will be vary mostly on build time)
Preview: HTML goes into key based on
manifest. “current” key points to that key. 51
Slide 52
Slide 52 text
Preview: HTML goes into key based on
manifest. “current” key points to that key. 52
Slide 53
Slide 53 text
53
Dynamic
HTML Rewriting
Slide 54
Slide 54 text
Dynamic HTML Rewriting
As the HTML content passes through
the controller, we have the opportunity
to make adjustments.
54
Slide 55
Slide 55 text
55
Dynamic HTML Rewriting: Controller Example
Slide 56
Slide 56 text
Dynamic HTML Rewriting:
Other Use Cases
Adding CSRF tokens
Including dynamic analytics params
Embedding dynamic configuration
(e.g. feature flags)
56
Slide 57
Slide 57 text
57
A/B Testing
Slide 58
Slide 58 text
A/B Testing
I’ve experimented with two types of
A/B testing
Setting global flags based on A/B bucket
Serving up wholly different HTML based
on A/B bucket
58
Slide 59
Slide 59 text
59
A/B Testing: Controller Example 1
Slide 60
Slide 60 text
60
A/B Testing: Controller Example 2
Slide 61
Slide 61 text
Possibilities
Emerge
61
Slide 62
Slide 62 text
Thanks to my @YappLabs colleagues
who helped create this approach:
Kris Selden
Stefan Penner
Ray Cohen
62
We got ideas about this from rumors we heard about Square using a
similar approach. So thank you, nameless Square engineers.
Slide 63
Slide 63 text
Q&A
Some examples appear courtesy of my company.
Yapp Labs offers Ember.js consulting and training.
Creative Commons photo credits: https://www.flickr.com/photos/atelier_tee/109448791, https://www.flickr.com/photos/napdsp/12124061354
63
Follow me @lukemelia