线上环境部署flask,nginx+uWSGI 和nginx+gunicorn,这两种方案,应该如何选择?
前者,高并发稳定一点,部署麻烦一些,坑略多。后者高并发差一点,部署简单,坑少。我的项目是个人的小项目,没有高并发的需求,所以就选择了后者这个方案。在部署方案解说之前我们先来补补课。
因为djaong或者flask自带了一个实现了WSGI协议的server 和 application, 各个web框架也基本上都有自己实现的WSGI server, 但这个server基本上只能用来调试,不能用于生产环境,性能没保障。
WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通信的规范。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Bottle, Flask, Django。
WSGI协议主要包括server和application两部分:
WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如uWSGI和Gunicorn都是实现了WSGI server协议的服务器(他们把HTTP协议转化成WSGI协议,让Python可以直接使用),Django,Flask是实现了WSGI application协议的web框架,同时他们也都有自己实现的简单的WSGI server(一般用于服务器调试,生产环境下建议用其他WSGI server),因此,实际生产环境中,可以选取最合适的server和application组合来实现自己的应用。
uwsgi:与WSGI一样是一种通信协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型的描述,与WSGI协议是两种不同的东西。
uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等。
正向代理(Forward Proxy)
正向代理是一个位于客户端和原始服务器之间的服务器。为了从原始服务器取得内容,客户端向代理发送一个请求并制定目标(原始服务器),然后代理向原始服务器转发请求获得内容,然后将获得的内容返回给客户端。我们平常说的代理通常都是指的正向代理。
可以举个形象的例子来说明:有一天,A君看到C君有巧克力(资源),然后A君就想吃C君的巧克力。可是怎么办,A君和C君不熟吖,不能直接向C君要巧克力吃(访问受限)。这时A君就很苦恼,要怎样做才能吃到C君的巧克力呢?于是A君想啊想啊终于想到了一个办法,就是找和C君关系好的B君(代理),找他去和C君要巧克力,然后再从B君那里拿到巧克力吃。这样,A君就吃到C君的巧克力了,即使C君可能永远不会知道A君吃了TA的巧克力。
正向代理主要有以下应用:
反向代理(Reverse Proxy)
反向代理则是以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求的客户端。这个时候的代理服务器对外表现就是一个反向代理服务器。在用户看来,他只是访问了代理服务器而已,实际访问的服务器他并不知道,也无需关心。
同样举个例子来说明:有一天,A君想吃巧克力了,就打电话给B君,B君也没有巧克力,可是却不告诉A君,而是悄悄去向C君要了巧克力,然后给A君送了过去。A君只会以为巧克力是B君的,永远不知道C君的存在。实际上,A君只要知道B君能给到TA巧克力就足够了,至于巧克力是怎么来的,其实一点都不重要。
反向代理主要有以下应用:
正向代理和反向代理的区别
正向代理和方向代理的区别,主要在代理对象、服务器架设位置、用途和安全性几个方面。
1.代理对象不同
2.服务器架设位置不同
3.用途不同
4.安全性不同
大意如图:
值得一提的是:如果你选择的架构是:Nginx + uWSGI或者gunicorn+ web应用,uWSGI或者gunicorn相当于一个中间件;如果选择的架构是uWSGI或者gunicorn + web应用,uWSGI或者gunicorn则为一个web服务器,同一个东西的作用是不能一概而论的,要根据实际来看我们用到它的什么功能,实事求是,一切从实际出发!(升华主题)所以不要问我哪些是web服务器,哪些是应用服务器。
就不从创建环境等基础操作开始说了,我现在的步骤基于已经存在一个线上的环境开始
1.安装gunicorn
pip install gunicorn
2.将gunicorn 加入到代码中,
if __name__ == '__main__':
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
app.run()
3.运行
在 入口文件.py 路径下,一定要记住是当前路径下!!
gunicorn app:app #gunicorn 入口文件名:app
提示说到:监听127.0.0.1:8000,所以我们去试一试
curl http://127.0.0.1:8000
为处理高并发则要开多个进程 或者 你有修改监听端口的需求:
gunicorn -w 4 -b 127.0.0.1:5000 app:app # -w 4表示4个进程 -b 127.0.0.1:5000表示监听该端口 # app:app 前者代表启动程序文件名, 后者为实例化对象命名即 app = Flask(__name__)
通过执行如下命令,可以获取Gunicorn进程树:
pstree -ap|grep gunicorn
本机测试成功了,那接下来我们就进行外网访问的配置
其实很简单,就是把上边命令里的127.0.0.1改成内网的ip ,然后用外网ip加相应的端口就访问到了。
如果你没有成功,那么请检查一下安全组设置,防火墙设置。
我还遇到一个坑:就是用了云服务器那边推荐的模板安全组,大致是把最常用的22,80,443等端口外网准入,内网放通等等,然后我想既然443和80都已经开了,于是就用这两个端口来绑定我的后台,结果一直不成功。(虽然这些端口没有被占用)
排除了各种失败的可能,我猜难道是这些端口太特殊,腾讯那边做了什么处理吗?也不知道对不对,反正就重设了安全组,加了一个5000,然后,用5000端口我的程序就起来了hhh,用浏览器也能访问到了。
到现在我也不知道是服务器的问题还是我程序的问题,如果哪位大佬知道的话,还请不吝赐教。
至此,如上所示,不加上nginx也可以运行
加上nginx的话,gunicorn的配置要再改一下,讲到下边就懂了
1.装nginx
sudo apt-get install nginx
过程会让选一个Y同意占用内存
2.如果出现无法定位nginx包,进行如下操作
sudo apt-get update
3.更新完成之后,安装nginx
sudo apt-get install nginx
4.Ubuntu安装之后的文件结构大致为:
1)所有的配置文件都在/etc/nginx下,并且每个虚拟主机已经安排在了/etc/nginx/sites-available下
2)程序文件在/usr/sbin/nginx
3)日志放在了/var/log/nginx中
4)并已经在/etc/init.d/下创建了启动脚本nginx
5)默认的虚拟主机的目录设置在了/var/www/nginx-default (有的版本默认的虚拟主机的目录设置在了/var/www, 请参考/etc/nginx/sites-available里的配置)
5.配置文件
编辑文件:/etc/nginx/sites-available/default(建议先备份,万一崩了)
server {
listen 8088;#绑定的服务器的端口
server_name 49.235.***.**; # 这是HOST机器的外部域名,用地址也行(外网ip)
location / {
#这里是指向 gunicorn host 的服务地址,是Gunicorn与Ningx通信的端口。和Gunicorn的配置相同
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
gunicorn -w 4 -b 127.0.0.1:5000 app:app
启动后 开启另一个服务器链接,输入:
sudo service nginx start
这样你便可以在互联网上 用 你的服务器外网ip地址:8088 访问到你的flask项目了。
原理:通过外网ip地址:8088 访问nginx,nginx反向代理到本机的5000端口,这时候,flask监听在5000端口,然后就访问到flask,响应再一步一步传回来
此时安全组只放通8088给nginx就好,其他端口(如5000)不必外露。
下边科普一下nginx相关命令
启动:sudo service nginx start
重启:sudo service nginx restart
停止:sudo service nginx stop
# 完全卸载nginx
$ sudo apt-get remove --purge nginx
$ sudo apt-get autoremove --purge
# 更新nginx,保留配置文件
# 亲测nginx.conf不会被删除和覆盖,但保险起见还是建议先备份
$ sudo apt-get remove nginx
$ sudo apt-get autoremove
$ sudo apt update
$ sudo apt-get install nginx
# 安装软件包
# sudo apt-get install 软件包名称`
# eg:
$ sudo apt-get install nginx
# 卸载软件包
# sudo apt-get remove 软件包名称
# eg:
$ sudo apt-get remove nginx
# 卸载软件包且不保留配置文件
# sudo apt-get remove --purge 软件包名称
# eg:
$ sudo apt-get remove --purge nginx
# 卸载自动安装的软件包且不保留配置文件
# sudo apt-get autoremove --purge
# 列出本地软件包列表
# dpkg --get-selections [| grep 筛选关键字]
# eg:(列出本地所有软件包)
$ dpkg --get-selections
# eg:(列出本地与ngnix有关的软件包)
$ dpkg --get-selections | grep nginx
# 查看进程信息
# ps -ef [| grep 筛选关键字]
# eg:(列出所有进程信息)
$ ps -ef
# eg:(列出与nginx有关的进程信息)
$ ps -ef | grep nginx
# 查看端口占用信息
$ sudo netstat -nlp
至此,nginx和gunicorn的配置都ok了,
但是还有一个问题,到目前为止,gunicorn都是直接通过命令行运行,并不能够在后台运行,也是当我们关闭了xShell(或者你使用的是Putty及其他SSH连接的软件),将无法再访问到你的应用。所以我们需要让gunicorn在后台运行,也就是所谓的daemon(守护进程)。
如果你熟悉Linux命令,你应该知道在Linux中后台运行可以通过nohup命令,例如我们要让gunicorn在后台运行,我们只需要运行nohup命令:
nohup gunicorn -w 2 -b 127.0.0.1:5000 app:app &
运行后你可以通过如下指令来查看到当前gunicorn的运行状态:
ps -e | grep gunicorn
这样你就可以关闭连接服务器的终端,还能通过你的浏览器访问到你的Flask应用了!
但是nohup运行的后台程序并不能够可靠的在后台运行,我们最好让我们的后台程序能够监控进程状态,还能在意外结束时自动重启,这就可以使用一个使用Python开发的进程管理程序supervisor。
首先我们通过apt来安装supervisor:
sudo apt-get install supervisor
安装完成后,我们在/etc/supervisor/conf.d/目录下创建我们控制进程的配置文件,并以.conf结尾,这样将会自动应用到主配置文件当中,创建后添加如下的配置内容:
[program:WilliamFlaskGunicornSupervisor]
command=/home/william/anaconda3/envs/py36/bin/gunicorn -w 4 -b 127.0.0.1:5000 app:app
directory=/home/william/NJUST_Postgraduate_Curriculum_Flask ; 项目目录
user=william ; 执行的用户
autostart=true ; 设置为随 supervisord 启动而启动
autorestart=true ; 设置为随 supervisord 重启而重启
startretires=5 ; supervisord尝试启动一个程序时尝试的次数。默认是3
startsecs=3 ; 自动重启时间间隔(s)
stderr_logfile=/var/log/WilliamFlaskGunicornSupervisor.err.log ; 错误日志文件
stdout_logfile=/var/log/WilliamFlaskGunicornSupervisor.out.log ; 输出日志文件
在上面的配置文件中,
[program:WilliamFlaskGunicornSupervisor]设置了进程名,这与之后操作进程的状态名称有关,为WilliamFlaskGunicornSupervisor;
command为进程运行的命令,必须使用绝对路径,并且使用虚拟环境下的gunicorn命令;
directory=/home/william/NJUST_Postgraduate_Curriculum_Flask //项目目录
user指定了运行进程的用户,这里设置为william
其他的supervisor的操作详细见 https://www.cnblogs.com/Dicky-Zhang/p/6171954.html
保存配置文件之后,我们需要通过命令来更新配置文件:
sudo supervisorctl update
然后我们来启动这个WilliamFlaskGunicornSupervisor进程:
sudo supervisorctl start WilliamFlaskGunicornSupervisor
当然你也直接在命令行输入以下命令进入supevisor的客户端,查看到当前的进程状态
sudo supervisorctl
通过stop命令便可以方便的停止该进程:
supervisor> stop WilliamFlaskGunicornSupervisor
因为目前的项目是微信小程序,有https的需求,于是还要配置nginx的https
1.申请证书
从腾讯云,阿里云等都能找到免费的申请渠道,这个我就不多说了,有的是教程。
2.下载证书之后得到
Nginx目录下是这样的
3.把Nginx目录下的文件传到服务器,找个目录放好
我是放在了/etc/nginx/cert/目录
4.配置文件
重新配置/etc/nginx/sites-available/default
server {
listen 443; # 绑定的服务器的端口
server_name 49.235.***.**; # 这是HOST机器的外部域名,用地址也行(外网ip)
ssl on; # 启用 SSL 功能
# 证书文件名称
ssl_certificate /etc/nginx/cert/1_www.k8rh.top_bundle.crt;
# 私钥文件名称
ssl_certificate_key /etc/nginx/cert/2_www.k8rh.top.key;
ssl_session_timeout 5m;
# 请按照以下协议配置
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# 请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
# 这里是指向gunicorn host的服务地址,是Gunicorn与Ningx通信的端口
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80;
# 填写绑定证书的域名
server_name 49.235.***.**;
# 把http的域名请求转成https
rewrite ^(.*)$ https://$host$1 permanent;
}
规范起见,不用8088了,把安全组的8088删了,改用443,然后把对应http的80也做了转安全的处理。重启nginx如下图,万事大吉!!
借鉴自:
做python Web开发你要理解:WSGI & uwsgi - 简书
nginx+uwsgi 和nginx+gunicorn区别、如何部署 - 简书
非常抱歉,全站内容审核中... - 博客园团队 - 博客园
为什么要使用gunicorn和nginx部署项目?_Aifore的博客-CSDN博客_为什么使用gunicorn