flask部署到nginx_flask部署404

2022-10-01 16:16:49 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

文章目录

  • 前言
    • 为什么要使用flask nginx uWSGI?
  • 1. 什么是uWSGI?什么是flask?什么是nginx?
    • 1.1 什么是uWSGI?
    • 1.2 什么是flask?
    • 1.3 什么是nginx?
      • 题外:正向代理和反向代理
  • 2. flask中部署uWSGI
  • 2.1 安装flask
  • 2.2 安装uWSGI
  • 2.3 启动一个本地HTTP服务器
  • 2.4 启动一个uWSGI 服务器
  • 2.5 小结
  • 3. 部署nginx
    • 3.1 安装nginx
  • 3.2 配置nginx
    • 3.2 小结
  • 4. 参考文献

前言

为什么要使用flask nginx uWSGI?

虽然flask的开发模式也是可以作为一个web 服务器使用的,但是同一个客户端ip请求同一个服务器ip好像是相互阻塞的。也就是说,我在访问页面A的时候(A正在加载中),然后再去访问页面B,页面B会延迟一会儿才能加载出来。然后使用如下的flask的命令行多开了几个进程能够快一些,但是这也不能解决本质,所以才想到要用flask nginx uWSGI来实现这个项目。

代码语言:javascript复制
python3 manage.py runserver -h 0.0.0.0 -p 8080 --processes 10

manage.py 中的内容约如下:

代码语言:javascript复制
# /home/myflaskproject/manage.py
import config
import os
from flask_script import Manager, Shell
from app import create_app

app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)

if __name__ == '__main__':
	manager.run()

1. 什么是uWSGI?什么是flask?什么是nginx?

1.1 什么是uWSGI?

推荐这个博文 uwsgi、wsgi和nginx的区别和关系 一个网站 = web框架(如django、flask) web服务器(如uWSGI)

一个分布式网站 = nginx(也是一个web服务器,负载均衡、反向代理) web框架(django、flask) Web服务器(如uWSGI)

需要注意的是,

  • WSGI 是一种接口,一种规范,一种标准,全称the Python Web Server Gateway Interface (WSGI),用来保证不同Web服务器可以和不同的Python程序之间相互通信。
  • uwsgi 是一种协议。uwsgi是一种线路协议而不是通信协议,常用于在uWSGI服务器与其他网络服务器的数据通信。uwsgi协议是一个uWSGI服务器自有的协议。
  • uWSGI 是一个web服务器。uWSGI是实现了uwsgi和WSGI两种协议的Web服务器。

1.2 什么是flask?

flask是一个python开发的web微框架。

1.3 什么是nginx?

nginx其实也是一个web服务器,它是一个代理服务器,客户端的请求必须经过nginx,然后nginx再将请求中的动态请求转发给上游的web服务器

nginx有几个优点:

  1. 负载均衡:根据请求情况和服务器负载情况,将请求分配给不同的web服务器,保证服务器性能。负载均衡的机制有3种, (1) 循环 – 对应用程序服务器的请求以循环方式分发, (2) 最少连接 – 下一个请求被分配给活动连接数最少的服务器, (3) ip-hash – 哈希函数用于确定应为下一个请求选择哪个服务器(基于客户端的IP地址)。
  2. 反向代理:客户端的请求由代理服务器分配给某web服务器,而不是客户端指定的目标服务器。对于一些静态文件,可以直接由反向代理处理,不经过web服务器。
  3. 安全性:客户端无法得知真正的服务器IP地址,保证了服务器的安全。

题外:正向代理和反向代理

原文 Nginx(三)——nginx 反向代理 正向代理:例如:VPN 就是做正向代理的。正向代理服务器位于客户端和服务器之间,为了向服务器获取数据,客户端要向代理服务器发送一个请求,并指定目标服务器,代理服务器将目标服务器返回的数据转交给客户端。这时,客户端和代理服务器可以看做一个客户端整体向目标服务器发送请求,所以客户端需要设置一些正向代理的配置。此时,目标服务器并不知道是谁真正想要请求这些数据的。

反向代理:其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,再返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器整体,暴露的是代理服务器地址,隐藏了真实服务器IP地址客户端并不知道真正的服务器是谁。

正向代理代理客户端,反向代理代理服务器。

2. flask中部署uWSGI

2.1 安装flask

代码语言:javascript复制
pip3 install flask

