展开

文章目录

修改历史

使用gunicorn启动flask项目无法读取.env文件问题

2020-10-12 16:41:12 应用部署 2133

简介

通过gunicorn部署服务,可以使得我们的应用更加健壮,相比于使用Flask内置的开发服务器进行部署,但是随之而来又会出现一些问题,就比如这个。

问题

这个问题实在项目部署的时候出现的,因为在开发的时候使用flask内置的开发环境服务器就足够满足了,但是在实际的生产环境中,development服务器就肯定不会满足要求了。因此,我选择的方案是gunicorn+nginx+supervisor来进行项目部署的。

使用如下命令启动服务

gunicorn -w 4 -b 127.0.0.1:5000 wsgi:app

启动后的日志如下,没有任何报错问题

于是乎,打开http://127.0.0.1:5000,发现返回500,于是查看日志文件,发现是数据库连接报错,问题如下。

上面报错的原因主要是说数据库连接使用了密码登录,但是连接提供的密码是非法的,所以出现权限拒绝的提示。

排查

于是去查看数据库配置信息是否正确,发现数据库配置都是正确的。

我的配置信息都是保存在.env文件中,flask 通过dotenv会自动加载.env文件中的信息。由于我使用 flask run 命令可以成功启动服务器,因此.env 文件配置应该不会有错误。

因此我感觉有可能是gunicorn 没有正确加载.env 中的信息,所以导致这些问题的发生。

于是我在setting.py 文件中打印出了.env 文件中对应数据库键的值,发现都是None,因此可以知道是由于.env 文件没有被加在,所以导致读取不到数据库的信息导致了这种错误的发生。

解决办法

百度了一圈,没有找到一个相关的问题!!!于是去看gunicorn 的官方文档,发现了下面的内容。

上图中的意思是,使用gunicorn 命令启动服务的时候,如果需要配置环境变量,可以通过-e 或者 --env 的方式来配置,例如:

gunicorn -b 127.0.0.1:5000 -e user=123 -e pwd=123

于是使用了上面的方法测试了一下,发现确实不会数据库连接的错误了,但是出现了其他的错误,也是由于没有读取到.env 文件中配置信息所导致的。

当下我的项目所配置的环境变量信息不多,可以采用这种方法,但是当环境变量配置变多的时候这种方法可行性就比较低了。

于是我上了谷歌,不得不得感叹谷歌的强大,当我在搜索框键入gunicorn 的时候,就自动给我提示.env 了,看到这个我心里就知道,肯定有幸运儿跟我一样,遇到过同样的问题,于是乎搜索到了相关的问题,如下。

问题1:

问题2:

上面所描述的问题基本与我的一致,都是使用gunicorn 启动的时候无法读取.env 文件中的内容所导致的。

在问题下面也有很多热心网友给了一些解决方案,比如前一节我所提到的加上-e参数,或者修改gunicorn.service 配置,指定EnvironmentFile 参数。

我感觉第二种方法比较靠谱,但是我的gunicorn 是在虚拟环境中运行的,因此第二种方法也做不了。于是往下翻,发现一片名为

AUTOMATICALLY LOAD ENVIRONMENT VARIABLES IN FLASK 的文章。

(可能需要翻墙:链接),进去看了一下,作者把问题描述的十分清楚。原话如下:

This is because the environment variables are no longer being loaded for us. To load them, we'll have to use the load_dotenv function from python-dotenv.

大概意思就是不会自动加载环境变量,需要我们手动去加载。因此我按照他的方法,在wsgi.py 文件中加入下面的代码:

# wsgi.py
from dotenv import load_dotenv
load_dotenv('.env')

保存,重新运行,我发现还是获取不到环境变量。这他喵的就奇怪了。

于是又在wsgi.py 文件里打印出来环境变量值。

# wsgi.py
import os
print(os.getenv('DATABASE_USER'))
print(os.getenv('DATABASE_PWD'))

神奇的事情发生了,他喵的环境变量值居然不是空,都是.env 文件中正确的值。这下就让人很费解了。python

于是乎,返回我的程序配置初始化setting.py 文件中添加如下代码,打印信息。

# setting.py
import os
print('setting ', os.getenv('DATABASE_USER'))
print('setting ', os.getenv('DATABASE_PWD'))

他喵的,这里输出的居然是两个None

原因大概清楚了,因为setting.py 文件执行于 wsgi.py 文件之前,所以在wsgi.py 文件中手动加载环境变量在这里不启作用,于是修改setting.py 代码,加入下面两行代码。

# setting.py
from dotenv import load_dotenv
load_dotenv('.env')

保存,重启,访问,一切正常了!!!

总结

关于整个问题的解决方案就是在你的配置文件中手动去加载环境变量使用dotenv中的load_dotenv函数。

但是为什么setting.py文件会在wsgi.py 文件执行呢?知道的大哥,可以在评论中留言告诉小弟我一下。

项目的github地址

1条评论


weijiang 用户

太感谢了,终于找到解决办法了,困扰我好几天了…

Macv 博主 回复:weijiang

不客气,大家一起加油