This document describes the production deployment of the Python backend. The Python backend is a standard WSGI application, which means that there are numerous ways to deploy it. There are even standalone WSGI servers that you can deploy it with, including one that comes with Paste and Spawning.
Bespin uses Paver to handle its builds. The top-level pavement file knows how to package up Bespin for a production deployment. At Bespin's top-level directory, run the following command to build Bespin:
Copy the tarball built in the previous step to your production server. It will go into the home directory of the account that you'll be running the app as. We use mod_wsgi's daemon model (vs. in-process model), which allows us to run Bespin as a different user than the web server.
untar the file, and you will end up with a BespinServer directory. In there, run:
python bootstrap.py --no-site-packages
This will set up a virtualenv and then install all of the dependencies. This installation procedure requires that the production server have outbound access to the internet to grab the packages required by Bespin. If the production server does not have outbound access, you can look into packaging up the dependencies into a single collection. We're using pip to install the dependencies, and there are some options to help collect up the requirements.
- as of 5/20/09 2 packages path and Paste had conflicts with pip install. Manually install path and install it:
bin/pip install http://pypi.python.org/packages/source/p/path.py/path-2.2.zip
the latest release of Paste didn't include the required patches for bespin. Manually copy over Paste from your dev system to production system under <mybespindirectory>/libs and install:
bin/pip install libs/Paste-1.7.3dev-r7791.tar.gz
Apache with mod_wsgi
We chose Apache with mod_wsgi because that combination will automatically manage the Python processes for us. We are using mod_wsgi 2.3. It is not uncommon for people to Apache or nginx with a proxy to a Python web server.
Here is the Bespin part of our Apache configuration with comments inline to describe the different parts of the config:
Most of the configuration above is optional. In fact, the only parts required are:
- directory directive to provide access to the wsgi-apps directory (where you put the bespin.wsgi script)
- The WSGI* directives that tell mod_wsgi what to run and how to run it.
The WSGI script
mod_wsgi scripts are standard Python scripts that include an "application" object in their namespace. That is the WSGI application that is attached to the server.
Bespin's run time configuration is all stored in variables in the bespin.config module in a dictionary-like object called "c". The WSGI script *could* do all of the itself by directly setting the values. In the script below, we set the values via a simple .ini file. Additionally, we set up Python's logging system to put any log output that interests us into logs/bespin.log.
import os import logging import logging.handlers import ConfigParser from bespin import config, controllers import sys #redirect stdout to go to stderr, it will appear in the apache error log sys.stdout = sys.stderr # setting the configuration profile starts us off with reasonable values for our # environment. config.set_profile("prod") # load config values from the INI file cp = ConfigParser.ConfigParser() cp.read(os.path.abspath(os.path.dirname(__file__) + "/../config/bespin.ini")) # config values are available as a dictionary, and config.c is a dictionary # so we can just do config.c.update. config.c.update(cp.items("config")) # set up logging root_log = logging.getLogger() root_log.setLevel(logging.WARN) handler = logging.handlers.RotatingFileHandler( os.path.abspath(os.path.dirname(__file__) + "/../logs/bespin.log")) root_log.addHandler(handler) handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) # email out errors handler2 = logging.handlers.SMTPHandler('localhost', 'YOU@YOU.COM', ['YOU@YOU.COM'], 'Bespin Error') handler2.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) handler2.setLevel(logging.ERROR) # uncomment the next line if you actually want the errors mailed, the # above is sample config #root_log.addHandler(handler2) # activate profile is responsible for taking the config and creating database # connections, etc. config.activate_profile() # this is the bit for mod_wsgi. controllers.make_app() gives us the top-level WSGI # application. application = controllers.make_app()
If you don't plan to run a full asynchronous setup on the server (requiring you to install beanstalkd and run bespin_worker processes), you'll want to set this flag *before* the config.activate_profile() in the WSGI script.
The .ini file
The INI file is where your site-specific configuration will go.
[config] dburl=mysql://user:password@localhost/bespin secret=YOUR SECRET USED IN HASHING static_dir=/home/WSGIUSER/BespinServer/frontend fsroot=YOUR HOME DIRECTORY FOR ALL BESPIN FILES
Creating the database
Assuming bespin.wsgi is in a directory called wsgi-apps that is at the same level as your BespinServer directory, you can then run
To set up your production database.