前言

之前自己写的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)中设定的hostportdebug等参数也就失效了。

因此,无论是使用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反向代理的生产环境部署及踩坑记录的更多相关文章

  1. [转]Nginx反向代理和负载均衡部署指南

    Nginx反向代理和负载均衡部署指南   1.        安装 1)         从Nginx官网下载页面(http://nginx.org/en/download.html)下载Nginx最 ...

  2. Nginx反向代理和负载均衡部署指南

    1.        安装 1)         从Nginx官网下载页面(http://nginx.org/en/download.html)下载Nginx最新版本(目前是1.5.13版本)安装包: ...

  3. Nginx反向代理腾讯云COS的一个坑

    版权声明:本文由黄希彤   原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/668639001484812620 来源:腾云 ...

  4. Nginx入门及如何反向代理解决生产环境跨域问题

    1.Nginx入门与基本操作篇 注:由于服务器是windows系统,所以本文主要讲解Nginx在windows下的操作. 首先下载Nginx 解压缩,我们所有的配置基本都在万能的 nginx/conf ...

  5. Nginx反向代理其他使用方式

    Nginx反向代理在生产环境中使用很多的. 场景1: 域名没有备案,可以把域名解析到香港一台云主机上,在香港云主机做个代理,而网站数据是在大陆的服务器上. 示例1: server { listen 8 ...

  6. Vue Nginx反向代理配置 解决生产环境跨域

    Vue本地代理举例: module.exports = { publicPath: './', devServer: { proxy: { '/api': { target: 'https://mov ...

  7. nginx 反向代理配置示例

    Nginx反向代理在生产环境中使用很多的. 场景1: 域名没有备案,可以把域名解析到香港一台云主机上,在香港云主机做个代理,而网站数据是在大陆的服务器上. server { listen ; serv ...

  8. Server 主机屋云服务器 宝塔面板 部署nginx反向代理的vue项目

    图文记录云服务器上部署需要nginx反向代理的vue项目: 一.先登录并购买云服务器,根据自己需求购买,此处不详细介绍: 二.登录后如下图,点击进入云服务器界面: 三.在云服务器界面点击管理,进入管理 ...

  9. Nginx反向代理实现Tomcat多个应用80端口访问

    应用背景 一般我们在开发时,一个工程里会有多个Web应用,比如一个前台一个后台,那我们就需要配置2个Tomcat服务器,比如一个是http://localhost:8080,一个是http://loc ...

  10. CORS跨域与Nginx反向代理跨域优劣对比

    最近写了一些关于前后端分离项目之后,跨域相关方案的基本原理和常见误区的帖子,主要包括CORS和Nginx反向代理.这两种方案项目中都有在用,各有优缺,关于具体使用哪种方案,大家的观点也不大一致,本文主 ...

随机推荐

  1. 帝国cms 随机调取新闻

    <?php $hits_r = $empire->query("select * from {$dbtbpre}ecms_music as t1 join (select rou ...

  2. Karmada v1.5发布:多调度组助力成本优化

    摘要:在最新发布的1.5版本中,Karmada 提供了多调度组的能力,利用该能力,用户可以实现将业务优先调度到成本更低的集群,或者在主集群故障时,优先迁移业务到指定的备份集群. 本文分享自华为云社区& ...

  3. ThreadLocal实现原理和使用场景

    ThreadLocal是线程本地变量,每个线程中都存在副本. 实现原理: 每个线程中都有一个ThreadLocalMap,而ThreadLocalMap中的key即是ThreadLocal.  内存泄 ...

  4. 微服务 - Nginx网关 · 进程机制 · 限流熔断 · 性能优化 · 动态负载 · 高可用

    系列目录: 微服务 - 概念 · 应用 · 通讯 · 授权 · 跨域 · 限流 微服务 - 集群化 · 服务注册 · 健康检测 · 服务发现 · 负载均衡 微服务 - Redis缓存 · 数据结构 · ...

  5. 安卓机上 4G 内存跑 alpaca,欢迎试用轻量级 LLM 模型推理框架 InferLLM

    从 LLM 火爆以来,社区已经出现了非常多优秀的模型,当然他们最大的特点就是体积大,最近为了让大模型可以在更低端的设备上运行,社区做了非常多的工作, gptq 实现了将模型进行低比特量化,因此降低了运 ...

  6. OpenResty学习笔记03:再探WAF

    一. 再谈WAF 我们上一篇安装的WAF来自另一位技术大神 赵舜东,花名 赵班长,一直从事自动化运维方面的架构设计工作.阿里云MVP.华为云MVP.中国SaltStack用户组发起人 .新运维社区发起 ...

  7. 2022-08-27:以下go语言代码输出什么?A:[0];B:panic;C:7;D:不清楚。 package main import ( “fmt“ ) func main() { a

    2022-08-27:以下go语言代码输出什么?A:[0]:B:panic:C:7:D:不清楚. package main import ( "fmt" ) func main() ...

  8. 2022-02-06:等差数列划分 II - 子序列。 给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。 如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称

    2022-02-06:等差数列划分 II - 子序列. 给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目. 如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称 ...

  9. 2021-07-02:正则表达式匹配。给定一个字符串s和一个匹配串p。“.“匹配单个字符。“*“匹配左边元素的多个字符。判断p是否匹配s。比如s=“ab“,p=“a.“,返回true。比如s=“ab“

    2021-07-02:正则表达式匹配.给定一个字符串s和一个匹配串p."."匹配单个字符.""匹配左边元素的多个字符.判断p是否匹配s.比如s="ab ...

  10. 2021-08-25:给定数组father大小为N,表示一共有N个节点,father[i] = j 表示点i的父亲是点j, father表示的树一定是一棵树而不是森林,queries是二维数组,大小为

    2021-08-25:给定数组father大小为N,表示一共有N个节点,father[i] = j 表示点i的父亲是点j, father表示的树一定是一棵树而不是森林,queries是二维数组,大小为 ...