Deploying Django with Python 3

by
1st May 2016

On the last few days I’ve had a hard time deploying a personal project of mine. First of all, I was not deploying a regular Django app. Instead, I separated my frontend from my backend, so I had to serve my frontend’s static files on one hand, and django and its statics for the admin and the rest of the framework, on the other. Secondly, I was dealing with python3, and I couldn’t find much information about that. And finally, I had never had to deploy an app from the ground up, so here I am, trying to share what I’ve learnt.

I have my AWS EC2 small instance, I can access through ssh. It’s like using my own linux, ubuntu in this case, and I’m going to use nginx and uwsgi to serve the app. If you are wondering what nginx and uwsgi are, you still have a long way to go, but, what you need to know is that the first one, will serve all the static files, and will delegate to the second one  (uwsgi), the requests that django must handle. If you want to know more, the internet is full of information my friend.

Before going on, I will take the existence of a virtualenv running python3 for granted and all your requirements already installed. And that both projects, frontend and backend, are in different folders located at /home/ubuntu/.

Let’s install the web services system wide (recommended). This will avoid many pains in the neck, doing:

sudo apt-get install nginx uwsgi uwsgi-plugin-python3

And check their status:

$ sudo service nginx status
* nginx is running

Okay, nginx looks fine. And uwsgi?

$ sudo service uwsgi status
* which one?

 

Configuring UWSGI

Okay, don’t panic here mate. This happens when you have no app running yet. We will get there eventually and we should not check uwsgi status doing that. We should observe and analyze the logs, like a real detective. This took me several hours to understand :).

Let me tell you that uwsgi has A LOT of configurations and options, and you can get A LOT of stuff done with it. What we are doing with uwsgi is the top of the iceberg AFAIK.:

After several hours of reading and trying hard, I got this very basic configuration:

# /etc/uwsgi/apps-available/random_app.ini
 [uwsgi]
 socket = /tmp/random_app.sock # Socket which will share with nginx
 chown-socket=www-data:www-data
 master = true
 buffer-size = 8192 # Big JSON request if you need it
 enable-threads = true
 processes = 2
 threads = 2
 virtualenv = /home/ubuntu/.virtualenvs/app_env/ # Virtualenv running python3
 chdir = /home/ubuntu/backend/random_project/ # Root django project (containing the manage.py)
 module = random_project.wsgi:application # indicate where is the wsgi module inside chdir
 touch-reload = /home/ubuntu/backend/app.reload # File that will be used to reload the app once modified
 logto = /var/log/uwsgi/app/digito.log # Our beloved logs location

I’ve seen many configurations including the “plugin = python3” option, but if you have installed the python3 plugin and uwsgi system wide (as recommended on top), you shouldn’t need to specify it, nor have any problems, if you have set the “virtualenv” option. We can debug our config by running uwsgi in the terminal doing:

sudo uwsgi --ini /etc/uwsgi/apps-available/random_app.ini

This should help us to debug any other errors that may appear, and you can terminate it with ctrl + C. Remember to create the touch file:

touch /home/ubuntu/backend/app.reload

Now that we have that file created at /etc/uwsgi/apps-available/random_app.ini, a symbolic link must be created in the appsenabled folder. To do so, it’s recommendable to use full path, to avoid further problems:

sudo ln -s /etc/uwsgi/apps-available/random_app.ini /etc/uwsgi/apps-enabled/random_app.ini

Our application will live In apps-available, and when on apps-enabled, it will be activated. Let’s restart the service:

sudo service uwsgi restart

Once this is done, we can now check the status, just to see that uwsgi exists. We won’t get any other information by doing that. If you want to know more, check the log.

sudo service uwsgi status random_app

At this point, we are done configuring uwsgi, and it should be working fine. Remember to reload doing touch to the file. It is not necessary to restart uwsgi whenever you change a file of your app.

Configuring NGINX

After installing nginx, just by going to our browser to “http://localhost/”, we should see a message showing that it’s up and running (if you are in your personal computer). Instead, in amazon, do this in the terminal “curl 127.0.0.1”.

Nginx follows the same logic as uwsgi. It has two folders “sites-available” and “sites-enabled”. In the first will be our conf and in the second the symbolic link.

Note: Remember to configure your django settings for statics (which will serve admin and rest framework statics), and after that, to do “python manage.py collectstatic”

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'public')

This is the config that serves the frontend’s static files and the backend with uwsgi:

# /etc/nginx/sites-available/app
server {
 listen 80 default_server;
 listen [::]:80 default_server ipv6only=on;
 root /home/ubuntu/frontend; #
 index index.html index.htm;
 gzip on;
 underscores_in_headers on;

 location ~ ^/(api|admin) { # api or admin urls will be served here
   root /etc/nginx/; # root specific for this location
   include uwsgi_params;
   uwsgi_pass unix:/tmp/random_app.sock; # socket that will share with uwsgi
 }

 # This are django static files
 location /static/ {
   alias /home/ubuntu/backend/random_project/public/;
 }
 
 location /ping/ {
   return 200 'Pong';
 }

 # When on / search for the files and if nothing is found a 404 is returned.
 location / {
   try_files $uri $uri/ =404;
 }
}

We create the symbolic link:

sudo ln -s  /etc/nginx/sites-available/app  /etc/nginx/sites-enabled/

Restart Nginx:

sudo service nginx restart

And that’s all folks. Your project should be up and running!

PDRemember to open the port 80, that nginx and every http server uses by default on amazon, and that would be everything.