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。

  

 

六、收尾

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

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

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

posted @ 2019-05-23 20:27  执着的怪味豆  阅读(1761)  评论(0编辑  收藏  举报