Flask 应用如何部署

1. Why Flask+Gunicorn+Nginx

Flask+Gunicorn+Nginx是最经常使用的Flask部署方案,你们深究过为什么用这样的搭配么?html

1.1 Why?

Flask 是一个web框架,而非web server,直接用Flask拉起的web服务仅限于开发环境使用,生产环境不够稳定,也没法承受大量请求的并发,在生茶环境下须要使用服务器软件来处理各类请求,如Gunicorn、 Nginx或Apache,而Gunicorn+Nginx的搭配,好处多多,一方面基于Nginx转发Gunicorn服务,在生产环境下能补充Gunicorn服务在某些状况下的不足,另外一方面,若是作一个Web网站,除了服务外,还有不少静态文件须要被托管,这是Nginx的强项,也是Gunicorn不适合作的事情。因此,基于Flask开发的网站,部署时用Gunicorn和Nginx,是一个很好的选择。前端

1.2 Anything More?

一、为何须要Nginx转发Gunicorn服务?

Nginx功能强大,使用Nginx有诸多好处,但用Nginx转发Gunicorn服务,重点是解决“慢客户端行为”给服务器带来的性能下降问题;另外,在互联网上部署HTTP服务时,还要考虑的“快客户端响应”、SSL处理和高并发等问题,而这些问题在Nginx上一并能搞定,因此在Gunicorn服务之上加一层Nginx反向代理,是个一举多得的部署方案。python

二、为何会有“慢客户端行为”带来的服务性能下降问题?

服务器和客户端的通讯,咱们简略的分为三个部分:request,request handling,和response,即客户端向服务器发起请求,服务器端响应并处理请求,和将请求结果返回客户端,这三个过程。nginx

一般,request handling这部分即服务端的计算,拼的是服务器的性能,处理是比较高效和稳定的,而request和response部分,影响因素比较多,若是这三个过程放到同一个进程中同步处理,若是request和response部分耗时比较多,会使计算资源被占据并没有法及时释放,致使计算资源没法有效利用,下降服务器的处理能力。git

上述“慢客户端行为”,指的就是request(或response)部分耗时比较多的状况,Gunicorn刚好会把上面三个过程放到同一个进程中,当出现“慢客户端行为”时,效率很低:github

Gunicorn 是一个pre-forking的软件,这类软件对低延迟的通讯,如负载均衡或服务间的互相通讯,是很是有效的。但pre-forking系统的不足是,每一个通讯都会独占一个进程,当向服务器发出的请求多于服务器可用的进程时,因为服务器端没有更多进程响应新的请求,其响应效率会下降。web

对于Web网站或服务而言,因为request和response延时是不可控的,咱们须要在考虑处理高延迟客户端请求的状况。这些请求会占据服务器端的进程。当慢客户端直接与服务通讯时,因为慢客户端请求会占据进程,可用于处理新请求的进程就会减小,若是有不少慢客户端请求把全部进程都占据后,新的请求只能等待有进程被释放掉后,获得响应。另外,若是应用但愿有更高的并发,服务器与客户端的通讯要更高效,异步的通讯会比同步的通讯更有效。mongodb

Nginx这类异步的服务器软件擅长用不多的内存和cpu开销来处理大量的请求。因为他们擅长于同时处理大量客户端请求,因此慢客户端请求对他们影响不大。就Nginx而言,如今通常的服务器硬件条件下,同时处理上万个请求都不在话下。docker

因此把Nginx挡在pre-forking服务前面处理请求是一种很好的选择。Nginx可以异步、高并发的响应客户端request(慢客户端请求对Nginx影响不大),Nginx一旦接收到的请求后马上转给Gunicorn服务处理,处理结果再由Nginx以response的形式发回给客户端。这样,整个服务端和客户端的通讯,就由原来仅经过Gunicorn的同步通讯,变成了基于Nginx和Gunicorn的异步通讯,通讯效率和并发能力获得大大提高。bash

对于网站而言,除了要考虑上面介绍的状况,还要考虑各类静态文件的托管问题。静态文件既包括CSS、JavaScript等前端文件,也包括图片、视频和各种文档等,因此静态文件要么可能会比较大,要么会调用比较频繁,静态文件的托管功能,就是要保证各种静态能正常的加载、预览或下载,这其实就是Response耗时长的“慢客户端行为”。用Gunicorn托管静态文件,也会严重影响Gunicorn的响应效率,而这偏偏又是Nginx擅长的工做,因此静态文件的托管也交给Nginx搞定就好。

2. Flask网站如何部署

结合上一节的解释,Flask网站如何部署也很明确了:

  1. 用一个服务器软件(如Gunicorn)把Flask写好的应用拉起来
  2. 用Nginx给上一步拉起的应用作一个反向代理
  3. 网站涉及到的静态文件,用Nginx作文件托管

