Websockets с фреймворком rest каналов Django в docker-compose
<#routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import re_path
from gnss.consumers import CoordinateConsumer # noqa
from operations.consumers import OperationConsumer # noqa
websocket_urlpatterns = [
re_path(r'ws/coordinates/', CoordinateConsumer.as_asgi()),
re_path(r'ws/operations/', OperationConsumer.as_asgi()),
]
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter(websocket_urlpatterns)
),
})
# consumers.py
from djangochannelsrestframework.decorators import action # noqa
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer # noqa
from djangochannelsrestframework.observer import model_observer # noqa
from djangochannelsrestframework.observer.generics import action # noqa
from djangochannelsrestframework.permissions import AllowAny # noqa
from .models import Coordinate
from .serializers import CoordinateSerializer
class CoordinateConsumer(GenericAsyncAPIConsumer):
queryset = Coordinate.objects.all()
serializer_class = CoordinateSerializer
permission_classes = (AllowAny,)
@model_observer(Coordinate)
async def coordinates_activity(self, message, action=None, **kwargs):
await self.send_json(message)
@coordinates_activity.serializer
def coordinates_activity(self, instance: Coordinate, action, **kwargs):
return dict(CoordinateSerializer(instance).data,
action=action.value, pk=instance.pk)
@action()
async def subscribe_to_coordinates_activity(self, request_id, **kwargs):
await self.coordinates_activity.subscribe(request_id=request_id)
Что касается consumers.py для ws/operations/, то он точно такой же. Когда я запускаю свое приложение Django на локальной машине на сервере разработки (python manage.py runserver), все работает нормально. Но по какой-то причине, когда я запускаю свое приложение в docker-compose, ws/operations/ работает, а ws/coordinates нет. По какой-то причине я не получаю сообщения от Django.
Мой docker-compose
version: "3.7"
x-api-common: &api
build: ./backend
restart: always
env_file: ./config/api/.env
volumes:
- ./backend/api:/opt/code
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/media
networks:
- nginx_network
- postgres_network
depends_on:
- redis
- postgres
services:
wsgi:
<<: *api
command: "bash /opt/code/docker-wsgi-entrypoint.sh"
container_name: operations_wsgi
environment:
is_wsgi: 'true'
ports:
- 8000:8000
asgi:
<<: *api
command: "bash /opt/code/docker-asgi-entrypoint.sh"
container_name: operations_asgi
ports:
- 8001:8001
depends_on:
- wsgi
admin:
<<: *api
container_name: operations_admin
ports:
- 8002:8002
depends_on:
- wsgi
command: gunicorn api.wsgi:application --bind 0.0.0.0:8002 --workers 2 --timeout 600
web:
build:
context: ./frontend
dockerfile: Dockerfile
restart: "on-failure"
ports:
- 80:80
volumes:
- ./frontend:/app
- '/app/node_modules'
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/media
depends_on:
- wsgi
- asgi
- admin
networks:
- nginx_network
redis:
image: "redis:6.2.7-alpine"
restart: always
command:
- /bin/sh
- -c
- redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
ports:
- "6379:6379"
env_file:
- ./config/redis/.env
networks:
- nginx_network
postgres:
image: postgres:13.0
restart: always
ports:
- 5432:5432
env_file: config/postgres/.env
networks:
- postgres_network
volumes:
- ./data/postgres-data:/var/lib/postgresql/data
rabbitmq:
image: 'rabbitmq:3.6-management-alpine'
restart: always
ports:
- '5672:5672'
- '15672:15672'
env_file: config/rabbitmq/.env
networks:
nginx_network:
driver: bridge
postgres_network:
driver: bridge
volumes:
static_volume:
media_volume:
И моя конфигурация nginx
upstream wsgi_server {
server wsgi:8000;
}
upstream asgi_server {
server asgi:8001;
}
upstream admin_server {
server admin:8002;
}
server {
listen 80;
server_name localhost;
charset utf-8;
server_tokens off;
client_max_body_size 20M;
resolver 10.0.0.2 valid=300s;
resolver_timeout 10s;
location ~* ^/ws/ {
proxy_pass http://asgi_server;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header REMOTE_ADDR $remote_addr;
}
location ~ ^/static/(?<base_path>rest_framework|admin|drf-yasg)/(?<tail>.*)$ {
autoindex on;
alias /home/app/web/staticfiles/$base_path/$tail;
}
location ~ ^/media/(?<base_path>images)/(?<tail>.*)$ {
autoindex on;
alias /home/app/web/media/$base_path/$tail;
add_header Content-disposition "attachment";
}
location ~ ^/api|swagger|redoc/ {
proxy_pass http://wsgi_server;
proxy_pass_request_headers on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
location ~ ^/admin/ {
proxy_pass http://admin_server;
proxy_pass_request_headers on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Я не знаю, что здесь не так
Для решения проблемы:
- Добавьте функцию def ready() в apps.py следующим образом:
from django.apps import AppConfig
class GnssConfig(AppConfig):
name = 'gnss'
verbose_name = 'GNSS management'
def ready(self) -> None:
from .consumers import CoordinateConsumer # noqa
- Настройте nginx следующим образом:
location ~ ^/ws/operations|coordinates/ {
proxy_pass http://asgi_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Это пример для двух конечных точек ws/operations и ws/coordinates