我们正在尝试通过使用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