2.2 安装uWSGI

首先安装好相关的依赖:

代码语言:javascript复制
yum install python-devel

有两种安装方式:

代码语言:javascript复制
# 1.pip包管理器
pip3 install uWSGI
# 2.编译安装
# pypi 中下载uwsgi的压缩包,
tar zxvf uwsgi-2.0.18.tar.gz
mv uwsgi-2.0.18 /usr/local/uwsgi/ 
cd uwsgi-2.0.18
python3 uwsgiconfig.py --build
python3 setup.py install
# 执行完之后会出现一个绿色的文件uwsgi,这个一个可执行文件
# 为uwsgi添加软连接
ln -s /usr/local/uwsgi/uwsgi-2.0.18/uwsgi /usr/bin/uwsgi

uWSGI的配置可以写为4种格式 .ini 、.xml 、.yaml 、.json。但是我们常用的就是.ini,此后只以.ini 为例。

2.3 启动一个本地HTTP服务器

代码语言:javascript复制
# /home/myflaskproject/foobar.py

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

uWSGI Python 加载器将会搜索的默认函数 application,所以在uWSGI的配置文件中通常要使用callable=应用名,指明文件中的应用名 。 接下来我们启动 uWSGI 来运行一个 本地的HTTP 服务器,将程序部署在HTTP端口 9090 上:

代码语言:javascript复制
uwsgi --http :9090 --wsgi-file foobar.py

或者写入配置文件,文件名字自己取,后缀名一般为 .ini : 在这种格式的文件中,注释请使用“;”。

代码语言:javascript复制
[uwsgi]
http = :5000     # 启动程序时所使用的地址和端口,通常在本地运行flask项目,
chdir = /home/flaskproject/          # 项目目录
wsgi-file = manage.py      # flask程序的启动文件,通常在本地是通过运行 python manage.py runserver 来启动项目的
callable = app      	   # 程序内启用的application变量名
processes = 4     	   # 处理器个数,进程个数
threads = 2     	   # 线程个数
stats = 127.0.0.1:9191     # 获取uwsgi统计信息的服务地址
pidfile = uwsgi.pid        # 保存pid信息,方便停止服务和重启的时候用
daemonize = ./log/uwsgi.log  # 后台运行时记录uwsgi的运行日志
lazy-apps = true             # 当需要连接cassandra时,uwsgi无法启动服务,可以使用该选项设置
master-fifo = /opt/mt-search/web-service/mfifo   # 使用chain-reloading 逐个work重启,服务不中断, 命令是 echo c > mfifo
touch-chain-reload = true

啥叫本地HTTP服务器呢?就是,uWSGI是python的一个库,安装了这个库之后,我们可以使用命令uwsgi,通过这个命令和一些配置,我们能够产生一个web服务器,产生的web服务器有两种方式。

  1. 无代理的web服务器,也就是说flask框架所在的机器就作为一个独立的web服务器直接和客户端进行通信,因为客户端是通过HTTP/HTTPS来通信的,所以这个web服务器必须使用相应的协议,否则无法通信。
  2. 有代理的Web服务器,例如nginx。这时flask框架所在的机器不需要直接与客户端通信,只需要和代理服务器通信就行了(这时使用的协议就不限于HTTP/HTTPS了,这就看服务器之间协议的支持情况了)。而对于本博文使用的uWSGI web服务器而言,最佳的协议是uwsgi。

使用 --http 应该是指明了通信协议为http或https。因此这种模式,通过浏览器可以直接与web服务器通信。 这种情况并没有使用nginx,仅仅是通过uWSGI flask。

http/https

http/https

其它协议

其它协议

其它协议

其它协议

其它协议

客户端

nginx

uWSGIweb服务器1

uWSGIweb服务器2

uWSGIweb服务器3

uWSGIweb服务器4

uWSGIweb服务器5

这种情况应该是uWSGI web服务器和nginx进行通信,并不是通过http协议或者htttps协议,而是通过其它协议(通常是uwsgi协议)进行的。

整体流程是:

2.4 启动一个uWSGI 服务器

假设这是我的flask应用。

代码语言:javascript复制
# myflaskapp.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "<span style='color:red'>I am app 1</span>"

在命令行中使用的配置如下:

代码语言:javascript复制
uwsgi --socket 127.0.0.1:3031 --wsgi-file myflaskapp.py --callable app --processes 4 --threads 2 --stats 127.0.0.1:9191

