Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Heroku 102
Search
Rhys Elsmore
June 18, 2014
Programming
210
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Heroku 102
Rhys Elsmore
June 18, 2014
More Decks by Rhys Elsmore
See All by Rhys Elsmore
How to Lose Friends and Influence Burnout
rhyselsmore
0
85
Other Decks in Programming
See All in Programming
OSもどきOS
arkw
0
530
AIチームを指揮するOSS「TAKT」活用術 / How to Use “TAKT,” an OSS Tool for Orchestrating AI Teams
nrslib
6
880
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
3
1.3k
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
390
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
160
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
150
Modding RubyKaigi for Myself
yui_knk
0
920
These Five Tricks Can Make Your Apps Greener, Cheaper, & Nicer
hollycummins
0
280
技術記事、 専門家としてのプログラマ、 言語化
mizchi
4
2.8k
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
13
3.6k
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
260
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
120
Featured
See All Featured
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
320
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.8k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
390
Design in an AI World
tapps
1
240
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Site-Speed That Sticks
csswizardry
13
1.2k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.4k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.5k
Are puppies a ranking factor?
jonoalderson
1
3.5k
The Invisible Side of Design
smashingmag
302
52k
Building AI with AI
inesmontani
PRO
1
1.1k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
340
Transcript
[email protected]
102 @rhyselsmore
@rhyselsmore >>> import rhyselsmore Platform Security Engineer at Heroku. I
focus on Anti-Abuse Systems. I develop using a mixture of Python/Go.
@rhyselsmore Heroku is a Platform as a Service
@rhyselsmore You build your application, not infrastructure
@rhyselsmore Deploying is as easy as ‘git push’
@rhyselsmore Scaling and upgrading is simple
@rhyselsmore tl;dr: Spend time on your application’s code
@rhyselsmore 3,000,000+ apps created. 130+ add-ons services. 90,000+ requests per
second. 500,000+ log entries per second.
@rhyselsmore Run anything* on Heroku
@rhyselsmore Python & Ruby & PHP & Java & NodeJS
@rhyselsmore Play & Scala & Clojure & Gradle & Grails
& C & Common Lisp & Dart & Elixir & Erlang & Go & Haskell & JRuby & Lua & Perl & Rust & …
@rhyselsmore
@rhyselsmore The Twelve-Factor App 12factor.net
@rhyselsmore The Twelve-Factor Flask App 12factor.net
@rhyselsmore Lets assume that You want to learn about Heroku
You know (a bit) of Python You have Python, pip, and can create a virtualenv
@rhyselsmore Lets assume that You want to learn about Heroku
You know (a bit) of Python You have Python, pip, and can create a virtualenv install.python-guide.org
@rhyselsmore Get an account at heroku.com
@rhyselsmore Install the Heroku Toolbelt toolbelt.heroku.com
@rhyselsmore $ heroku login Login
@rhyselsmore The Twelve-Factor App 12factor.net
@rhyselsmore $ mkdir heroku101 $ cd heroku101 $ git init
@rhyselsmore $ heroku create Creating myapp... done, stack is cedar
http://myapp.herokuapp.com/ |
[email protected]
:myapp.git Git remote heroku added
@rhyselsmore $ cat .git/config [core] repositoryformatversion = 0 filemode =
true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "heroku"] url =
[email protected]
:myapp.git fetch = +refs/heads/*:refs/remotes/heroku/*
@rhyselsmore $ virtualenv venv $ pip install flask gunicorn $
pip freeze > requirements.txt
@rhyselsmore $ cat requirements.txt Flask==0.10.1 Jinja2==2.7.3 MarkupSafe==0.23 Werkzeug==0.9.6 gunicorn==19.0.0 itsdangerous==0.24
wsgiref==0.1.2
@rhyselsmore import os! from flask import Flask! ! app =
Flask(__name__)! ! @app.route('/')! def index():! return "Hello, World!" heroku101.py
@rhyselsmore web: gunicorn heroku101:app Procfile
@rhyselsmore $ foreman start 21:51:53 web.1 | started with pid
20276 Launch your application locally
@rhyselsmore Visit http://localhost:5000/ in your browser.
@rhyselsmore venv *.pyc .gitignore
@rhyselsmore $ git add . $ git commit -m ‘initial
commit’ Store our changes in git
@rhyselsmore Now, lets send it up to Heroku
@rhyselsmore $ git push heroku master ! -----> Python app
detected -----> Preparing Python runtime (python-2.7.7) -----> Installing Setuptools (3.6) -----> Installing Pip (1.5.6) ! …. ! -----> Discovering process types Procfile declares types -> web ! -----> Compressing... done, 31.6MB -----> Launching... done, v3 http://myapp.herokuapp.com/ deployed to Heroku ! To
[email protected]
:myapp.git * [new branch] master -> master
@rhyselsmore $ heroku open Check out our new application
@rhyselsmore Codebase Recap Dependencies Dev/Prod Parity Port Binding Processes
@rhyselsmore Codebase Recap Dependencies Dev/Prod Parity Port Binding Processes
@rhyselsmore I. Codebase One codebase tracked in revision control, many
deploys
@rhyselsmore Codebase Recap Dependencies Dev/Prod Parity Port Binding Processes
@rhyselsmore II. Dependencies Explicitly declare and isolate dependencies
@rhyselsmore Codebase Recap Dependencies Dev/Prod Parity Port Binding Processes
@rhyselsmore X. Dev/Prod Parity Keep development, staging, and production as
similar as possible
@rhyselsmore Codebase Recap Dependencies Dev/Prod Parity Port Binding Processes
@rhyselsmore VII. Port Binding Export services via port binding
@rhyselsmore Codebase Recap Dependencies Dev/Prod Parity Port Binding Processes
@rhyselsmore VI. Processes Execute the app as one or more
stateless processes
@rhyselsmore Runs processes concurrently locally via a Procfile Foreman Exposes
a configuration to our processes Creates parity between production and development
@rhyselsmore web: gunicorn heroku101:app Procfile
@rhyselsmore web: gunicorn heroku101:app worker: bin/celeryd worker clock: bin/clock monitor:
bin/monitor Procfile
@rhyselsmore Runs processes concurrently locally via a Procfile Foreman Exposes
a configuration to our processes Creates parity between production and development
@rhyselsmore VIII. Concurrency Scale out via the process model
@rhyselsmore web
@rhyselsmore $ heroku ps:scale web=6
@rhyselsmore web web web web web web
@rhyselsmore web: gunicorn heroku101:app worker: bin/celeryd worker clock: bin/clock monitor:
bin/monitor Procfile
@rhyselsmore $ heroku ps:scale clock=1 monitor=1 worker=6
@rhyselsmore web web web web web worker worker worker worker
worker clock monitor web worker
@rhyselsmore $ heroku ps:scale monitor=1:2X
@rhyselsmore web web web web web worker worker worker worker
worker clock ! monitor web worker
@rhyselsmore $ heroku ps:scale worker=1:PX
@rhyselsmore web web web web web clock ! monitor web
! ! ! ! ! worker ! ! ! !
@rhyselsmore $ heroku ps:scale clock=0
@rhyselsmore web web web web web ! monitor web !
! ! ! ! worker ! ! ! !
@rhyselsmore Runs processes concurrently locally via a Procfile Foreman Exposes
a configuration to our processes Creates parity between production and development
@rhyselsmore III. Config Store config in the environment
@rhyselsmore import os! from flask import Flask! ! app =
Flask(__name__)! ! @app.route('/')! def index():! name = os.environ.get('PAGE_NAME', 'Rhys')! return "Hello, {0}!".format(name)! heroku101.py
@rhyselsmore PAGE_NAME=‘Heroku User’ .env (Be sure to add ‘.env’ to
.gitignore)
@rhyselsmore $ foreman start 21:51:53 web.1 | started with pid
20276 Launch your application locally. Did it work?
@rhyselsmore $ git add heroku101.py $ git commit -m ‘loading
name from configuration’ $ git push heroku master … $ heroku open Commit our changes, and push to Heroku
@rhyselsmore Now, lets set our configuration on Heroku.
@rhyselsmore $ heroku config:set PAGE_NAME=‘App User’ $ heroku open Launch
your application locally. Did it work?
@rhyselsmore V. Build, Release, Run Strictly separate build and run
stages
@rhyselsmore $ heroku releases === myapp v5 Set PAGE_NAME config
vars
[email protected]
2014/06/17 22:49:44 v4 Deploy 0b7df5b
[email protected]
2014/06/17 22:49:33 v3 Deploy f6fc054
[email protected]
2014/06/17 22:00:32 v2 Enable Logplex
[email protected]
2014/06/17 21:10:58 v1 Initial release
[email protected]
2014/06/17 21:10:57 Viewing your Releases
@rhyselsmore $ heroku rollback v4 Rolling back myapp... done, v4
! Warning: rollback affects code and config vars; it doesn't add or remove addons. To undo, run: heroku rollback v5 Rolling back to another Release
@rhyselsmore IV. Backing Services Treat backing services as attached resources
@rhyselsmore ! app ! postgres ! redis ! s3 !
outbound mail
@rhyselsmore ! app ! redis Dev ! app ! redis
Prod redis://my.production.redis redis://localhost
@rhyselsmore X. Dev/Prod Parity Keep development, staging, and production as
similar as possible
@rhyselsmore ! app ! sqlite Dev ! app ! postgres
Prod
@rhyselsmore CREATE TABLE ips ( id serial primary key, ip
inet not null …
@rhyselsmore CREATE TABLE ips ( id serial primary key, ip
inet not null …
@rhyselsmore ! app ! postgres Dev ! app ! postgres
Prod
@rhyselsmore Homebrew (http://brew.sh) Vagrant (http://vagantup.com) Docker (http://docker.io) Some Solutions Postgres.app
(http://postgresapp.com)
@rhyselsmore
@rhyselsmore $ pip install redis $ pip freeze > requirements.txt
@rhyselsmore import os! from flask import Flask! import redis! !
app = Flask(__name__)! db = redis.from_url(os.environ.get('REDISCLOUD_URL'))! ! ! @app.route('/name/<name>')! def setname(name):! db.set('PAGE_NAME', name)! return "Name successfully updated"! ! ! @app.route('/')! def index():! name = db.get('PAGE_NAME') or "Rhys"! return "Hello, {0}!".format(name)! heroku101.py
@rhyselsmore $heroku addons:add rediscloud Adding rediscloud on myapp... done, v7
(free) Use `heroku addons:docs rediscloud` to view documentation. Add RedisCloud
@rhyselsmore $ heroku config REDISCLOUD_URL=xxxxxxx PAGE_NAME=Rhys Addons are exposed to
your application as config vars
@rhyselsmore $ git add heroku101.py requirements.txt $ git commit -m
‘using redis as our db store’ $ git push heroku master … $ heroku open Commit our changes, and push to Heroku
@rhyselsmore XI. Logs Treat logs as event streams
@rhyselsmore $ heroku logs —tail
@rhyselsmore 2014-06-17T16:03:07.702584+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=7be32154-cbca-4e9c-b235-2a86a204f32d fwd="50.31.252.204" dyno=web.1
connect=1ms service=3ms status=200 bytes=172 2014-06-17T16:03:21.624306+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=180ee5c1-5c7e-4370-925c-4532bab44bb1 fwd="50.31.252.204" dyno=web.1 connect=1ms service=7ms status=200 bytes=172 2014-06-17T16:03:26.160610+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=71337d5d-ca00-49ed-98f6-e007123142de fwd="50.31.252.204" dyno=web.1 connect=0ms service=2ms status=200 bytes=172 2014-06-17T16:03:31.690643+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=b156826f-6db3-480d-bcc0-34239b2ad5a2 fwd="50.31.252.204" dyno=web.1 connect=1ms service=3ms status=200 bytes=172 2014-06-17T16:03:33.123754+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=a3768595-4fa5-41b7-aa2b-24ac4fa81880 fwd="50.31.252.204" dyno=web.1 connect=1ms service=3ms status=200 bytes=172 2014-06-17T16:03:34.186475+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=2e02b838-bdd6-47b2-b9c9-cd4d999ef1bc fwd="50.31.252.204" dyno=web.1 connect=0ms service=30ms status=200 bytes=172 2014-06-17T16:03:38.245823+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=c50b652a-1632-4727-b27a-03b0e89cdc4f fwd="50.31.252.204" dyno=web.1 connect=8ms service=30ms status=200 bytes=172 2014-06-17T16:03:48.101998+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.herokuapp.com request_id=94bd1cb4-446e-4309-9a67-94b210cdc3b2 fwd="50.31.252.204" dyno=web.1 connect=1ms service=2ms status=200 bytes=172 2014-06-17T16:03:49.663855+00:00 heroku[router]: at=info method=GET path="/" host=whispering-lake-5026.he
@rhyselsmore XI. Logs Treat logs as event streams
@rhyselsmore $ heroku labs:enable log-runtime-metrics Enabling log-runtime-metrics for myapp... done
log-runtime-metrics
@rhyselsmore log2viz.herokuapp.com
@rhyselsmore $ heroku addons:add librato $ heroku addons:open librato Librato
@rhyselsmore
@rhyselsmore $ pip install log-metrics $ pip freeze > requirements.txt
@rhyselsmore import os! from flask import Flask! import log_metrics, random,
time! ! app = Flask(__name__)! ! ! @app.route('/')! def index():! with log_metrics.timer('timed-function'):! time.sleep(random.uniform(0.5, 1.5))! log_metrics.increment('requests')! return "Hello, Librato!"! heroku101.py
@rhyselsmore 00:18:20 web.1 | measure#timed-function.ms=1261.87 00:18:20 web.1 | count#requests=1 00:18:24
web.1 | measure#timed-function.ms=925.83 00:18:24 web.1 | count#requests=1
@rhyselsmore
@rhyselsmore $ heroku addons:add papertrail $ heroku addons:add loggly Log
Retention
@rhyselsmore $ heroku drains:add https://my-drain-app.com Log Drains
@rhyselsmore $ heroku domains:add mycustomdomain.com Custom Domains
@rhyselsmore The Heroku Platform API
@rhyselsmore >>> import requests! >>> import json! ! >>> heroku
= requests.session()! >>> heroku.auth = ('', '<my API token>')! >>> heroku.headers.update({! "Accept": "application/vnd.heroku+json; version=3",! "Content-Type": "application/json"! })! ! >>> heroku.get('/apps').json()! {! "id":49939049,! "name":"my-app-1",! "dynos":0,! "workers":0,! "repo_size":null,! "slug_size":null,! "stack":"cedar",! "requested_stack":null,
@rhyselsmore >>> heroku.get('/apps/myapp/config-vars').json()! {! "NAME": "My App",! "SECRET_KEY": "hunter2"! }
>>> heroku.patch(‘/apps/myapp/config-vars’, json.dumps({! ! "NAME": "My Apps new config var",! }))
@rhyselsmore Thanks!
@rhyselsmore Further Questions… ! SSL Endpoints? File Storage? Multiple Languages?
Free Limits? Does an add-on for $task exist? EU vs US region? Show us how to…