Zero Downtime Deploy
Python
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
bin
build
samples
zdd
.gitignore
LICENSE
README.rst
TODO
setup.py

README.rst

About

Zero Downtime Deploy is a script that enables graceful code deploys by always spawning a fresh instance of the server and using a proxying server (nginx) to gracefully fail over.

Installation

zdd requires Python 2.x >= 2.5.

Install from PyPI:

$ easy_install zdd

Or install from sources:

$ python setup.py install

Samples

See the samples directory for example server configurations for node.js, gunicorn and twisted.

Basic Usage

After installing zdd, you will have access to the zddeploy command line script that can be used to do zero downtime cutover between versions. Using gunicorn as an example (but any server can work with zdd):

$ cd ./samples/gunicorn/
$ zddeploy

This causes zddeploy to read the deploy.conf in ./samples/gunicorn:

[nginx]
template: ./nginx.conf.template
pid_file: ./nginx.pid

[service:gunicorn]
pid_file: gunicorn.pid
start: gunicorn -D -c settings_gunicorn.py app:app
stop: kill -WINCH

The only requirement beyond this simple configuration file is that the web server must ask the OS to listen on a random port (by binding to port 0 instead of a specific port), and then write a portfile in addition to the commonly written pidfile. A portfile is nothing more than a single line containing the OS assigned random port the server is listening to. To avoid race conditions, the portfile is named by adding .port to the end of the pid (i.e. 94383.pid).

For example, here's the 12 lines of code it takes to add this behavior to gunicorn:

def get_port_filename():
    pid = os.getpid()
    return os.path.join(os.path.dirname(pidfile), "%s.port" % pid)

def when_ready(server):
    host, port = server.LISTENER.sock.getsockname()
    port_filename = get_port_filename()
    with file(port_filename, 'w') as portfile:
        print >>portfile, port

    @atexit.register
    def remove_portfile():
        os.unlink(port_filename)

Ideally, your server has a graceful stop option, as gunicorn has by default via the SIGWINCH signal (graceful stop code for node.js and twisted included in the samples directory). If it doesn't, you can get a cheap alternative by simply waiting a long enough time period and then killing the server:

::
#!/usr/bin/env bash sleep 600 kill $1

Configuration File

By default, zddeploy will look for ./deploy.conf. Alternatively you can pass the path to the conf file via -c/--conf. The easiest way to learn the configuration file is to look at working example:

Configuration File: nginx section

  • template The path to the nginx configuration template file. Every time you deploy, zdd will rerender this template into nginx.conf (in the same directory).
  • pid_file The path to the nginx pid file, so zdd can SIGHUP nginx. Must match your template. Provided in the template as {nginx_pid_filename}.

Configuration File: service section

  • pid_file The path to the pid file generated by the service. Required for reading the port file.
  • start The command to run to start the service (A shell script is recomended for non-trivial commands).
  • stop The command to run to stop the service. Appended with the pid of the instance to stop (A shell script is recomended for non-trivial commands).

LICENSE

Zero Downtime Deploy is released under the MIT License. See the LICENSE file for more details.