Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can not connect to websocket using django-channels , nginx on docker as services

I'm using docker compose to build a project with django, nginx as services. When I launch the daphne server, and a client tries to connect to the websocket server, I get this error:

*1 recv() failed (104: Connection reset by peer) while reading response header from upstream

Client-side shows this

failed: Error during WebSocket handshake: Unexpected response code: 502

Here is my docker-compose.yml

version: '3'
services:
  nginx:
    image: nginx
    command: nginx -g 'daemon off;'
    ports:
      - "1010:80"
    volumes:
      - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf
      - .:/makeup
    links:
      - web
  web:
    build: .
    command: /usr/local/bin/circusd /makeup/config/circus/web.ini
    environment:
      DJANGO_SETTINGS_MODULE: MakeUp.settings
      DEBUG_MODE: 1
    volumes:
      - .:/makeup
    expose:
      - '8000'
      - '8001'
    links:
      - cache
    extra_hosts:
      "postgre": 100.73.138.65

Nginx:

server {
        listen 80;
        server_name thelab518.cloudapp.net;

        keepalive_timeout 15;
        root /makeup/;

        access_log /dev/stdout;
        error_log /dev/stderr;

        location /api/stream {
            proxy_pass http://web:8001;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }


location / {
        try_files $uri @proxy_to_app;
    }

    location @proxy_to_app {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://web:8000;
    }

And the circusd's web.ini file:

[watcher:web]
cmd = /usr/local/bin/gunicorn MakeUp.wsgi:application -c config/gunicorn.py
working_dir = /makeup/
copy_env = True
user = www-data

[watcher:daphne]
cmd = /usr/local/bin/daphne -b 0.0.0.0 -p 8001 MakeUp.asgi:channel_layer
working_dir = /makeup/
copy_env = True
user = root

[watcher:worker]
cmd = /usr/bin/python3 manage.py runworker
working_dir = /makeup/
copy_env = True
user = www-data
like image 425
Асхат Билялов Avatar asked Apr 07 '17 03:04

Асхат Билялов


1 Answers

As quite explicitly stated in the fine manual, to successfully run Channels you need to have a dedicated application server implementing the ASGI protocol, such as the supplied daphne

The entire Django execution model has been changed with Channels, so that there are separate "interface servers" taking care of receiving and sending messages over, for example, WebSockets or HTTP or SMS, and "worker servers" that run the actual code (potentially on a different server or VM or container or...). The two are connected by a "Channel layer" that carries messages and replies back and forth.

The current implementation supplies 3 channel layers that talk ASGI between an interface server and a worker server:

  • An In-memory channel layer, used mainly for running the test server (it's single process)
  • An IPC based channel layer, usable to run different workers on the same server
  • A redis based channel layer, that should be used for heavy production sites, able to connect interface servers to multiple worker servers.

You configure them like you do for DATABASES::

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "asgi_redis.RedisChannelLayer",
        "ROUTING": "my_project.routing.channel_routing",
        "CONFIG": {
            "hosts": [("redis-channel-1", 6379), ("redis-channel-2", 6379)],
        },
    },
}

Of course this means that your docker config has to change and add one or more interface servers instead of, or in addition to, nginx (even if, in that case, you'll need to accept websocket connections on a different port with all the connected possible problems) and, quite likely, an instance of redis connectin all them.

This in turn means that until circus and nginx can support ASGI, it won't be possible to use them with django-channels, or that this support will only be for the regular http part of your system.

You can find more info in the Deploying section of the official documentation.

like image 165
RobM Avatar answered Sep 23 '22 07:09

RobM