在flask中的.ini配置如下:

代码语言:javascript复制
[uwsgi]
socket = 127.0.0.1:5000     # 启动程序时所使用的地址和端口,通常在本地运行flask项目,
# Flask地址和端口是127.0.0.1:5000,
# 不过在服务器上是通过uwsgi设置端口,通过uwsgi来启动项目,
# 也就是说启动了uwsgi,也就启动了项目。
chdir = /home/flaskproject/ # 项目目录
wsgi-file = manage.py      # flask程序的启动文件,通常在本地是通过运行 python manage.py runserver 来启动项目的
callable = app      	   # 程序内启用的application变量名
processes = 4     	   # 处理器个数,进程个数
threads = 2     	   # 线程个数
stats = 127.0.0.1:9191     # 获取uwsgi统计信息的服务地址
pidfile = uwsgi.pid        # 保存pid信息,方便停止服务和重启的时候用
daemonize = ./log/uwsgi.log  # 后台运行时记录uwsgi的运行日志
lazy-apps = true             # 当需要连接cassandra时,uwsgi无法启动服务,可以使用该选项设置
master-fifo = /opt/mt-search/web-service/mfifo   # 使用chain-reloading 逐个work重启,服务不中断, 命令是 echo c > mfifo
touch-chain-reload = true

按照这个方式配置了uWSGI之后,我们还无法访问服务器,因为我们还没有配置中间的nginx代理服务器,因为客户端使用的协议(http/https)和咱的uWSGI 服务器使用的协议并不相同(uwsgi),就像两个说不通语言的人一样,无法交流,传递信息。

2.5 小结

那一定有同学不太清楚,--http--socket 这两个选项究竟有何不同呢(比如我)?为什么出现这种思考呢?就是我用--socket 启动了uWSGI之后,通过浏览器并不能访问。这是因为,–socket使用的协议并不是http/https。

3. 部署nginx

nginx在上面已经简单介绍了,nginx中的协议支持如下如所示,客户端是下游,nginx之后的web服务器是上游

3.1 安装nginx

  1. 首先安装nginx的依赖
代码语言:javascript复制
//一键安装
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
  1. 下载nginx压缩包: nginx官网 下载,也可以使用 wget 下载:
代码语言:javascript复制
wget http://nginx.org/download/nginx-1.14.2.tar.gz
  1. 解压:
代码语言:javascript复制
tar zxvf nginx-1.14.2.tar.gz
  1. 编译和安装:
代码语言:javascript复制
cd nginx-1.14.2/
./configure
make && make install

安装成功的话会在 /usr/local/ 下出现一个新的目录 nginx ,根据需要修改此目录下的配置。

3.2 配置nginx

nginx 官方文档 进入nginx的安装目录,如果完全按照上述方法,那么该目录是 /usr/local/nginx/

代码语言:javascript复制
cd /user/local/nginx/conf/
ll 会发现一个 nginx.conf , 一个nginx.conf.default
# nginx.conf.default是ngnix的默认配置文件,咱不用修改它
# 修改nginx.conf文件中的配置为我们所需要的配置即可。

如果nginx代理的三个服务器都在端口*:80上监听,那么nginx首先决定哪个服务器应该处理请求。 :

代码语言:javascript复制
server { 

listen      80; # 监听端口
server_name example.org www.example.org; # 服务器名称
...
}
server { 

listen      80;# 监听端口
server_name example.net www.example.net;# 服务器名称
...
}
server { 

listen      80;# 监听端口
server_name example.com www.example.com;# 服务器名称
...
}

nginx是根据请求中的”Host”字段来决定应当将这个客户端的请求转发给哪一个web服务器,这个”Host”的值应当是与某一个server_name 相匹配的。但是, 如果其值与任何服务器的 server_name 都不匹配,或者请求根本不包含”Host”字段,则nginx会将请求转发到此端口的默认服务器。 在上面的配置中,默认服务器是第一个 – 这是nginx的标准默认行为。 它也可以使用listen指令中的default_server参数明确设置哪个服务器应该是默认的,如下所示 example.net www.example.net 将是默认的 server_name:

代码语言:javascript复制
server { 

listen      80 default_server; # 监听端口, 此服务器为默认服务器
server_name example.net www.example.net; # 服务器名称
...
}

请注意,默认服务器是监听端口(listen)的属性,而不是 server_name 的属性。