常见的服务器软件是Gunicorn和uWSGI,因为Gunicorn配置使用简单,效率也不错,Gunicorn拉起Flask网站的配置极为简单,因此一般用Gunicorn来部署Flask网站是最多见的部署方案。(另,Gevin我的还有一个喜欢Gunicorn的缘由是,Gunicorn是纯Python写的,直接pip安装便可,而uwsgi还要系统装额外的依赖,这在与docker配合使用时,Gunicorn的简单性尤其突出)

对于静态文件的托管,因为在开发阶段一般会基于Flask框架作静态文件托管的实现,因此当用Gunicorn拉起Flask网站时,网站已经实现了基于Gunicorn的文件托管功能,因此配置Nginx的静态文件托管URL时,能够直接配置成与基于Gunicorn托管一致的文件路径,这样能简化开发和部署的逻辑,并且因为Nginx比Gunicorn更靠外一层,客户端请求静态文件时,Nginx就直接返回Response了,不用担忧会请求到Gunicorn中去影响服务器效率。

2.1 Gunicorn

Gunicorn如何部署Flask网站,直接看Flask或Gunicorn官方文件便可,一般只要执行相似下面的一行命令:

/usr/local/bin/gunicorn -w 2 -b :4000 manage:app 

2.2 Nginx

用Nginx作反向代理和托管静态文件,也很简单,我这里提供一个可用Demo,更多玩法你们自行查阅Nginx文档吧

server { listen 80; server_name localhost; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { proxy_pass http://localhost:8000/; proxy_redirect off; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /media { alias /usr/share/nginx/html/media; } location /static { alias /usr/share/nginx/html/static; } } 

其中,作反向代理的配置是:

location / { proxy_pass http://localhost:8000/; proxy_redirect off; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } 

作静态文件托管的配置是:

location /media  {
        alias /usr/share/nginx/html/media; } location /static { alias /usr/share/nginx/html/static; } 

我这里对两个文件夹的文件作了托管。

3. 基于Docker的Flask网站部署

Docker具备一次部署,处处运行的好处,把上述传统部署的方法,封装到docker image中,而后配合Docker Compose编排服务,在实践中更方便。

3.1 构建Flask网站的镜像

一般,镜像中包含Flask网站的运行环境,而后把Gunicorn拉起服务做为image的运行命令便可,好比,个人OctBlog的Dockerfile编写以下:

# MAINTAINER Gevin <flyhigher139@gmail.com> # DOCKER-VERSION 18.03.0-ce, build 0520e24 FROM python:3.6.5-alpine3.7 LABEL maintainer="flyhigher139@gmail.com" RUN mkdir -p /usr/src/app && \ mkdir -p /var/log/gunicorn WORKDIR /usr/src/app COPY requirements.txt /usr/src/app/requirements.txt RUN pip install --no-cache-dir gunicorn && \ pip install --no-cache-dir -r /usr/src/app/requirements.txt COPY . /usr/src/app ENV PORT 8000 EXPOSE 8000 5000 CMD ["/usr/local/bin/gunicorn", "-w", "2", "-b", ":8000", "manage:app"] 

这里,Gevin直接用了最小的Python-alpine镜像做为基础镜像,大大减小了即将构建的Flask应用镜像的体积。对于alpine这种只有几M的极简image而言,不安装其余系统依赖,直接pip install uwsgi就会报错。

3.2 Nginx 相关的配置

Nginx上主要作反向代理和静态文件托管,和上面的配置文件一致,如:

server { listen 80; server_name localhost; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { proxy_pass http://blog:8000/; proxy_redirect off; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /media { alias /usr/share/nginx/html/media; } location /static { alias /usr/share/nginx/html/static; } } 

这个配置文件和上一章节的惟一区别是,第10行的proxy_pass http://blog:8000/; 这里的反向代理的服务为blog,是下面Docker-compose中配置的OctBlog网站服务。

3.3 用Docker-compose编排服务

OctBlog的Docker-compose编排文件以下:

version: '3' services: blog: # restart: always image: gevin/octblog:0.4.1 volumes: - blog-static:/usr/src/app/static env_file: .env networks: - webnet mongo: # restart: always image: mongo:3.2 volumes: - /Users/gevin/projects/data/mongodb:/data/db networks: - webnet blog-proxy: # restart: always image: nginx:stable-alpine ports: - "8080:80" volumes: - ./default.conf:/etc/nginx/conf.d/default.conf - blog-static:/usr/share/nginx/html/static:ro - blog-static:/usr/share/nginx/html/media:ro networks: - webnet volumes: blog-static: networks: webnet: 

其中,为了让多个服务能互通,建立了自定义的network webnet,为了让文件能在多个服务之间共享,公用了volume blog-static

4. 其余Python web网站的部署

基于上面的内容,触类旁通,咱们也能够梳理出Python 各种web框架作网站部署时的通常套路:

  1. 用一个Python WSGI HTTP Server来部署代码,如:
  2. 用Nginx作反向代理
  3. 作好静态文件的托管
相关文章
相关标签/搜索