Flask结合gunicorn和nginx反向代理的生产环境部署及踩坑记录
前言
之前自己写的flask使用gunicorn上线生产环境没有什么问题,但是最近搭建了一个现成的flask项目,当使用python直接运行时不会有问题,而使用gunicorn时则会出现一些问题。
部署过程
运行测试
这里使用pyenv创建了一个虚拟环境,并安装好依赖
pyenv virtualenv 3.9.6 freegpt
pyenv activate freegpt
pip install -r requirements.txt
下面是入口函数run.py:
from server.app import app
from server.website import Website
from server.backend import Backend_Api
from json import load
if __name__ == '__main__':
# Load configuration from config.json
config = load(open('config.json', 'r'))
site_config = config['site_config']
# Set up the website routes
site = Website(app)
for route in site.routes:
app.add_url_rule(
route,
view_func=site.routes[route]['function'],
methods=site.routes[route]['methods'],
)
# Set up the backend API routes
backend_api = Backend_Api(app, config)
for route in backend_api.routes:
app.add_url_rule(
route,
view_func=backend_api.routes[route]['function'],
methods=backend_api.routes[route]['methods'],
)
# Run the Flask server
print(f"Running on port {site_config['port']}")
app.run(**site_config)
print(f"Closing port {site_config['port']}")
其中site_config.json文件如下:
{
"site_config": {
"host": "127.0.0.1",
"port": 1234,
"debug": false
},
"use_auto_proxy": false
}
意思是,运行flask服务于127.0.0.1:1234,只运行本地访问,后期我们需要搭建Nginx进行反向代理。
我们先使用python直接运行测试一下看能否跑起来以及能否正常访问。
python run.py

我们在服务器使用curl进行请求
curl 127.0.0.1:1234

数据返回正常,说明可以正常访问。
gunicorn搭建
Gunicorn是一个WSGI HTTP Server,是针对Python的、在Unix系统上运行的、用来解析HTTP请求的网关服务。
它的特点是:能和大多数的Python web框架兼容;使用简单;轻量级的资源消耗;高性能。
首先在当前虚拟环境下安装gunicorn
pip install gunicorn
然后我们使用gunicorn将flask项目跑起来,并且仅对本机开放,端口4444
gunicorn run:app -b 127.0.0.1:4444 --access-logfile access.log --error-logfile error.log &
使用ps命令可以看到当前已经成功在后台运行起来了
ps aux | grep gunicorn

踩坑
但是此时当我们再次使用curl访问127.0.0.1:4444时:

出现了404的错误。
我们查看gunicorn生成的日志文件:
# access.log
127.0.0.1 - - [13/Jul/2023:12:51:11 -0400] "GET / HTTP/1.1" 404 207 "-" "curl/7.76.1"
可以看到成功的请求到了我们的wsgi server,但是返回了404。在外网论坛上摸索了一番,问题出在了run.py上。
run.py文件中的所有代码都是写在if __name__ == "__main__":之下的,这在python语法中代表着主函数入口。
- 当使用Python直接运行脚本时(例如:
python run.py),if __name__ == '__main__'条件下的代码块会被执行,包括app.run()。这将启动Flask服务器,并让应用程序开始监听指定的主机和端口。 - 当使用
Gunicorn运行应用程序时(例如:gunicorn --bind 127.0.0.1:4444 run:app),if __name__ == '__main__'条件下的代码块不会被执行。因为Gunicorn实际上是将你的代码作为一个模块导入,而不是直接运行该代码。在这种情况下,Gunicorn会在内部处理Flask服务器的启动逻辑,并监听指定的主机和端口。也就因此自己在app.run(**kwargs)中设定的host、port、debug等参数也就失效了。
因此,无论是使用Python直接运行还是使用Gunicorn运行应用程序,app.run()只会在Python直接运行脚本时执行。而在使用Gunicorn运行时,if __name__ == '__main__'条件下的代码块将被跳过,包括app.run()。这是因为Gunicorn已经处理了服务器的启动逻辑。
因此,if __name__ == '__main__'条件的目的是为了确保在直接运行脚本时才执行特定的代码块,而在被导入为模块时跳过这些代码块。这样可以确保在使用Gunicorn启动应用程序时不会重复启动Flask服务器,并避免出现意外行为。
解决方案
既然已经知道了错误的逻辑,那么解决方法就很简单了,只要把除了app.run()的其他代码全部移出if __name__ == "__main__"即可。修改后的run.py如下:
from server.app import app
from server.website import Website
from server.backend import Backend_Api
from json import load
# Load configuration from config.json
config = load(open('config.json', 'r'))
site_config = config['site_config']
# Set up the website routes
site = Website(app)
for route in site.routes:
app.add_url_rule(
route,
view_func=site.routes[route]['function'],
methods=site.routes[route]['methods'],
)
# Set up the backend API routes
backend_api = Backend_Api(app, config)
for route in backend_api.routes:
app.add_url_rule(
route,
view_func=backend_api.routes[route]['function'],
methods=backend_api.routes[route]['methods'],
)
if __name__ == '__main__':
# Run the Flask server
print(f"Running on port {site_config['port']}")
app.run(**site_config)
print(f"Closing port {site_config['port']}")
这样就可以保证python和gunicorn方式均可正常运行。
先kill掉之前正在运行的gunicorn,并重新启动
kill -9 1275864 1275865
gunicorn run:app -b 127.0.0.1:4444 --access-logfile access.log --error-logfile error.log &
可以看到现在请求127.0.0.1:4444已经正确响应了

