============================= Capitulo 12: Desplegar Django =============================
Este capítulo cubre el último paso esencial para construir una aplicación Django: el despliegue en un servidor de producción.
Si has estado siguiendo los ejemplos presentados, probablemente has estado usando
runserver
, como ya sabes este comando inicia el servidor web y hace que las
cosas sean realmente muy fáciles -- con runserver
, no tienes que preocuparte
por instalar un servidor Web. Sin embargo runserver
está diseñado únicamente
para ser usado en el desarrollo de forma local, no para exponerlo en un sitio
publico Web. Para desplegar una aplicación Django, es necesario enlazarla a un
servidor Web poderoso tal como Apache. En este capítulo te mostraremos como
hacerlo -- pero primero, es necesario comprobar una lista de cosas que hacer
en el código antes de llevarlo a un entorno de producción.
Afortunadamente el comando runserver
es bastante parecido a un servidor
"real", por lo que no necesitas realizar muchos cambios a tu aplicación Django
para dejarla lista para producción. Sin embargo hay algunas cosas esenciales
que necesitas conocer antes de servirla en producción.
Cuando creamos un proyecto en el :doc:`capítulo 2<chapter02>`, el comando
django-admin.py startproject
creo un archivo llamado settings.py
el
cual contiene la variable DEBUG
fijada en True
por defecto, es decir
que esta en modo de depuración. Muchas de las partes internas de Django
comprueban esta configuración y cambian su comportamiento si el modo
DEBUG
esta activado. Por ejemplo, si DEBUG
está fijado en True
,
entonces:
- Todas las consultas a la base de datos se guardan en memoria como objetos
django.db.connection.queries
. Como puedes imaginar, ¡esto consume mucha memoria! - Cualquier error 404, renderizara una página especial, a decir una página no encontrada (cubierta en el capítulo 3) más que retornar una apropiada respuesta 404. Esta página contiene información potencialmente sensible y no debe ser expuesta al público en Internet.
- Cualquier excepción no atrapada en tu aplicación Django -- desde errores básicos en la sintaxis Python, errores de la base de datos o en la sintaxis de la plantilla -- serán renderizados por la páginas de errores bonitas de Django que probablemente adoras. Esta página contiene mas información sensible incluso que las páginas 404, por lo que nunca deben ser expuestas al publico.
Resumiendo, la variable de configuración DEBUG
= True
le dice a Django
que asuma que únicamente desarrolladores confiables están usando el sitio.
La internet está llena de personas poco fiables, y la primera cosa que debes
hacer cuando estés preparando tu aplicación para el despliegue en producción
es fijar la variable DEBUG
a False
.
De igual forma, es necesario fijar la variable TEMPLATE_DEBUG
a False
en producción. Esto para desactivar el modo depuración de las plantillas.
Ya que si está fijada en True
, esta configuración le dice al
sistema de plantillas de Django que guarde información extra acerca de cada
plantilla, para mostrarla en las útiles páginas de error, si el modo
DEBUG``=``True
(esta activado).
Si DEBUG
es True
, Django mostrara una muy útil página de error 404. Pero
si DEBUG
es False
, hará algo completamente diferente: renderizara
una plantilla llamada 404.html
en la raíz del directorio de plantillas.
Entonces, cuando estás listo para el despliegue, necesitas crear esta
plantilla y ponerle algo útil, como un mensaje de "Página no encontrada".
Aquí está un ejemplo de una página 404.html
, puedes usarla como el punto
de partida, para crear tu propia plantilla. La plantilla asume que estas
usando la herencia de plantillas y tiene definida una plantilla base.html
con bloques llamados titulo
y contenido
.
{% extends "base.html" %}
{% block title %}Página no encontrada {% endblock %}
{% block content %}
<h1>Página no encontrada</h1>
<p>Lo sentimos, pero la página que buscas no ha sido encontrada.</p>
{% endblock %}
Para probar que la plantilla 404.html
funciona, solo cambia el modo DEBUG
a False
y visita una URL que no exista. (Esto trabaja de igual forma en
el servidor de desarrollo con runserver
, que en un servidor de producción.)
De igual forma si DEBUG
es False
, Django no mostrara la útil página
de traza de errores, en caso de excepciones no manejadas. En su lugar mostrara y
renderizara una plantilla llamada 500.html
. Tal como la página 404.html
,
esta plantilla debe de localizarse en la raíz del directorio de plantillas.
Hay una ligera trampa acerca de las páginas 500.html
. Nunca puedes
estar seguro por qué se renderiza esta plantilla, así que no deberías hacer
nada que requiere una conexión a la base de datos o confiar en cualquier parte
potencialmente rota de la infraestructura. (Por ejemplo, no deberías usar
etiquetas personalizadas en la plantilla.) Si usas la herencia de plantillas,
entonces la plantilla padre, no debería poder conectarse a la infraestructura
potencialmente rota. Por consiguiente, la mejor forma de aprovechar esto, es
evitar la herencia de plantillas y usar algo muy simple y solo en HTML. Aquí
hay un ejemplo de una página 500.html
, como un punto de partida, para que
diseñes la tuya:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<title>Página no disponible</title>
</head>
<body>
<h1>Página no disponible</h1>
<p>Lo sentimos, pero la página pedida no está disponible por que
el servidor tiene hipo.</p>
<p>Los ingenieros han sido notificados, así que vuelva a revisar más
tarde</p>
</body>
</html>
Cuando tu sitio creado con Django está corriendo y se lanza una excepción, necesitas estar al tanto, para poder corregir cualquier defecto. Por omisión, Django está configurado para enviar un e-mail a los desarrolladores del sitio, cuando el código lanza una excepción; pero necesitas hacer algunas cosas para configurarlo.
Primero cambia la configuración
ADMINS
para incluir la dirección de e-mail, con las direcciones de cada una de las personas que necesitan ser notificadas. Esta configuración es una tupla del tipo(nombre, email)
, la tupla puede ser asíADMINS = ( ('John Lennon', '[email protected]'), ('Paul McCartney', '[email protected]'), )
Segundo, asegúrate de que tu servidor este configurado para enviar e-mails. Configurar
postfix
,sendmail
o cualquier otro servidor de e-mails, esta fuera del alcance de este libro, pero del lado de Django, debes de asegurarte de configurarEMAIL_HOST
y establecer el "hostname" apropiado para tu servidor de correo. Si lo estableces a'localhost'
por defecto, trabajara fuera de la caja en muchos entornos de servicios compartidos. También es necesario que establezcas los parámetros de las siguientes configuraciones:EMAIL_HOST_USER
,EMAIL_HOST_PASSWORD
,EMAIL_PORT
oEMAIL_USE_TLS
, dependiendo de la complejidad de tu infraestructura.Por último, también establece el prefijo con
EMAIL_SUBJECT_PREFIX
el cual controla el nombre que Django usa delante de los errores en los correos electrónicos. Por defecto esta establecido en'[Django] '
.
Si tienes la clase CommonMiddleware
instalada (en tu archivo de configuración en
la variable MIDDLEWARE_CLASSES
que incluye
'django.middleware.common.CommonMiddleware'
, lo cual ocurre por defecto)
tienes la opción de recibir un e-mail cada vez que alguien visita una página
creada con Django que lanze un error 404 con una referencia a no-vacía -- esto
es cada vez que encuentre enlaces rotos. Si quieres activar esta característica,
establece SEND_BROKEN_LINK_EMAILS
a True
(esta es False
por defecto)
y establece en la configuración MANAGERS
a la persona o personas que recibirán
esos e-mails de enlaces rotos. MANAGERS
usa la misma sintaxis que
ADMINS
, un tupla. por ejemplo:
MANAGERS = ( ('George Harrison', '[email protected]'), ('Ringo Starr', '[email protected]'), )
Nota que los e-mails de errores pueden llegar a ser muy molestos; no son para todo el mundo.
Hasta ahora en este libro, hemos tratado únicamente con un simple archivo
de configuraciones: settings.py
generado por el comando django-admin.py
startproject
. Pero como estamos listos para desplegarlo, probablemente
nos encontremos con la necesidad de usar múltiples archivos de configuraciones
para mantener el entorno de desarrollo aislado del ambiente de producción.
(Por ejemplo, probablemente no quieras cambiar DEBUG
de False
a
True
cada vez que quieras probar los cambios al código, en tu maquina de
forma local.) Django hace esto muy sencillo, permitiéndote usar múltiples archivos
de configuraciones.
Si quieres organizar tus archivos de configuraciones, dividiéndolos en "desarrollo" y "producción", puedes lograrlo usando una de las siguientes tres formas.
- Establece dos archivos de configuraciones completos, de forma independiente.
- Establece un archivo de configuraciones "base" (uno para desarrollo) y un segundo (para producción) el archivo de configuraciones simplemente importa del primero y define lo que necesita y lo que debe sobrescribirse.
- Usa únicamente un archivo de configuraciones y deja que Python se encargue de la lógica y haga los cambios a las configuraciones, basado en el contexto.
Veamos estas tres opciones, una por una.
Primero, la forma más básica para aprovechar esto, es definir dos archivos
separados. Si estas siguiendo esto, ya tienes un archivo settings.py
. Ahora
solo es necesario realizar una copia llamada settings_production.py
.( Si no
te gusta el nombre, puedes llamarlo como quieras) En este nuevo archivo,
cambia las variables necesarias como DEBUG
, etc.
La segunda forma de aprovechar esto es muy parecida, pero reduce algo de redundancia, En lugar de tener dos archivos de configuraciones con contenido similar, tratamos solo con un archivo "base" y creamos otro archivo que lo importe. Por ejemplo:
# settings.py DEBUG = True TEMPLATE_DEBUG = DEBUG DATABASE_ENGINE = 'postgresql_psycopg2' DATABASE_NAME = 'devdb' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_PORT = '' # ... # settings_produccion.py from settings import * DEBUG = TEMPLATE_DEBUG = False DATABASE_NAME = 'production' DATABASE_USER = 'app' DATABASE_PASSWORD = 'dejameentrar'
Aquí tenemos que settings_production.py
importa cualquier cosa de
settings.py
y solo redefine las configuraciones que son usadas especialmente
en producción. En este caso DEBUG
está fijado en False
, pero también
hemos fijado diversos parámetros, como el acceso a la base de datos para
configurarla en producción. (Mas adelante te mostraremos como redefinir
cualquier configuración, no únicamente las básicas como DEBUG
. )
Finalmente, la forma más concisa para lograr tener dos entornos de configuraciones es usar un solo archivo, que se ramifique basado en el entorno. Una de las formas de lograr esto es comprobar el actual "hostname." Por ejemplo:
# settings.py import socket if socket.gethostname() == 'my-laptop': DEBUG = TEMPLATE_DEBUG = True else: DEBUG = TEMPLATE_DEBUG = False # ...
Aquí, importamos el modulo socket
de la librería estándar de Python y lo
usamos para comprobar el nombre actual del sistema. Podemos comprobar el
"nombre" para determinar si el código está siendo ejecutado en un servidor de
producción.
La lección central, es que el archivo de configuraciones es solo código Python. Que puede importar otros archivos, puede ejecutar lógica arbitraria, etc. Solo asegúrate de que el código Python en tu archivo de configuraciones sea a prueba de balas. Si lanza una excepción, Django probablemente se estrellara de fea manera.
Renombrar settings.py
Siéntete libre de renombrar tu archivo settings.py
a settings_dev.py
o settings/dev.py
o foobar.py
-- A Django no le importa como lo
llames, solo necesita saber que archivo estas usando para usarl las
configuraciones.
Solo ten en cuenta que si renombras el archivo settings.py
que es
generado por el comando django-admin.py startproject
, te encontraras con
que manage.py
lanza un mensaje de error, diciendo que no puede encontrar
el archivo de configuraciones. Esto es debido a que trata de importar un
modulo llamado settings
. Puedes arreglar esto editando manage.py
y
cambiándole el nombre de settings
por el nombre de tu modulo o usando
django-admin.py
en lugar de manage.py
. En este último caso, necesitas
fijar DJANGO_SETTINGS_MODULE
con las variables de entorno de la ruta
de búsqueda Python en tu archivo de configuraciones (por ejemplo
'misitio.settings'
)
Cuando usas Django tienes que indicarle qué configuración estás usando. Haz esto
mediante el uso de de la variable de entorno DJANGO_SETTINGS_MODULE
.
El valor de DJANGO_SETTINGS_MODULE
debe respetar la sintaxis de rutas de Python
(por ej. misitio.settings. Observa que el módulo de configuración debe de
encontrarse en la ruta de búsqueda para las importaciones de Python (PYTHONPATH).
Con todos estos cambios en el código, la siguiente parte de este capítulo se
centra en las instrucciones especificas para desplegar distintos entornos, tal
como Apache, Gunicorn... Las instrucciones son diferentes para cada entorno, pero
una cosa es la misma: en cada caso necesitas decirle al servidor Web cuál es tu:
DJANGO_SETTINGS_MODULE
. Este es el punto de entrada de tu aplicación Django.
DJANGO_SETTINGS_MODULE
enlaza tu archivo de configuraciones, el cual apunta
a ROOT_URLCONF
, que a su vez enlaza tus vistas y así sucesivamente.
El concepto clave para implementar usando WSGI
es el llamable application
que es el servidor de aplicaciones utiliza para comunicarse con el código. Este
comúnmente se provee como un objeto llamado application
el cual es un
modulo Python accesible por el servidor.
El comando startproject
crea un archivo llamado wsgi.py este contiene
una application
llamable. Este es usado tanto en el servidor de desarrollo,
como en el despliegue para producción, por lo que no necesitas crearlo.
El servidor WSGI obtiene la ruta de el llamable application
de su
configuración. El servidor de desarrollo "runserver" lee la configuración de
WSGI_APPLICATION
, la cual enlaza a el llamable application
en el archivo
wsgi.py
, lo mismo pasa con un servidor en producción.
Cuando el servidor WSGI carga tu aplicación, Django necesita importar
el modulo de configuraciones settings
-- que es donde se define
completamente la aplicación.
Django usa la variable de entorno DJANGO_SETTINGS_MODULE
que contiene la
ruta para localizar apropiadamente este modulo. Puedes usar diferentes valores
para desarrollo y producción; todo depende de cómo organices tus configuraciones.
Si esta variable no está establecida, el valor por defecto es misitio.settings
,
donde misitio
es el nombre de tu proyecto y settings
es el nombre del archivo
settings.py
. Esta es la forma en que runserver
carga el archivo de
configuraciones por defecto.
La plataforma dominante de implementación para Django es WSGI, el estándar Python para servidores y aplicaciones Web.
El comando administrativo startproject
establece por defecto, un simple
archivo de configuración WSGI, el cual puedes personalizarse según las
necesidades de tu proyecto, y directamente adaptarse a cualquier servidor que
ofrezca soporte para WSGI, como los siguientes servidores:
- mod_wsgi
- apache
- gunicorn
- uwsgi
Desplegar Django con Apache y mod_wsgi es la manera probada y comprobada de usar Django en un servidor en producción.
mod_wsgi es un modulo de Apache que puede hospedar cualquier aplicación Python WSGI, incluyendo Django. El cual trabaja con cualquier versión de Apache que soporte mod_wsgi.
La Documentación oficial es fantástica, y el código fuente incluye detalles sobre el modo de usar mod_wsgi. Por lo que probablemente quieras empezar por leer la documentación sobre instalación y configuración.
Para configurar Django con mod_wsgi, primero debes asegurarte de que tienes instalado Apache con el módulo mod_wsgi activado. Esto usualmente significa tener una directiva LoadModule en tu archivo de configuración de Apache. Parecida a esta::
LoadModule wsgi_module /modules/mod_wsgi.so
Una vez que has instalado y activado mod_wsgi, edita el archivo httpd.conf
de tu servidor Web Apache y agrega lo siguiente. Si estas usando una versión
de Apache anterior a la 2.4, remplaza Require all granted
con
Allow from all
y tambien agrega la línea Order deny,allow
arriba de esta.
WSGIScriptAlias / /path/to/mysite.com/mysite/wsgi.py
WSGIPythonPath /path/to/mysite.com
<Directory /path/to/mysite.com/mysite>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
El primer fragmento de la línea WSGIScriptAlias
es la ruta base que quieres
servir tu aplicaciones en (/
indica la raíz de la url) y la segunda es la
localización de el "archivo WSGI" --ver más abajo en tu sistema, usualmente
dentro del paquete de tu proyecto (misitio
en este ejemplo.) Esto le dice
al servidor Apache “Usa mod_wsgi para cualquier petición URL en ‘/’ o bajo ella,
usando la aplicación WSGI definida en ese archivo".
La línea WSGIPythonPath
se asegura que el paquete del proyecto este
disponible para importar la ruta de búsqueda de Python; en otras palabras se
asegura que import misitio
trabaje.
La pieza <Directory>
solo se asegura de que Apache pueda acceder al archivo
wsgi.py
, ya que se utiliza para apuntar a lugares de nuestra sistema de
archivos,
Lo siguiente que necesitas hacer, es asegurarte que exista una archivo
wsgi.py
, como seguramente te diste cuenta el comando startproject
crea
este archivo por defecto al crear el proyecto, de otra forma tendrías que crear
este archivo manualmente.
Warning
Si varios sitios están siendo ejecutados en un simple proceso mod_wsgi,
todos ellos usarán las configuraciones de cualquiera de los procesos que
corra primero. Esto puede se solventado con un pequeño cambio en el archivo
wsgi.py
(ve los comentarios en el archivo para detalles) o puedes
asegurarte de cada sitio sea ejecutado en un proceso independiente, usando su
propio demonio, sobrescribiendo el valor por defecto usando:
os.environ["DJANGO_SETTINGS_MODULE"] = "misitio.settings"
en el arhvio
wsgi.py
Si has instalado las dependencias Python de tu proyecto dentro de virtualenv,
necesitas agregar la ruta de "virtualenv's" al directorio site-packages
, así
como el camino de búsqueda. Para hacer esto agrega una ruta de búsqueda adicional
a la directiva WSGIPythonPath
, con las múltiples rutas separadas por dos
puntos (:
) si estas usando un sistema del tipo UNIX, o punto y coma (;
)
si estas usando Windows. Si cualquier parte de la ruta al directorio contiene
caracteres como espacios, la cadena completa de argumentos para WSGIPythonPath
debe ser citada:
WSGIPythonPath /path/to/mysite.com:/path/to/your/venv/lib/python3.X/site-packages
Asegúrate de darle la ruta correcta a tu virtualenv, y remplazar python3.X
con la versión correcta de Python (por ejemplo python3.4
).
"Modo Demonio" es el modo recomendado para ejecutar mod_wsgi (en plataformas que
no son Windows). Para crear el grupo de procesos requeridos por el demonio y
delegar la instancia Django para ejecutarse, necesitas agregar apropiadamente
las directivas WSGIDaemonProcess
y WSGIProcessGroup
. Un cambio mas
es requerido en la anterior configuración si utilizas el modo demonio no
puedes usar WSGIPythonPath
, en lugar de eso debes usar la opción
python-path
para WSGIDaemonProcess
, por ejemplo:
WSGIDaemonProcess example.com python-path=/path/to/mysite.com:/path/to/venv/lib/python2.7/site-packages WSGIProcessGroup example.com
Puedes consultar La documentación oficial de mode_wsgi para más detalles.
Django no debería ser utilizado para servir archivos multimedia (imágen, audio, video, pdf) por sí mismo; mejor deja ese trabajo a un servidor Web especializado en estas tareas.
Recomendamos usar un servidor Web separado (es decir, uno que no está corriendo a la vez Django) para servir estos archivos. Estos son algunas buenas opciones:
Sin embargo, si no tienes otra opción para servir los archivos multimedia, que
no sea el mismo VirtualHost
Apache que usa Django, puedes configurar Apache
para que sirva algunas URLs estáticas y otras usando la interface mod_wsgi
para Django
Este ejemplo configura Django en la raíz del sitio, pero explícitamente sirve
robots.txt
, favicon.ico
, y cualquier archivo CSS, y cualquier cosa en
el espacio de URL /static/
y /media/
será tratado como archivos
estáticos. Todas las demás URLs será servidas usando mod_wsgi:
Alias /robots.txt /path/to/mysite.com/static/robots.txt
Alias /favicon.ico /path/to/mysite.com/static/favicon.ico
AliasMatch ^/([^/]*\.css) /path/to/mysite.com/static/styles/$1
Alias /media/ /path/to/mysite.com/media/
Alias /static/ /path/to/mysite.com/static/
<Directory /path/to/mysite.com/static>
Require all granted
</Directory>
<Directory /path/to/mysite.com/media>
Require all granted
</Directory>
WSGIScriptAlias / /path/to/mysite.com/mysite/wsgi.py
<Directory /path/to/mysite.com/mysite>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
Si estas usando una versión de Apache anterior a la 2.4, remplaza
Require all granted
con Allow from all
y tambien agrega la línea
Order deny,allow
arriba de esta.
Cuando django.contrib.staticfiles
está en el archivo de configuraciones en
la variable INSTALLED_APPS
El entorno de desarrollo de Django sirve
automáticamente los archivos estáticos de la interfaz administrativa (y de
cualquier aplicación instalada). Este no es el caso cuando usas otro servidor
para este trabajo. Tu eres responsable de configurar Apache o cualquier otro
servidor Web que utilices, para servir los archivos de la interfaz
administrativa.
Los archivos de la interfaz administrativa se localizan en
django/contrib/admin/static/admin
en la distribucion de Django
Es fuertemente recomendable usar django.contrib.staticfiles
para manejar
los archivos de la interfaz administrativa (Junto con el servidor Web, esto
significa que puedes usar el comando collectstatic
para recolectar todos
los archivos estáticos en STATIC_ROOT
y luego configurar el servidor Web
para servir STATIC_ROOT
, pero aquí estan otras tres formas de hacer lo mismo:
- Crea un enlace simbólico a los archivos estáticos de la interfaz
administrativa desde la raíz de Apache (esto puede requerir usar
+FollowSymLinks
en la configuración de Apache) - Usa una directiva
Alias
, como mostramos arriba, un alias apropiado a la URL (probablementeSTATIC_URL
+admin/
) a la posición actual de los archivos estáticos. - Copia los archivos estáticos de la interfaz administrativa de modo que se localicen dentro de la raíz de Apache.
Gunicorn ('Unicornio verde') es un servidor WSGI implementado en Python. No tiene dependencias y es fácil de instalar y usar.
Hay dos formas de usar Gunicorn con Django. La primera es tratar a Gunicorn simplemente como otra aplicación WSGI. La segunda es usar Gunicorn de forma especial integrándolo con Django.
Una vez instalado Gunicorn, tenemos disponible un comando gunicorn
que
inicia el proceso del servidor Gunicorn. Esto es tan simple, ya que gunicorn
solo necesita ser llamado con la localización del objeto aplication
del WSGI.:
gunicorn [OPTIONS] APP_MODULE
Donde APP_MODULE
es un patron del tipo NOMBRE_MODULO:NOMBRE_VARIABLE_
.
El nombre del modulo debe ser la ruta completa al proyecto, mientras que el
nombre de la variable se refiere al llamable WSGI el cual debe de encontrarse
en el modo especificado.
Así para un típico proyecto Django, invocar a gunicorn se vería así:
gunicorn misitio.wsgi:application
Donde misitio
es el nombre del proyecto y application
es el llamable
del WSGI.
(Esto requiere que el proyecto este en la ruta de búsqueda de Python;
la forma más simple de hacer esto, es asegurarse de ejecutar el comando
"gunicorn" desde el mismo directorio en que esta el archivo manage.py
)
Nota:
Es altamente recomendable simplemente ejecutar la aplicación con la interface
WSGI usando el comando gunicorn
descrito anteriormente.
Para usar Gunicorn integrado con Django, primero agrega "gunicorn"
a tus
aplicaciones instaladas, en la variable INSTALLED_APPS
. Luego ejecuta el
comando python manage.py run_gunicorn
Este ofrece algunas sutiles opciones específicas para Django.
- Fija el nombre del proceso de gunicorn para ser el del proyecto
- Valida los modelos instalados
- Permite agregar una opción
--adminmedia
para pasarle la localización de los archivos media de la interfaz administrativa.
Puedes consultar la documentación oficial para encontrar consejos adicionales sobre cómo empezar a usar y mantener un servidor Gunicorn.
uWSGI es un servidor rápido codificado en C, autoregenerable y desarrollado como una aplicación amigable para los administradores de sistemas.
La wiki de WSGI describe algunos método de instalación. Usando pip, el manejador de paquetes python puedes instalar cualquier versión de uWSGI con un simple comando. Por ejemplo:
# Instala la actual versión estable.
sudo pip install uwsgi
# O installa la LTS (Soporte a largo plazo).
sudo pip install http://projects.unbit.it/downloads/uwsgi-lts.tar.gz
Warning
Algunas distribuciones, incluidas Debían y Ubuntu, incluyen versiones
antiguas de uWSGI que no cumplen las especificaciones WSGI. Versiones
anteriores a la 1.2.6 no llaman a close
en el objeto de repuesta
después de manejar una petición. En estos casos
django.core.signals.request_finished
no envía las señales. Esto
puede dar lugar a conexiones lentas a los servidores de la base de datos y
memcache.
uWSGI opera en un modelo cliente-servidor. El servidor web (por ejemplo nginx, Apache) se comunicacn con un proceso del "trabajo" django-uwsgi para servir dinámicamente el contexto. Consulta la documentación a fondo de uWSGI's para obtener más detalles.
uWSGI soporta múltiples formas para configurar el proceso. Puede iniciar mediante un comando o leyendo un archivo de configuraciones de inicio. Puedes consultar más ejemplos y la documentación de configuraciones para obtener detalles mas específicos.
Este es un ejemplo de un comando para iniciar el servidor uWSGI:
uwsgi --chdir=/path/to/your/project \
--module=misitio.wsgi:application \
--env DJANGO_SETTINGS_MODULE=misitio.settings \
--master --pidfile=/tmp/project-master.pid \
--socket=127.0.0.1:49152 \ # can also be a file
--processes=5 \ # number of worker processes
--uid=1000 --gid=2000 \ # if root, uwsgi can drop privileges
--harakiri=20 \ # respawn processes taking more than 20 seconds
--max-requests=5000 \ # respawn processes after serving 5000 requests
--vacuum \ # clear environment on exit
--home=/path/to/virtual/env \ # optional path to a virtualenv
--daemonize=/var/log/uwsgi/yourproject.log # background the process
Este asume que estas situado en el nivel superior de un paquete llamado
misitio
y dentro del existe un modulo wsgi.py, que contienen un objeto
application
WSGI. No deberías tener ningún problema si ejecutaste el comando
django-admin.py startproject misitio
, ya que este se encarga de crear la
estructura del proyecto por default. Si este archivo no existe necesitas
crearlo.
Las opciones especificas de Django son:
chdir
: La ruta al directorio, que necesita estar en la ruta de importacion de Python. -- es decir el directorio que contiene el paquetemisitio
module
: El modulo WSGI para usar --probablemente el modulomisitio.wsgi
questartproject
creo.env
: Debe contener por lo menosDJANGO_SETTINGS_MODULE
.home
: Opcionalmente la ruta al proyecto, si estas usando virtualenv.
Ejemplo de un archivo de configuración ini:
[uwsgi]
chdir=/path/to/your/project
module=misitio.wsgi:application
master=True
pidfile=/tmp/project-master.pid
vacuum=True
max-requests=5000
daemonize=/var/log/uwsgi/yourproject.log
Ejemplo del uso de un archivo de configuración ini:
uwsgi --ini uwsgi.ini
Consulta el manejo de procesos uWSGI para más información sobre iniciar, detener y recargar los procesos del servidor uWSGI.
lighttpd es un servidor Web liviano usado habitualmente para servir archivos estáticos. Admite FastCGI en forma nativa y por lo tanto es también una opción ideal para servir tanto páginas estáticas como dinámicas, si tu sitio no tiene necesidades específicas de Apache.
Asegúrate que mod_fastcgi
está en tu lista de módulos, en algún lugar
después de mod_rewrite
y mod_access
, pero no antes de mod_accesslog
.
Probablemente desees también mod_alias
, para servir los archivos media de
la interfaz administrativa.
Agrega lo siguiente a tu archivo de configuración de lighttpd:
server.document-root = "/home/user/public_html"
fastcgi.server = (
"/mysite.fcgi" => (
"main" => (
# Use host / port instead of socket for TCP fastcgi
# "host" => "127.0.0.1",
# "port" => 3033,
"socket" => "/home/user/mysite.sock",
"check-local" => "disable",
)
),
)
alias.url = (
"/media" => "/home/user/django/contrib/admin/media/",
)
url.rewrite-once = (
"^(/media.*)$" => "$1",
"^/favicon\.ico$" => "/media/favicon.ico",
"^(/.*)$" => "/mysite.fcgi$1",
)
lighttpd te permite usar “configuración condicional” para permitir la configuración personalizada para cada host. Para especificar múltiples sitios FastCGI, solo agrega un bloque condicional en torno a tu configuración FastCGI para cada sitio:
# If the hostname is 'www.example1.com'...
$HTTP["host"] == "www.example1.com" {
server.document-root = "/foo/site1"
fastcgi.server = (
...
)
...
}
# If the hostname is 'www.example2.com'...
$HTTP["host"] == "www.example2.com" {
server.document-root = "/foo/site2"
fastcgi.server = (
...
)
...
}
Puedes también ejecutar múltiples instalaciones de Django en el mismo sitio simplemente especificando múltiples entradas en la directiva fastcgi.server. Agrega un host FastCGI para cada una.
Ahora que sabes cómo tener a Django ejecutando en un servidor simple, veamos como puedes escalar una instalación Django. Esta sección explica cómo puede escalar un sitio desde un servidor único a un clúster de gran escala que pueda servir millones de hits por hora.
Es importante notar, sin embargo, que cada sitio grande es grande de diferentes formas, por lo que escalar es cualquier cosa menos una operación de una solución única para todos los casos. La siguiente cobertura debe ser suficiente para mostrar el principio general, y cuando sea posible, trataremos de señalar donde se puedan elegir distintas opciones.
Primero, haremos una buena presuposición, y hablaremos exclusivamente acerca de escalamiento bajo Apache y wsgi. A pesar de que conocemos vario casos exitosos de desarrollos FastCGI y mod_python medios y grandes, estamos mucho más familiarizados con Apache.
La mayoría de los sitios Web, empiezan ejecutándose en un servidor único, con una arquitectura parecida a la de la Figura 12-1.
Esto funciona bien para sitios pequeños y medianos, y es relativamente barato – puedes instalar un servidor único diseñado para Django por menos de 3,000 dólares.
Sin embargo, a medida que el tráfico se incremente, caerás rápidamente en contención de recursos entre las diferentes piezas de software. Los servidores de base de datos y los servidores Web adoran tener el servidor entero para ellos, y cuando corren en el mismo servidor siempre terminan “peleando” por los mismos recursos (RAM, CPU) que prefieren monopolizar.
Esto se resuelve fácilmente moviendo el servidor de base de datos a una segunda máquina, como se explica en la siguiente sección.
En lo que tiene que ver con Django, el proceso de separar el servidor de bases
de datos es extremadamente sencillo: simplemente necesitas cambiar la
configuración de DATABASE_HOST
a la IP o nombre DNS de tu servidor.
Probablemente sea una buena idea usar la IP si es posible, ya que depender de
la DNS para la conexión entre el servidor Web y el servidor de bases de datos
no se recomienda.
Con un servidor de base de datos separado, nuestra arquitectura ahora se ve como en la Figura 12-2.
Aquí es donde empezamos a movernos hacia lo que usualmente se llama una arquitectura n-tier. No te asustes por la terminología – sólo se refiere al hecho de que diferentes “tiers” de la pila Web separadas en diferentes máquinas físicas.
A esta altura, si anticipas que en algún momento vas a necesitar crecer más allá de un servidor de base de datos único, probablemente sea una buena idea empezar a pensar en pooling de conexiones y/o replicación de bases de datos. Desafortunadamente, no hay suficiente espacio para hacerle justicia a estos temas en este libro, así que vas a necesitar consultar la documentación y/o a la comunidad de tu base de datos para más información.
Aún tenemos un gran problema por delante, usando únicamente un servidor: servir los archivos media desde la misma caja que maneja el contenido dinámico.
Estas dos actividades tienen su mejor performance bajo distintas circunstancias, y encerrándolas en la misma caja terminarás con que ninguna de las dos tendrá un buen rendimiento. Así que el siguiente paso es separar los medios – esto es, todo lo que no es generado por una vista de Django – a un servidor dedicado (ver Figura 12-3).
Idealmente, este servidor de medios debería ejecutarse en un servidor Web, optimizado para la entrega de medios estáticos. lighttpd y tux (http://www.djangoproject.com/r/tux/) son dos excelentes elecciones aquí, pero un servidor Apache bien ‘personalizado’ también puede funcionar.
Para sitios pesados en contenidos estáticos (fotos, videos, etc.), moverse a un servidor de medios separado es doblemente importante y debería ser el primer paso en el escalamiento hacia arriba.
De todos modos, este paso puede ser un poco delicado. Si tu aplicación necesita subir archivos Django necesita poder escribir sobre los medios ‘subidos’ en el servidor de medios. (la configuración de MEDIA_ROOT controla donde escriben estos medios). Si un medio habita en otro servidor, de todas formas necesitas organizar una forma de que esa escritura se pueda hacer a través de la red.
A esta altura, ya hemos separado las cosas todo lo posible. Esta configuración de tres servers debería manejar una cantidad muy grande de tráfico – nosotros servimos alrededor de 10 millones de hits por día con una arquitectura de este tipo– así que si creces más allá, necesitarás empezar a agregar redundancia.
En realidad, esto es algo bueno. Una mirada a la Figura 20-3 te permitirá ver que si falla aunque sea uno solo de los servidores, el sitio entero se cae. Así que a medida que agregas servidores redundantes, no sólo incrementas capacidad, sino también confiabilidad.
Para este ejemplo, asumamos que el primero que se ve superado en capacidad es el servidor Web. Es fácil tener múltiples copias de un sitio Django ejecutando en diferente hardware. – simplemente copia el código en varias máquinas, e inicia Apache en cada una de ellas.
Sin embargo, necesitas otra pieza de software para distribuir el tráfico entre los servidores: un balanceador de carga. Puedes comprar balanceadores de carga por hardware caros y propietarios, pero existen algunos balanceadores de carga por software de alta calidad que son open source.
mod_proxy de Apache es una opción, pero hemos encontrado que Perlbal (http://www.djangoproject.com/r/perlbal/) es simplemente fantástico. Es un balanceador de carga y proxy inverso escrito por las mismas personas que escribieron memcached.
Con los servidores Web en cluster, nuestra arquitectura en evolución empieza a verse más compleja, como se ve en la Figura 12-4.
Observar que en el diagrama nos referimos a los servidores Web como “el cluster” para indicar que el numero de servidores básicamente es variable. Una vez que tienes un balanceador de carga en el frente, puedes agregar y eliminar servidores Web back-end sin perder un segundo fuera de servicio.
En este punto, los siguientes pasos son derivaciones del último:
- A medida que necesites más performance en la base de datos, necesitarás agregar servidores de base de datos replicados. MySQL tiene replicación incorporada; los usuarios de PostgreSQL deberían mirar a Slony (http://www.djangoproject.com/r/slony/) y pgpool (http://www.djangoproject.com/r/pgpool/) para replicación y pooling de conexiones, respectivamente.
- Si un solo balanceador de carga no es suficiente, puedes agregar más máquinas balanceadoras de carga y distribuir entre ellas usando DNS round-robin.
- Si un servidor único de medios no es suficiente, puedes agregar más servidores de medios y distribuir la carga con tu cluster de balanceadores de carga.
- Si necesitas más almacenamiento cache, puedes agregar servidores de cache dedicados.
- En cualquier etapa, si un cluster no tiene buena performance, puedes agregar más servidores al cluster.
Después de algunas de estas iteraciones, una arquitectura de gran escala debe verse como en la Figura 12-5
A pesar de que mostramos solo dos o tres servidores en cada nivel, no hay un límite fundamental a cuantos puedes agregar.
Si tienes grandes cantidades de dinero, simplemente puedes irle arrojando hardware a los problemas de escalado. Para el resto de nosotros, sin embargo, el ajuste de performance es una obligación.
Nota:
Incidentalmente, si alguien con monstruosas cantidades de dinero está leyendo este libro, por favor considere una donación sustancial al proyecto Django. Aceptamos diamantes en bruto y lingotes de oro.
Desafortunadamente, el ajuste de performance es más un arte que una ciencia, y es aun más difícil de escribir sobre eso que sobre escalamiento. Si estás pensando seriamente en desplegar una aplicación Django de gran escala, deberás pasar un buen tiempo aprendiendo como ajustar cada pieza de tu stack.
Las siguientes secciones, sin embargo, presentan algunos tips específicos del ajuste de performance de Django que hemos descubierto a través de los años.
Incluso la RAM más costosa es relativamente costeable en estos días. Compra toda la RAM que puedas, y después compra un poco más.
Los procesadores más rápidos no mejoran la performance tanto. La mayoría de los servidores Web desperdician el 90% de su tiempo esperando I/O del disco. En cuanto empieces a swappear, la performance directamente se muere. Los discos más rápidos pueden ayudar levemente, pero son mucho más caros que la RAM, así que no cuentan.
Si tienes varios servidores, el primer lugar donde poner tu RAM es en el servidor de base de datos. Si puedes, compra suficiente ram como para tener toda tu base de datos en memoria. Esto no es tan difícil. Hemos diseñado sitios que incluye medio millón de artículos en menos de menos de 2 GB de espacio.
Después, maximiza la RAM de tu servidor Web. La situación ideal es aquella en la que ningún servidor swapea – nunca. Si llegas a ese punto, debes poder manejar la mayor parte del tráfico normal.
Keep-Alive
es una característica de HTTP que permite que múltiples pedidos
HTTP sean servidos sobre una conexión TCP única, evitando la sobrecarga de
conectar y desconectar.
Esto parece bueno a primera vista, pero puede asesinar al performance de un sitio Django. Si estás sirviendo medios desde un servidor separado, cada usuario que esté navegando tu sitio solo requerirá una página del servidor Django cada diez segundos aproximadamente. Esto deja a los servidores HTTP esperando el siguiente pedido keep-alive, y un servidor HTTP ocioso consume RAM que debería estar usando un servidor activo.
A pesar de que Django admite varios back-ends de cache diferentes, ninguno de ellos siquiera se acerca a ser tan rápido como memcached. Si tienes un sitio con tráfico alto, ni pierdas tiempo con los otros –- usa directamente memcached.
Por supuesto, seleccionar memcached no te hace mejor si no lo usas realmente. EL :doc:`capítulo <chapter15>` te dice como usarlo: aprende a usar el framework de cache de Django, y úsalo en todas las partes que te sea posible. Un uso de cache agresivo y preemptico es usualmente lo único que se puede hacer para mantener un sitio Web funcionando bajo el mayor tráfico.
Cada pieza del stack de Django – desde Linux a Apache a PostgreSQL o MySQL – tiene una comunidad maravillosa detrás. Si realmente quieres obtener ese último 1% de tus servidores, únete a las comunidades open source que están detrás de tu software y pide ayuda. La mayoría de los miembros de la comunidad del software libre estarán felices de ayudar.
Y también asegúrate de unirte a la comunidad Django. Tus humildes autores son solo dos miembros de un grupo increíblemente activo y creciente de desarrolladores Django. Nuestra comunidad tiene una enorme cantidad de experiencia colectiva para ofrecer.
El resto de los capítulos se enfocan en otras características de Django, que puedes o no necesitar, dependiendo de tus aplicaciones. Siéntete libre de leerlos en el orden que prefieras.