Heroku Under The Hood - Django Under The Hood 2015

Heroku Under The Hood - Django Under The Hood 2015

2f5463832ccb768ccb4a1ca3607c27ef?s=128

Jacob Kaplan-Moss

November 07, 2015
Tweet

Transcript

  1. 2.

    Agenda 1. Performance tuning and dyno sizing 2. Buildpacks 3.

    app.json and the Heroku button 4. Platform API
  2. 4.

    Performance basics Which WSGI server should I use? Recommendations: gunicorn,

    uWSGI Worth trying: waitress, twisted Not even once: runserver How many workers should I run? Start with WEB_CONCURRENCY Tune appropriately (more on this later) How should I serve static assets? Small scale: use Whitenoise Large scale, option A: S3 + CDN Large scale, option B: Whitenoise + CDN
  3. 5.

    Dyno types Type RAM Compute Cost Free / Hobby 512

    MB 1-4x $0 / $7 Standard-1X 512 MB 1-4x $25 Standard-2X 1 GB 2x - 4x $50 Performance-M 2.5 GB 12x $250 Performance-L 14 GB 50x $500
  4. 6.

    Optimizing dyno types Assumption: your application is RAM-bound. Recommendation: optimize

    for cost per process. You’ll need to measure: RAM per process number of required processes Math: cost_per_process = (dyno_ram / process_ram) * dyno_cost H/T: https://medium.com/swlh/running-a-high-traffic-rails-app-on-heroku-s-performance-dynos-d9e6833d34c
  5. 7.

    Example 1 Dyno Processes / dyno Required # of dynos

    Total cost Cost / process 1X 4 5 $125 $6 2X 8 3 $150 $8 P-M 21 1 $250 $13 P-L 119 1 $500 $25 RAM per process: 120 Total processes: 20
  6. 8.

    Example 2 Dyno Processes / dyno Required # of dynos

    Total cost Cost / process 1X 1 60 $1,500 $25 2X 2 30 $1,500 $25 P-M 5 12 $3,000 $50 P-L 31 2 $1,000 $17 RAM per process: 450 Total processes: 60
  7. 9.

    Making measurements 1. Observe memory performance: log-runtime-metrics Heroku Dashboard Librato

    2. Load test Siege 
 https://www.joedog.org/siege-home/ Bees with Machine Guns 
 https://github.com/newsapps/beeswithmachineguns Load testing addons 
 https://elements.heroku.com/addons#testing
  8. 10.

    Exercise 1: performance tuning A. What’s your RAM per process?

    B. What’s your number of required processes? C. What’s your optimal dyno formation?
  9. 12.

    $ git push heroku master ... -----> Python app detected

    -----> Installing runtime (python-2.7.10) -----> Installing dependencies using pip ... What’s a buildpack?
  10. 13.

    How buildpacks work bin/detect — will this buildpack build this

    app? bin/compile — compile source into app bin/release — emit default config/addons
  11. 16.

    Multiple buildpacks Use cases: nginx / pgbouncer asset collection via

    Gulp/Grunt/etc. …? $ heroku buildpacks === aqueous-shore-5792 Buildpack URL heroku/python $ heroku buildpacks:add --index 2 heroku/nodejs Buildpack added. Next release on aqueous-shore-5792 will use: 1. heroku/python 2. heroku/nodejs Run `git push heroku master` to create a new release using these buildpacks.
  12. 17.

    Exercise 2: try out multi-buildpack A. Try out nginx or

    pgbouncer. 
 Do they improve performance? B. Try a Node asset manager:
 https://github.com/beaugunderson/django-gulp
  13. 19.

    $ cat app.json { "name": "Appointment Reminders (Django)", "description": "Appointment

    Reminders in Django with Twilio", "repository": "https://github.com/atbaker/appointment-reminders-django", "addons": [ "heroku-postgresql:hobby-dev", "redistogo:nano" ], "env": { "TWILIO_ACCOUNT_SID": { "description": "Your Twilio account secret ID", "value": "enter_your_account_sid_here", "required": true }, ... } } app.json
  14. 22.

    Exercise 3: app.json / PR apps A. Create an app.json

    (and Heroku button?) for your app. B. Try out pull request apps.
  15. 24.

    >>> import netrc, requests, json >>> token = netrc.netrc().hosts['api.heroku.com'][2] >>>

    h = requests.session() >>> h.headers['Authorization'] = 'Bearer ' + token >>> h.headers['Accept'] = 'application/vnd.heroku+json; version=3' >>> h.headers['Content-Type'] = 'application/json' >>> formation = h.get('https://api.heroku.com/apps/NAME/formation').json() >>> {f['type']: f['quantity'] for f in formation} {'web': 1, 'worker': 0} >>> payload = {'quantity': 4, 'size': 'standard-1X'} >>> h.patch('https://api.heroku.com/apps/NAME/formation/web', data=json.dumps(payload)) >>> formation = h.get('https://api.heroku.com/apps/NAME/formation').json() >>> {f['type']: f['quantity'] for f in formation} {'web': 4, 'worker': 0} Intro
  16. 25.

    Authentication Authentication uses bearer tokens: For local dev and/or fooling

    around, reading a token from .netrc is fine. Better: direct authentication with token exchange: Best: proper use of OAuth + scopes: 
 https://devcenter.heroku.com/articles/oauth >>> h.post('https://api.heroku.com/oauth/authorizations', ... data=json.dumps({'description': 'demo auth'})).json() {'access_token': {'expires_in': None, 'id': '4d6e04f0-693b-4118-bb9b-646e723ff7fa', 'token': ‘4a1637b4-9bc8-4ad9-8747-43ec7c744621'}, ...} >>> h.headers['Authorization'] = 'Bearer ' + token
  17. 26.

    Build & Slug APIs Deploy to Heroku without git push

    heroku master! To build without releasing, use a build app and copy slugs:
 https://devcenter.heroku.com/articles/platform-api-copying-slugs You can also create slugs from scratch: 
 https://devcenter.heroku.com/articles/platform-api-deploying-slugs >>> source = h.post('https://api.heroku.com/apps/aqueous-shore-5792/sources').json() >>> requests.put(source['source_blob']['put_url'], data=open('foo.tgz').read()) >>> payload = {'source_blob': {'url': source['source_blob']['get_url'], ... 'version': 'abcd1234'}} >>> build = h.post('https://api.heroku.com/apps/aqueous-shore-5792//builds', 
 data=json.dumps(payload)).json()
  18. 27.

    Exercise 4: platform API A. Build an auto-scaler. B. Build

    an alternate deploy flow. C. Re-create pull-request apps (!)