Apache SetEnv 未按预期与 mod_wsgi 一起工作 [英] Apache SetEnv not working as expected with mod_wsgi

查看:24
本文介绍了Apache SetEnv 未按预期与 mod_wsgi 一起工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我编写的烧瓶应用程序中,我使用了一个可以使用环境变量配置的外部库.注意:我自己编写了这个外部库.所以我可以在必要时进行更改.从命令行运行时,使用以下命令运行烧瓶服务器:

# env = python 虚拟环境ENV_VAR=foo ./env/bin/python myapp/webui.py

一切都按预期进行.但是在将它部署到 apache 并使用 SetEnv 之后,它不再工作了.事实上,将 os.environ 打印到 stderr(所以它显示在 apache 日志中表明,wsgi 进程似乎在一个非常不同的环境(例如,os.environ['PWD'] 似乎方式.实际上,它指向我的开发文件夹.

为了帮助识别问题,以下是作为独立 hello-world 应用程序的应用程序的相关部分.错误输出和观察结果在帖子的最后.

应用文件夹布局:

Python 应用程序:

<预><代码>.├── myapp.ini├── setup.py└── 测试环境├── __init__.py├──模型│ └── __init__.py└── webui.py

Apache 文件夹 (/var/www/michel/testenv):

<预><代码>.├──环境│ ├── [...]├── 日志│ ├── access.log│ └── error.log└── wsgi└── app.wsgi

myapp.ini

[应用程序]某个变量=某个值

setup.py

from setuptools import setup, find_packages设置(名称=测试环境",版本='1.0dev1',description="一个测试应用程序",long_description="你好世界!",作者 =一些作者",author_email="author@example.com",许可证=BSD",include_package_data=真,install_requires = ['烧瓶',],包=find_packages(exclude=["tests.*", "tests"]),zip_safe=假,)

testenv/init.py

# 空

testenv/model/init.py

from os.path import expanduser, join,exists从 os 导入 getcwd、getenv、pathep导入日志导入系统__version__ = '1.0dev1'LOG = logging.getLogger(__name__)def find_config():"""搜索适当的配置文件.如果找到,返回文件名,然后解析的搜索路径"""path = [getcwd(), expanduser('~/.mycompany/myapp'), '/etc/mycompany/myapp']env_path = getenv("MYAPP_PATH")config_filename = getenv("MYAPP_CONFIG", "myapp.ini")如果 env_path:路径 = env_path.split(pathsep)detection_conf = 无对于路径中的目录:conf_name = join(dir, config_filename)如果存在(conf_name):检测到的_conf = conf_name休息返回detected_conf,路径def load_config():"""加载配置文件.如果未找到文件,则引发 OSError."""从 ConfigParser 导入 SafeConfigParserconf, path = find_config()如果不是conf:raise OSError("找不到配置文件!搜索路径是 %r" % path)解析器 = SafeConfigParser()parser.read(conf)LOG.info("从 %r 加载设置" % conf)返回解析器尝试:CONF = load_config()除了 OSError,例如:# 提供有用的信息而不是可怕的堆栈跟踪打印 >>sys.stderr, str(ex)sys.exit(1)

testenv/webui.py

from testenv.model import CONF从烧瓶进口烧瓶app = Flask(__name__)@app.route('/')定义索引():返回你好世界 %s!"% CONF.get('app', 'somevar')如果 __name__ == '__main__':app.debue = 真应用程序运行()

Apache 配置

服务器名称 testenv-test.my.fq.dnServerAlias testenv-testWSGIDaemonProcess testenv user=michelthreads=5WSGIScriptAlias//var/www/michel/testenv/wsgi/app.wsgiSetEnv MYAPP_PATH/var/www/michel/testenv/config<目录/var/www/michel/testenv/wsgi>WSGIProcessGroup testenvWSGIApplicationGroup %{GLOBAL}订单拒绝,允许所有人都允许</目录>ErrorLog/var/www/michel/testenv/logs/error.log日志级别警告CustomLog/var/www/michel/testenv/logs/access.log 组合</虚拟主机>

app.wsgi

activate_this = '/var/www/michel/testenv/env/bin/activate_this.py'execfile(activate_this, dict(__file__=activate_this))从 os 导入 getcwd导入日志,系统从 testenv.webui 导入应用程序作为应用程序# 如果您使用其他日志设置,您可能需要更改此设置logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)LOG = logging.getLogger(__name__)LOG.debug('当前路径:{0}'.format(getcwd()))#应用配置application.debug = False# vim: 设置 ft=python :

错误和观察

这是apache错误日志的输出.

[2012 年 1 月 26 日星期四 10:48:15] [错误] 找不到配置文件!搜索路径为 ['/home/users/michel', '/home/users/michel/.mycompany/myapp', '/etc/mycompany/myapp'][2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] mod_wsgi (pid=17946):目标 WSGI 脚本 '/var/www/michel/testenv/wsgi/app.wsgi' 无法加载为蟒蛇模块.[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] mod_wsgi (pid=17946):WSGI 脚本/var/www/michel/testenv/wsgi/app.wsgi"引发的 SystemExit 异常被忽略.[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] 回溯(最近一次通话):[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] 文件/var/www/michel/testenv/wsgi/app.wsgi",第 10 行,[Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] from testenv.webui import app as application[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] 文件/var/www/michel/testenv/env/lib/python2.6/site-packages/testenv-1.0dev1-py2.6.egg/testenv/webui.py",第 1 行,在 <module>[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] 从 testenv.model 导入 CONF[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] 文件/var/www/michel/testenv/env/lib/python2.6/site-packages/testenv-1.0dev1-py2.6.egg/testenv/model/__init__.py",第 51 行,在 <module> 中[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] sys.exit(1)[2012 年 1 月 26 日星期四 10:48:15] [错误] [客户端 10.115.192.101] SystemExit: 1

我的第一个观察是环境变量 MYAPP_PATH 没有出现在 os.environ 中(这在这个输出中是不可见的,但我测试了它,它不是那里!).因此,配置解析器"回退到默认路径.

我的第二个观察是配置文件的搜索路径将 /home/users/michel 列为 os.getcwd() 的返回值.我实际上期待在 /var/www/michel/testenv 里面有一些东西.

我的直觉告诉我,我进行配置解析的方式不对.主要是因为代码是在导入时执行的.这让我想到,也许配置解析代码在 WSGI 环境正确设置之前被执行.我在那里有什么东西吗?

简短讨论/切题

在这种情况下,将如何进行配置解析?鉴于模型"子文件夹实际上是一个外部模块,它也应该在非 wsgi 应用程序中工作,并且应该提供一种配置数据库连接的方法.

就我个人而言,我喜欢搜索配置文件的方式,同时仍然能够覆盖它.只是,代码在导入时执行的事实让我的蜘蛛感觉像疯了一样刺痛.这背后的基本原理:使用此模块的开发人员完全隐藏了配置处理(抽象障碍),并且它正常工作".他们只需要导入模块(当然还有现有的配置文件),并且可以在不知道任何数据库详细信息的情况下直接进入.这也为他们提供了一种使用不同数据库(开发/测试/部署)并在它们之间轻松切换的简单方法.

现在,在 mod_wsgi 中它不再是 :(

更新:

刚才,为了测试我的上述想法,我将 webui.py 更改为以下内容:

导入操作系统从烧瓶导入烧瓶,jsonifyapp = Flask(__name__)@app.route('/')定义索引():返回 jsonify(os.environ)如果 __name__ == '__main__':app.debue = 真应用程序运行()

网页上的输出如下:

<代码>{朗:C",APACHE_RUN_USER: "www-data",APACHE_PID_FILE: "/var/run/apache2.pid",PWD: "/home/users/michel/tmp/testenv",APACHE_RUN_GROUP: "www-data",路径:"/usr/local/bin:/usr/bin:/bin",家:/家/用户/米歇尔/"}

这显示了与其他调试方法看到的环境相同的环境.所以我最初的虽然是错误的.但现在我意识到了一些更奇怪的事情.os.environment['PWD'] 设置为我的开发文件所在的文件夹.这不是应用程序运行的所有.更奇怪的是,os.getcwd() 返回 /home/users/michel?这与我在 os.environ 中看到的不一致.不应该和 os.environ['PWD'] 一样吗?

最重要的问题仍然存在:为什么在 os.environSetEnv(在本例中为 MYAPP_PATH)设置的值>?

解决方案

请注意,WSGI 环境是在应用程序对象的 environ 参数中根据每个请求传递到应用程序的.该环境与os.environ中保存的进程环境完全无关.SetEnv 指令对 os.environ 没有影响,并且无法通过 Apache 配置指令影响进程环境中的内容.

因此,您必须执行除 getenvironos.environ['PWD'] 以外的其他操作,才能从 apache 获取 MY_PATH.

Flask 将 wsgi 环境添加到请求中,而不是 app.environ,这是由底层 werkzeug 完成的.因此,在对应用程序的每个请求中,apache 都会添加 MYAPP_CONF 键,并且您可以在任何可以访问请求的地方访问它,request.environ.get('MYAPP_CONFIG') 例如.

In a flask application I wrote, I make use of an external library which can be configured using environment variables. Note: I wrote this external library myself. So I could make changes if necessary. When running from the command line an running the flask server with:

# env = python virtual environment
ENV_VAR=foo ./env/bin/python myapp/webui.py

it all woks as expected. But after deploying it to apache, and using SetEnv it does not work anymore. In fact, printing out os.environ to stderr (so it shows up in the apache logs reveals, that the wsgi process seems to be in a very different environment (for one, os.environ['PWD'] seems to be way off. In fact, it points to my development folder.

To help identifying the problem, following are the relevant parts of the application as a standalone hello-world app. The error output, and observations are at the very end of the post.

App folder layout:

Python app:

.
├── myapp.ini
├── setup.py
└── testenv
    ├── __init__.py
    ├── model
    │   └── __init__.py
    └── webui.py

Apache folder (/var/www/michel/testenv):

.
├── env
│   ├── [...]
├── logs
│   ├── access.log
│   └── error.log
└── wsgi
└── app.wsgi

myapp.ini

[app]
somevar=somevalue

setup.py

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,
)

testenv/init.py

# empty

testenv/model/init.py

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)

testenv/webui.py

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()

Apache config

<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>

app.wsgi

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 :

Error and observations

This is the output of the apache error log.

[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

My first observation is that the environment variable MYAPP_PATH does not appear in os.environ (this is not visible in this output, but I tested it, and it's not there!). As such, the config "resolver" falls back to the default path.

And my second observation is the search path for the config file lists /home/users/michel as return value of os.getcwd(). I was actually expecting something inside /var/www/michel/testenv.

My instinct tells me, that the way I am doing config resolution is not right. Mainly because code is executed at import-time. This leads me to the idea, that maybe the config-resolution code is executed before the WSGI environment is properly set up. Am I onto something there?

Short discussion / tangential question

How would you do the config resolution in this case? Given that the "model" sub-folder is in reality an external module, which should also work in non-wsgi applications, and should provide a method to configure a database connection.

Personally, I like the way I search for config files while still being able to override it. Only, the fact that code is executed at import-time is making my spider-senses tingling like crazy. The rationale behind this: Configuration handling is completely hidden (abstraction-barrier) by fellow developers using this module, and it "just works". They just need to import the module (with an existing config-file of course) and can jump right in without knowing any DB details. This also gives them an easy way to work with different databases (dev/test/deployment) and switch between them easily.

Now, inside mod_wsgi it does not anymore :(

Update:

Just now, to test my above idea, I changed the webui.py to the following:

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()

The output on the webpage is the following:

{
    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/"
}

This shows the same environment as the one seen by other debugging methods. So my initial though was wrong. But now I realised something stranger yet. os.environment['PWD'] is set to the folder where I have my development files. This is not at all where the application is running. Stranger yet, os.getcwd() returns /home/users/michel? This is inconsistent with what I see in os.environ. Should it not be the same as os.environ['PWD']?

The most important problem remains though: Why is the value set by apache's SetEnv (MYAPP_PATH in this case) not found in os.environ?

解决方案

Note that the WSGI environment is passed upon each request to the application in the environ argument of the application object. This environment is totally unrelated to the process environment which is kept in os.environ. The SetEnv directive has no effect on os.environ and there is no way through Apache configuration directives to affect what is in the process environment.

So you have to do something else other than getenviron or os.environ['PWD'] to get the MY_PATH from apache.

Flask adds the wsgi environ to the request, not app.environ, it is done by the underlaying werkzeug. So on each request to the application, apache will add the MYAPP_CONF key and youll access it wherever you can access request it seems as, request.environ.get('MYAPP_CONFIG') for example.

这篇关于Apache SetEnv 未按预期与 mod_wsgi 一起工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