我们正在尝试通过使用Django Channels 2,AWS和Nginx + Daphne在我们的网站上配置实时聊天。我们的设置在本地运行良好,但是在部署到生产环境时遇到了问题。
我们的生产环境包含两个使用Elastic Container Service(Fargate)部署到AWS的Docker容器。前面运行的容器是一个nginx配置,它充当代理服务器来提供静态文件。第二个容器运行我们的API / Django站点。代理在端口8000上运行,并将传入的请求转发到在端口9000上运行的API / Django容器。我还将注意到,我们正在使用terraform配置我们的AWS环境。
我已经参考了完成相似设置的多篇文章。例如:
https://medium.com/@elspanishgeek/how-to-deploy-django-channels-2-x-on-aws-elastic-beanstalk-8621771d4ff0
但是,此设置使用的是我们未使用的Elastic Beanstalk部署。
Image of setup example
代理Dockerfile:

FROM nginxinc/nginx-unprivileged:1-alpine
LABEL maintainer='CodeDank'

COPY ./default.conf.tpl /etc/nginx/default.conf.tpl
COPY ./uwsgi_params /etc/nginx/uwsgi_params

ENV LISTEN_PORT=8000
ENV APP_HOST=app
ENV APP_PORT=9000

USER root

RUN mkdir -p /vol/static
RUN chmod 755 /vol/static
RUN touch /etc/nginx/conf.d/default.conf
RUN chown nginx:nginx /etc/nginx/conf.d/default.conf

COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

USER nginx

CMD ["/entrypoint.sh"]
API /站点Dockerfile:
FROM python:3.7-alpine3.11
LABEL maintainer="CodeDank"

ENV PYTHONUNBUFFERED 1
ENV PATH="/scripts:${PATH}"

RUN pip install --upgrade pip

COPY ./requirements.txt /requirements.txt
RUN apk add --update --no-cache postgresql-client jpeg-dev
RUN apk add --update --no-cache --virtual .tmp-build-deps \
        gcc libc-dev linux-headers postgresql-dev \
        musl-dev zlib zlib-dev
RUN apk add --update --no-cache libressl-dev musl-dev libffi-dev
RUN apk add --update --no-cache g++ freetype-dev jpeg-dev
RUN pip install -r /requirements.txt
RUN apk del .tmp-build-deps

RUN mkdir /app
WORKDIR /app
COPY ./app /app
COPY ./scripts /scripts
RUN chmod +x /scripts/*

RUN mkdir -p /vol/web/media
RUN mkdir -p /vol/web/static
RUN adduser -D user
RUN chown -R user:user /vol/
RUN chmod -R 755 /vol/web
USER user

CMD ["entrypoint.sh"]
(入口点脚本如下所示)
我们创建了一个AWS Elasticache Redis服务器,用作Django通道的CHANNEL_LAYERS后端。 “REDIS_HOSTNAME”环境变量是Redis服务器的端点地址。
# Channels Settings
ASGI_APPLICATION = "app.routing.application"
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [
                (os.environ.get('REDIS_HOSTNAME'), 6379)
            ],
        },
    },
}
asgi.py文件:
import os
import django
from channels.routing import get_default_application


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
django.setup()
application = get_default_application()
根据Channels文档,我们尝试配置daphne以在我们的项目中运行asgi应用程序。理想情况下,我们希望此设置使nginx代理服务器将所有websocket请求转发到运行在端口9001上的daphne服务器。我们的所有websocket端点都将包含/ ws /,因此nginx代理配置已如下定义。
default.conf.tpl:
upstream channels-backend {
 server localhost:9001;
}

server {
    listen ${LISTEN_PORT};

    location /static {
        alias /vol/static;
    }

    location / {
        uwsgi_pass              ${APP_HOST}:${APP_PORT};
        include                 /etc/nginx/uwsgi_params;
        client_max_body_size    4G;
    }

    location /ws/ {

         proxy_pass http://channels-backend;
         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;
    }
}
代理入口点脚本:
#!/bin/sh

set -e

envsubst '${LISTEN_PORT},${APP_HOST},${APP_PORT}' < /etc/nginx/default.conf.tpl > /etc/nginx/conf.d/default.conf
nginx -g 'daemon off;'
API /站点入口点脚本:
#!/bin/sh

set -e

python manage.py collectstatic --noinput
python manage.py wait_for_db
python manage.py migrate

uwsgi --socket :9000 --workers 4 --master --enable-threads --module app.wsgi

daphne -b 0.0.0.0 -p 9001 app.asgi:application
尝试连接到我们网站上的websocket时,返回502错误。
Error during WebSocket handshake: Unexpected response code: 502.
我怀疑daphne服务器未按预期运行,或者未使用nginx服务器正确配置。在API入口点脚本中,daphne命令是否可以按当前状态运行?或者,是否有我们缺少的任何东西使daphne在nginx代理后面运行?我最初的想法是daphne命令不能在入口点脚本中的uwsgi命令之后运行。但是,我不确定该命令还需要放置在何处才能运行daphne进程。
代理的cloudwatch日志不是非常详细,但是尝试连接到站点上的Websocket时,我收到此错误消息。
[error] 8#8: *53700 connect() failed (111: Connection refused) while connecting to upstream, client: 10.1.1.190, server: , request: "GET /ws/chat/djagno/ HTTP/1.1", upstream: "http://127.0.0.1:9001/ws/chat/djagno/", host: "mycustomdomain.net"
我已经看到有其他方法可以解决此问题,其中不包括使用Nginx代理将websocket通信定向到daphne。也许我们的方法不是最好的解决方案?我们欢迎其他配置。
任何反馈将不胜感激。谢谢!

最佳答案

我想到的一件事是,您是否正在扩展Nginx容器?为了使websocket正常工作,您可能需要在Application Load Balancer上启用 session 粘性。
参考:
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html#sticky-sessions

10-08 20:03