Nginx反向代理
目前搭建的服务只能服务器自己访问到,下面我们通过nginx反向代理将其映射到对外的80端口
安装配置nginx就不多说了,下面讲讲配置文件的写法。其实很简单
vim /etc/nginx/nginx.conf
# nginx.conf
...
server {
listen 80;
server_name xxxxxxxx; # 此处填绑定的域名
location / {
proxy_pass http://localhost:4444;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
}
}
...
然后运行nginx即可
nginx -t
nginx
此时使用本机访问域名的80端口已可以正常访问了。

Flask结合gunicorn和nginx反向代理的生产环境部署及踩坑记录的更多相关文章
- [转]Nginx反向代理和负载均衡部署指南
Nginx反向代理和负载均衡部署指南 1. 安装 1) 从Nginx官网下载页面(http://nginx.org/en/download.html)下载Nginx最 ...
- Nginx反向代理和负载均衡部署指南
1. 安装 1) 从Nginx官网下载页面(http://nginx.org/en/download.html)下载Nginx最新版本(目前是1.5.13版本)安装包: ...
- Nginx反向代理腾讯云COS的一个坑
版权声明:本文由黄希彤 原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/668639001484812620 来源:腾云 ...
- Nginx入门及如何反向代理解决生产环境跨域问题
1.Nginx入门与基本操作篇 注:由于服务器是windows系统,所以本文主要讲解Nginx在windows下的操作. 首先下载Nginx 解压缩,我们所有的配置基本都在万能的 nginx/conf ...
- Nginx反向代理其他使用方式
Nginx反向代理在生产环境中使用很多的. 场景1: 域名没有备案,可以把域名解析到香港一台云主机上,在香港云主机做个代理,而网站数据是在大陆的服务器上. 示例1: server { listen 8 ...
- Vue Nginx反向代理配置 解决生产环境跨域
Vue本地代理举例: module.exports = { publicPath: './', devServer: { proxy: { '/api': { target: 'https://mov ...
- nginx 反向代理配置示例
Nginx反向代理在生产环境中使用很多的. 场景1: 域名没有备案,可以把域名解析到香港一台云主机上,在香港云主机做个代理,而网站数据是在大陆的服务器上. server { listen ; serv ...
- Server 主机屋云服务器 宝塔面板 部署nginx反向代理的vue项目
图文记录云服务器上部署需要nginx反向代理的vue项目: 一.先登录并购买云服务器,根据自己需求购买,此处不详细介绍: 二.登录后如下图,点击进入云服务器界面: 三.在云服务器界面点击管理,进入管理 ...
- Nginx反向代理实现Tomcat多个应用80端口访问
应用背景 一般我们在开发时,一个工程里会有多个Web应用,比如一个前台一个后台,那我们就需要配置2个Tomcat服务器,比如一个是http://localhost:8080,一个是http://loc ...
- CORS跨域与Nginx反向代理跨域优劣对比
最近写了一些关于前后端分离项目之后,跨域相关方案的基本原理和常见误区的帖子,主要包括CORS和Nginx反向代理.这两种方案项目中都有在用,各有优缺,关于具体使用哪种方案,大家的观点也不大一致,本文主 ...
随机推荐
- [Go] 递归获取目录下的文件
操作示例: ./scan /Document/dir 代码: // 定义递归文件树结构体 type treeList struct { Path string `json:"path&quo ...
- Linux操作系统网络模块
Linux操作系统的网络模块是负责网络通信的核心部分.它通过实现各种协议和算法,使得计算机能够在网络中进行数据交换和通信.网络模块主要包括以下几个方面的功能: (1)IP协议栈:负责处理网络层的数据包 ...
- Object-Advanced笔记
JavaScript对象进阶 数据劫持 1. 概念 一旦访问或者修改对象的属性时 拦截这个行为(访问/修改) 并对其添加除这个行为外的操作 最后返回结果 2. 实现 2.1 getter和setter ...
- JS逆向实战13——某市公共资源交易中心Cookie混淆加密
"本文地址:https://www.cnblogs.com/zichliang/p/17346860.html 目标网站 aHR0cDovL2xkZ2d6eS5obmxvdWRpLmdvdi ...
- NC20279 [SCOI2010]序列操作
题目链接 题目 题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成 ...
- etcd:增加30%的写入性能
etcd:增加30%的写入性能 本文最终的解决方式很简单,就是将现有卷升级为支持更高IOPS的卷,但解决问题的过程值得推荐. 译自:etcd: getting 30% more write/s 我们的 ...
- 2022-05-31:某公司游戏平台的夏季特惠开始了,你决定入手一些游戏。现在你一共有X元的预算。 该平台上所有的 n 个游戏均有折扣,标号为 i 的游戏的原价a_i元,现价只要b_i元, 也就是说该
2022-05-31:某公司游戏平台的夏季特惠开始了,你决定入手一些游戏.现在你一共有X元的预算. 该平台上所有的 n 个游戏均有折扣,标号为 i 的游戏的原价a_i元,现价只要b_i元, 也就是说该 ...
- React笔记-生命周期(七)
React笔记-生命周期(七) 生命周期值React组件从装载到卸载的全过程 在这个过程中React提供了多个内置函数供开发者在不同阶段执行需要的逻辑 状态组件由3个阶段组成 挂载阶段(MOUNTIN ...
- Selenium - 浏览器操作
Selenium - 浏览器操作 获取浏览器信息 from selenium import webdriver driver = webdriver.Chrome() driver.get(" ...
- Abp Vnext 动态(静态)API客户端源码解析
根据以往的经验,通过接口远程调用服务的原理大致如下: 服务端:根据接口定义方法的签名生成路由,并暴露Api. 客户端:根据接口定义方法的签名生成请求,通过HTTPClient调用. 这种经验可以用来理 ...