gunicorn + nginx:通过套接字或代理服务器?

Posted

技术标签:

【中文标题】gunicorn + nginx:通过套接字或代理服务器?【英文标题】:gunicorn + nginx: Server via socket or proxy? 【发布时间】:2013-11-23 19:48:09 【问题描述】:

我见过两种使用 gunicorn 和 nginx 托管 django 应用程序的策略。

一种策略是在网络端口上运行 gunicorn。例如(来自http://goodcode.io/blog/django-nginx-gunicorn/):

location / 
    proxy_pass_header Server;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Scheme $scheme;
    proxy_connect_timeout 10;
    proxy_read_timeout 10;
    proxy_pass http://localhost:8000/;

另一种策略是在启动时将 gunicorn 绑定到 UNIX 套接字(例如 http://michal.karzynski.pl/blog/2013/06/09/django-nginx-gunicorn-virtualenv-supervisor/)

upstream hello_app_server 
    server unix:/tmp/gunicorn.sock fail_timeout=0;


...

location / 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    if (!-f $request_filename) 
        proxy_pass http://hello_app_server;
        break;
    

认为哪种策略更好?任何 cmets 的正确方法?我倾向于使用套接字方法,因为我想象 TCP 会引入开销。我最关心的是我见过的实现示例之间在标头、连接超时等方面的差异。

【问题讨论】:

你能解释一下fail_timout=0吗? "fail_timeout=0 意味着我们总是重试上游,即使它未能返回良好的 HTTP 响应(以防 Unicorn master 核对单个 worker 超时)。" The docs 说:如果组中只有一个服务器,则忽略max_fails、fail_timeout和slow_start参数,永远不会认为这样的服务器不可用。 【参考方案1】:

除了较小的 TCP/IP 开销外,没有太大区别。每个listen() 套接字都有一个连接队列,而accept() 只是从该队列中弹出一个连接。在 gunicorn 中,每个工作人员只是从该队列中弹出一个新连接,因为它不会改变。区别在于性能(套接字快一点)和可移植性(端口:IP 更灵活)。 Unix 域套接字将为您提供更好的性能,而连接到 localhost 的套接字为您提供更好的可移植性,如果您将服务器应用程序移动到不同的操作系统,您只需将 IP 地址从 localhost 更改为不同的主机名即可.

【讨论】:

在安全性(即暴露端口)方面有什么不同吗?还是因为ALLOWED_HOSTS 将有效域列入白名单,这无关紧要? 你不应该暴露端口,如果你的 WSGI 服务在端口 8000 上运行并且你的 nginx 在本地使用该端口的上游并且 nginx 在端口 80 上,你只需要暴露端口80. 所以不应该有任何安全问题,应该没有必要暴露 WSGI 端口。【参考方案2】:

会更喜欢 TCP/IP 上的套接字流量,因为不需要打开额外的端口。打开的端口越少,你的系统就越坚固

正如这里所说的“偏执” https://hynek.me/talks/python-deployments/

“具有限制性权限的 UNIX 文件套接字是你的朋友。你可以停止使用端口号”

【讨论】:

【参考方案3】:

如果您的网络服务器和应用服务器(wsgi)都存在于同一台机器上,那么套接字流量将是一个简单的选择。但是,您将需要通过网络连接的网络端口,因为套接字不能在网络上工作,所以..

    如果 webserver 和 appserver 位于同一台机器上 - GO SOCKET 如果 webserver 和 appserver 在网络上 - GO PORTS

【讨论】:

【参考方案4】:

我知道我参加这个聚会迟到了,如果你想让它在带有 SELinux 强制的 Red Hat 风味 Linux 上工作,这可能会有用。

如果您尝试使用套接字,则会很麻烦。我放弃了。

如果您尝试通过任意 TCP 端口绑定 Gunicorn,它也会妨碍您。默认情况下(在 Centos 1708 上),SELinux 很乐意使用一部分端口:80,81,443,488,8008,8009,8443,9000

我选择了 8009,但显然你可以使用其他一些端口

semanage -a -t http_port_t -p tcp $PORTNUMBER

并查看端口列表

semanage port -l

【讨论】:

【参考方案5】:

这是我通过 Unix 套接字测试 TCP 代理的结果:

设置: nginx + gunicorn + django 在 AWS 上的 4 个 m4.xlarge 节点上运行。 每个节点的设置都是统一的(来自同一图像)。

在大约 30 分钟的时间内发出 100 万次请求:

由于其中一台服务器上运行不相关的作业,一个实例的 CPU 使用率为 100%。其他 3 个是 70% CPU,每个代表实际应用程序负载。

TCP 与套接字几乎相同

发出 1000000 个请求的时间

TCP 代理是 27 分钟

unix 套接字需要 31 分钟。

在这个特定的设置中没有 unix socket 性能优势。

【讨论】:

以上是关于gunicorn + nginx:通过套接字或代理服务器?的主要内容,如果未能解决你的问题,请参考以下文章

通过优化Gunicorn配置获得更好的性能

如何在 Amazon EC2 上使用 Gunicorn 设置 Nginx 代理缓冲?

Gunicorn 不会从 nginx 记录真实 ip

Django应用程序中的空REMOTE_ADDR值,当使用nginx作为反向代理与gunicorn

python web 部署:nginx + gunicorn + supervisor + flask 部署笔记

如何更改 nginx proxy_pass 中的 request_uri?