exh*_*uma 25 python apache mod-wsgi flask
在我写的烧瓶应用程序中,我使用了可以使用环境变量配置的外部库.注意:我自己写了这个外部库.所以我可以在必要时进行更改.从命令行运行时,运行flask服务器:
# env = python virtual environment
ENV_VAR=foo ./env/bin/python myapp/webui.py
Run Code Online (Sandbox Code Playgroud)
这一切都如预期的那样.但是在将它部署到apache后,使用SetEnv它不再起作用了.事实上,打印出os.environ到stderr(所以它显示了在Apache日志显示,该wsgi过程似乎是在一个非常不同的环境(一个,os.environ['PWD']好像是这样了.其实,它指向我的发展的文件夹.
为了帮助确定问题,以下是作为独立的hello-world应用程序的应用程序的相关部分.错误输出和观察结果都在帖子的最后.
Python应用程序:
.
??? myapp.ini
??? setup.py
??? testenv
??? __init__.py
??? model
? ??? __init__.py
??? webui.py
Run Code Online (Sandbox Code Playgroud)
Apache文件夹(/var/www/michel/testenv):
.
??? env
? ??? [...]
??? logs
? ??? access.log
? ??? error.log
??? wsgi
??? app.wsgi
Run Code Online (Sandbox Code Playgroud)
[app]
somevar=somevalue
Run Code Online (Sandbox Code Playgroud)
from setuptools import setup, find_packages
setup(
name="testenv",
version='1.0dev1',
description="A test app",
long_description="Hello World!",
author="Some Author",
author_email="author@example.com",
license="BSD",
include_package_data=True,
install_requires = [
'flask',
],
packages=find_packages(exclude=["tests.*", "tests"]),
zip_safe=False,
)
Run Code Online (Sandbox Code Playgroud)
# empty
Run Code Online (Sandbox Code Playgroud)
from os.path import expanduser, join, exists
from os import getcwd, getenv, pathsep
import logging
import sys
__version__ = '1.0dev1'
LOG = logging.getLogger(__name__)
def find_config():
"""
Searches for an appropriate config file. If found, return the filename, and
the parsed search path
"""
path = [getcwd(), expanduser('~/.mycompany/myapp'), '/etc/mycompany/myapp']
env_path = getenv("MYAPP_PATH")
config_filename = getenv("MYAPP_CONFIG", "myapp.ini")
if env_path:
path = env_path.split(pathsep)
detected_conf = None
for dir in path:
conf_name = join(dir, config_filename)
if exists(conf_name):
detected_conf = conf_name
break
return detected_conf, path
def load_config():
"""
Load the config file.
Raises an OSError if no file was found.
"""
from ConfigParser import SafeConfigParser
conf, path = find_config()
if not conf:
raise OSError("No config file found! Search path was %r" % path)
parser = SafeConfigParser()
parser.read(conf)
LOG.info("Loaded settings from %r" % conf)
return parser
try:
CONF = load_config()
except OSError, ex:
# Give a helpful message instead of a scary stack-trace
print >>sys.stderr, str(ex)
sys.exit(1)
Run Code Online (Sandbox Code Playgroud)
from testenv.model import CONF
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World %s!" % CONF.get('app', 'somevar')
if __name__ == '__main__':
app.debue = True
app.run()
Run Code Online (Sandbox Code Playgroud)
<VirtualHost *:80>
ServerName testenv-test.my.fq.dn
ServerAlias testenv-test
WSGIDaemonProcess testenv user=michel threads=5
WSGIScriptAlias / /var/www/michel/testenv/wsgi/app.wsgi
SetEnv MYAPP_PATH /var/www/michel/testenv/config
<Directory /var/www/michel/testenv/wsgi>
WSGIProcessGroup testenv
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
ErrorLog /var/www/michel/testenv/logs/error.log
LogLevel warn
CustomLog /var/www/michel/testenv/logs/access.log combined
</VirtualHost>
Run Code Online (Sandbox Code Playgroud)
activate_this = '/var/www/michel/testenv/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
from os import getcwd
import logging, sys
from testenv.webui import app as application
# You may want to change this if you are using another logging setup
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
LOG = logging.getLogger(__name__)
LOG.debug('Current path: {0}'.format(getcwd()))
# Application config
application.debug = False
# vim: set ft=python :
Run Code Online (Sandbox Code Playgroud)
这是apache错误日志的输出.
[Thu Jan 26 10:48:15 2012] [error] No config file found! Search path was ['/home/users/michel', '/home/users/michel/.mycompany/myapp', '/etc/mycompany/myapp']
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] mod_wsgi (pid=17946): Target WSGI script '/var/www/michel/testenv/wsgi/app.wsgi' cannot be loaded as Python module.
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] mod_wsgi (pid=17946): SystemExit exception raised by WSGI script '/var/www/michel/testenv/wsgi/app.wsgi' ignored.
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] Traceback (most recent call last):
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] File "/var/www/michel/testenv/wsgi/app.wsgi", line 10, in <module>
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] from testenv.webui import app as application
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] File "/var/www/michel/testenv/env/lib/python2.6/site-packages/testenv-1.0dev1-py2.6.egg/testenv/webui.py", line 1, in <module>
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] from testenv.model import CONF
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] File "/var/www/michel/testenv/env/lib/python2.6/site-packages/testenv-1.0dev1-py2.6.egg/testenv/model/__init__.py", line 51, in <module>
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] sys.exit(1)
[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] SystemExit: 1
Run Code Online (Sandbox Code Playgroud)
我的第一个观察是环境变量MYAPP_PATH没有出现os.environ(这在这个输出中是不可见的,但我测试了它,它不存在!).因此,配置"解析器"回退到默认路径.
我的第二个观察是配置文件列表的搜索路径为/home/users/michel返回值os.getcwd().我实际上期待内心的东西/var/www/michel/testenv.
我的直觉告诉我,我正在进行配置解析的方式不对.主要是因为代码是在导入时执行的.这引出了我的想法,即可能在正确设置WSGI环境之前执行配置解析代码.我在那里做点什么吗?
在这种情况下,您将如何进行配置解析?鉴于"model"子文件夹实际上是一个外部模块,它也应该在非wsgi应用程序中工作,并且应该提供一种配置数据库连接的方法.
就个人而言,我喜欢搜索配置文件的方式,同时仍能覆盖它.只是,代码在导入时执行的事实让我的蜘蛛感觉像疯了似的刺痛.这背后的基本原理:使用这个模块的开发人员完全隐藏(抽象 - 屏障)配置处理,它"正常工作".他们只需要导入模块(当然使用现有的配置文件),并且可以在不知道任何数据库详细信息的情况下直接进入.这也为他们提供了一种简单的方法来处理不同的数据库(开发/测试/部署)并轻松地在它们之间切换.
现在,在mod_wsgi里面它不再了:(
刚才,为了测试我的上述想法,我将其更改webui.py为以下内容:
import os
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def index():
return jsonify(os.environ)
if __name__ == '__main__':
app.debue = True
app.run()
Run Code Online (Sandbox Code Playgroud)
网页上的输出如下:
{
LANG: "C",
APACHE_RUN_USER: "www-data",
APACHE_PID_FILE: "/var/run/apache2.pid",
PWD: "/home/users/michel/tmp/testenv",
APACHE_RUN_GROUP: "www-data",
PATH: "/usr/local/bin:/usr/bin:/bin",
HOME: "/home/users/michel/"
}
Run Code Online (Sandbox Code Playgroud)
这显示了与其他调试方法所看到的环境相同的环境.所以我的初期虽然错了.但是现在我意识到了一些奇怪的东西.os.environment['PWD']设置为我有我的开发文件的文件夹.这不是在所有地方运行应用程序.更奇怪的是,os.getcwd()回归/home/users/michel?这与我所看到的不一致os.environ.它应该不一样os.environ['PWD']吗?
但最重要的问题仍然存在:为什么apache SetEnv(MYAPP_PATH在这种情况下)设置的值没有找到os.environ?
rap*_*ura 23
请注意,WSGI环境environ在应用程序对象的参数中对应用程序的每个请求传递.这种环境与保留的过程环境完全无关os.environ.该SetEnv指令不起作用,os.environ并且无法通过Apache配置指令来影响流程环境中的内容.
因此,除了getenviron或者从apache os.environ['PWD']获取之外,你还必须做其他事情MY_PATH.
Flask将wsgi environ添加到请求中,而不是app.environ通过底层来完成werkzeug.因此,在对应用程序的每个请求中,apache将添加MYAPP_CONF密钥,并且您可以访问它,request.environ.get('MYAPP_CONFIG')例如,您可以访问它的任何地方.
jer*_*kan 11
@rapadura答案是正确的,因为您无法直接访问SetEnvApache配置中的值,但您可以解决它.
如果application在app.wsgi文件中添加包装器,则可以os.environ在每个请求上设置.请参阅以下修改app.wsgi示例:
activate_this = '/var/www/michel/testenv/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
from os import environ, getcwd
import logging, sys
from testenv.webui import app as _application
# You may want to change this if you are using another logging setup
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
LOG = logging.getLogger(__name__)
LOG.debug('Current path: {0}'.format(getcwd()))
# Application config
_application.debug = False
def application(req_environ, start_response):
environ['MYAPP_CONF'] = req_environ['MYAPP_CONF']
return _application(req_environ, start_response)
Run Code Online (Sandbox Code Playgroud)
如果您在Apache配置中设置了更多环境变量,那么您需要在application包装器函数中显式设置它们.