Flask结合gunicorn和nginx反向代理的生产环境部署及踩坑记录

个人博客:https://xzajyjs.cn

前言

之前自己写的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 loadif __name__ == '__main__':# Load configuration from config.jsonconfig = load(open('config.json', 'r'))site_config = config['site_config']# Set up the website routessite = 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 routesbackend_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 serverprint(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

image-20230714004534660

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

curl 127.0.0.1:1234

image-20230714004621346

数据返回正常,说明可以正常访问。


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

image-20230714005029807

踩坑

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

image-20230714005121785

出现了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 serverprint(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已经正确响应了

image-20230714010629837


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端口已可以正常访问了。

image-20230714011222108

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/834.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

听GPT 讲K8s源代码--pkg(一)

在 Kubernetes 代码仓库中,pkg/api和pkg/apis目录都包含用于定义 Kubernetes API 对象的代码,但它们的作用略有不同。 pkg/api目录包含 Kubernetes 的旧版本 API 对象定义,这些定义在 Kubernetes 1.7 版本之前使用。这些对象定义已经过时&…

k8s 持久化存储

我们继续来查看 k8s 的卷,上一次我们分享了将磁盘挂载到容器中,empyDir 和 gitRepo 都是会随着 pod 的启动而创建,随着 pod 的删除而销毁 那么我们或许会有这样的需求,期望在 pod 上面读取节点的文件或者使用节点的文件系统来访问…

Spring Boot 中的 Redis 的数据操作配置和使用

Spring Boot 中的 Redis 的数据操作配置和使用 Redis 是一种高性能的 NoSQL 数据库,它支持多种数据结构,包括字符串、哈希、列表、集合和有序集合。Redis 还提供了丰富的命令,可以对数据进行快速的 CRUD 操作。Spring Boot 是一个基于 Sprin…

基于单片机智能加湿器 水位防干烧加湿器的设计与实现

功能介绍 以51/STM32单片机作为主控系统;LCD1602液晶显示当前温湿度,当前模式,湿度下限;按键设置湿度下限,当湿度低于下限时开启加湿器;水位传感器检查加湿器是否有水,如果没有水到话加湿器不进行工作,蜂鸣…

怎么用PDF24 Tools工具在线进行PDF文件合并

PDF文件是经常会被用到,它在我们的日常生活和工作中扮演着重要的角色。PDF文件合并是将多个PDF文件合并为单个文件,这个过程通常是为了方便管理多个PDF文件,或者将多个PDF文件合并为一个整体以便于共享或打印。既然如此,如何快速合…

kotlin forEach循环return/break

kotlin forEach循环return/break fun main(args: Array<String>) {var a mutableListOf("0", "1", "2", "3", "4")var b mutableListOf<String>()a.forEachIndexed { index, s ->if (index > 2) {retu…

【Mac使用笔记】之 Homebrew

Homebrew更新&#xff1a; brew update && brew upgrade 当出现错误&#xff1a; fatal: couldnt find remote ref refs/heads/master 执行&#xff1a; brew tap --repair Ruby安装&#xff1a; 1、查看当前Homebrew版本&#xff1a; brew --version2、查看当前…

libbpf-bootstrap 开发指南:概念与如何安装

目录 概念 如何安装& 使用 git 地址 使用git clone 下载代码 安装依赖环境 安装libbpf 编译example 概念 libbpf-bootstrap 是一个项目&#xff0c;旨在帮助开发者快速启动和开发使用 eBPF (Extended Berkeley Packet Filter) 和 libbpf 的程序。eBPF 是一种可以在…

TTX1994-可调谐激光器控制系统

花了两周时间&#xff0c;利用下班时间&#xff0c;设计了一个ITLA可调谐激光器控制系统&#xff0c;从硬件到软件。下面这个图片整套硬件系统&#xff0c;软件硬件都自己设计&#xff0c;可以定制&#xff0c;做到单片机问题也不大。相当于一套光源了 这是软件使用的界面&…

Kafka 概述、Filebeat+Kafka+ELK

Kafka 概述、FilebeatKafkaELK 一、为什么需要消息队列&#xff08;MQ&#xff09;1、使用消息队列的好处2、消息队列的两种模式 二、Kafka 定义1、Kafka 简介2、Kafka 的特性3、Kafka 系统架构 三、部署 kafka 集群1.下载安装包2.安装 Kafka3.Kafka 命令行操作 四、Kafka 架构…

基于linux串口实现语音刷抖音

目录 1.开发逻辑图及模块 2.编程实现语音和开发板通信 3.手机接入Linux热拔插相关,打开手机开发者模式允许USB调试 4.用shell指令来操作手机屏幕&#xff0c;模拟手动滑屏幕 5.最终主程序代码 1.开发逻辑图及模块 逻辑图&#xff1a; 模块 &#xff08;1&#xff09;语音…

运维小知识(二)——Linux大容量磁盘分区及挂载

centos系统安装&#xff1a;链接 目录 1.&#x1f353;&#x1f353;命令格式化磁盘 2.&#x1f353;&#x1f353;大容量硬盘分区 3.&#x1f353;&#x1f353;自动挂载 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01; 新系统装完之后&#xff0…

C语言图书管理系统

一&#xff0c;开发环境 操作系统&#xff1a;windows10, windows11, linux, mac等。开发工具&#xff1a;Qt, vscode, visual studio等开发语言&#xff1a;c 二&#xff0c;功能需求 1. 图书信息管理&#xff1a; 这个功能的主要任务是保存和管理图书的所有信息。这应该包…

数据库多表查询作业

数据库多表查询作业 创建数据库 插入数据 mysql> insert into student values(901,张老大,男,1985,计算机系,北京市海淀区),-> (902,张老二,男,1986,中文系,北京市昌平市),-> (903,张三,女,1990,中文系,湖南省永州市), -…

opencv实战--角度测量和二维码条形码识别

文章目录 前言一、鼠标点击的角度测量二、二维码条形码识别 前言 一、鼠标点击的角度测量 首先导入一个带有角度的照片 然后下面的代码注册了一个鼠标按下的回调函数&#xff0c; 还有一个点的数列&#xff0c;鼠标事件为按下的时候就记录点&#xff0c;并画出点&#xff0c;…

【软件测试】Git 远程仓库的使用(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 查看远程仓库 想…

SQL性能规范

一、随聊 记录一下吧&#xff0c;2023年7月13日00:11:11&#xff0c;现在的状态真的很&#xff0c;忙&#xff0c;干不完的活&#xff0c;希望巨大的压力&#xff0c;能够让自己快速成长&#xff0c;回想我这一路&#xff0c;21年大专毕业&#xff0c;用一年时间熟悉软件&…

STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066

STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066 Proteus仿真小实验&#xff1a; STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 LCD1602显示器DHT11温度湿度电位器模拟…

golang中的【数组】相关知识点

目录 数组含义数组定义形式数组的声明初始化方式数组变量的类型 arr的特征数组的遍历遍历的用法二数组的值传递数组的引用&取地址 传送二维数组的相关操作定义二维数组初始化遍历 Golang是一种高效的编程语言&#xff0c;具有很多特性和优点&#xff0c;其中之一就是其对数…

RNN、LSTM、Seq2Seq

目录 时间序列表示梯度弥散和梯度爆炸RNN&#xff08;循环神经网络&#xff09;LSTMSeq2SeqELMo 时间序列表示 核心是加入历史信息去预测下一步骤 在NLP中&#xff0c;序列维度一般是这个样子:[b,seq_len,feature_len] b个句子&#xff0c;每个句子seq_len个单词&#xff0c;…