# 结合uWSGI和Nginx部署flask项目



### 结合uWSGI和Nginx部署flask项目

> 在开发环境，我们一般使用python起一个web服务即可访问，但是对于生产环境来说，我们一般使用nginx+uWSGI的方式进行部署。

&#x20;

#### 使用Nginx优点：

* 安全：不管什么请求都要经过代理服务器，这样就避免了外部程序直接攻击web服务器
* 负载均衡：根据请求情况和服务器负载情况，将请求分配给不同的web服务器，保证服务器性能
* 提高web服务器的IO性能：对于一些静态文件，可以直接由反向代理处理，不经过web服务器 &#x20;

#### 相关概念：

* wsgi web应用程序之间的接口。它的作用就像是桥梁，连接在web服务器和web应用框架之间。
* uwsgi 是一种传输协议，用于定义传输信息的类型。
* uWSGI 是实现了uwsgi协议WSGI的web服务器。

#### 1. 添加测试项目

```python
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return 'flask project'


@app.route("/user/")
def user_profile(uid):
    return f'user id:{uid}'


if __name__ == '__main__':
    app.run()
```

&#x20;

本项目是基于虚拟环境进行搭建，具体安装查看virtualenv相关博客

&#x20;

#### 2.创建新的虚拟环境，并安装uwsgi

```python
mkvirtualenv flask_env

pip3 install uwsgi
```

&#x20;

#### 3. 在本项目下创建uwsgi配置文件，添加内容如下：

```python
[uwsgi]
module = app:app  # 相当于命令下的-w 指定模块（wsgi-file,callable 这两个可以被注视掉）
#http = 127.0.0.1:5000
socket = 127.0.0.1:5000  # 支持http+socket两种方式，这里选用socket
virtualenv = /root/python_env/flask_project # 如果使用虚拟环境，需要进行执行
chdir = /data/python_projects/flask_project # 指定项目路径
#wsgi-file = app.py # 项目入口文件
#callable = app #  flask应用对象
process = 2 # 进程数
threads = 2 # 每个进程开启的线程数
buffer-size = 32768
master = true #  主进程
daemonize = %(chdir)/uwsgi/uwsgi.log  # 指的后台启动 日志输出的地方
pidfile = %(chdir)/uwsgi/uwsgi.pid # 保存主进程的进程号 
py-autoreload=1 #热加载开启
touch-logreopen = %(chdir)/uwsgi/touchforlogrotate  # 日志分割监听文件

stats　= 127.0.0.1:9191  # 添加监听  使用nc  127.0.0.1 9191 查看 (nc 安装:yum -y install nmap-ncat)
thunder-lock = true  #防止惊群
harakiri = 60  #超时时间（秒）
vacuum = true  #自动移除unix Socket和pid文件当服务停止的时候
```

#### 完整配置项：

```python
socket ： 地址和端口号，例如：socket = 127.0.0.1:50000

processes ： 开启的进程数量

workers ： 开启的进程数量，等同于processes（官网的说法是spawn the specified number of  workers / processes）

chdir ： 指定运行目录（chdir to specified directory before apps loading）

wsgi-file ： 载入wsgi-file（load .wsgi file）

stats ： 在指定的地址上，开启状态服务（enable the stats server on the specified address）

threads ： 运行线程。由于GIL的存在，我觉得这个真心没啥用。（run each worker in prethreaded mode with the specified number of threads）

master ： 允许主进程存在（enable master process）

daemonize ： 使进程在后台运行，并将日志打到指定的日志文件或者udp服务器（daemonize uWSGI）。实际上最常用的，还是把运行记录输出到一个本地文件上。

log-maxsize ：以固定的文件大小（单位KB），切割日志文件。 例如：log-maxsize = 50000000  就是50M一个日志文件。 

pidfile ： 指定pid文件的位置，记录主进程的pid号。

vacuum ： 当服务器退出的时候自动清理环境，删除unix socket文件和pid文件（try to remove all of the generated file/sockets）

disable-logging ： 不记录请求信息的日志。只记录错误以及uWSGI内部消息到日志中。如果不开启这项，那么你的日志中会大量出现这种记录：

[pid: 347|app: 0|req: 106/367] 117.116.122.172 () {52 vars in 961 bytes} [Thu Jul  7 19:20:56 2016] POST /post => generated 65 bytes in 6 msecs (HTTP/1.1 200) 2 headers in 88 bytes (1 switches on core 0)

log-maxsize: 日志大小，当大于这个大小会进行切分 (Byte)

log-truncate: 当启动时切分日志
```

