Deploying Pyramid to Heroku¶
In which we learn how to make a simple Pyramid application run in the Heroku environment.
Heroku’s Environment¶
Heroku is a great system for getting web applicatons up and running fast. But to work with it, you have to make sure that your application meets with Heroku’s expectations. Let’s take a moment to walk through setting up a simple Pyramid application to run on Heroku.
Assumptions¶
This quick tutorial assumes that you have:
- Created an account with Heroku
- Installed the Heroku Toolbelt
- Authenticated the toolbelt via heroku login or similar
Create a Simple App¶
Let’s begin by creating a simple Pyramid application.
First, make a project directory then create and activate a virtual environment in that directory:
$ mkdir pyramid_starter
$ cd pyramid_starter
$ python3 -m venv ./
$ . bin/activate
(pyramid_starter)$ pip install -U pip setuptools
Next, install pyramid:
(pyramid-heroku-demo)$ pip install pyramid
...
Successfully installed PasteDeploy-1.5.2 WebOb-1.6.1 pyramid-1.7 repoze.lru-0.6
translationstring-1.3 venusian-1.0 zope.deprecation-4.1.2 zope.interface-4.2.0
Use the pcreate
command to create a simple application using the starter
scaffold:
(pyramid-heroku-demo)$ pcreate -s starter demoapp
...
Welcome to Pyramid. Sorry for the convenience.
Install the new application so that you can test that it works:
(pyramid_starter)$ pip install -e .
Obtaining file:///Users/cewing/Desktop/pyramid_starter/demoapp
...
Successfully installed Chameleon-2.24 Mako-1.0.4 MarkupSafe-0.23 Pygments-2.1.3
demoapp pyramid-chameleon-0.3 pyramid-debugtoolbar-3.0.4 pyramid-mako-1.0.2 waitress-0.9.0
Now, test the application by starting it up and loading http://localhost:6543
(pyramid_starter)$ pserve development.ini
Starting server in PID 6795.
serving on http://127.0.0.1:6543
If you see the nice, cranberry-colored front page of the starter app, you’re all set so far. Go ahead and quit the pyramid server.
Put it in Git¶
The application deployment story for Heroku is tightly coupled to git. It is not tightly coupled to GitHub. We’ll use GitHub here because it is familiar. But the same process will work for GitLab, BitBucket, or whatever.
Start by initializing a new git repository in your application root:
(pyramid_starter)$ git init
Initialized empty Git repository in /Users/cewing/Desktop/pyramid_starter/demoapp/.git/
Set up a .gitignore
file to ignore whatever you don’t want:
(pyramid_starter)$ touch .gitignore
(pyramid_starter)$ echo "*.py[cod]" > .gitignore
(pyramid_starter)$ echo "__pycache__" >> .gitignore
(pyramid_starter)$ echo "*.egg-info" >> .gitignore
(pyramid_starter)$ more .gitignore
*.py[cod]
__pycache__
*.egg-info
Now, add the .gitignore
file and then the rest of your files to your git repository:
(pyramid_starter)$ git add .
(pyramid_starter)$ git commit -m "adding a simple starter app for demo purposes"
At this point, your application is a git repository. It isn’t connected to any remote repository, like GitHub, GitLab, or BitBucket. But that doesn’t really matter for this demo. Let’s go on.
Build Heroku Needs¶
Now that we have an application in a git repository, we are ready to integrate Heroku.
Tell Heroku This is Python¶
Heroku uses a series of heuristics to determine what type of application you have.
The primary heuristic for a Python app is the presence of a requirements.txt
(note: NOT requirements.pip
) file
Let’s create that file and add it to our repository:
(pyramid_starter)$ pip freeze > requirements.txt
The file that was created will contain a reference to your demoapp
.
However, you don’t actually want to install this with pip when on heroku.
So edit the requirements.txt
file to remove these lines:
## !! Could not determine repository location
demoapp==0.0
Add that file to your git repository and commit:
(pyramid_starter)$ git add requirements.txt
(pyramid_starter)$ git commit -m "adds requirements file so Heroku knows it is a Python app"
Tell Heroku How to Run Your App¶
Heroku requires a plain text file called Procfile
(spelling and capitalization count).
This file tells Heroku what to do to run your application.
Add this file to your repository, containing the text web: ./run
:
(pyramid_starter)$ echo web: ./run > Procfile
(pyramid_starter)$ git add Procfile
(pyramid_starter)$ git commit -m "Tells Heroku how to run my app"
Now Heroku is going to look for an executable script by the name run
in our application’s root directory.
We need to make that file.
We’d like it to install our application and then start up a server to serve it.
Create the file run
in the demoapp
directory.
Then type the following text into it:
#!/bin/bash
set -e
python setup.py develop
python runapp.py
This script tells the Heroku server to use the bash
shell (#!/bin/bash
).
It says that if any part of the script returns an error, it should exit the script (set -e
).
It then installs our application in develop mode (equivalent to running pip install -e .
).
Finally, it executes a Python module called runapp.py
.
This file needs to be executable, so that Heroku can run it.
We can use the chmod
command to fix that:
(pyramid_starter)$ chmod u+x run
Now, add the file to your repository and commit it:
(pyramid_starter)$ git add run
(pyramid_starter)$ git commit -m "adds a shell script to start my app"
Create the runapp.py
module¶
Finally, we need to actually write the Python module that will run our application.
Create a file runapp.py
and type the following Python code into it:
1 2 3 4 5 6 7 8 9 10 | import os
from paste.deploy import loadapp
from waitress import serve
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app = loadapp('config:production.ini', relative_to='.')
serve(app, host='0.0.0.0', port=port)
|
In line 6, we use a “main” block to make this module a Python script. The code in this block will only be executed when the script is run.
In line 7, we read the “PORT” variable from the operating system environment. Heroku uses environmental variables to pass information to applications. This allows you to separate configuration from code and is a good pattern. Notice that we default to port 5000 if no ‘PORT’ has been set in the environment.
In line 8, we create an application, using the production.ini
file that is located adjacent to this Python module.
Finally, in line 10, we serve our application, setting it up to listen on any available IP address.
Add this file to our git repository and commit your changes:
(pyramid_starter)$ git add runapp.py
(pyramid_starter)$ git commit -m "adds a python script to run our application"
Set Up Heroku and Deploy¶
Okay, with that, we’ve got all we need to get our app running on Heroku. Next, we’ll use the Heroku toolbelt to create a new app:
(pyramid_starter)$ $ heroku create
Creating app... done, ⬢ safe-scrubland-24595
https://safe-scrubland-24595.herokuapp.com/ | https://git.heroku.com/safe-scrubland-24595.git
Finally, we push our app to heroku:
(pyramid_starter)$ git push heroku master
...
remote: Verifying deploy... done.
And once that is finished, you can view your app in a browser:
(pyramid_starter)$ heroku open
Cleaning Up the Edges¶
There’s one last problem here.
Heroku defaults to serving our application over https
.
This is desireable.
But our Pyramid application has no idea that it is being served securely.
When it generates the URLs for CSS files, it uses http
.
Our browsers will not appreciate this.
We can fix it using configuration.
Open the file production.ini
from your application root directory in your text editor.
First, find the first section header at the top that contains this [app:main]
.
Change that to read [app:demoapp]
Next, find the section of the file that looks like this:
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
Before this section, add the following configuration:
1 2 3 4 5 6 7 | [filter:paste_prefix]
use = egg:PasteDeploy#prefix
[pipeline:main]
pipeline =
paste_prefix
demoapp
|
Lines 1-2 create a wsgi middleware filter that will detect the https
scheme and make that information available to our Pyramid app.
Lines 4-7 set up a wsgi pipeline which puts this filter before our new app (remember, we changed the app name above to demoapp
).
Save these changes, add them to the stage, and commit them to your repository.
Then you can re-deploy your application using git push
:
(pyramid_starter)$ git add production.ini
(pyramid_starter)$ git commit -m "enables pyramid to properly render https urls for static resources"
(pyramid_starter)$ git push heroku master
And that finishes us up. We now have a small, functional Pyramid application running on Heroku, serving resources over https. Later, you’ll repeat this process with your own learning journal application