Deploying with Fabric

Fabric is a tool for Python similar to Makefiles but with the abilityto execute commands on a remote server. In combination with a properlyset up Python package (Larger Applications) and a good concept forconfigurations (Configuration Handling) it is very easy to deploy Flaskapplications to external servers.

Before we get started, here a quick checklist of things we have to ensureupfront:

  • Fabric 1.0 has to be installed locally. This tutorial assumes thelatest version of Fabric.

  • The application already has to be a package and requires a workingsetup.py file (Deploying with Setuptools).

  • In the following example we are using mod_wsgi for the remoteservers. You can of course use your own favourite server there, butfor this example we chose Apache + mod_wsgi because it’s very easyto setup and has a simple way to reload applications without rootaccess.

Creating the first Fabfile

A fabfile is what controls what Fabric executes. It is named fabfile.pyand executed by the fab command. All the functions defined in that filewill show up as fab subcommands. They are executed on one or morehosts. These hosts can be defined either in the fabfile or on the commandline. In this case we will add them to the fabfile.

This is a basic first example that has the ability to upload the currentsource code to the server and install it into a pre-existingvirtual environment:

  1. from fabric.api import *
  2.  
  3. # the user to use for the remote commands
  4. env.user = 'appuser'
  5. # the servers where the commands are executed
  6. env.hosts = ['server1.example.com', 'server2.example.com']
  7.  
  8. def pack():
  9. # build the package
  10. local('python setup.py sdist --formats=gztar', capture=False)
  11.  
  12. def deploy():
  13. # figure out the package name and version
  14. dist = local('python setup.py --fullname', capture=True).strip()
  15. filename = '%s.tar.gz' % dist
  16.  
  17. # upload the package to the temporary folder on the server
  18. put('dist/%s' % filename, '/tmp/%s' % filename)
  19.  
  20. # install the package in the application's virtualenv with pip
  21. run('/var/www/yourapplication/env/bin/pip install /tmp/%s' % filename)
  22.  
  23. # remove the uploaded package
  24. run('rm -r /tmp/%s' % filename)
  25.  
  26. # touch the .wsgi file to trigger a reload in mod_wsgi
  27. run('touch /var/www/yourapplication.wsgi')

Running Fabfiles

Now how do you execute that fabfile? You use the fab command. Todeploy the current version of the code on the remote server you would usethis command:

  1. $ fab pack deploy

However this requires that our server already has the/var/www/yourapplication folder created and/var/www/yourapplication/env to be a virtual environment. Furthermoreare we not creating the configuration or .wsgi file on the server. Sohow do we bootstrap a new server into our infrastructure?

This now depends on the number of servers we want to set up. If we justhave one application server (which the majority of applications willhave), creating a command in the fabfile for this is overkill. Butobviously you can do that. In that case you would probably call itsetup or bootstrap and then pass the servername explicitly on thecommand line:

  1. $ fab -H newserver.example.com bootstrap

To setup a new server you would roughly do these steps:

  • Create the directory structure in /var/www:
  1. $ mkdir /var/www/yourapplication
  2. $ cd /var/www/yourapplication
  3. $ virtualenv --distribute env
  • Upload a new application.wsgi file to the server and theconfiguration file for the application (eg: application.cfg)

  • Create a new Apache config for yourapplication and activate it.Make sure to activate watching for changes of the .wsgi file sothat we can automatically reload the application by touching it.(See mod_wsgi (Apache) for more information)

So now the question is, where do the application.wsgi andapplication.cfg files come from?

The WSGI File

The WSGI file has to import the application and also to set an environmentvariable so that the application knows where to look for the config. Thisis a short example that does exactly that:

  1. import os
  2. os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg'
  3. from yourapplication import app

The application itself then has to initialize itself like this to look forthe config at that environment variable:

  1. app = Flask(__name__)
  2. app.config.from_object('yourapplication.default_config')
  3. app.config.from_envvar('YOURAPPLICATION_CONFIG')

This approach is explained in detail in the Configuration Handling section of thedocumentation.

The Configuration File

Now as mentioned above, the application will find the correctconfiguration file by looking up the YOURAPPLICATION_CONFIG environmentvariable. So we have to put the configuration in a place where theapplication will able to find it. Configuration files have the unfriendlyquality of being different on all computers, so you do not version themusually.

A popular approach is to store configuration files for different serversin a separate version control repository and check them out on allservers. Then symlink the file that is active for the server into thelocation where it’s expected (eg: /var/www/yourapplication).

Either way, in our case here we only expect one or two servers and we canupload them ahead of time by hand.

First Deployment

Now we can do our first deployment. We have set up the servers so thatthey have their virtual environments and activated apache configs. Now wecan pack up the application and deploy it:

  1. $ fab pack deploy

Fabric will now connect to all servers and run the commands as writtendown in the fabfile. First it will execute pack so that we have ourtarball ready and then it will execute deploy and upload the source codeto all servers and install it there. Thanks to the setup.py file wewill automatically pull in the required libraries into our virtualenvironment.

Next Steps

From that point onwards there is so much that can be done to makedeployment actually fun:

  • Create a bootstrap command that initializes new servers. It couldinitialize a new virtual environment, setup apache appropriately etc.

  • Put configuration files into a separate version control repositoryand symlink the active configs into place.

  • You could also put your application code into a repository and checkout the latest version on the server and then install. That way youcan also easily go back to older versions.

  • hook in testing functionality so that you can deploy to an externalserver and run the test suite.

Working with Fabric is fun and you will notice that it’s quite magical totype fab deploy and see your application being deployed automaticallyto one or more remote servers.