1. Introduction
本文主要系翻译自digitalocean的教程How To Serve Flask Applications with uWSGI and Nginx on Ubuntu 16.04,部分进行了修改。
主要介绍了在nginx服务器上利用uWSGI部署Flask应用的步骤。
2. 准备工作
在开始之前,先确保有一个非root的用户部署在你的服务器上,这个用户必须使用sudo才能执行管理员命令。
FindHao注:非root用户是为了安全性考虑,但是由于购买的vps系统一般默认root用户,通过lnmp.org一键安装lnmp之后,会创建www用户和www用户组,因此下文的配置是以root用户配置为例,但是期间会插入修改目录权限的步骤。
3. 安装必要的软件
我们将尽量通过piporpip3来安装包,而非使用发行版源里的工具,避免源里工具版本过低造成的问题。
更新源,并安装python。
python2:
apt update
apt install python-pip python-dev
python3:
apt update
apt install python3-pip python3-dev
4. 创建python虚拟环境
4.1 安装virtualenv
为了将我们的Flask应用与系统里的其他python文件项目隔离开,接下来配置python虚拟环境。
使用pip安装virtualenv:
# python2
pip install virtualenv
# python3
pip3 install virtualenv
现在,为我们的Flask应用创建目录:
mkdir ~/myproject
cd ~/myproject
或者直接在lnmp的目录里,clone你的项目:
cd /home/wwwroot/
git clone XXXX/myproject.git
# 或者直接拷贝你的项目到对应的域名目录下
FindHao注:
由于我们是在root下操作的,因此应该将项目的文件所属权都移交给www
chown -R www.www ./*
由于lnmp会在域名目录下创建一个.user.ini的文件,导致你无法删除lnmp创建的目录,运行下面代码后删除即可:
chattr -i ``/home/wwwroot/yoursite/``.user.ini
如果是需要修改文件,记得修改完以后运行:
chattr +i ``/home/wwwroot/yoursite/``.user.ini
4.2 创建并激活虚拟环境
创建虚拟环境的目录:
virtualenv myenv
myenv的目录是用来存放本地python的镜像,以及后面通过pip安装的包将安装到myenv目录里,而不是系统的目录里。
在安装之前,需要先激活刚刚建立的虚拟环境:
source myenv/bin/activate
你的命令行前面会多个提示,表明你是在虚拟环境操作:
(myenv)user@host:~/myproject#.
5. 设置Flask应用
5.1 安装Flaks和uWSGI
注意,无论你使用的哪个python版本,进入虚拟环境以后,应该使用pip,而不是pip3
(myenv) # pip install uwsgi flask
5.2 建立sample
现在我们创建一个简单的样例程序来测试flask和uwsgi是否正常运行。
(myenv) # vim index.py
FindHao注:
修改文件权限:
chown www.www index.py
内容如下:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "<h1 style='color:blue'>Hello There!</h1>"
if __name__ == "__main__":
app.run(host='0.0.0.0')
如果你开启了防火墙,要在你防火墙规则里关闭对5000端口的禁用。
运行我们的flask sample:
(myenv) # python index.py
在浏览器里访问你的vps ip+:5000端口:
http://server_domain_or_IP:5000
如果看到如下内容表示运行正常,可以CTRL C终止sample的运行继续下一步了。

6. 配置uWSGI
6.1 测试uWSGI
首先我们需要测试uWSGI运行正常:
(myenv) # uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app
你应该再次看到上面的hell there的提示。
CTRL C终止,并deactivate退出虚拟环境
6.2 创建uWSGI配置文件
为了让我们的项目启动和配置更灵活,我们创建uWSGI配置文件:
# vim myproject.ini
内容如下:
[uwsgi]
module = wsgi:app
master = true
processes = 5
socket = myproject.sock
chmod-socket = 660
vacuum = true
die-on-term = true
stats = 127.0.0.1:1234
logto = /var/log/myproject.log
[uwsgi]头表示我们这是个uwsgi的配置。module指向wsgi.py文件,文件里的回调是app。
接下来的部分表明项目运行在master mode,并且有五个worker进程处理请求。
在上面测试的时候,我们的uWSGI是通过网络端口暴露出来的,但是由于后面我们将使用nginx来处理实际的client连接,然后传递给uwsgi,而由于这些操作都是在一个机器上运行的,因此这里我们改成socket的模式更安全快速。我们指定socket文件是当前项目目录下的myproject.sock。
我们还必须改变socket的权限。待会我们会迁移uWSGI进程的所属组到nginx的组,因此我们必须确保socket的所属组用户能从它那里读写信息。同时在进程结束的时候,也需要清理socket(vacuum)。
die-on-term,可以确保init system和uWSGI有相同的环境。
logto是保存日志文件。
stats监控程序的url,只有设置了这个参数以后才能用 uwsgitop :1234来观看监控,类似于linux的top命令
你可能会注意到,我们没有像前面命令行一样指定一个协议,这是因为uWSGI默认使用uwsgi协议,一个快速的二进制协议,nginx内置了这个协议,用这个协议比http协议更快。
6.3 uwsgitop
前面的配置文件定义了stats参数,则可以安装pip3 install uwsgitop并通过uwsgitop :1234来像top一样查看前面项目的运行情况。
6.4 日志
前面我们指定了logto来保存日志,但是www用户对日志的目录没有写权限,因此需要手动建立这个log文件,并chown给www。
同时由于默认日志是一直在写入,文件会不停的增长,因此还需要使用系统的logrotate日志工具对其进行处理。
创建一个日志的配置文件
# vim /etc/logrotate.d/myproject-log-file
内容如下:
/var/log/myproject.log {
monthly
rotate 5
compress
delaycompress
missingok
notifempty
create 644 root root
postrotate
/usr/bin/killall -HUP rsyslogd
endscript
}
- monthly: 日志文件将按月轮循。其它可用值为‘daily’,‘weekly’或者‘yearly’。
- rotate 5: 一次将存储5个归档日志。对于第六个归档,时间最久的归档将被删除。
- compress: 在轮循任务完成后,已轮循的归档将使用gzip进行压缩。
- delaycompress: 总是与compress选项一起用,delaycompress选项指示logrotate不要将最近的归档压缩,压缩将在下一次轮循周期进行。这在你或任何软件仍然需要读取最新归档时很有用。
- missingok: 在日志轮循期间,任何错误将被忽略,例如“文件无法找到”之类的错误。
- notifempty: 如果日志文件为空,轮循不会进行。
- create 644 root root: 以指定的权限创建全新的日志文件,同时logrotate也会重命名原始日志文件。
- postrotate/endscript: 在所有其它指令完成后,postrotate和endscript里面指定的命令将被执行。在这种情况下,rsyslogd 进程将立即再次读取其配置并继续运行。
7. 创建systemd文件
Systemd 是 Linux 系统工具,用来启动守护进程,已成为大多数发行版的标准配置。
接下来我们将创建一个service文件来保证系统自动启动uWSGI和我们的Flask应用。
以Debian为例,创建一个myproject.service文件:
# vim /lib/systemd/system/myproject.service
内容如下:
[Unit]
Description=uWSGI instance to serve myproject
After=network.target
[Service]
User=www
Group=www
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
PIDFile=/run/findyoutube.net.pid
ExecReload=/home/wwwroot/www.findyoutube.net/ftbenv/bin/uwsgi --reload /run/findyoutube.net.pid
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/findyoutube.net.pid
TimeoutStopSec=3
KillMode=mixed
[Install]
WantedBy=multi-user.target
7.1 Unit字段
[Unit]区块通常是配置文件的第一个区块,用来定义 Unit 的元数据,以及配置与其他 Unit 的关系。
- Description:简短描述
- After:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之前启动
7.2 Service字段
[Service]区块用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。
- ExecStart:启动当前服务的命令
- ExecReload:重启当前服务时执行的命令
- ExecStop:停止当前服务时执行的命令
- Environment:指定环境变量
- KillMode: mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
user和group用来指定进程所属用户和组。lnmp默认是www.www。设置working directory和path来告诉init system我们的项目在哪里,虚拟环境是怎样的。然后执行uwsgi开启Flask应用。
FindHao注:
在这个service文件里,我添加了PIDFile、ExecReload、ExecStop等几个字段,因为如果没有reload和stop字段,要重启我们的Flask应用,只能通过重启vps或者杀掉uwsgi进程的方式。
PIDFile指定了我们Flask应用的进程文件,可以传给后面stop和reload用。
reload使用uwsgi自带的reload。
stop则通过进程文件的防止找到指定进程并结束掉它。
7.3 Install 字段
[Install]通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。
- WantedBy:它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入/etc/systemd/system目录下面以 Target 名 + .wants后缀构成的子目录中
7.4 运行
保存上面的配置文件后,常用的systemd命令有:
systemctl start myproject # 开启应用
systemctl enable myproject # 添加开机自启动
systemctl disable myproject # 取消开机启动
systemctl restart myproject # 重启应用
systemctl reload myproject # 重新加载配置
systemctl stop myproject # 停止应用
systemctl status myproject # 查看应用状态
开启应用后,查看应用状态,如果是active,表明成功启动运行,如果failed,可以通过journalctl查看运行日志,并G(vim的查看形式,即Shift + g到最后一行)看看启动的错误是什么。
8. 配置Nginx来处理请求
如果是lnmp安装的nginx,则nginx的配置目录为/usr/local/nginx/conf/vhost。
如果之前没有安装,则可以通过apt直接安装nginx,但是源里的nginx可能版本比较老,对于一些新特性不支持,建议添加nginx官方源里安装,参考 《nginx配置https》 3.6 nginx 支持HTTP2。单独安装的nginx配置文件目录一般为/etc/nginx/sites-available
在nginx的配置目录新建一个nginx的配置文件myproject,内容如下:
server {
listen 80;
server_name server_domain_or_IP;
location / {
include uwsgi_params;
uwsgi_pass unix:/home/sammy/myproject/myproject.sock;
}
}
listen表示监听80端口,server_name则是你的域名或者ip。
localtion /则表示对于域名或者ip/的请求处理方式。首先通过包含uwsgi_params来加载一些默认的uWSGI参数。然后uwsgi_pass转发请求给我们定义的socket。
这只是创建了可用的配置文件,如果要启用,还需要将其软连接到enable目录:
# ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
检查我们的配置文件是否编写正确:
# nginx -t
如果提示ok,则重启nginx:
systemctl restart nginx
访问我们的域名或者ip,可以看成已经正常运行。