Django Channels 2.4 和 Websockets 在 Elastic Beanstalk 和 ElastiCache 上给出 502 错误

Posted

技术标签:

【中文标题】Django Channels 2.4 和 Websockets 在 Elastic Beanstalk 和 ElastiCache 上给出 502 错误【英文标题】:Django Channels 2.4 and Websockets giving 502 error on Elastic Beanstalk and ElastiCache 【发布时间】:2020-12-16 20:27:45 【问题描述】:

我已通过 django 频道将聊天组件集成到我的 django web 应用程序中,其方式与文档中的 here 非常相似。我使用 elasticache 来创建我的 redis 实例。我的 settings.py 如下所示:

ASGI_APPLICATION = 'MyApp.routing.application'
CHANNEL_LAYERS = 
    'default': 
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': 
            "hosts": [('my-redis-instance.cache.amazonaws.com', 6379)], #This is a placeholder in the question for the cluster's actual endpoint
        ,
    ,

在将我的应用程序部署到弹性 beantalk 时,我尝试关注 this tutorial 和 this tutorial,但没有成功。我的 django.config 文件目前如下所示:

container_commands:
    01_collectstatic:
        command: "source /opt/python/run/venv/bin/activate && python manage.py collectstatic --noinput"
    02_migrate:
        command: "django-admin.py migrate"
        leader_only: true
    03_load-data:
        command: "python manage.py load_database"
        leader_only: true

