Passenger y Python WSGI

Generalidades

  • Las instrucciones proporcionadas en la siguiente sección se consideran avanzadas.
  • Se espera que estés bien informado sobre el shell de UNIX.
  • El soporte técnico para estas instrucciones no está disponible en el soporte técnico de DreamHost.
  • Los cambios en el servidor pueden hacer que esto se rompa.
  • Está preparado para solucionar este problema tu mismo si esto sucede

Passenger es mejor conocido por usarse con aplicaciones Ruby on Rails, sin embargo, también puede servir aplicaciones web Python que usan la interfaz WSGI, incluida cualquier aplicación que use Django framework. Dado que Passenger permite que tu aplicación resida temporalmente en la memoria mientras se usa activamente, permitirá que tu sitio responda significativamente más rápido de lo que sería posible de otra manera.

El soporte WSGI de Passenger funciona razonablemente bien, sin embargo, otra opción disponible es Python FastCGI en caso de que tengas problemas.

Configurar Passenger WSGI

Para iniciar un sitio de ejemplo de Python usando Passenger WSGI, tu primer paso debe ser configurar el dominio para usar Passenger como se muestra en el artículo de Passenger. Ten en cuenta que la raíz del documento debe terminar en /public para una aplicación Passenger, ya que este directorio se utilizará para servir medios estáticos.

Una vez que hayas configurado el dominio para usar Passenger, crea un archivo llamado passenger_wsgi.py en la carpeta arriba de la raíz del documento (es decir, si estableces la raíz de tu documento en /home/username/example.com/public, pondrías este archivo en /home/username/example.com/passenger_wsgi.py). Este archivo debe exportar un servidor WSGI con el nombre application. Aquí hay un ejemplo mínimo:

def application(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    return ["Hello, world!"]

Esta aplicación devolverá un archivo de texto con el contenido "Hello, world!" para cualquier solicitud.

Passenger WSGI y Django

Ve los siguientes artículos para obtener instrucciones sobre cómo configurar Passenger para ejecutar Django.

Passenger WSGI y virtualenv

Mientras Passenger carga tú passenger_wsgi.py en un contenedor especial (actualmente /dh/passenger/lib/phusion_passenger/wsgi/request_handler.py, aunque esto puede cambiar), no puedes seleccionar directamente qué intérprete de Python se usa para ejecutar tu aplicación. Sin embargo, puedes cambiar de intérprete mientras se ejecuta agregando el siguiente código al comienzo de tú passenger_wsgi.py:

import sys, os
INTERP = "/home/username/local/bin/python"
#INTERP is present twice so that the new Python interpreter knows the actual executable path
if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)

Establece INTERP en el intérprete de Python que deseas utilizar en lugar del predeterminado.

Passenger WSGI y Pylons/Pyramid

Si estás utilizando un sitio compatible con Pyramid-framework, lo siguiente debería funcionar para tú passenger_wsgi.py, asumiendo que has configurado el entorno virtual de Python en INTERP:

import sys, os
INTERP = "/home/username/local/bin/python"
#INTERP is present twice so that the new Python interpreter knows the actual executable path
if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)

from paste.deploy import loadapp
application = loadapp('config:/home/path/to/site/production.ini')

Ten en cuenta que si estás utilizando un sitio creado a partir de una de las plantillas de inicio de Pyramid, el archivo de configuración development.ini envuelve tu sitio en la capa ErrorMiddleware, similar a lo que se hace en la siguiente sección. Sin embargo, ErrorMiddleware no admite entornos en los que wsgi.multiprocess es True, por lo que debes utilizar la configuración de producción o modificar el entorno para establecer wsgi.multiprocess en False. (Nota: esto puede causar problemas si anulas manualmente la configuración). El siguiente enlace puede ofrecer más ayuda:

Pyramid-en-DreamHost

Solución alterna de errores 500 con Passenger WSGI

Passenger WSGI en este momento tiene dificultades para tratar con errores. Es decir, cuando tu aplicación WSGI (por ejemplo, pero sin limitarse a, Django) genera una excepción no detectada, Passenger muere, se muestra una página 500 en el navegador y el mensaje de error no se registra en el archivo home/username/logs/sitename/http/error.log. Esto dificulta la depuración.

Una solución es utilizar Python Paste como middleware WSGI entre Passenger y tu aplicación:

  1. Obtén Paste de aquí: http://pypi.python.org/pypi/Paste.
  2. Descomprime los archivos. Todo lo que necesitas es el directorio paste.
  3. Ponlo en el directorio de tu aplicación (por ejemplo, /home/username/sitename/myapp/paste)
  4. Edita tu archivo passenger_wsgi.py para incluir ese directorio en la ruta de Python y luego carga Paste.
