Apache SetEnv no funciona como se esperaba con mod_wsgi
En una aplicación de matraz que escribí, utilizo una biblioteca externa que se puede configurar utilizando variables de entorno. Nota: escribí esta biblioteca externa yo mismo. Asiquepodrí hacer cambios si es necesario. Cuando se ejecuta desde la línea de comandos, se ejecuta el servidor de matraz con:
# env = python virtual environment
ENV_VAR=foo ./env/bin/python myapp/webui.py
it todos los woks como se esperaba. Pero después de implementarlo en Apache y usarSetEnv
lo hacen trabajo más. De hecho, imprimiendoos.environ
astderr
(por lo que aparece en los registros de apache revela que lawsgi
l proceso @ parece estar en un entorno muy diferente (por ejemplo,os.environ['PWD']
parece sercamin apagado. De hecho, apunta a mi carpeta de desarrollo.
Para ayudar a identificar el problema, a continuación se detallan las partes relevantes de la aplicación como una aplicación independiente de hello-world. La salida del error y las observaciones se encuentran al final de la publicación.
Diseño de carpeta de la aplicación:plicación @Python:
.
├── myapp.ini
├── setup.py
└── testenv
├── __init__.py
├── model
│ └── __init__.py
└── webui.py
Carpeta Apache /var/www/michel/testenv
):
.
├── env
│ ├── [...]
├── logs
│ ├── access.log
│ └── error.log
└── wsgi
└── app.wsgi
myapp.ini[app]
somevar=somevalue
setup.pyfrom setuptools import setup, find_packages
setup(
name="testenv",
version='1.0dev1',
description="A test app",
long_description="Hello World!",
author="Some Author",
author_email="[email protected]",
license="BSD",
include_package_data=True,
install_requires = [
'flask',
],
packages=find_packages(exclude=["tests.*", "tests"]),
zip_safe=False,
)
testenv /en es .py# empty
testenv / model /en es .pyfrom 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.pyfrom 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.wsgiactivate_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 y observacionesEsta es la salida del registro de errores de 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
i primera observación es que la variable de entornoMYAPP_PATH
no aparece enos.environ
(esto no es visible en esta salida, pero lo probé, ¡y no está allí!). Como tal, la configuración "resolver" vuelve a la ruta predeterminada.
Y mi segunda observación es la ruta de búsqueda para las listas de archivos de configuración/home/users/michel
como valor de retorno deos.getcwd()
. En realidad esperaba algo dentro de/var/www/michel/testenv
.
Mi instinto me dice que la forma en que estoy haciendo la resolución de configuración no es correcta. Principalmente porque el código se ejecuta en el momento de la importación. Esto me lleva a la idea de que tal vez el código de resolución de configuración se ejecuteantes d El entorno WSGI está configurado correctamente. ¿Estoy en algo allí?
Discusión breve / pregunta tangencialCómo podríat ¿la resolución de configuración en este caso? Dado que la subcarpeta "modelo" es en realidad un módulo externo, que también debería funcionar en aplicaciones que no sean wsgi, y debería proporcionar un método para configurar una conexión de base de datos.
Personalmente, me gusta la forma en que busco los archivos de configuración sin dejar de anularlos. Solo que el hecho de que el código se ejecute en el momento de la importación hace que mis sentidos de araña se estremezcan como locos. La razón detrás de esto: el manejo de la configuración está completamente oculto (barrera de abstracción) por otros desarrolladores que usan este módulo, y "simplemente funciona". Solo necesitan importar el módulo (con un archivo de configuración existente, por supuesto) y pueden saltar directamente sin conocer los detalles de la base de datos. Esto también les brinda una manera fácil de trabajar con diferentes bases de datos (desarrollo / prueba / implementación) y cambiar entre ellas fácilmente.
Ahora, dentro de mod_wsgi ya no lo hace:
ActualizarAhora mismo, para probar mi idea anterior, cambié lawebui.py
a lo siguiente:
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()
La salida en la página web es la siguiente:
{
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/"
}
Esto muestra el mismo entorno que el visto por otros métodos de depuración. Así que mi inicial fue incorrecta. Pero ahora me di cuenta de algo extraño todavía. @os.environment['PWD']
está configurado en la carpeta donde tengo mis archivos de desarrollo. Esto no está entodo donde se ejecuta la aplicación. Más extraño aún,os.getcwd()
devoluciones/home/users/michel
? Esto es inconsistente con lo que veo enos.environ
. ¿No debería ser lo mismo queos.environ['PWD']
?
in embargo, el problema más importante sigue siendo: ¿por qué el valor es establecido por @ de apachSetEnv
(MYAPP_PATH
en este caso) no encontrado enos.environ
?