Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lightning Fast Deployment of Your Rails-Backed Javascript App

Lightning Fast Deployment of Your Rails-Backed Javascript App

Are you restarting your Rails server every time you deploy JavaScript? Are you waiting 5 minutes or more to deploy static JavaScript? Stop! We were able to cut our JavaScript front-end deployment times from more than 5 minutes to less than 15 seconds with zero downtime. As a bonus, we can preview new releases on production before making them live. I will share all the details of an approach you can implement on your Rails-backed Javascript app to make deploying JavaScript updates a joy. Video: https://www.youtube.com/watch?v=QZVYP3cPcWQ

Luke Melia

April 22, 2014
Tweet

More Decks by Luke Melia

Other Decks in Technology

Transcript

  1. Lightning Fast Deployment of

    Your Rails-Backed Javascript App
    Luke Melia, Yapp Labs
    RailsConf Chicago
    April 22nd, 2014
    1

    View Slide

  2. About this Rubyist
    2

    View Slide

  3. Based in New York & Seattle
    3
    Yapp Labs
    !
    Ember.js
    Consulting & Training

    View Slide

  4. 4
    Deploying my app
    was driving me nuts!

    View Slide

  5. 5
    Javascript App
    JSON API
    T&Cs page
    Home page
    Our app consisted of:

    View Slide

  6. 6
    Our app consisted of:
    Javascript App
    JSON API
    T&Cs page
    Home page

    View Slide

  7. 7
    Javascript App
    JSON API
    T&Cs page
    Home page
    Got changes?

    Build and deploy everything!

    View Slide

  8. Q: How long

    does it take

    to deploy a
    Rails app?
    8

    View Slide

  9. Install dependencies. Boot app.
    A: It takes at
    least a few
    minutes

    to deploy a
    Rails app. 9
    Transfer lots of files.

    View Slide

  10. 10
    Javascript App
    JSON API
    T&Cs page
    Home page
    I went days without deploying
    anything but Javascript changes.

    View Slide

  11. 11
    Javascript App
    JSON API
    T&Cs page
    Home page
    And waiting 5 minutes each time

    I deployed static JS changes!

    View Slide

  12. 12
    Javascript App
    JSON API
    T&Cs page
    Home page
    I wasn’t just annoying myself.
    Our users had “hiccups” each deploy.

    View Slide

  13. 13
    Javascript App
    JSON API
    T&Cs page
    Home page
    I wasn’t just annoying myself.
    Our users had “hiccups? each deploy.

    View Slide

  14. 14

    View Slide

  15. 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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. 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

    View Slide

  19. 19
    Initial request
    Request: /index.html
    Response: text/html

    View Slide

  20. 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:

    <br/>

    View Slide

  21. 21
    Page is parsed, and then a short time later…
    Request: /assets/app-abc123.js
    Response: text/javascript

    View Slide

  22. 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:

    <br/>

    View Slide

  23. 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

    View Slide

  24. 24

    View Slide

  25. 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

    View Slide

  26. 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

    View Slide

  27. 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

    View Slide

  28. 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?

    View Slide

  29. 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

    View Slide

  30. 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

    View Slide

  31. 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

    View Slide

  32. 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?

    View Slide

  33. 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.

    View Slide

  34. 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

    View Slide

  35. 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

    View Slide

  36. Deploy into redis. Serve out of redis via
    Rails controller. 36

    View Slide

  37. Deploy into redis. Serve out of redis via
    Rails controller. 37

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. 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

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. 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)

    View Slide

  45. Possibilities
    Emerge
    45

    View Slide

  46. 46
    Preview

    View Slide

  47. ➜ cd yapp-prefs; rake dist
    Build complete. To deploy, run rake deploy:assets[b35b97f3]
    ➜ rake deploy:assets[b35b97f3]
    yapp-assets -> prefs/yapp-prefs.min-530b21e2.js
    yapp-assets -> prefs/yapp-prefs.min-ab0d7f5e.css
    yapp-assets -> prefs-manifest-latest.yml
    yapp-assets -> b35b97f3.yml
    YAPP_ENV=qa|prod rake deploy:generate_index[b35b97f3]
    ➜ YAPP_ENV=qa rake deploy:generate_index[b35b97f3]
    redis.set('prefs:index:b35b97f3', '..')
    To preview: https://www.yappqa.us/prefs/?manifest_id=b35b97f3
    To activate: YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    ➜ YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    redis.set('prefs:index:current', 'b35b97f3')
    47
    Preview: sample deployment session
    Build

    View Slide

  48. ➜ cd yapp-prefs; rake dist
    Build complete. To deploy, run rake deploy:assets[b35b97f3]
    ➜ rake deploy:assets[b35b97f3]
    yapp-assets -> prefs/yapp-prefs.min-530b21e2.js
    yapp-assets -> prefs/yapp-prefs.min-ab0d7f5e.css
    yapp-assets -> prefs-manifest-latest.yml
    yapp-assets -> b35b97f3.yml
    YAPP_ENV=qa|prod rake deploy:generate_index[b35b97f3]
    ➜ YAPP_ENV=qa rake deploy:generate_index[b35b97f3]
    redis.set('prefs:index:b35b97f3', '..')
    To preview: https://www.yappqa.us/prefs/?manifest_id=b35b97f3
    To activate: YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    ➜ YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    redis.set('prefs:index:current', 'b35b97f3')
    48
    Preview: sample deployment session
    Upload assets to S3

    View Slide

  49. ➜ cd yapp-prefs; rake dist
    Build complete. To deploy, run rake deploy:assets[b35b97f3]
    ➜ rake deploy:assets[b35b97f3]
    yapp-assets -> prefs/yapp-prefs.min-530b21e2.js
    yapp-assets -> prefs/yapp-prefs.min-ab0d7f5e.css
    yapp-assets -> prefs-manifest-latest.yml
    yapp-assets -> b35b97f3.yml
    YAPP_ENV=qa|prod rake deploy:generate_index[b35b97f3]
    ➜ YAPP_ENV=qa rake deploy:generate_index[b35b97f3]
    redis.set('prefs:index:b35b97f3', '..')
    To preview: https://www.yappqa.us/prefs/?manifest_id=b35b97f3
    To activate: YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    ➜ YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    redis.set('prefs:index:current', 'b35b97f3')
    49
    Preview: sample deployment session
    Add HTML

    to redis

    View Slide

  50. ➜ cd yapp-prefs; rake dist
    Build complete. To deploy, run rake deploy:assets[b35b97f3]
    ➜ rake deploy:assets[b35b97f3]
    yapp-assets -> prefs/yapp-prefs.min-530b21e2.js
    yapp-assets -> prefs/yapp-prefs.min-ab0d7f5e.css
    yapp-assets -> prefs-manifest-latest.yml
    yapp-assets -> b35b97f3.yml
    YAPP_ENV=qa|prod rake deploy:generate_index[b35b97f3]
    ➜ YAPP_ENV=qa rake deploy:generate_index[b35b97f3]
    redis.set('prefs:index:b35b97f3', '..')
    To preview: https://www.yappqa.us/prefs/?manifest_id=b35b97f3
    To activate: YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    ➜ YAPP_ENV=qa rake "deploy:set_current_index[b35b97f3]"
    redis.set('prefs:index:current', 'b35b97f3')
    50
    Preview: sample deployment session
    Activate

    redis key

    View Slide

  51. Preview: HTML goes into key based on
    manifest. “current” key points to that key. 51

    View Slide

  52. Preview: HTML goes into key based on
    manifest. “current” key points to that key. 52

    View Slide

  53. 53
    Dynamic

    HTML Rewriting

    View Slide

  54. Dynamic HTML Rewriting
    As the HTML content passes through
    the controller, we have the opportunity
    to make adjustments.
    54

    View Slide

  55. 55
    Dynamic HTML Rewriting: Controller Example

    View Slide

  56. Dynamic HTML Rewriting:

    Other Use Cases
    Adding CSRF tokens
    Including dynamic analytics params
    Embedding dynamic configuration

    (e.g. feature flags)
    56

    View Slide

  57. 57
    A/B Testing

    View Slide

  58. 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

    View Slide

  59. 59
    A/B Testing: Controller Example 1

    View Slide

  60. 60
    A/B Testing: Controller Example 2

    View Slide

  61. Possibilities
    Emerge
    61

    View Slide

  62. 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.

    View Slide

  63. 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

    View Slide