爱意满满的作品展示区。
itskingname

使用 uwsgi 部署 Flask,不使用 Unix 套接字连接 Nginx

  •  1
     
  •   itskingname · Jul 8, 2019 · 5245 views
    This topic created in 2683 days ago, the information mentioned may be changed or developed.

    如果你在 Google 或者百度或者某些技术社区上面搜索uwsgi + Flask,你会发现大量的文章,是教你如何使用uwsgi + Flask + Nginx搭建网站。如下图所示:

    怪现状

    而且这些文章,全部都像是约定俗成一样,一定会首先用命令行启动 uwsgi,测试 uwsgi 与 Flask 运行是否正常,然后写 uwsgi 的配置文件。然后使用Unix 套接字沟通 uwsgi 与 Nginx。所以 uwsgi 的配置文件里面一定会写成类似于下面这样:

    socket = /xxx/yyy/zzz.sock
    

    Nginx 的配置一定有类似于下面这一段:

    location / {
            include uwsgi_params;
            uwsgi_pass unix:///xxx/yyy/zzz.sock;
        }
    

    他们为什么要这样写?因为他们看的别的博客上就是这样写的!他们知其然,但是不知其所以然。

    有什么问题?

    这种写法本身没有问题,甚至 Flask 的官方文档里面也是这样写的,如下图所示:

    但是他们这样写,有一个基本前提——就是 Flask 程序、uwsgi、Nginx 三个东西运行在同一个服务器上。如果用 Docker,那么这三个东西甚至需要运行到一个容器里面。

    如果是一个小网站,服务器资源足够,那么这样写没有问题,Unix 套接字安全性高,速度也快。

    那么如果你同一个服务器上有三个 Docker 容器,每一个容器都有一个不同的网站,是不是每个容器里面都需要安装一个 Nginx ?

    对于大一些的网站,Nginx 需要做负载均衡,如果把 Nginx 和网站放在同一台服务器上,无论是 Nginx 拖垮了服务器,还是网站拖垮了服务器,都会导致很严重的问题。

    能不能实现,一个服务器上直接安装 Nginx,然后服务器上的三个网站分别在三个 Docker 容器里面,每个容器里面只有 Flask 和 uwsgi,没有 Nginx ?

    如果你的网站大一些,你在 A 服务器安装 Nginx,在 B、C、D、E、F 服务器上不安装 Nginx,只安装 uwsgi + Flask,又怎么做?

    所以进入我们今天的主题,安装 uwsgi + Flask(或者 Django),但是不安装 Nginx ( Deploy Flask with uwsgi but without Nginx )

    不使用 Unix 套接字的 uwsgi

    Unix 套接字,本质上是一个文件( Unix/Linux 哲学:一切皆文件),Nginx 和 uwsgi 通过这个文件来进行通信。所以需要 Nginx 与 uwsgi 放在同一个机器上。

    但实际上,uwsgi 本身就是一个服务器,A 服务器上的 Nginx 与 B 服务器上的 uwsgi 之间是可以通过 http 进行通信的。

    要让 uwsgi 使用 http 进行通信,我们可以修改 uwsgi 的配置文件 xxx.ini:

    [uwsgi]
    module = wsgi:app
    master = true
    process = 5
    threads = 100
    gevent = 100
    async = 100
    http-socket = 0.0.0.0:5001
    virtualenv = /Users/kingname/.local/share/virtualenvs/ActiveScoreApi-Ax_h-Y5w
    

    其他参数的意义不是本文的重点,我们要关心的是http-socket = 0.0.0.0:5001。它的作用把网站部署在本机的 5001 端口,并允许外网通过 http 访问。

    写了这个配置文件以后,通过以下命令来启动 uwsgi:

    uwsgi --ini xxx.ini
    

    然后你使用IP:5001就可以访问你的网站了。此时,如果你有 Nginx,那么只需要在 Nginx 上设置反向代理,把 80 端口的请求代理到 5001 端口即可。

    同理,把 uwsgi 和网站放在 Docker 镜像里面,容器开放 5001 端口。宿主机或者其他机器上的 Nginx 直接通过 IP:端口 就可以访问容器里面的 uwsgi,不再需要设置 Unix 套接字了。

    另外,如果你阅读过 uwsgi 的官方文档,你还会发现,除了http-socket = 0.0.0.0:5001外,你也可以把它改成http = 0.0.0.0:5001。那么这两种写法是否一样呢?

    在官方文档里面特别区分了它们的使用场景:

    The http and http-socket options are entirely different beasts. The first one spawns an additional process forwarding requests to a series of workers (think about it as a form of shield, at the same level of apache or nginx), while the second one sets workers to natively speak the http protocol. TL/DR: if you plan to expose uWSGI directly to the public, use --http, if you want to proxy it behind a webserver speaking http with backends, use --http-socket.

    简言之,如果你直接把 uwsgi 作为服务器,uwsgi 启动以后,直接就把 IP:端口拿给别人访问,那么你就可以使用http;如果你的 uwsgi 前面还挡了一个 Nginx,那么你就使用http-socket。

    本文首发于我的微信公众号 未闻 Code ( ID:itskingname )

    34 replies  •  2019-07-12 10:48:07 +08:00
    miniyao
    miniyao
    Jul 8, 2019❤️2
    1
    flask 不是应该用 gunicorn 吗?
    est
    est
    Jul 8, 2019
    2
    还可以通过 fastcgi。
    itskingname
    itskingname
    OP
    Jul 8, 2019
    3
    @miniyao 是的,gunicorn 确实非常简单。
    itskingname
    itskingname
    OP
    Jul 8, 2019
    4
    @est fastcgi 有点老了。
    NoirStrike
    NoirStrike
    Jul 8, 2019
    5
    收藏下
    ericls
    ericls
    Jul 9, 2019 via iPhone
    6
    还可以 mount static
    dongxiaozhuo
    dongxiaozhuo
    Jul 9, 2019
    7
    总结下来就两个问题:
    1. Nginx 与 uWSGI + Flask App 是否在同一个系统空间下(容器是隔离的系统)?
    同一个系统空间: 既可以是 sock 通信(文件),也可以是 tcp 通信(网络,uWSGI 监听一个端口)
    不同的系统空间:只能使用 tcp 通信

    2. Nginx 与 uWSGI 之间的通信用 uwsgi (注意全是小写)协议,还是用 HTTP 协议?
    uwsgi 协议是二进制的。使用起来的区别是 Nginx 的 pass 写成 proxy_pass 转发的是 HTTP 协议,uwsgi_pass 转发的是 uwsgi 协议。
    ryd994
    ryd994
    Jul 9, 2019
    8
    1. unix socket 可以通过 mount 共享
    2. uwsgi 协议可以直接走 TCP,不需要 http。https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
    Nginx 本身就需要解析 http 协议,然后反代的时候又要重新组合 http 请求,然后 uwsgi 又要再解析一次,这完全就是浪费。
    uwsgi 协议的目的之一就是避免这样的浪费
    ryd994
    ryd994
    Jul 9, 2019
    9
    @miniyao gunicorn 的优点是可以直接部署,配置简单。
    前面套 nginx 或者反代的话的话没必要用 gunicorn。gevent 的目标场景是大量闲置连接。有反代的情况下,nginx 负责处理 buffer,而且能 pipeline 请求,这时用 gevent 反而浪费。

    @itskingname fastcgi 老,你用 http 不是更老?任何协议,只要能高效的完成目的,都可以用。无论是 fcgi 还是 uwsgi,都比 http 有效率的多。
    cloudyplain
    cloudyplain
    Jul 9, 2019❤️1
    10
    这篇文章也太水了吧
    cloudyplain
    cloudyplain
    Jul 9, 2019
    11
    @ryd994 gevent 并不浪费而是为了异步非阻塞,gunicorn 和 uswgi 个人更偏向 gunicorn,代码清晰明了,另外 uswgi 是短连接。
    carlclone
    carlclone
    Jul 9, 2019 via Android
    12
    docker 没学好吧
    ryd994
    ryd994
    Jul 9, 2019
    13
    @cloudyplain 正常情况下,nginx 已经缓冲了完整请求。内网带宽假设总是能跑满,这时同步阻塞处理反而更有效率。
    举个例子,CPU 已经满载时再加多线程反而会降低性能。异步非阻塞并不总是更好。Nginx 已经把这部分做好了,前提是正确配置。
    ynkkdev
    ynkkdev
    Jul 9, 2019
    14
    > "有一个基本前提——就是 Flask 程序、uwsgi、Nginx 三个东西运行在同一个服务器上。如果用 Docker,那么这三个东西甚至需要运行到一个容器里面。"
    从这里开始出现偏差。即使是使用 Unix 套接字,nginx 也可用与 flask 用两个容器,挂载同一个文件区,来共享 sock 文件就可以了。所以下面的,由于不能使用多容器部署引出的长篇大论,可以不用看了。
    est
    est
    Jul 9, 2019
    15
    @cloudyplain uwsgi 是短链接的说法是哪里来的?
    @ryd994 gunicorn 的优点,uwsgi 不也是优点么。直接部署配置简单。
    tony9413
    tony9413
    Jul 9, 2019
    16
    网上这些技术文章很多都是复制粘贴的,也是正常的
    STRRL
    STRRL
    Jul 9, 2019 via Android
    17
    11 楼正解 只需把 unix domain socket 文件挂载过去就可以了
    cloudyplain
    cloudyplain
    Jul 9, 2019
    18
    @ryd994 nginx 缓冲了请求不假,但后面业务代码中一个数据库或者 redis 查询就 block 一个线程,那就需要开多个线程或进程来处理,one request per thread 显然不适用,gevent 或者 twisted 就是处理这种问题。
    sagaxu
    sagaxu
    Jul 9, 2019 via Android
    19
    一句话的信息量也能写这么长,文字功底不错
    gaigechunfeng
    gaigechunfeng
    Jul 9, 2019
    20
    [uwsgi]
    socket=127.0.0.1:8086
    plugin=python
    wsgi-file=app.py
    callable= wxapp
    #master=true
    #processes= 4
    #threads= 2
    stats= 127.0.0.1:9192
    daemonize = ./uwsgi.log

    socket 后面也可以直接接 ip 加端口号吧。

    和楼主写的 http-socket 是同样的效果吗?
    shuizhengqi
    shuizhengqi
    Jul 9, 2019
    21
    我 tm 真的是服气的,乌拉乌拉的说一大堆,你自己对 docker 的理解有问题吧,服务器起一个 docker 放 nginx,然后起三个 docker 放应用,映射 nginx 的端口出去,nginx 再做转发到其它容器的端口,这有问题么?
    shuizhengqi
    shuizhengqi
    Jul 9, 2019
    22
    也就是为了在这水文章吧
    itskingname
    itskingname
    OP
    Jul 9, 2019
    23
    @gaigechunfeng 是的,这样走的是 TCP
    itskingname
    itskingname
    OP
    Jul 9, 2019
    24
    @shuizhengqi 麻不麻烦~
    zw1027
    zw1027
    Jul 9, 2019
    25
    你赢了,我一开始还认真看,还以为楼主发现了什么了不得的内容

    结果...
    Imr
    Imr
    Jul 9, 2019 via iPhone
    26
    多节点 k8s 调度确实有这个烦恼,py 写的一些服务还是得依赖 gunicron 来做,http-sock 以前没发现,下午我也去瞅瞅能不能替代容器内封装 nginx 的做法
    shuizhengqi
    shuizhengqi
    Jul 9, 2019
    27
    @itskingname 你直接暴露 ip 跟端口让别人访问?
    itskingname
    itskingname
    OP
    Jul 9, 2019
    28
    @shuizhengqi 我的 ip 和端口,只暴露给专门跑在另外一台服务器上面的 Nginx。
    itskingname
    itskingname
    OP
    Jul 9, 2019
    29
    @Imr 如果帮助到了你,麻烦回来帮我怼一下反驳我的人。感谢。
    vipppppp
    vipppppp
    Jul 9, 2019
    30
    没什么请求的网站别说不使用 nginx,你甚至可以 python app.py ,我相信只要你机器正常服务也不会挂
    itskingname
    itskingname
    OP
    Jul 9, 2019
    31
    @vipppppp emmmm, 你看完了正文?
    baojiweicn2
    baojiweicn2
    Jul 10, 2019 via Android
    32
    核心的本质是要开多进程,绕开 gil。况且也可以把.socket 文件挂到 docker 外面,nginx 直接怼上去就可以了,也可以开一个 docker 的 network 也成啊。况且都 docker 了为啥不 k8s
    baojiweicn2
    baojiweicn2
    Jul 10, 2019 via Android
    33
    @shuizhengqi 直接用云商的 lbs 也可以吧。这波操作太麻烦了吧
    xhinliang
    xhinliang
    Jul 12, 2019
    34
    水文...
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   948 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 72ms · UTC 22:22 · PVG 06:22 · LAX 14:22 · JFK 17:22
    ♥ Do have faith in what you're doing.