option_settings:
    aws:elasticbeanstalk:application:environment:
        DJANGO_SETTINGS_MODULE: MyApp.settings
    aws:elasticbeanstalk:container:python:
        WSGIPath: MyApp/wsgi.py
    "aws:elasticbeanstalk:container:python:staticfiles":
        /static/: "static/"
    aws:elbv2:listener:80:
        DefaultProcess: http
        ListenerEnabled: 'true'
        Protocol: HTTP
        Rules: ws
    aws:elbv2:listenerrule:ws:
        PathPatterns: /ws/*
        Process: websocket
        Priority: 1
    aws:elasticbeanstalk:environment:process:http:
        Port: '80'
        Protocol: HTTP
    aws:elasticbeanstalk:environment:process:websocket:
        Port: '5000'
        Protocol: HTTP

我还尝试创建一个 Procfile 来配置 gunicorn 和 daphne。它看起来像这样:

web: gunicorn --bind :8000 --workers 3 --threads 2 MyApp.wsgi:application
websocket: daphne -b 0.0.0.0 -p 5000 MyApp.asgi:application

附加到我的 ElastiCache redis 实例的安全组有一个入站规则,其中自定义 TCP 设置为端口 6379,源设置为任何。在尝试所有这些不同的部署方法时,我不断收到以下错误:

[Error] WebSocket connection to 'ws://my-site-url.com/ws/messages/' failed: Unexpected response code: 502

我不知道该怎么做。我看过几个与此相关的堆栈溢出帖子,但没有一个有帮助。这个post 似乎有一个有效的解决方案,但我相信它现在已经过时了。

更新:

在进行更多搜索时,我在我的 .ebextensions 文件夹中创建了一个 websockets-python.config 文件,该文件应该配置 Apache 代理服务器以允许使用 Web 套接字。我还删除了配置 Daphne 和 Gunicorn 的 Procfile。 websockets-python.config 文件如下所示:


files:
  "/etc/httpd/conf.d/proxy-pass.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      ProxyPass /ws/ ws://127.0.0.1:5000/
      ProxyPassReverse /ws/ ws://127.0.0.1:5000/

  "/etc/httpd/conf.modules.d/99-mod_proxy_wstunnel.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      <IfModule !proxy_wstunnel_module>
      LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
      </IfModule>

使用此文件重新部署后,我在 Web 套接字上收到无效状态错误,然后 Web 套接字连接最终超时。我相信网络套接字正在尝试连接,但由于某种原因无法连接(可能是我的安全组的配置方式),然后最终超时。

安全组配置:

ELB:从端口 80 上的任何地方的入站规则。从端口 80 和 5000 到任何地方的出站规则

我的服务器:ELB 在端口 80 和 5000 上的入站规则。所有流量到任何地方的出站规则。

ElastiCache 实例:来自任何地方的端口 6379 和来自服务器的端口 6439 的入站规则。 All Traffic to Anywhere 的出站规则。

【问题讨论】:

【参考方案1】:

经过几天的工作,我找到了解决方案。我无法使负载均衡器上的端口转发工作,但是我将我的配置为转发到 nginx 上的端口 5000。

redis 网址:

CHANNEL_LAYERS = 
    "default": 
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": 
            "hosts": [(REDIS_URL, 6379)],
        ,
    ,

Nginx 配置:

.platform/nginx/nginx.conf

#Elastic Beanstalk Nginx Configuration File

user                    nginx;
error_log               /var/log/nginx/error.log warn;
pid                     /var/run/nginx.pid;
worker_processes        auto;
worker_rlimit_nofile    32137;

events 
    worker_connections  1024;


http 
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    include       conf.d/*.conf;

    map $http_upgrade $connection_upgrade 
        default     "upgrade";
    

    server 
        listen        80 default_server;
        access_log    /var/log/nginx/access.log main;

        client_header_timeout 60;
        client_body_timeout   60;
        keepalive_timeout     60;
        gzip                  off;
        gzip_comp_level       4;
        gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;

        # Include the Elastic Beanstalk generated locations
        include conf.d/elasticbeanstalk/*.conf;
        

        location /ws 
            proxy_pass          http://127.0.0.1:5000;
            proxy_http_version  1.1;

            proxy_set_header    Connection          $connection_upgrade;
            proxy_set_header    Upgrade             $http_upgrade;
            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-Proto   https;
        
    


过程文件:

web: gunicorn --bind 127.0.0.1:8000 --workers=1 --threads=15 project.wsgi:application
websocket: daphne -b 0.0.0.0 -p 5000 project.asgi:application

关于安全组,在创建redis时,需要在安全组上添加当前EC2实例上使用的安全组的id,在入站的destination字段中。

【讨论】:

【参考方案2】:

~~使用 ElastiCache 时,您必须使用集群的主端点。您可以转到 ElastiCache 的仪表板,转到 Redis,然后单击下拉菜单。找到主要端点并使用它而不是my-redis-instance.cache.amazonaws.com~~

编辑,因为端点可能只是正确的:问题可能出在 ElastiCache 上的安全组中。 ElastiCache 集群应该只允许从服务器的 SG 入站(在端口 6439 上),它只允许从 ELB 的安全组(在端口 5000 上?)入站,它只允许从端口 443 和端口 80 从任何地方入站。

最后,我觉得你配置 Gunicorn 和 Daphne 很奇怪。除了 Daphne 可以运行 ASGI 之外,它们不是一回事吗? (只有使用 Gunicorn 的 Uvicorn 工作者才允许使用 ASGI;您应该只使用 Daphne 而不要启用 Gunicorn)。

这都是理论上你的 AWS 实例有足够的内存和 CPU;这也可能是个问题。我向开发人员推荐 Sentry(免费),以便您查看。由于一些巧妙的错误,我的很多 502 错误都得到了解决。

【讨论】:

是的,这就是我正在做的。我将“my-redis-instance.cache.amazonaws.com”作为占位符放在问题中,因为我不希望我的实际 redis 端点成为 Stack Overflow 等网站上的公共信息。 根据我以前的经验,502 错误代码通常是由于配置错误(我第一次没有让我的安全组正确,但你似乎把它关闭了......也许)。我很好奇,你的 ELB 上的安全组是什么?我在 ELB 上有一个 80 和 443 的安全组;对于我的服务器,它只是来自我的 ELB 的入站(您必须选择 ELB 的安全组);对于 ElastiCache,您必须将其设置为服务器的安全组。这可能就是为什么您的 ElastiCache 安全组完全暴露在您的 VPC 之外的原因;附加到服务器 SG 应该可以正常工作。 端口 6439 来自哪里还是只是一个标准? 我认为这可能是我的安全组配置的问题。我刚刚用我的安全组配置更新了这个问题。我现在没有设置 HTTPS,所以我不用担心端口 443。如何将 ElastiCache SG 连接到服务器的 SG? 我明白了。对于您服务器的 SG,您应该将 All Traffic 与 Source Type 设置为您的 ELB 的 SG。查看编辑后的答案(除非您这样做并且令人困惑)。

以上是关于Django Channels 2.4 和 Websockets 在 Elastic Beanstalk 和 ElastiCache 上给出 502 错误的主要内容,如果未能解决你的问题,请参考以下文章

如何处理高传入数据并将受限制的数据发送到 django-channels 中的 Web 套接字

带有 3rd 方数据库的 Django Channels 和 Web Socket

如何在 Heroku 上使用 Channels 和 Celery 部署 Django?

Django Channels - 根据打开的 Web 套接字的数量多次调用接收器函数

Django使用Channels实现WebSocket--下篇

如何从 Django Channels Web 套接字数据包中获取当前用户?