网站首页 文章专栏 在Apache服务器上同时运行多个Django程序的方法
由于腾讯云服务器特别便宜(120元/年),禁不住诱惑买了两年。然后前前后后搭建了几个网站,分别是一个科技新闻抓取网站 https://news.stackoverflow.club, 一个书籍分享网站 https://book.stackoverflow.club, 一个网站内容开源api(还没有做前端界面) https://api.stackoverflow.club, 一个机器学习的数据集论坛 https://data.stackoverflow.club.
昨天刚刚找了一个基于Django的开源微型论坛框架Spirit,部署在自己的小服务器上。一开始运行好好的,但是当我试着同时访问上述几个网站时,有一定概率出现Server internal error
, 查看error.log
发现log如下:
[Sun Nov 11 02:38:31.200426 2018] [wsgi:error] [pid 10994:tid 139733405464320] [client 60.207.237.35:59123] mod_wsgi (pid=10994): Target WSGI script '/var/www/data_forum/data_forum/wsgi.py' cannot be loaded as Python module., referer: https://data.stackoverflow.club/ [Sun Nov 11 02:38:31.200483 2018] [wsgi:error] [pid 10994:tid 139733405464320] [client 60.207.237.35:59123] mod_wsgi (pid=10994): Exception occurred processing WSGI script '/var/www/data_forum/data_forum/wsgi.py'., referer: https://data.stackoverflow.club/ ...(略去无关log) [Sun Nov 11 02:38:31.200763 2018] [wsgi:error] [pid 10994:tid 139733405464320] [client 60.207.237.35:59123] ImportError: No module named 'news', referer: https://data.stackoverflow.club/
并且,如果先访问book.stackoverflow.club,该网站正常,但data.stackoverflow.club就会出问题;反之亦然。
观察上述log, 本应该是访问data.stackoverflow.club,但是却发现news无法找到,此处的news为新闻网站的网站模块名称。可以判断,是由于多站点并存,导致django环境错乱。
在脚本之家搜索到了一篇名为在Apache服务器上同时运行多个Django程序的方法,该文章声称可以在apache的配置文件中使用SetEnv
指令来部署多站点Django, 但是在wsgi.py
中已经存在os.environ.setdefault()
的情况下,此举似乎没有用。
我还特意试了下,保留wsgi.py
中已经存在os.environ.setdefault()
不动,单独在apache的配置文件中使用SetEnv
,证明确实没有解决问题。
搜索到了官方文档How to use Django with Apache and mod_wsgi, 其中明明白白写着
Warning If multiple Django sites are run in a single mod_wsgi process, all of them will use the settings of whichever one happens to run first. This can be solved by changing: os.environ.setdefault("DJANGO_SETTINGS_MODULE", " project_name }}.settings") in wsgi.py, to: os.environ["DJANGO_SETTINGS_MODULE"] = " project_name }}.settings" or by using mod_wsgi daemon mode and ensuring that each site runs in its own daemon process.
即如果在单一进程中,django会使用最先运行的那个站点的配置文件,所以我们要么使用os.environ
,要么使用mod_wsgi的daemon模式(未尝试)。
我去掉apache的配置文件中的SetEnv
,将wsgi.py
中的os.environ.setdefault()
换为os.environ
,重启apache,问题解决。
原因呢?为什么使用os.environ.setdefault()
会导致使用最先运行站点的配置呢?直到我看了这篇django os.environ慎用setdefault操作环境变量!,里面解释到
在绝大多数情况下,如果需要在程序运行过程中设置环境变量,使用os.environ.setdefault函数是没有任何问题的,但是有两种场景下setdefault会造成意外的问题,需要慎用: 如果程序执行前,系统里已经存在了某环境变量(如ENV=VAL1),此时如果在程序中用setdefault函数对该环境变量设置另一个不同的值(如VAL2),会因为setdefault函数的特性导致无法设置为新值 也是因为上述这一点,如果进程A先设置了环境变量(如ENV=VAL1),而A启动了子进程B,子进程B会继承A进程的所有与环境变量,会导致B运行的时候,程序运行环境里已经存在环境变量ENV,导致如果此时用setdefault函数对该环境变量设置另一个不同的值(如VAL2),也会因为同样的原因导致无法设置为新值 因此,在程序运行中设置系统环境变量的最安全方法还是: os.environ['ENV'] = 'VAL'
即os.environ.setdefault
无法对子进程、线程设置新值。
那么SetEnv
究竟有用没用?我去掉了wsgi.py
中的os.environ
语句,在apache配置文件中使用SetEnv
进行配置文件的选择,奇怪的是不论在SetEnv
后面有没有使用引号,该问题都无法解决,有时候报错为模块找不到(与背景中的报错信息相同),有时候报如下错误:
[Sun Nov 11 11:22:53.970319 2018] [wsgi:error] [pid 15279:tid 140525466273536] [client 60.207.237.35:63684] django.core.exceptions.ImproperlyConfigured: Reque sted setting LOGGING_CONFIG, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configur e() before accessing settings.
所以,SetEnv
到底有没有设置环境变量,由于调试困难暂不得而知。
中文文档、博客虽然快,但总时不时进入死胡同。很多时候我们想要的答案明明白白的写在了英文文档最显眼的位置,却因为不是母语不想阅读。
要改要改。