Skip to content

Instantly share code, notes, and snippets.

@mortenege
Created March 4, 2018 06:48
Show Gist options
  • Save mortenege/68be5e859a6e91320be940212abfb207 to your computer and use it in GitHub Desktop.
Save mortenege/68be5e859a6e91320be940212abfb207 to your computer and use it in GitHub Desktop.
Django Channels on AWS Elastic Beanstalk using an ALB

Django Channels on AWS Elastic Beanstalk using an ALB

This post will describe how to set up a Django Channels solution using AWS Elastic Beanstalk [EB]. This guide is written in response to there being very little information on the combination of Channels and AWS. Mainly many solutions end with problems regarding websockets, which is a fundamental part of Channels. See here and here. This guide will consist of multiple step-by-step parts including

  • How to set up a local Django Channels solution
  • How to set up an Application Load Balancer [ALB] to serve websockets
  • How to configure EB to deploy Channels in a production environment

Django Channels in a local environment

We are not going to go through how to create a Django Channels project as it has been very well documented many times, ie. here and here. There is also multiple posts on this blog regarding Django, Channels, Supervisord and websockets, ie. here.

Serving websockets with a load balancer

Elastic Beanstalk runs an Apache webserver serving Django projects as wsgi (mod_wsgi), but it does not natively support websockets. There are different Apache configurations possible to serve websockets, ie. reverse proxy (see here), but for this use case we are going to use a load balancer in front of our servers to route different traffic to different backends based on their relative url. We are going to let Apache continue serving our Django project as wsgi on port 80, but we are going to route all traffic with the relative url "/ws/" to port 5000 where we will have Daphne running, serving our websockets.  More on running Daphne can be read in previous posts. With AWS we can configure an Application Load Balancer to route this traffic.

Step 1:

Create an Application Load Balancer (here is a good tutorial).

Step 2

Create 2 target groups. Point one target group to port 80 and add the EC2 instance to Registered instances. Point the second target group to port 5000 and add the same instance.

Step 3

Under the Load Balancer tab create 2 rules. The default path points to the target group that is pointing to port 80. Set the second path to "/ws/" and point it to the target group that is pointing to port 5000.

NOTE: If we want further allowance on the path, add "/ws/*" instead to catch different paths.

Step 4

Edit the Security Group of the instance to allow inbound connections on port 80 (type HTTP) and port 5000 (type Custom TCP Rule) from the ALB we just created.

Now the Load Balancer is set up to route requests to two different backends based on the relative url. Next step is to configure our EB deployment files to run and manage a Daphne backend.

Configuring the EB environment

We are going to run our Daphne backend through the monitoring program Supervisord.  This means we have to configure supervisord.conf and to make sure it is available on both boot and deployment.

Step 1

Download and edit the supervisord.conf-file found in /opt/python/etc/ to a folder in our Django project .ebextensions/supervisord/. Add Daphne and a few workers. Notice which port and interface Daphne runs on. Notice also the numprocs and process_name for the workers.

[program:Daphne]
environment=PATH="/opt/python/run/venv/bin"
command=/opt/python/run/venv/bin/daphne -b 0.0.0.0 -p 5000 ebdjango.asgi:channel_layer
directory=/opt/python/current/app
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/tmp/daphne.out.log

[program:Worker]
environment=PATH="/opt/python/run/venv/bin"
command=python manage.py runworker
directory=/opt/python/current/app
process_name=%(program_name)s_%(process_num)02d
numprocs=4
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/tmp/workers.out.log

Step 2

Create a file .ebextensions/channels.config.

container_commands:
  01_copy_supervisord_conf:
    command: "cp .ebextensions/supervisord/supervisord.conf /opt/python/etc/supervisord.conf"
  02_reload_supervisord:
    command: "supervisorctl -c /opt/python/etc/supervisord.conf reload"

Now we can use eb deploy and to verify that supervisord is running our backend we can use supervisorctl statusNotice that the Apache server httpd is also monitored by supervisord.

$ sudo /usr/local/bin/supervisorctl -c /opt/python/etc /supervisord.conf status
Daphne           RUNNING pid 22726, uptime 0:00:21
Worker:Worker_00 RUNNING pid 22730, uptime 0:00:21
Worker:Worker_01 RUNNING pid 22784, uptime 0:00:20
Worker:Worker_02 RUNNING pid 22785, uptime 0:00:20
Worker:Worker_03 RUNNING pid 22790, uptime 0:00:19
httpd            RUNNING pid 22727, uptime 0:00:21

Conclusion

We are now able to serve websockets with Elastic Beanstalk using an Application Load Balancer and Django Channels.

@aarora08
Copy link

aarora08 commented Jun 1, 2019

Hi, I am trying to set up a django chat app on Elastic Beanstalk using django channels and this seems to be the only available guide for it. My issue is that the command python manage.py runworker breaks, as it gives an error that manage.py runworker: error: the following arguments are required: channels. So, my solution to that was to add a channel name router, as follows:
application = ProtocolTypeRouter({ 'websocket': AuthMiddlewareStack( URLRouter( communication.routing.websocket_urlpatterns ) ), "channel": ChannelNameRouter({ 'chat-server': consumers.ChatConsumer, }) })
although this does solve the runworker problem but i get an error in my website console that : Firefox can’t establish a connection to the server at wss://www.WEBSITE_NAME_HERE.com/ws/chat/CHAT_UUID/.

Do you have any idea how I can resolve this?

@mortenege
Copy link
Author

mortenege commented Jun 3, 2019

Without knowing more about your setup I will have to guess that either the program is not running or the port is not open on your server. Check your logs to see where that request ends up. Is there an issue locally as well?

There is some more information regarding debugging channels here https://gist.github.com/mortenege/c9f4d1b6dbda1999693515a1177eb495

@srijannnd
Copy link

I included full venv path in worker command like
command=/opt/python/run/venv/bin/python manage.py runworker default
this worked for me. By the way, default is name of the channel. You can add multiple channels separated by space like "python manage.py runworker channel1 channel2 channel3 ... "

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment