Wednesday 2 November 2016

Deploy django channels app on Nginx with Daphne, Asgi and Supervisor


So finally you have successfully created a django app that is utilizing the new cutting-edge technology django channels. Before that absorbing the power of web sockets in django is somewhat a a bane in your life. But after the channels revelation, the bad days are no more.

Though channels are very major and wonderful addition in django family, But it still in initial phases so resources for it is also very few. But instead of all of these thing, if you got success in the integration of channels in your project, that's not a last job, you still need to serve your app across the internet so that your audience can see it.

In order to achieve this, your old friend WSGI wont works. Since channels required Asgi for its capability to serve over the internet. Again Since this is quite a new technology so there isn't enough resources that will guide you for this deployment at least at the time I am right this.

When I was deploying my django channel app, it almost took my good time because of this lackness of online resources and reviews. So I have decided to share my all the efforts in black and white for others, through which I have successfully made deployment on Nginx with Daphne and Asgi as well as involving supervisor with this.




If you are still not familiar with Daphne and Asgi, I recommend you to visit this wonderfully tutorial by Abu Ashraf Masnun, that will provide you good understanding of both of these channels backers.

Here I will also show you how you can use supervisor to automate server up down without your manual interference.

asgi.py

To run Daphne, it just needs to be supplied with a channel backend, in much the same way a WSGI server needs to be given an application. Create asgi.py file that looks like this in the same directory where there is wsgi.py:
import os 

from channels.asgi import get_channel_layer 




os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_project.settings") channel_layer = get_channel_layer()

Install Supervisor

Since we are using supervisor to automate our server process so, Install supervisor in your machine. In ubuntu for this I have done

apt-get install supervisor
After this we need to configure the supervisor for our program, you. I am keeping my program conf separated from the supervisor base configurations i.e used [include] to include external configurations, but you can also add directly to this file. Add the following lines in /etc/supervisor/supervisord.conf

; supervisor config file
[unix_http_server]
file=/var/run/supervisor.sock   ; (the path to the socket file)
chmod=0700                       ; sockef file mode (default 0700)
[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
childlogdir=/var/log/supervisor            ; ('AUTO' child log dir, default $TEMP)
; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL  for a unix socket
; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.
[include]
files = /etc/supervisor/conf.d/*.conf
stdout_logfile=/var/log/django-supervisor.log
stderr_logfile=/var/log/django-supervisor.log 
And then create your configuration file program.conf in /etc/supervisor/conf.d/ as:

[program:server_workers]
command=/my_venvs/django-venv/bin/python /home/django/your_project/manage.py runworker
directory=/home/django/your_project/
stdout_logfile=/home/django/logs/gunicorn_supervisor.log            ; Where to write log messages
stderr_logfile=home/django/logs/gunicorn_supervisor.log
autostart=true
autorestart=true
redirect_stderr=true
stopasgroup=true
[program:server_interface]
command=/my_venvs/django-venv/bin/daphne -b 127.0.0.1 -p 8000 your_project.asgi:channel_layer
directory=/home/django/your_project/
stdout_logfile=/home/django/logs/daphne.log            ; Where to write log messages
stderr_logfile=home/django/logs/daphne.log
autostart=true
autorestart=true
stopasgroup=true
Make sure to replace python path, project path, daphne path, logs files path according to your project in this configurations.

Till now we have configured our supervisor script to run Daphne server and the workers that interact with Daphne server.

Nginx 

The last step in our server deployment is to setup Nginx to divert all traffic to Daphne 

Create confuration file of your site in /etc/nginx/sites-available/ and paste the following configurations: project.conf

# Enable upgrading of connection (and websocket proxying) depending on the
# presence of the upgrade field in the client request header
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# Create an upstream alias to where we've set daphne to bind to
upstream your_project {
server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name YOUR_SERVER_IP;

    client_max_body_size 4G;

    access_log /home/django/logs/nginx-access.log;
    error_log /home/django/logs/nginx-error.log;


    location /static/ {
        root  /home/public_html;
    }

    location /media/ {
        alias   /home/public_html/media/;
    }

    location / {
        # an HTTP header important enough to have its own Wikipedia entry:
        #   http://en.wikipedia.org/wiki/X-Forwarded-For
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # enable this if and only if you use HTTPS, this helps Rack
        # set the proper protocol for doing redirects:
        # proxy_set_header X-Forwarded-Proto https;

        # pass the Host: header from the client right along so redirects
        # can be set properly within the Rack application
        proxy_set_header Host $http_host;

        # we don't want nginx trying to do something clever with
        # redirects, we set the Host: header above already.
        proxy_redirect off;

        # set "proxy_buffering off" *only* for Rainbows! when doing
        # Comet/long-poll stuff.  It's also safe to set if you're
        # using only serving fast clients with Unicorn + nginx.
        # Otherwise you _want_ nginx to buffer responses to slow
        # clients, really.
        # proxy_buffering off;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Try to serve static files from nginx, no point in making an
        # *application* server like Unicorn/Rainbows! serve static files.
        if (!-f $request_filename) {
            proxy_pass http://your_project;
            break;
        }
    }

    # Error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /home/public_html/static/;
    }
}
Again make sure to replace all the settings according to your understanding for example replace YOUR_SERVER_IP with the address of yours. Similarly replace static file paths as well as project names etc.

Now the final step is to create the symlink of your site configuration from sites-available to sites-enabled, here's how to do this in ubuntu:

cd sites-enabledsudo ln -s ../sites-available/project.conf .

That's it: 

If everything goes fine as mentioned above, you will now be able to see your site in your browser. If this helps you don't forget to appreciate me via comments :)
Furthermore, if found anything wrong in this tutorial please help others by commenting it.
Thanks