Así es como podría verse tu archivo passenger_wsgi.py:
import sys, os
 cwd = os.getcwd()
 myapp_directory = cwd + '/myapp'
 sys.path.insert(0,myapp_directory)
 sys.path.append(os.getcwd())
 os.environ['DJANGO_SETTINGS_MODULE'] = "myapp.settings"
 from paste.exceptions.errormiddleware import ErrorMiddleware
 import django.core.handlers.wsgi
 application = django.core.handlers.wsgi.WSGIHandler()
 # To cut django out of the loop, comment the above application = ... line ,
 # and remove "test" from the below function definition.
 def testapplication(environ, start_response):
   status = '200 OK'
   output = 'Hello World! Running Python version ' + sys.version + '\n\n'
   response_headers = [('Content-type', 'text/plain'),
                       ('Content-Length', str(len(output)))]
   # to test paste's error catching prowess, uncomment the following line
   # while this function is the "application"
   #raise("error")
   start_response(status, response_headers)    
   return [output]
 application = ErrorMiddleware(application, debug=True)

Alternativa de depuración local

import os, sys

def _get_log():
    return file('/home/username/passengerwsgi.log', 'a')

log = _get_log()
print >>log, "Running %s" % (sys.executable)

INTERP = "/home/username/local/bin/python"
if sys.executable != INTERP: 
    print >>log, "Detected wrong interpreter location, swapping to %s" % (INTERP)
    #swapping interpreters will not flush any files
    log.flush()
    log.close()
    os.execl(INTERP, INTERP, *sys.argv)
    #Should resume execution from the top of the file

log.flush()
log.close() 

from paste.deploy import loadapp

def application(environ, start_response):
    log = _get_log()
    print >>log, "Application called:"
    print >>log, "environ: %s" % str(environ)
    results = []
    try:
        app = loadapp('config:/home/path/to/site/production.ini')
        print >>log, "App loaded, attempting to run"
        log.flush()
        results = app(environ, start_response)
        print >>log, "App executed successfully"
    except Exception, inst:
        print >>log, "Error: %s" % str(type(inst))
        print >>log, inst.args
        log.flush()
    finally:
        log.close()
    return results

Una alternativa de depuración local un poco más robusta

Este ejemplo usa el módulo de registro de Python. No contiene el bit de intérprete alternativo ni carga el archivo ini. Muestra cómo cargar módulos locales usando getcwd().

Aparte del nombre myappmodule y myappmodule.application, esto no hace ninguna suposición sobre su aplicación.

import os
import sys
import logging
# append current dir to module path
cwd = os.getcwd()
sys.path.append(cwd)
# assuming this module is in the same dir as passenger_wsgi, this now works!
import myappmodule

# create a logfile in the current directory
logfilename = os.path.join(cwd, 'passenger_wsgi.log')
# configure the logging
logging.basicConfig(filename=logfilename, level=logging.DEBUG)
logging.info("Running %s", sys.executable)

def application(environ, start_response):
    logging.info("Application called:")
    logging.info("environ: %s", str(environ))
    results = []
    try:
        results = myappmodule.application(environ, start_response)
        logging.info("App executed successfully")
    except Exception, inst:
        logging.exception("Error: %s", str(type(inst)))
    logging.info("Application call done")
    return results

Otra solución

Esta solución solo está disponible en un servidor VPS.

Otra solución es iniciar un servidor de desarrollo ejecutando:

[server]$ ./manage.py runserver

Siguiente, abre otro shell SSH y ejecuta:

[server]$ lynx localhost:8000

Esto abre tu aplicación en el navegador web lynx, sin pasar por Passenger usando el servidor de desarrollo Django. Si tienes suerte, te devolverá algunos comentarios útiles.

Esta solución no ayudará si el problema está en la configuración de Passenger, ya que este método omite a Passenger por completo.

Consejos y trucos

Intenta ejecutar tú passenger_wsgi.py desde tu línea de comando lo que puede señalar cualquier error de Python.

Passenger parece usar una sesión de Python persistente. Después de actualizar passenger_wsgi.py, asegúrate de ejecutar pkill python para restablecer la sesión y obligar al servidor a usar tus nuevos cambios.

Ver también

¿Este artículo ha respondido sus preguntas?

Última actualización el PST.

¿Aún no encuentra lo que busca?