如果客户端的请求中没有”Host”字段,那么我们可以定义配置文件,来扔掉这类的客户端请求。

如下的配置中,server_name 设置为一个空字符串,它将匹配没有“Host”头字段的请求,并返回一个特殊的nginx非标准代码444来关闭连接。

代码语言:javascript复制
server { 

listen      80;
server_name "";
return      444;
}

不同ip地址的服务器:

代码语言:javascript复制
# 第一个服务器
server { 

listen      192.168.1.1:80; # 监听此ip的80端口
server_name example.org www.example.org; # 服务器名
...
}
# 第二个服务器
server { 

listen      192.168.1.1:80 default_server; # 监听此ip的80端口
server_name example.net www.example.net; # 服务器名,为此ip,端口的默认服务器
...
}
# 第三个服务器
server { 

listen      192.168.1.2:80 default_server; # 监听此ip的80端口
server_name example.com www.example.com; # 服务器名,为此ip,端口的默认服务器
...
}

在上面的配置中,nginx首先根据配置中 server 的 listen指令 监听请求的ip地址和端口。

然后,在监听此 ip 和端口的 server 中找到与请求中”Host”字段匹配的 server_name ,让这个 server_name 来处理此请求。如果未找到匹配的 server_name ,则由默认服务器处理该请求。

例如,在 192.168.1.1:80 端口上收到的 host 为 www.example.com 请求将由192.168.1.1:80端口的默认服务器处理,即由第二台服务器处理,因为192.168.1.1:80 端口上没有名为 www.example .com 的 server_name。

现在我们知道了 nginx 配置中是符合选择服务器来处理请求的了。

那么在指定的服务器中由哪一个 location 来处理请求呢??? 下面的配置中由3个location,

匹配 location 的过程如下:

首先,nginx不管location的顺序,而是从location中找到与请求的url最匹配、最具体的这个location前缀。需要注意的是,/ 根目录能够匹配到所有的请求,也就是说,所有的请求都可以由 / 根目录的这个location来处理。因此,/ 根目录的location是只有没有其他的location匹配这个url的时候,才会由 / 根目录的location来处理该请求。

其次, nginx 检查由正则表达式组成的location。一旦找到匹配的location,则停止查找,由此location来处理该请求。

然后,如果没有匹配的正则表达式location的话,则由第一步中找到的location前缀来处理该请求。

代码语言:javascript复制
server { 

listen      80; # 监听本机的80端口
server_name example.org www.example.org; # 服务器名
root        /data/www; 
location / { 
 # 这里的 / 指明的前缀位置为根目录
index   index.html index.php;
}
location ~* .(gif|jpg|png)$ { 

expires 30d;
}
location ~ .php$ { 

fastcgi_pass  localhost:9000;
fastcgi_param SCRIPT_FILENAME
$document_root$fastcgi_script_name;
include       fastcgi_params;
}
}

需要注意的是,只匹配请求url中的非参数部分。这是因为,参数可以有很多种方式给出,例如:

代码语言:javascript复制
/index.php?user=john&page=1
/index.php?page=1&user=john
/index.php?page=1&something else&user=john # 查询字符串中的内容种类太多了,不好匹

举几个例子看看上面的nginx配置是如何处理请求的吧。

  1. 请求url “/logo.gif” 首先与location 前缀 “/” 匹配,也与正则表达式 “.(gif|jpg|png)” 匹配,因此,它由第二个location处理。 使用指令”root” /data/www 将请求映射到文件/data/www/logo.gif,并将该文件返回给客户端。
  2. 请求url “/index.php” 首先和lcoation 前缀 “/“匹配,也与正则表达式 “.php ” 匹配,因此,由第三个location来处理请求。请求被传递给监听localhost:9000的FastCGI服务器。 fastcgi_param指令将FastCGI参数SCRIPT_FILENAME设置为“/data/www/index.php”,FastCGI服务器执行该文件。 变量 document_root等于root指令的值(/data/www),变量
  3. 请求 “/about.html” 仅与location前缀 “/” 匹配,因此,该请求由此locatoin处理。 使用 “root” 指令(值 /data/www)将请求映射到文件/data/www/about.html,并将文件返回给客户端。
  4. 请求 “/” 仅与 location前缀 “/” 匹配,因此该请求由此location处理。然后索引指令根据其参数和 “root” 指令的值/data/www查找文件是否存在。 如果文件/data/www/index.html不存在,并且文件/data/www/index.php存在,则指令执行内部重定向到“/index.php”,并且nginx再次搜索位置 如果请求是由客户发送的。 正如我们之前看到的,重定向的请求最终将由FastCGI服务器处理。
