Note – instructions assume Ubuntu Linux.
See Getting Started with Python on Heroku (Flask) for the official instructions. The instructions below tackle things differently and include redis-specific steps.
Don’t need postgresql for my app even though needed for heroku demo app. Using redis for simple key-value store.
Main reason for each step is indicated in bold at start. There are lots of steps but there are lots of things being achieved. And each purpose only requires a few steps so probably hard to streamline any further.
- APP & BEST PRACTICE
>> sudo apt-get install python3 python3-pip python-virtualenv git ruby redis-server redis-tools - HEROKU
Get free heroku account - HEROKU
Install heroku toolbelt Heroku setup. Sets up virtualenvwrapper for you too (one less thing to figure out) - HEROKU
Do the once-ever authentication
>> heroku login - APP
Make project folder e.g.
>> mkdir ~/projects/myproj - APP
>> cd ~/projects/myproj - HEROKU
>> echo “web: python main.py” > Procfile - HEROKU & BEST PRACTICE
>> git init - HEROKU & BEST PRACTICE
>> mkvirtualenv stickySo requirements for specific project can be separated from other project – lets heroku identify actual requirements. Normally “workon sticky” thereafter; deactivate to exit virtual env
- APP
>> pip3 install flask
Note – installed within virtualenv - HEROKU
Save the following as requirements.txt – needed by heroku so it knows the dependencies. Update version of redis as appropriate. gunicorn is a better approach than the flask test server
flask
gunicorn
redis==2.10.3
- HEROKU
So we can use Python 3.4 instead of the current default of 2.7:
>> echo “python-3.4.3” > runtime.txt - APP & HEROKU
Make a toy app to get started from.
Note – modify the standard demo flask app to add a port to ease eventual heroku deployment. Otherwise the app will fail because of a problem with the port when running
heroku ps:scale web=1
Starting process with command `python main.py`
...
Web process failed to bind to $PORT within 60 seconds of launchHere is an example (may need updating if flask changes):
import os
from flask import Flask
app = Flask(__name__)@app.route("/")
def hello():
return "Hello World!"if __name__ == "__main__":
port = int(os.environ.get("PORT", 33507))
app.run(host='0.0.0.0', port=port) - BEST PRACTICE
>> deactivate -
Make a module to make it easier to work with redis – let’s call it store.py:
import os
import urllib
import redisurl = urllib.parse.urlparse(os.environ.get('REDISTOGO_URL',
'redis://localhost:6379'))
redis = redis.Redis(host=url.hostname, port=url.port, db=0,
password=url.password)
We can then use redis like this:
from store import redis
- APP
Keep building app locally. The following is good for redis: Redis docs. And flasks docs are always good: Flask Docs – Minimal Application - HEROKU & BEST PRACTICE
Before deploying to production:
- Update git otherwise you’ll be deploying old code – heroku uses git for deployment
- set app.debug to False (although no rush when just getting started and not expecting the app to get hit much)
- probably switch to gunicorn sooner or later (will need to change ProcFile to
web: gunicorn main:app --workers $WEB_CONCURRENCY
) -
Example nginx.conf:
# As long as /etc/nginx/sites-enable/ points to
# this conf file nginx can use it to work with
# the server_name defined (the name of the file
# doesn't matter - only the server_name setting)
# sudo ln -s /home/vagrant/src/nginx.conf ...
# ... /etc/nginx/sites-enabled/myproj.com
# Confirm this link is correct
# e.g. less /etc/nginx/sites-enabled/myproj.comserver {
listen 80;
server_name localhost;location /static { # static content is
# handled directly by NGINX which means
# the nginx user (www-data) will need
# read permissions to this folder
root /home/vagrant/src;
}location / { # all else passed to Gunicorn to handle
# Pass to wherever I bind Gunicorn to serve to
# Only gunicorn needs rights to read, write,
# and execute scripts in the app folders
proxy_pass http://127.0.0.1:8888;
}
}
-
Example gunicorn.conf
import multiprocessing
bind = "127.0.0.1:8888" # ensure nginx passes to this port
logfile = "/home/vagrant/gunicorn.log"
workers = multiprocessing.cpu_count() * 2 + 1
- HEROKU
>> heroku createShould now be able to browse to the url supplied as stdout fom command e.g.
https://not-real-1234.herokuapp.com/. Note – not working yet – still need to deploy to new app>> git push heroku master
Must then actually spin up the app:
>> heroku ps:scale web=1
A shortcut for opening is
>> heroku open
- HEROKU
Add redis support (after first deployment – otherwise
)
! No app specified.
! Run this command from an app folder or specify which app to use with --app APP.
>> heroku addons:create redistogoNote – need to register credit card to use any add-ons, even if free ones. Go to https://heroku.com/verify
Some other points: when developing on a different machine, I needed to supply my public key to heroku from that other machine (Permission denied (publickey) when deploying heroku code. fatal: The remote end hung up unexpectedly).
heroku keys:add ~/.ssh/id_rsa.pub
And the full sequence for upgrading your app after the prerequisites have been fulfilled is:
- git commit to local repo
- Then git push to heroku
- Then run
heroku ps:scale web=1
again
And I had a problem when I switched from Python 2 to 3 with redis – my heroku push wouldn’t work. By looking at the logs (>> heroku logs –tail) I found that import imap wouldn’t work and searching on that generally found I needed a newer version of redis than I had specified foolishly in requirements.txt.