使用 uwsgi 部署 Flask,不使用 Unix 套接字连接 Nginx - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
itskingname

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

  •  1
     
  •   itskingname 2019 年 7 月 8 日 4604 次点击
    这是一个创建于 2482 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果你在 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-socke

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

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

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

    @itskingname fastcgi 老,你用 http 不是更老?任何协议,只要能高效的完成目的,都可以用。无论是 fcgi 还是 uwsgi,都比 http 有效率的多。
    cloudyplain
        10
    cloudyplain  
       2019 年 7 月 9 日   1
    这篇文章也太水了吧
    cloudyplain
        11
    cloudyplain  
       2019 年 7 月 9 日
    @ryd994 gevent 并不浪费而是为了异步非阻塞,gunicorn 和 uswgi 个人更偏向 gunicorn,代码清晰明了,另外 uswgi 是短连接。
    carlclone
        12
    carlclone  
       2019 年 7 月 9 日 via Android
    docker 没学好吧
    ryd994
        13
    ryd994  
       2019 年 7 月 9 日
    @cloudyplain 正常情况下,nginx 已经缓冲了完整请求。内网带宽假设总是能跑满,这时同步阻塞处理反而更有效率。
    举个例子,CPU 已经满载时再加多线程反而会降低性能。异步非阻塞并不总是更好。Nginx 已经把这部分做好了,前提是正确配置。
    ynkkdev
        14
    ynkkdev  
       2019 年 7 月 9 日
    > "有一个基本前提就是 Flask 程序、uwsgi、Nginx 三个东西运行在同一个服务器上。如果用 Docker,那么这三个东西甚至需要运行到一个容器里面。"
    从里开始出现偏差。即使是使用 Unix 套接字,nginx 也可用与 flask 用两个容器,挂载同一个文件区,来共享 sock 文件就可以了。所以下面的,由于不能使用多容器部署引出的长篇大论,可以不用看了。
    est
        15
    est  
       2019 年 7 月 9 日
    @cloudyplain uwsgi 是短链接的说法是哪里来的?
    @ryd994 gunicorn 的优点,uwsgi 不也是优点么。直接部署配置简单。
    tony9413
        16
    tony9413  
       2019 年 7 月 9 日
    网上这些技术文章很多都是复制粘贴的,也是正常的
    STRRL
        17
    STRRL  
       2019 年 7 月 9 日 via Android
    11 楼正解 只需把 unix domain socket 文件挂载过去就可以了
    cloudyplain
        18
    cloudyplain  
       2019 年 7 月 9 日
    @ryd994 nginx 缓冲了请求不假,但后面业务代码中一个数据库或者 redis 查询就 block 一个线程,那就需要开多个线程或进程来处理,one request per thread 显然不适用,gevent 或者 twisted 就是处理这种问题。
    sagaxu
        19
    sagaxu  
       2019 年 7 月 9 日 via Android
    一句话的信息量也能写这么长,文字功底不错
    gaigechunfeng
        20
    gaigechunfeng  
       2019 年 7 月 9 日
    [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
        21
    shuizhengqi  
       2019 年 7 月 9 日
    我 tm 真的是服气的,乌拉乌拉的说一大堆,你自己对 docker 的理解有问题吧,服务器起一个 docker 放 nginx,然后起三个 docker 放应用,映射 nginx 的端口出去,nginx 再做转发到其它容器的端口,这有问题么?
    shuizhengqi
        22
    shuizhengqi  
       2019 年 7 月 9 日
    也就是为了在这水文章吧
    itskingname
        23
    itskingname  
    OP
       2019 年 7 月 9 日
    @gaigechunfeng 是的,这样走的是 TCP
    itskingname
        24
    itskingname  
    OP
       2019 年 7 月 9 日
    @shuizhengqi 麻不麻烦~
    zw1027
        25
    zw1027  
       2019 年 7 月 9 日
    你赢了,我一开始还认真看,还以为楼主发现了什么了不得的内容

    结果...
    Imr
        26
    Imr  
       2019 年 7 月 9 日 via iPhone
    多节点 k8s 调度确实有这个烦恼,py 写的一些服务还是得依赖 gunicron 来做,http-sock 以前没发现,下午我也去瞅瞅能不能替代容器内封装 nginx 的做法
    shuizhengqi
        27
    shuizhengqi  
       2019 年 7 月 9 日
    @itskingname 你直接暴露 ip 跟端口让别人访问?
    itskingname
        28
    itskingname  
    OP
       2019 年 7 月 9 日
    @shuizhengqi 我的 ip 和端口,只暴露给专门跑在另外一台服务器上面的 Nginx。
    itskingname
        29
    itskingname  
    OP
       2019 年 7 月 9 日
    @Imr 如果帮助到了你,麻烦回来帮我怼一下反驳我的人。感谢。
    vipppppp
        30
    vipppppp  
       2019 年 7 月 9 日
    没什么请求的网站别说不使用 nginx,你甚至可以 python app.py ,我相信只要你机器正常服务也不会挂
    itskingname
        31
    itskingname  
    OP
       2019 年 7 月 9 日
    @vipppppp emmmm, 你看完了正文?
    baojiweicn2
        32
    baojiweicn2  
       2019 年 7 月 10 日 via Android
    核心的本质是要开多进程,绕开 gil。况且也可以把.socket 文件挂到 docker 外面,nginx 直接怼上去就可以了,也可以开一个 docker 的 network 也成啊。况且都 docker 了为啥不 k8s
    baojiweicn2
        33
    baojiweicn2  
       2019 年 7 月 10 日 via Android
    @shuizhengqi 直接用云商的 lbs 也可以吧。这波操作太麻烦了吧
    xhinliang
        34
    xhinliang  
       2019 年 7 月 12 日
    水文...
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     882 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 138ms UTC 20:53 PVG 04:53 LAX 13:53 JFK 16:53
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86