• CentOS下实现Flask + Virtualenv + uWSGI + Nginx部署


    一、项目简介

      在本文中,将一步一步搭建一个简单的Flask + Virtualenv + uWSGI + Nginx 架构的Web服务,可以作为新手的学习也可作为记录备忘。

      如果你安装好了环境并有一定基础可以直接从第五节开始部署。

      项目中只是演示了浏览器访问地址,获得文本返回的过程,本人尽量把配置解释的清晰。基于搭建好的架构,后续可以将业务层(Python)进行扩展,本文不做研究 ,比如:

      1、扩展业务代码:实现json、静态资源等等的请求响应。

      2、基于业务的数据库查询和部署。

      3、服务器端的部署完备(优化):域名、缓存、负载均衡、安全、备份、防火墙、异步IO等。

      4、项目代码管理。

    二、框架介绍

    1、Virtualenv

    • virtualenv可以创建新的Python环境,独立的虚拟环境之间互不干扰,在有些场景下非常有用,例如:

      (1)同时拥有两个python项目,一个是python2.7的,另一个是python3的,可以创建两个虚拟环境。

      (2)同时拥有两个python项目,都依赖一个module(模块)的不同版本,可以创建两个不同的虚拟环境,分别安装这个module的不同版本。

          (3)同时拥有两个python项目,各自依赖不同的module,可以在两个不同的虚拟环境里安装模块并开发项目。

    • virtualenvwrapper工具:在virtualenv的基础上提供了一些更方便的命令,本文只介绍通过此工具管理虚拟环境。

    2、Flask

      Flask是一个Python的轻量级web框架,是基于Python开发并且依赖jinja2模板(模板语言)和 Werkzeug WSGI(WSGI工具集)服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。

      Werkzeug的补充:Werkzeug是WSGI工具包,他可以作为一个Web框架的底层库。它不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西( python web WSGI 开发相关的功能),例如 :

    • 路由处理:如何根据请求 URL 找到对应的视图函数
    • request 和 response 封装: 提供更好的方式处理request和生成response对象
    • 自带的 WSGI server: 测试环境运行WSGI应用

    3、uWSGI

    (1)WSGI

      全称 Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口(协议)。WSGI是Web 服务器(uWSGI)与 Web 应用程序或应用框架(Flask,Django)之间的一种低级别的接口。可以参考阅读 PEP3333

      WSGI 分为两个部分

    • Server/Gateway: 即是HTTP Server, 负责从客户端(Nginx、apache、IIS)接收请求,将 request 转发给 application, 并将 application(可能是个Flask应用) 返回的response 返回给客户端。
    • Application/Framework: 一个python web 应用或 web 框架接收由 server 转发的request,处理请求,并将处理结果返回给 server。

    (2)uWSGI

      uWSGI是一个Web服务器,C语言编写,它实现了WSGI协议、uwsgi、http等协议。它要做的就是把HTTP协议转化成语言支持的网络协议。比如把HTTP协议转化成WSGI协议,让Python可以直接使用。

    (3)uwsgi

      与WSGI一样,是uWSGI服务器的独占通信协议,用于定义传输信息的类型(type of information)。每一个uwsgi packet前4byte为传输信息类型的描述。

    4、Nginx

      Nginx ("engine x") 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器 。可以作为一个HTTP服务器进行网站的发布处理,另外nginx可以作为反向代理进行负载均衡的实现。因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。

     

    三、访问过程

    1、描述一:web客户端->web服务器->业务代码的访问过程 (通过协议通信)

     

    2、描述二:不同用户场景->服务器->不同应用的访问过程(简单架构,不包括复杂的部署,缓存等)

      (1) 部署多个APP,采用单个Nginx,多个uwsgi+Flask,每个uWSGI监听不同端口。(标准)

      (2) 部署多个APP,采用单个Nginx,单个uWSGI,多个Flask路由,Nginx和uwsg通过一个端口通信。(不好)

      注意:这种模式不是标准方式,“转发”任务应该是由Nginx实现,通过多个端口和uWSGI通信,每个端口对应一个应用,如同描述一。这里靠路由区分,是不好的。

     

    四、环境准备

     1、基本环境

     (1)阿里ECS服务器 CentOS7.4 ,如需用户密钥部署可以参考https://www.cnblogs.com/cleven/p/10899171.html

     (2)Python环境:Python3.7,pip ,安装过程省略。

     2、虚拟环境:

     (1)安装  

    sudo pip3 install virtualenv
    sudo pip3 install virtualenvwrapper

     (2)创建虚拟环境

    mkvirtualenv -p python3 env1

      -p 后面的参数指定了python3(也有可能要换成python3.2/python3.4,具体要看你系统里面/use/bin/里面的文件是什么名字),如果去掉这个参数,就会使用系统默认的python。最后一个参数env1是创建的这个环境的名字。

     (3)管理虚拟环境

    deactivate            # 退出当前虚拟环境
    workon env1           # 使用虚拟环境env1
    rmvirtualenv env1     # 删除某个虚拟环境
    lsvirtualenv          # 列出所有虚拟环境        
    cdvirtualenv      # 进入虚拟环境存储目录

     (4)在服务器上同步本地的虚拟环境包

      在开发机器上执行下面这个命令,来列出所有的包并保存到packages.txt,其中-l参数是只列出当前虚拟环境的包:

    pip3 freeze -l > packages.txt

      然后在部署到生产环境的时候,把packages.txt也复制到每个机器,并在每个机器上执行:

    pip3 install -r packages.txt

     (5)注意点

      安装完虚拟环境,重启shell后,workon等命令就无法使用了,这是因为没有添加环境变量:

    export WORKON_HOME=/home/.virtualenv  #虚拟环境所放置的目录,可以自行指定
    source /usr/local/bin/virtualenvwrapper.sh

      配置完上面,使用 source ~/.bashrc 命令就可以了

     3、web环境

     (1)Flask ,虚拟环境下安装

    pip3 install  flask

       检验一下Flask和虚拟环境的安装情况:

       <1>  创建项目目录

    mkdir webtest 

         <2>  进入虚拟环境

    workon env1

       <3> 进入webtest,创建hello.py

    from flask import Flask
    app = Flask(__name__)
    
    @app.route("/app/flask/")
    def hello_flask():
        return "Hello Flask!"
    
    if __name__ == "__main__":
    
        #Flask 开启监听所有地址的8888端口
        app.run(host='0.0.0.0', port=8888)

       <4> 启动服务,并测试。 Flask是有内置web server的(一般只是测试时使用,不安全,可控性差,线上还是使用uWSGI部署)

    python3 hello.py

        在浏览器访问 [服务器的ip地址]:8888/app/flask/ ,浏览器中如果显示“Hello Flask”,则Flask配置正常。

     (2)uWSGI,虚拟环境下安装

    pip3 install uwsgi

     (3)Nginx:不用在虚拟环境下安装

    sudo yum install nginx

    五、部署步骤

     1、创建uWSGI项目目录 

     在/home/project/mysite目录下创建如下结构

     

     2、编写run.py 启动脚本

    from flask import Flask
    app = Flask(__name__)
    
    @app.route("/app/uwsgi/")
    def hello_flask():
        return "Hello uWSGI!"

     3、配置uWSGI:

     (1)进入项目目录,编辑uwsgi.ini 

    vim uwsgi.ini

     (2)下面给出配置文件的详细说明(有些字段注释掉了,毕竟我们是简单的搭建uWSGI服务)

    [uwsgi]
    
    chdir=/home/project/mysite/               # 项目目录
    
    home=/home/zhangqi/.virtualenvs/env1      # 虚拟环境的路径
    
    wsgi-file=%(chdir)/run.py                 # 项目的启动脚本文件路径
    #module=run                               # 项目的启动脚本名字,不能是路径,和wsgi-file功能类似
    callable=app       # 程序内启用的application变量名,一般而言都是app=Flask(__name__),所以这里是app
    
    master=true                        # 启用主进程
    
    processes=2                        # worker进程个数
    
    threads=2                         # 每个进程的线程数
    
    procname-prefix-spaced=mysite          # uwsgi的进程名称前缀 ,使用 ps -ef | grep mysite查看
    
    #############————————  注释掉的一些配置 ————————##############
    #chmod-socket=666                    # socket文件的访问权限(socket字段配置的是文件的情况)
    
    #logfile-chmod=644                  #log权限
    
    #uid=zhangqi                         # 启动uwsgi的用户名
    
    #gid=zhangqi                        # 启动uwsgi的用户组
    
    #py-autoreload=1                    # py文件修改,自动加载
    
    #vacuum=true                        # 退出uwsgi是否清理中间文件,包含pid、sock和status文件
    
    #harakiri=30                       # 设置自中断时间
    
    #post-buffering=4096                # 设置缓冲
    
    #touch-reload=%(chdir)               # 动态监控文件变化
    ###########################################################
    
    
    
    
    
    ############————————  设置uWSGI的socket连接  ————————############
    #
    # 方式一: socket文件,配置nginx时候使用。socket文件需要使用socket函数编写。本文中没有使用此方式
    #socket=%(chdir)/uwsgi/uwsgi.sock
    #
    # 方式二:绑定地址+端口
    socket=:8001
    #
    # 方式三:监听http端口,测试时候使用。如果不使用Nginx,浏览器是http协议,无法使用socket直接通信
    #http=0.0.0.0:8001 
    #
    ################################################################
        
    
    ############————————  设置uWSGI的管理文件  ————————############
    #
    # status文件,可以查看uwsgi的运行状态
    # 命令:uwsgi --connect-and-read uwsgi/uwsgi.status
    stats=%(chdir)/uwsgi/uwsgi.status
    #
    # pid文件,通过该文件可以控制uwsgi的重启和停止
    # 命令:uwsgi --reload uwsgi/uwsgi.pid
    # 命令:uwsgi --stop uwsgi/uwsgi.pid
    pidfile=%(chdir)/uwsgi/uwsgi.pid
    #
    # 日志文件,通过该文件查看uwsgi的日志
    daemonize=%(chdir)/uwsgi/uwsgi.log
    #
    #############################################################

       

     (3)启动uWSGI,注意是虚拟环境。  

    uwsgi --ini uwsgi.ini

     (4)调试uWSGI

    • 查看uWSGI进程 
    ps -ef | grep mysite
    #或者
    netstat -antp |grep 8001

      显示如下,uWSGI启动正常  

     

    • 查看uWSGI状态。

      会以json格式显示出完整内容,包括每个总的状态,每个work是状态,响应时间等,非常全面。

    uwsgi --connect-and-read uwsgi/uwsgi.status

      也有一些开源的监控工具可以使用:uwsgitop

    # pip3 install uwsgitop
    # uwsgitop uwsgi/uwsgi.status
    • 控制uWSGI服务器
    uwsgi --ini uwsgi.ini             # 启动
    uwsgi --reload uwsgi.pid          # 重启
    uwsgi --stop uwsgi.pid            # 关闭

       

      4、配置Nginx:

     (1)编辑配置文件 nginx.conf(主):

    vim /etc/nginx/nginx.conf 

      内容如下

    ########### 每个指令必须有分号结束 #################
    
    user nginx;              #配置用户或者组,默认为nobody nobody
    worker_processes auto;        #允许生成的进程数,默认为1
    pid /run/nginx.pid;          #指定nginx进程运行文件存放地址
    include /usr/share/nginx/modules/*.conf;  #加载动态模块
    
    #制定日志路径和级别。这个设置可以放入全局块,http块,server块
    #级别依次为:debug|info|notice|warn|error|crit|alert|emerg
    error_log /var/log/nginx/error.log; 
    
    
    events {
        worker_connections 1024;        #最大连接数,默认为512
        #use epoll;                     #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
        #accept_mutex on;               #设置网路连接序列化,防止惊群现象发生,默认为on
        #multi_accept on;               #设置一个进程是否同时接受多个网络连接,默认为off
    }
    
    http {
    
        default_type application/octet-stream; #默认文件类型,默认为text/plain
        keepalive_timeout 65;#连接超时时间,默认为75s,可以在http,server,location块配置
        include  /etc/nginx/conf.d/*.conf;   #虚拟主机配置,引入不同server的配置(uWSGI)
        include  /etc/nginx/mime.types;     #引入文件类型
    
        #sendfile_max_chunk 100k;  #每个进程每次调用传输数量最大值,默认为0(无上限)
    
    
        ################## 日志 #########################
        #自定义日志格式
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        #接入日志设置
        access_log  /var/log/nginx/access.log  main;
        #access_log off; #取消服务日志
        #
        ####################################################
    
    
    
    
        ####################    SSL证书加密  ###################
        #
        #ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;#启动特定的加密协议
        #ssl_prefer_server_ciphers on;#设置协商加密算法时,优先使用服务端的加密套件,而不是客户端浏览器
        #
        ####################################################
    
    
    
    
        ####################    缓存性能优化 ###################
        #
        #  1、open_file_cache :配置可以存储的缓存
        #  max设置缓存中的最大元素数; 在缓存溢出时,删除最近最少使用(LRU)的元素;
        #  inactive是指经过多长时间文件没被请求后删除缓存。
        #  inactive设置20s,等到至少20s不访问这个文件,相应缓存的这个文件的更改信息才会被删除。
        #  例:
        #  open_file_cache max=1000 inactive=20s;
        #
        #
        #  2、open_file_cache_valid:多长时间检查一次缓存的有效信息。
        # 设置为30s,也就是说即使一直访问这个文件,30s后会检查此文件的更改信息是否变化,发现变化就更新
        #  例:
        #  open_file_cache_valid 30s;
        #
        #
        # 3、open_file_cache_min_uses :
        # 在上面open_file_cache 的 inactive时间内文件的最少使用次数。如果超过这个数字,文件更改信息一直是在缓存中打开的。
        # 例:
        #  open_file_cache_min_uses 2;
        #
        # 4、文件错误是否也同样缓存
        #  open_file_cache_errors on;
        #
        ####################################################
    
    
    
        #################    数据传输优化    ###################
        #
        #  允许sendfile( )方式传输文件,提高传输性能,默认为off
        # 可以在http块,server块,location块配置
        sendfile      on;
        #
        # 设置调用tcp_cork方法,数据包不会马上传送出去,等到数据包最大时,一次性的传输出去,这样有助于解决网络堵塞。默认on,
        #  tcp_nopush    on;
        #
        # 打开tcp_nodelay ,禁用Nagle的缓冲算法,并在数据可用时立即发送,优化传输效率,默认on
        #  tcp_nodelay  on;
        #
        #
        ####################################################
    
    
    
        ################### 散列表大小 ########################
        # Nginx使用散列表来存储MIME type与文件扩展名。
        # types_hash_bucket_size 设置了每个散列表占用的内存大小,其影响散列表的冲突率。
        # 值越大,就会消耗更多的内存,但散列key的冲突率会降低,检索速度就更快。
        # 值越小,消耗的内存就越小,但散列key的冲突率可能上升。
        # 默认1024。
        types_hash_max_size 2048;
        ####################################################
    
    
    
    
        ################# Nginx负载均衡 (本文中没有使用)################
        #    负载均衡算法:
        #    1、热备
        #    2、轮询
        #    3、加权轮询
        #    4、ip_hash(相同的客户端ip请求相同的服务器)
        #
        #    使用服务器列表:
        #    1、定义服务器列表 mysvr
        #    2、server的location模块中将请求转向mysvr 定义的服务器列表
        #
        #    server {                      #服务器访问信息的配置
        #        .....
        #            location  ~*^.+$ {             #访问路由的配置
        #        proxy_pass  http://mysvr;      #请求转向mysvr 定义的服务器列表
        #                }
        #
        #
        #    写法举例:
        #    1、热备
        #     upstream mysvr { //服务器列表
        #          server 127.0.0.1:7878;
        #          server 192.168.10.121:3333 backup;  #热备
        #       }
        #
        #    2、轮询
        #    upstream mysvr {
        #          server 127.0.0.1:7878;
        #          server 192.168.10.121:3333;
        #        }
        #
        #    3、加权(参数)
        #    upstream mysvr {
        #          server 127.0.0.1:7878 weight=2 max_fails=2 fail_timeout=2;
        #          server 192.168.10.121:3333 weight=1 max_fails=2 fail_timeout=1;
        #        }
        #
        #    4、ip_hash
        #    upstream mysvr {
        #          server 127.0.0.1:7878;
        #          server 192.168.10.121:3333;
        #          ip_hash;
        #        }
        #
        #    nginx负载调优总结 :https://www.jianshu.com/p/4fa08f2a04ed
        #
        #################################################
    
    
    
    }

    (2)Flask站点对应的server配置:

      上面的主配置中,有此句“include /etc/nginx/conf.d/*.conf;”  ,我们将Flask对应的server模块,单独写在一个文件里(当然也可以直接写在上面的主配置中),一个server中又通过多个location指向不同应用。

      理论上,一个Nginx对应多个站点,一个站点对应一个server,一个server又可以对应多个location(应用)。

    • 进入conf.d文件夹,并创建flask.conf
    $ sodu touch /etc/nginx/conf.d/flask.conf
    • 内容如下
    server {
        listen 81;              #监听外部端口(浏览器)
        server_name 39.xxx.xxx.xxx;    #服务器地址
        charset utf-8;
        client_max_body_size 5M;
        root   /usr/share/nginx/html;  #默认根目录
        include /etc/nginx/default.d/*.conf;
    
      #配置路由 location / { #对根目录的访问都匹配,此处没有做正则匹配讲解 include uwsgi_params; #引入uwsgi uwsgi_pass localhost:8001; #通过localhost:8001端口和uWSGI通信(这是绑定的IP和端口,对应上文中uWSGI.ini里面的配置) # uwsgi_pass unix:/home/project/mysite/uwsgi/uwsgi.sock; #本文没有使用socket文件进行演示。 }
    location /static { #静态文件,直接访问路径,不用进入uWSGI进行处理 alias /home/project/mysite/static/; } error_page 404 /404.html;
    location = /40x.html {
        #可以更改错误页面路径 } error_page 500 502 503 504 /50x.html; location = /50x.html { #可以更改错误页面路径 } }

    (2)控制Nginx:

    • 启动
    $nginx
    或者
    $systemctl start nginx
    • 重启 
    nginx -s reload   #热启动,配置文件重装载
    kill -HUP 主进程号或进程号文件路径 #平滑重启
    systemctl restart nginx
    • 停止
    system stop nginx
    nginx -s stop     #快速关闭
    nginx -s quit     #正常关闭
    
    或者通过进程控制
    ps -ef | grep nginx
    kill -QUIT 主进程号 #从容停止
    kill -TERM 主进程号 #快速停止
    kill -9 主进程号 #强制停止

    (3)查看Nginx状态:  

    • ps -ef | grep nginx

    • systemctl status nginx

      5、测试服务

     (1)方法一:

      浏览器输入 39.xxx.xxx.xxx:81/app/uwsgi/ ,如果页面显示  “Hello uWSGI!” ,大功告成!

       (2)方法二:

      curl http://39.xxx.xxx.xxx:81/app/uwsgi/ ,终端输出 “Hello uWSGI!”,OK。

      

    六、收尾

      整个项目下来并不是很复杂,主要是熟悉配置流程,并且根据搭好的基础框架,升级业务功能。

      在调试过程中如果出现问题,可以查看各个日志,根据 上文: 三、访问过程 进行逐步排查解决。

      最后,后端领域还有很多东西需要学习,如有错误之处还望大家多多指教,本文多以做技术交流和配置备忘之用。

  • 相关阅读:
    Xshell 跳板机快速登录脚本按钮
    (一)angularjs5 环境搭建
    Myeclipse 内存溢出解决方法
    php 代码放到服务器上验证码不好用
    表格里面的input在底部
    Smarty忽略大括号
    php富友表单提交接口对接
    php富友接口对接http请求
    js去掉html标签
    删除的时候提示是否删除
  • 原文地址:https://www.cnblogs.com/cleven/p/10911099.html
Copyright © 2020-2023  润新知