Deploying Python applications

Overview

This document is a hands-on guide to deploying a simple Python application in tsuru. The example application will be a very simple Django project using a SQLite database. It’s applicable to any WSGI application.

Creating the app

To create an app, you use the command app create:

$ tsuru app create <app-name> <app-platform>

For Python, the app platform is, guess what, python! Let’s be over creative and develop a never-developed tutorial-app: a blog, and its name will also be very creative, let’s call it “blog”:

$ tsuru app create blog python

To list all available platforms, use the command platform list.

You can see all your applications using the command app list:

$ tsuru app list
+-------------+-------+--------------------------+
| Application | Units | Address                  |
+-------------+-------+--------------------------+
| blog        |       | blog.192.168.50.4.nip.io |
+-------------+-------+--------------------------+

You can then send the code of your application.

Application code

This document will not focus on how to write a Django blog, you can clone the entire source direct from GitHub: https://github.com/tsuru/tsuru-django-sample. Here is what we did for the project:

  1. Create the project (django-admin startproject blog)
  2. Create a “posts” app (django-admin startapp posts)
  3. Add a “Post” model to the app
  4. Register the model in django-admin

Deployment

You can just run tsuru app deploy command and your project will be deployed:

$ tsuru app deploy -a blog .
 -- Using python version: 2.7.13 (default) --
 /home/application/current /

 ---- Building image ----
  ---> Sending image to repository (0.01MB)
  ---> Cleaning up

Now you can check your deployed app running tsuru app info

$ tsuru app info -a blog
Application: blog
Repository: git@192.168.50.4.nip.io:blog.git
Platform: python
Teams: admin
Address: blog.192.168.50.4.nip.io
Owner: admin@example.com
Team owner: admin
Deploys: 0
Pool: theonepool
Units: 1
+-----------------------+---------+
| Unit                  | Status  |
+-----------------------+---------+
| blog-eab5151eff-51eff | started |
+-----------------------+---------+

App Plan:
+---------------+--------+-----------+---------+
| Name          | Memory | Cpu Share | Default |
+---------------+--------+-----------+---------+
| autogenerated | 0 MB   | 100       | false   |
+---------------+--------+-----------+---------+

Listing dependencies

In the last section we omitted the dependencies step of deploy. In tsuru, an application can have two kinds of dependencies:

  • Operating system dependencies, represented by packages in the package manager of the underlying operating system (e.g.: yum and apt-get);
  • Platform dependencies, represented by packages in the package manager of the platform/language (in Python, pip).

All apt-get dependencies must be specified in a requirements.apt file, located in the root of your application, and pip dependencies must be located in a file called requirements.txt, also in the root of the application. Since we will use Django, we need to install django package using pip. As this project doesn’t have any external dependencies, we don’t need a requirements.apt file. Here is the requirements.txt file contents:

Django<=1.11

You can see the complete output of installing these dependencies below:

$ tsuru app deploy -a blog .
-- Using python version: 2.7.13 (default) --
/home/application/current /
requirements.txt detected, using 'pip install -r ./requirements.txt' to install dependencies
Requirement already satisfied: Django<=1.11 in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from -r ./requirements.txt (line 1))
Requirement already satisfied: pytz in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from Django<=1.11->-r ./requirements.txt (line 1))
/

---- Building image ----
 ---> Sending image to repository (0.01MB)
 ---> Cleaning up

Running the application

As you can see, in the deploy output there is a step described as “Restarting your app”. In this step, tsuru will restart your app if it’s running, or start it if it’s not. But how does tsuru start an application? That’s very simple, it uses a Procfile (a concept stolen from Foreman). In this Procfile, you describe how your application should be started. We can use gunicorn, for example, to start our Django application. Here is how the Procfile should look like:

web: gunicorn -b 0.0.0.0:$PORT blog.wsgi

Now we commit the file and push the changes to tsuru git server, running another deploy:

$ tsuru app deploy -a blog .
 -- Using python version: 2.7.13 (default) --
 /home/application/current /
 requirements.txt detected, using 'pip install -r ./requirements.txt' to install dependencies
 Requirement already satisfied: Django<=1.11 in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from -r ./requirements.txt (line 1))
 Requirement already satisfied: pytz in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from Django<=1.11->-r ./requirements.txt (line 1))
 /

 ---- Building image ----
  ---> Sending image to repository (0.01MB)
  ---> Cleaning up
  ---> Restarting your app
 /var/lib/tsuru/hooks/start: line 13: gunicorn: command not found
  ---> Deploy done!

Now we get an error: gunicorn: command not found. It means that we need to add gunicorn to requirements.txt file:

$ cat >> requirements.txt
gunicorn==19.6
^D

Now run another deploy:

$ tsuru app deploy -a blog .
 -- Using python version: 2.7.13 (default) --
 /home/application/current /
 requirements.txt detected, using 'pip install -r ./requirements.txt' to install dependencies
 Requirement already satisfied: Django<=1.11 in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from -r ./requirements.txt (line 1))
 Requirement already satisfied: gunicorn==19.6 in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from -r ./requirements.txt (line 2))
 Requirement already satisfied: pytz in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from Django<=1.11->-r ./requirements.txt (line 1))
 /

 ---- Building image ----
  ---> Sending image to repository (0.01MB)
  ---> Cleaning up
  ---> Restarting your app

  ---> Deploy done!

Now that the app is deployed, you can access it from your browser, getting the IP or host listed in app list and opening it. For example, in the list below:

$ tsuru app list
+-------------+-----------+---------------------+
| Application | Units     | Address             |
+-------------+-----------+---------------------+
| blog        | 1 started | blog.cloud.tsuru.io |
+-------------+-----------+---------------------+

We can access the admin of the app in the URL http://blog.cloud.tsuru.io/admin/.

Deployment hooks

It would be boring to manually run syncdb and/or migrate after every deployment. So we can configure an automatic hook to always run before or after the app restarts.

tsuru parses a file called tsuru.yml and runs restart hooks. As the extension suggests, this is a YAML file, that contains a list of commands that should run before and after the restart. Here is our example of tsuru.yml:

hooks:
  build:
    - python manage.py collectstatic -c --noinput
    - python manage.py migrate

For more details, check the hooks documentation.

tsuru will look for the file in the root of the project. Let’s commit and deploy it:

$ tsuru app deploy -a blog .
 -- Using python version: 2.7.13 (default) --
 /home/application/current /
 requirements.txt detected, using 'pip install -r ./requirements.txt' to install dependencies
 Requirement already satisfied: Django<=1.11 in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from -r ./requirements.txt (line 1))
 Requirement already satisfied: gunicorn==19.6 in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from -r ./requirements.txt (line 2))
 Requirement already satisfied: pytz in /var/lib/pyenv/versions/2.7.13/envs/app_env_2.7.13/lib/python2.7/site-packages (from Django<=1.11->-r ./requirements.txt (line 1))
 /

 ---- Building image ----
  ---> Sending image to repository (0.01MB)
  ---> Cleaning up
 ---- Running build hooks ----
  ---> Running "python manage.py collectstatic -c --noinput"
  ---> Restarting your app

  ---> Deploy done!

It’s done! Now we have a Django project deployed on tsuru.

Going further

For more information, you can dig into tsuru docs, or read complete instructions of use for the tsuru client.