&#x20;

&#x20;

#### 4.启动服务

```python
uwsgi --ini config.ini
```

&#x20;

#### 5.配置nginx代理转发

```python
location / {
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:5000;
        }

因为转发之后的通信是使用的uwsgi://127.0.0.1:5000进行通信，所以uwsgi配置socket方式


如果使用http的方式进行通信的话:
location / {
    proxy_pass http://127.0.0.1:5000;
}

 
```

其他命令：

```python
# 重启uwsgi服务
uwsgi  --reload uwsgi.pid

# 停止uwsgi服务
uwsgi --stop uwsgi.pid
```

命令下的 服务启动：

```python
$ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app

--manage-script-name 会把 SCRIPT_NAME 处理移向 uwsgi ， 因为 uwsgi 会更智能一些。
与 --mount 联用可以把向 /yourapplication 发送的请求 重定向到 myapp:app 。
如果应用可以在根级别访问，那么可以使用单个 / 来代替 /yourapplication 。 
myapp 指 flask 应用的文件名称（不含扩展 名）或者提供 app 的模块名称。 
app 在应用内部可被调用（通常是 app = Flask(__name__) ）。

如果要把应用部署于一个虚拟环境，则还需要加上 --virtualenv /path/to/virtual/environment 。
可能还需要根据项目所使用的 Python 版本相应地加上 --plugin python 或者 --plugin python3 。
```

&#x20;

#### 添加日志分割:

> uwsgi没有提供按天的日志切割配置，只提供了一个log-maxsize配置，当文件达到多大的时候自动切分，对于查找历史日志还是很不方便。 这里可以用mv+touch-logreopen参数，移动日志文件后，让uwsgi重新打开日志记录，不过需要配合sh+crontab

配置文件添加:

```python
touch-logreopen = %d./touchforlogrotat #指定监听文件，修改后重新打开日志

这里我们指定日志文件就在项目目录下叫uwsgi.daemonize.log，
监听项目目录下的touchforlogrotat文件，如果文件发生变化，就重新打开日志，
我们可以先将当前的 uwsgi.daemonize.log 文件移动到别的地发，再touch一下touchforlogrotat，
之前的文件便停止写入，重新生成了一个叫uwsgi.daemonize.log文件。
```

在当前项目下新建一个 touchforlogrotate.sh，并新建日志文件备份的日志，这里就放在项目中的logs文件夹

```python
#!/bin/bash

DIR=`echo $(cd "$(dirname "$0")"; pwd)`
LOGDIR="${DIR}/logs"

sourcelogpath="${DIR}/uwsgi.daemonize.log"
touchfile="${DIR}/touchforlogrotate"


DATE=`date -d "yesterday" +"%Y%m%d"`
destlogpath="${LOGDIR}/uwsgi.daemonize.${DATE}.log"

mv $sourcelogpath $destlogpath
touch $touchfile
```

```python
crontab定时调用
0 0 * * * sh /projectpath/touchforlogrotate.sh
```

&#x20;

&#x20;

&#x20;

* 注意点：

请务必把 ​​app.run()​​​ 放在 ​​if **name** == '**main**':​​ 内部或者放在单独 的文件中，这样可以保证它不会被调用。 因为，每调用一次就会开启一个本地 WSGI 服务器。当我们使用 uWSGI 部署应用时，不需要使用本地服务器。

#### 结合NGXIN

* http 方式

```
server {
    listen 80;

    server_name _;

    access_log  /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;

    location / {
        proxy_pass         http://127.0.0.1:8000/;
        proxy_redirect     off;

        proxy_set_header   Host                 $host;
        proxy_set_header   X-Real-IP            $remote_addr;
        proxy_set_header   X-Forwarded-For      $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto    $scheme;
    }
}
```

* socket 方式

```python
location / {
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:5000;
        }
```
