pbdeploy (port-based deploy) is a script that handles starting, reloading and stopping your server processes, without requiring a daemon or pidfiles.
It also supports running scripts before and after certain services are loaded, such as installing requirements, performing database migrations, and running tests, to make continuous deployment a breeze.
to install: pip install pbdeploy
First, create a settings_deploy.py
file anywhere in your project.
Here's an example:
SERVICES = {
"django": {
"port": 8000,
"start": "python manage.py runserver",
},
"nginx": {
"port": 8080,
"start": "nginx -c {project_dir}/nginx.conf",
"restart": "kill -s SIGHUP {pid}",
},
}
In this example, we run django and also nginx. project_dir
and pid
are special variables that are always available, project_dir
being the
directory containing settings_deploy.py
and pid
being the pid of the
process being managed.
Now, simply run pbdeploy
from the same directory, and pbdeploy will
run the start command if there isn't a process listening on the
specified port, otherwise it will issue the restart command. If no
restart command is specified, in the case of Django, pbdeploy won't do
anything if the process is already running.
To stop your processes, run pbdeploy stop
.
Here's a more complex example that takes advantage of more of pbdeploy's features. Notice how the file is just Python so we can add any special logic we want, in this case using an environment variable for Solr:
import os
SERVICES = {
"nginx": {
"port": 12818,
"start": "nginx -c {project_dir}/nginx.conf",
"restart": "kill -s SIGHUP {pid}",
"templates": ["nginx.conf.template"],
},
"gunicorn": {
"port": 25590,
"start": "gunicorn -D -c settings_gunicorn.py yourapp.wsgi:application",
"restart": "kill -s SIGHUP {pid}",
"before": "pip install -r requirements.txt",
"after": "manage.py syncdb --migrate ",
},
"solr": {
"port": 28426,
"start": "java -Djetty.port={port} -Djetty.pid={project_dir}/run/solr.pid -Dsolr.solr.home={project_dir}/../solr -jar","start.jar",
"cwd": os.getenv("SOLR_EXAMPLE"),
"daemonizes": False,
},
}
pbdeploy's philosophy is:
Both of these make pbdeploy perfect for running on a shared server like WebFaction where memory is limited and services are already bound to specific ports, and it also works just swell anywhere from local development on OSX to production deployment on EC2.
start
: the command to run to start the process if it isn't already running.restart
: the command to run to restart the process if it is already running. If you leave this absent, pbdeploy won't do anything for this process if it is already running.stop
: the command to run to stop the process via pbdeploy stop
. If you leave this blank, kill {pid}
is assumed.port
: the port that the process runs on. pbdeploy uses this to determine if the process is running or not and get its pid. you'll need to specify either this or pidfile
.pidfile
: if your process runs on a dynamic port, or already writes a pidfile that you'd prefer to use, you can specify the location here instead of a port
.cwd
: the directory to run the start/restart commands from. default: the directory where pbdeploy
is run.before
: a command to run before the service is started or restarted. For running multiple commands, put them in a script and specify that, as in the example above. This won't be run if you pbdeploy --quick
.after
: a command to run after the service is started or restarted. For running multiple commands, put them in a script and specify that, as in the example above. This won't be run if you pbdeploy --quick
.daemonizes
: by default pbdeploy will wait for the start
command toA great use for before and after scripts is to run idempotent commands so that pbdeploy
handles everything necessary for continuous deployment.
For Django, a before script might be:
pip install -r ../requirements.txt | egrep -v "(Requirement already satisfied|Cleaning up)" || true
python manage.py collectstatic --noinput
and an after script might be:
python manage.py syncdb --migrate
python manage.py test yourapp 2>&1
an example git post-receive hook that runs all this and restarts your services for you on push might look like:
unset GIT_DIR
cd $YOUR_APP_DIR
git pull
source env/bin/activate # if using virtualenv
pbdeploy
pbdeploy supports templating in commands as well as files. For example, notice how above for the gunicorn service we can specify the restart command using "{pid}". This is a special variable that is always available.
You can also specify a file to template, such as we do with nginx. When we specify templates: ["nginx.conf.template"]
, for each start/restart, pbdeploy will read nginx.conf.template
and replace variables using Python string formatting, writing it out as just nginx.conf
.
Variables that exist for templating are:
{pid}
: the pid of the process, determined either by the process listening on the specified port or the pidfile specified{project_dir}
: the absolute path of the directory pbdeploy was run from. This is especially useful for avoiding hard-coding paths in configuration files (like nginx.conf) that require absolute paths.