代码语言:javascript复制
http { 

upstream myapp1 { 

server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}
server { 

listen 80;
location / { 

proxy_pass http://myapp1;
}
}
}

在上面的示例中,在srv1-srv3上运行了3个相同应用程序的实例。 如果没有指明配置负载均衡的方法,则默认为循环方式复杂均衡。 所有请求都代理到服务器组myapp1,nginx应用HTTP负载平衡来分发请求。要为HTTPS而不是HTTP配置负载均衡,只需使用“https”作为协议。更多详情见 nginx负载均衡官方文档

nginx 配置文件的含义见 菜鸟教程-nginx 或 Nginx 服务器安装及配置文件详解 根据上面了解到的知识,修改 /usr/local/nginx/conf/nginx.conf 为如下:

代码语言:javascript复制
########### 每个指令必须有分号结束。#################
user root; # 配置用户或用户组,否则有可能会出错
worker_processes auto; # 允许生成的进程数,默认为1
error_log /var/log/nginx/error.log; # 指定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg
pid /run/nginx.pid; # 指定nginx进程运行文件存放地址
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events { 
 # events块
worker_connections 1024; # 最大连接数,默认为512
}
http { 
 # http块
include             /etc/nginx/mime.types; # 文件扩展名与文件类型映射表
default_type        application/octet-stream;  # 默认文件类型,默认为text/plain
# 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;
sendfile            on; # 允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。
# tcp_nopush on; 
# tcp_nodelay on;
keepalive_timeout   65; #连接超时时间,默认为75s,可以在http,server,location块。
# types_hash_max_size 2048;
# 设定负载均衡后台服务器列表, backend可以修改为其它名字
upstream backend { 

#ip_hash; # 指定负载均衡的方式,3种,默认为轮询。
server 192.168.10.100:8080 max_fails=2 fail_timeout=30s ;
server 192.168.10.101:8080 max_fails=2 fail_timeout=30s ;
server 127.0.0.1:8027;
server 127.0.0.1:8028;
server 127.0.0.1:8029;
}
server { 
 # server块
listen       80; # 通过80端口访问nginx时
server_name  localhost; # 服务器名,监听地址
location / { 
 # location块,# 请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。对以 / 所有地址进行负载均衡
root html;  # 定义服务器的默认网站根目录位置。如果locationURL匹配的是子目录或文件,root没什么作用,一般放在server指令里面或/下
# index index.html; #定义路径下默认访问的文件名,一般跟着root放
# proxy_pass http://mysvr; 请求转向backend定义的服务器列表,即反向代理,设置被代理服务器的端口或套接字以及URL
# deny 127.0.0.1; #拒绝的ip
# allow 172.18.5.54; #允许的ip 
include uwsgi_params;
uwsgi_pass backend;    # 非集群设置的是uWSGI套接字地址,集群则直接使用负载均衡组名
# uwsgi_pass 127.0.0.1:5000; 
}
location /static { 
 # 请求静态文件时
alias /home/myproject/static;	
}
error_page 404 /404.html; # 错误页
location = /40x.html { 

}
error_page 500 502 503 504 /50x.html; # 错误页
location = /50x.html { 

}
}
}

3.2 小结

nginx uWSGI flask的架构图如下所示:

uwsgi启动/停止

代码语言:javascript复制
uwsgi --ini uwsgi.ini # 启动
uwsgi --reload uwsgi.pid  # 重启
uwsgi --stop uwsgi.pid # 停止

nginx启动/停止

代码语言:javascript复制
nginx  //启动
nginx -s stop/quit //停止
nginx -s reload   //重启加载配置

4. 参考文献

[1] Flask uwsgi nginx项目部署 [2] uWSGI 官方文档 [3] 巧用 Nginx 实现大规模分布式集群的高可用性 [4] ython Web开发之 WSGI & uwsgi & uWSGI [5] nginx与uWSGI [6] 解决nginx uwsgi部署Django的所有问题 [7] Nginx(三)——nginx 反向代理 [8] uWSGI配置nginx官方文档

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/194634.html原文链接:https://javaforall.cn

0 人点赞