Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added chapter about fabric based deployments
- Loading branch information
Showing
5 changed files
with
219 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
.. _fabric-deployment: | ||
|
||
Deploying with Fabric | ||
===================== | ||
|
||
`Fabric`_ is a tool for Python similar to Makefiles but with the ability | ||
to execute commands on a remote server. In combination with a properly | ||
set up Python package (:ref:`larger-applications`) and a good concept for | ||
configurations (:ref:`config`) it is very easy to deploy Flask | ||
applications to external servers. | ||
|
||
Before we get started, here a quick checklist of things we have to ensure | ||
upfront: | ||
|
||
- Fabric 1.0 has to be installed locally. This tutorial assumes the | ||
latest version of Fabric. | ||
- The application already has to be a package and requires a working | ||
`setup.py` file (:ref:`distribute-deployment`). | ||
- In the following example we are using `mod_wsgi` for the remote | ||
servers. You can of course use your own favourite server there, but | ||
for this example we chose Apache + `mod_wsgi` because it's very easy | ||
to setup and has a simple way to reload applications without root | ||
access. | ||
|
||
Creating the first Fabfile | ||
-------------------------- | ||
|
||
A fabfile is what controls what Fabric executes. It is named `fabfile.py` | ||
and executed by the `fab` command. All the functions defined in that file | ||
will show up as `fab` subcommands. They are executed on one or more | ||
hosts. These hosts can be defined either in the fabfile or on the command | ||
line. In this case we will add them to the fabfile. | ||
|
||
This is a basic first example that has the ability to upload the current | ||
sourcecode to the server and install it into a already existing | ||
virtual environment:: | ||
|
||
from fabric.api import * | ||
|
||
# the user to use for the remote commands | ||
env.user = 'appuser' | ||
# the servers where the commands are executed | ||
env.hosts = ['server1.example.com', 'server2.example.com'] | ||
|
||
def pack(): | ||
# create a new source distribution as tarball | ||
local('python setup.py sdist --formats=gztar', capture=False) | ||
|
||
def deploy(): | ||
# figure out the release name and version | ||
dist = local('python setup.py --fullname').strip() | ||
# upload the source tarball to the temporary folder on the server | ||
put('sdist/%s.tar.gz' % dist, '/tmp/') | ||
# create a place where we can unzip the tarball, then enter | ||
# that directory and unzip it | ||
run('mkdir yourapplication') | ||
with cd('/tmp/yourapplication'): | ||
run('tar xzf /tmp/yourapplication.tar.gz') | ||
# now setup the package with our virtual environment's | ||
# python interpreter | ||
run('/var/www/yourapplication/env/bin/python setup.py install') | ||
# now that all is set up, delete the folder again | ||
run('rm -rf /tmp/yourapplication /tmp/yourapplication.tar.gz') | ||
# and finally touch the .wsgi file so that mod_wsgi triggers | ||
# a reload of the application | ||
run('touch /var/www/yourapplication.wsgi') | ||
|
||
The example above is well documented and should be straightforward. Here | ||
a recap of the most common commands fabric provides: | ||
|
||
- `run` - executes a command on a remote server | ||
- `local` - executes a command on the local machine | ||
- `put` - uploads a file to the remote server | ||
- `cd` - changes the directory on the serverside. This has to be used | ||
in combination with the `with` statement. | ||
|
||
Running Fabfiles | ||
---------------- | ||
|
||
Now how do you execute that fabfile? You use the `fab` command. To | ||
deploy the current version of the code on the remote server you would use | ||
this command:: | ||
|
||
$ 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. Furthermore | ||
are we not creating the configuration or `.wsgi` file on the server. So | ||
how do we bootstrap a new server into our infrastructure? | ||
|
||
This now depends on the number of servers we want to set up. If we just | ||
have one application server (which the majority of applications will | ||
have), creating a command in the fabfile for this is overkill. But | ||
obviously you can do that. In that case you would probably call it | ||
`setup` or `bootstrap` and then pass the servername explicitly on the | ||
command line:: | ||
|
||
$ fab -H newserver.example.com bootstrap | ||
|
||
To setup a new server you would roughly do these steps: | ||
|
||
1. Create the directory structure in ``/var/www``:: | ||
|
||
$ mkdir /var/www/yourapplication | ||
$ cd /var/www/yourapplication | ||
$ virtualenv --distribute env | ||
|
||
2. Upload a new `application.wsgi` file to the server and the | ||
configuration file for the application (eg: `application.cfg`) | ||
|
||
3. Create a new Apache config for `yourapplication` and activate it. | ||
Make sure to activate watching for changes of the `.wsgi` file so | ||
that we can automatically reload the application by touching it. | ||
(See :ref:`mod_wsgi-deployment` for more information) | ||
|
||
So now the question is, where do the `application.wsgi` and | ||
`application.cfg` files come from? | ||
|
||
The WSGI File | ||
------------- | ||
|
||
The WSGI file has to import the application and also to set an environment | ||
variable so that the application knows where to look for the config. This | ||
is a short example that does exactly that:: | ||
|
||
import os | ||
os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg' | ||
from yourapplication import app | ||
|
||
The application itself then has to initialize itself like this to look for | ||
the config at that environment variable:: | ||
|
||
app = Flask(__name__) | ||
app.config.from_object('yourapplication.default_config') | ||
app.config.from_envvar('YOURAPPLICATION_CONFIG') | ||
|
||
This approach is explained in detail in the :ref:`config` section of the | ||
documentation. | ||
|
||
The Configuration File | ||
---------------------- | ||
|
||
Now as mentioned above, the application will find the correct | ||
configuration file by looking up the `YOURAPPLICATION_CONFIG` environment | ||
variable. So we have to put the configuration in a place where the | ||
application will able to find it. Configuration files have the unfriendly | ||
quality of being different on all computers, so you do not version them | ||
usually. | ||
|
||
A popular approach is to store configuration files for different servers | ||
in a separate version control repository and check them out on all | ||
servers. Then symlink the file that is active for the server into the | ||
location where it's expected (eg: ``/var/www/yourapplication``). | ||
|
||
Either way, in our case here we only expect one or two servers and we can | ||
upload them ahead of time by hand. | ||
|
||
First Deployment | ||
---------------- | ||
|
||
Now we can do our first deployment. We have set up the servers so that | ||
they have their virtual environments and activated apache configs. Now we | ||
can pack up the application and deploy it:: | ||
|
||
$ fab pack deploy | ||
|
||
Fabric will now connect to all servers and run the commands as written | ||
down in the fabfile. First it will execute pack so that we have our | ||
tarball ready and then it will execute deploy and upload the source code | ||
to all servers and install it there. Thanks to the `setup.py` file we | ||
will automatically pull in the required libraries into our virtual | ||
environment. | ||
|
||
Next Steps | ||
---------- | ||
|
||
From that point onwards there is so much that can be done to make | ||
deployment actually fun: | ||
|
||
- Create a `bootstrap` command that initializes new servers. It could | ||
initialize a new virtual environment, setup apache appropriately etc. | ||
- Put configuration files into a separate version control repository | ||
and symlink the active configs into place. | ||
- You could also put your application code into a repository and check | ||
out the latest version on the server and then install. That way you | ||
can also easily go back to older versions. | ||
- hook in testing functionality so that you can deploy to an external | ||
server and run the testsuite. | ||
|
||
Working with Fabric is fun and you will notice that it's quite magical to | ||
type ``fab deploy`` and see your application being deployed automatically | ||
to one or more remote servers. | ||
|
||
|
||
.. _Fabric: http://fabfile.org/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters