Recetas de Implementación

Hay múltiples maneras de implementar web2py en un entorno de producción. Los detalles dependen de la configuración y los servicios provistos por el host.

En este capítulo consideramos los siguientes aspectos:

  • Implementación de Producción (Apache, Lighttpd, Cherokee)
  • Seguridad
  • Escalabilidad
  • Implementación el la plataforma del Motor de Aplicaciones de Google (GAE[13])

web2py viene con un servidor web SSL[21] habilitado, el servidor wsgi Rocket[22]. Aún cuando éste es un servidor web rápido, tiene una capacidad limitada de configuración. Es por esto que es mejor implementar web2py con Apache[78], Lightpd[85] o Cherokee[86]. Éstos son servidores web gratis y de código abierto que son personalizables y han demostrado ser fiables en entornos de producción de alto tráfico. Pueden configurarse para servir archivos estáticos directamente, encargarse de HTTPS, y pasar el control a web2py para el contenido dinámico.

Hasta hace unos pocos años, la interfaz estándar para comunicaciones entre servidores y aplicaciones web era la Interfaz de Pasarela Común (CGI por sus siglas en inglés)[77]. El principal problema con CGI es que crea un nuevo proceso por cada solicitud HTTP. Si la aplicación web está escrita en un lenguaje interpretado, cada solicitud HTTP servida por los scripts CGI comienzan una nueva instancia del intérprete. Ésto es lento, y debe ser evitado en un entorno de producción. Aún más, CGI sólo puede manejar respuestas simples.No puede manejar, por ejemplo, secuencias de archivos.

web2py provee un archivo modpythonhandler.py para interactuar con CGI.

Una solución a este problema es usar el módulo mod_python para Apache. Lo nombramos aquí porque su uso todavía es muy común, aún cuando el proyecto mod_python ha sido oficialmente abandonado por la Fundación de Software Apache. mod_python inicia una instancia del intérprete Python cuando Apache se inicia, y sirve cada solicitud HTTP en su propia tarea sin tener que reiniciar Python cada vez. Ésta es una mejor solución que CGI, pero no es una solución óptima, ya que mod_python usa su propia interfaz para la comunicación entre el servidor y las aplicaciones web. En mod_python, todas las aplicaciones alojadas corren bajo el mismo user-id/group-id, lo que representa problemas de seguridad.

web2py provee un archivo cgihandler.py para interactuar con mod_python.

En los últimos años, la comunidad Python se ha unido en una nueva interfaz estándar para la comunicación entre el servidor web y las aplicaciones web escritas en Python. Se llama Interfaz de Pasarela del Servidor Web (WSGI por sus siglas en inglés). web2py fue contruido sobre WSGI, y provee manejadores para usar otras interfaces cuando WSGI no está disponible.

Apache soporta WSGI por medio del módulo mod_wsgi[84] desarrollado por Graham Dumpleton.

web2py provee un archivo wsgihandler.py para interactuar con WSGI.

Algunos servicios de alojamiento web no soportan mod_wsgi. En éste caso, debemos usar Apache como un proxy y remitir todas las solicitudes entrantes al servidor incluido con web2py (funcionando, por ejemplo, en localhost:8000).

En ambos casos, con mod_wsgi y/o mod_proxy, Apache puede ser configurado para servir archivos estáticos y manejar encriptación SSL directamente, quitándole carga a web2py.

El servidor web Lighttpd actualmente no soporta la interfaz WSGI, pero soporta la interfaz FastCGI[87], que es una mejora sobre CGI. El objetivo principal de FastCGI es reducir la sobrecarga asociada con la interacción con el servidor web y programas CGI, permitiendo al servidor manejar mas solicitudes HTTP a la vez.

Según el sitio web de Lighttpd, “Lighttpd se usa en muchos sitios Web 2.0 populares como YouTube y Wikipedia. Su infraestructura de entrada y salida de alta velocidad les permite escalar mucho mejor con el mismo hardware que con servidores web alternativos”. Lighttpd con FastCGI es, de hecho, mas rápido que Apache con mod_wsgi.

web2py provee un archivo fcgihandler.py para interactuar con FastCGI.

web2py también incluye un gaehandler.py para interactuar con el Motor de Aplicaciones de Google (GAE). En GAE, las aplicaciones web funcionan “en la nube”. Ésto significa que el marco de trabajo abstrae completamente cualquier detalle de hardware. La aplicación web es replicada automáticamente tantas veces como sea necesario para servir todas las solicitudes concurrentes. Replicación en este caso significa más que multiples tareas en un único servidor; también significa multiples procesos en diferentes servidores. GAE logra este nivel de escalabilidad bloqueando el acceso de escritura al sistema de archivos, y toda la información persistente debe ser guardada en el almacén de datos BigTable de Google, o en la memoria caché.

En plataformas que no son GAE, la escalabilidad es un problema que debe ser abordado, y puede requerir algunos ajustes en las aplicaciones web2py. La manera más común de logra escalabilidad es usando múltiples servidores web detrás de un balanceador de cargas (un simple round robin, a algo más sofisticado, recibiendo señales directas desde los servidores).

Incluso cuando hayan múltiples servidores web, el servidor de base de datos debe ser uno, sólo uno. Por defecto, web2py usa el sistema de archivos para almacenar las sesiones, tickets de error, archivos cargados, y el caché. Ésto significa que en la configuración por defecto, las carpetas correspondientes deben ser carpetas compartidas:

scability

En el resto del capítulo, consideraremos varias recetas que pueden proporcionar una mejora sobre éste ingenuo enfoque, incluyendo:

  • Almacenar sesiones en la base de datos, en caché o simplemente no almacenar la sesión.
  • Almacenar los tickets en los sistemas de archivos locales y moverlos a la base de datos en lotes.
  • Usar memcache en vez de cache.ram y cache.disk.
  • Almacenar los archivos cargados en la base de datos en vez del sistema de archivos compartido.

Aún cuando recomendamos el seguimiento de las primeras tres recetas, la cuarta puede proporcionar una ventaja principalmente en el caso de archivos pequeños, pero puede llegar a ser contraproducente con archivos grandes.

Linux/Unix

Implementación de Producción en un sólo paso

Aquí hay algunos pasos a seguir para instalar apache+python+mod_wsgi+web2py+postgresql desde cero.

En Ubuntu:

wget http://web2py.googlecode.com/hg/scripts/setup-web2py-ubuntu.sh

chmod +x setup-web2py-ubuntu.sh

sudo ./setup-web2py-ubuntu.sh

En Fedora:

wget http://web2py.googlecode.com/hg/scripts/setup-web2py-fedora.sh

chmod +x setup-web2py-fedora.sh

sudo ./setup-web2py-fedora.sh

Ambos scrips deben funcionar sin problemas, pero cada instalación de Linux es diferente, así que asegurese de revisar el código fuente de ambos scrips antes de ejecutarlos. En el caso de Ubuntu, la mayoría de lo que hacen es explicado abajo. No implementan las optimizaciones de escalabilidad discutidas más adelante.

Configuración Apache

En esta sección, usamos Ubuntu 8.04 Server Edition como la plataforma de referencia. Los comandos de configuración son muy parecidos en otra distribución Linux basada en Debian, pero pueden ser diferentes en sistemas basados en Fedora (que usa yum en vez de apt-get).

Primero, asegúrese que todos los paquetes Python y Apache necesarios están instalados escribiendo los siguientes comandos en el terminal:

1
2
3
4
5
6
7
8
sudo apt-get update
sudo apt-get -y upgrade
sudo apt-get -y install openssh-server
sudo apt-get -y install python
sudo apt-get -y install python-dev
sudo apt-get -y install apache2
sudo apt-get -y install libapache2-mod-wsgi
sudo apt-egt -y install libapache2-mod-proxy-html

Luego, habilite el módulo SSL, el módulo proxy, y el módulo WSGI en Apache:

1
2
3
4
5
6
sudo ln -s /etc/apache2/mods-available/proxy_http.load \
           /etc/apache2/mods-enabled/proxy_http.load
sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod wsgi

Cree una carpeta SSL, y coloque los certificados SSL dentro de ella:

1
sudo mkdir /etc/apache2/ssl

Usted debe obtener sus certificados SSL de una Autoridad Certificada como verisign.com, pero, para fines de prueba, usted puede generar sus propios certificados auto-firmados siguiendo las instrucciones en la ref.[83]

Luego reinicie el servidor web:

1
sudo /etc/init.d/apache2 restart

El archivo de configuración de Apache es:

1
/etc/apache2/sites-available/default

Los registros de Apache son:

1
/var/log/apache2/

mod_wsgi

Descargue y descomprima la fuente web2py en la máquina donde usted instalo el servidor web mostrado arriba.

Instale web2py en /users/www-data/, por ejemplo, y de permisología de pertenencia al usuario www-data y el grupo www-data. Estos pasos pueden ser realizados con los siguientes comandos en el terminal:

1
2
3
4
cd /users/www-data/
sudo wget http://web2py.com/examples/static/web2py_src.zip
sudo unzip web2py_src.zip
sudo chown -R www-data:www-data /user/www-data/web2py

Para configurar web2py con mpd_wsgi, cree un nuevo archivo de configuración de Apache:

1
/etc/apache2/sites-available/web2py

en incluya el siguiente código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<VirtualHost *:80>
  ServerName web2py.example.com
  WSGIDaemonProcess web2py user=www-data group=www-data \
                           display-name=%{GROUP}
  WSGIProcessGroup web2py
  WSGIScriptAlias / /users/www-data/web2py/wsgihandler.py

  <Directory /users/www-data/web2py>
    AllowOverride None
    Order Allow,Deny
    Deny from all
    <Files wsgihandler.py>
      Allow from all
    </Files>
  </Directory>

  AliasMatch ^/([^/]+)/static/(.*) \
           /users/www-data/web2py/applications/$1/static/$2
  <Directory /users/www-data/web2py/applications/*/static/>
    Order Allow,Deny
    Allow from all
  </Directory>

  <Location /admin>
  Deny from all
  </Location>

  <LocationMatch ^/([^/]+)/appadmin>
  Deny from all
  </LocationMatch>

  CustomLog /private/var/log/apache2/access.log common
  ErrorLog /private/var/log/apache2/error.log
</VirtualHost>

Luego reinicie Apache, debe pasar todas las solicitudes a web2py sin pasar por el servidor wsgi Rocket.

He aquí algunas explicaciones:

1
2
WSGIDaemonProcess web2py user=www-data group=www-data
                        display-name=%{GROUP}

define un grupo de proceso demonio en contexto de “web2py.example.com”. Al definir ésto dentro del host virtual, sólo este host virtual puede acceder usando WSGIProcessGroup, incluyendo cualquier host virtual con el mismo nombre de servidor pero en un puerto diferente. Las opciones “user” y “group” deben establecerse al usuario que tiene acceso de escritura al directorio donde se configuró web2py. No necesita establecer “user” y “group” si configuró en directorio de instalación de web2py con permiso de escritura para el usuario por defecto en el que corre Apache. La opción “display-name” hace que el nombre del proceso aparezca en la salida de ps como “(wsgi:web2py)” en vez del nombre del archivo ejecutable del servidor web Apache. Como no hay opciones “processes” y “threads” especificadas, el grupo del proceso demonio tendra un único proceso con 15 tareas corriendo dentro de ese proceso. Usualmente esto es más adecuado para la mayoría de los sitios y debe dejarse como está. Si se sustituye, no use “processes=1” ya que hacerlo deshabilita cualquier herramienta de depuración WSGI dentro del buscador que verifique la bandera “wsgi.multiprocess”. Ésto se debe a que cualquier uso de la opción “processes” causará que esa bandera se establezca en True, incluso si es un único proceso, y esas herramientas esperan que esté en False. Nota: si el código de su aplicación o módulo de extensión de terceros no es seguro, use en cambio las opciones “processes=5 threads=1”. Ésto creará 5 procesos en el grupo de procesos demonios donde cada proceso es de una sóla tarea. Usted podría considerar usar “maximum-request=1000” si su aplicación tiene fugas de objetos Python por no ser capaz de recoger la basura correctamente.

1
WSGIProcessGroup web2py

delega el funcionamiento de todas las aplicaciones WSGI al grupo de procesos demonios que fué configurado usando la directiva WSGIDaemonProcess.

1
WSGIScriptAlias / /users/www-data/web2py/wsgihandler.py

monta la aplicación web2py. En este caso es montada en la raíz del sitio web.

1
2
3
<Directory /users/www-data/web2py>
  ...
</Directory>

le da a Apache permisología de acceso al archivo script WSGI.

1
2
3
4
<Directory /users/www-data/web2py/applications/*/static/>
  Order Allow,Deny
  Allow from all
</Directory>

Instruye a Apache a eludir a web2py al buscar archivos estáticos.

1
2
3
<Location /admin>
  Deny from all
</Location>

y

1
2
3
<LocationMatch ^/([^/]+)/appadmin>
  Deny from all
</LocationMatch>

bloquea el acceso público a admin y appadmin

Normalmente sólo daríamos permiso a todo el directorio donde el archivo script WSGi está ubicado, pero web2py coloca el archivo script WSGI en un directorio que contiene otro código fuente, incluyendo la clave de la interfaz de administración. El abrir todo el directorio causaría problemas de seguridad, porque técnicamente a Apache le sería dado permiso para servir todos los archivos a cualquier usuario que haya llegado a ese directorio por medio de una URL mapeada. Para evitar problemas de seguridad, niegue explícitamente el acceso al contenido del directorio, excepto para el archivo script WSGI, y prohíba que cualquier usuario pueda hacer reemplazos desde un archivo .httpaccess para estar extra seguros.

Usted puede encontrar una completa y comentada configuración del archivo de configuración wsgi de Apache en:

1
scripts/web2py-wsgi.conf

Esta sección fué creada con ayuda de Graham Dumpleton, desarrollador de mod_wsgi.

mod_wsgi and SSL

Para forzar que algunas aplicaciones (por ejemplo admin appadmin) a pasar por HTTPS, almacene el certificado SSL y archivos claves:

1
2
/etc/apache2/ssl/server.crt
/etc/apache2/ssl/server.key

y edite el archivo y de configuración Apache web2py.conf y agregue:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<VirtualHost *:443>
  ServerName web2py.example.com
  SSLEngine on
  SSLCertificateFile /etc/apache2/ssl/server.crt
  SSLCertificateKeyFile /etc/apache2/ssl/server.key

  WSGIProcessGroup web2py

  WSGIScriptAlias / /users/www-data/web2py/wsgihandler.py

  <Directory /users/www-data/web2py>
    AllowOverride None
    Order Allow,Deny
    Deny from all
    <Files wsgihandler.py>
      Allow from all
    </Files>
  </Directory>

  AliasMatch ^/([^/]+)/static/(.*) \
        /users/www-data/web2py/applications/$1/static/$2

  <Directory /users/www-data/web2py/applications/*/static/>
    Order Allow,Deny
    Allow from all
  </Directory>

  CustomLog /private/var/log/apache2/access.log common
  ErrorLog /private/var/log/apache2/error.log

</VirtualHost>

Reinicie Apache y usted debería tener acceso a:

https://www.example.com/admin

https://www.example.com/examples/appadmin

http://www.example.com/examples

pero no a:

http://www.example.com/admin

http://www.example.com/examples/appadmin

mod_proxy

Algunas distribuciones de Unix/Linux pueden corren Apache, pero no soporta mod_wsgi. En este caso, la solución más simple es correr Apache como un proxy y hacer que Apache se encargue sólo de los archivos estáticos.

Aquí está una configuración Apache minimalista:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
NameVirtualHost *:80
### deal with requests on port 80
<VirtualHost *:80>
   Alias / /users/www-data/web2py/applications
   ### serve static files directly
   <LocationMatch "^/welcome/static/.*">
    Order Allow, Deny
    Allow from all
   </LocationMatch>
   ### proxy all the other requests
   <Location "/welcome">
     Order deny,allow
     Allow from all
     ProxyRequests off
     ProxyPass http://localhost:8000/welcome
     ProxyPassReverse http://localhost:8000/
     ProxyHTMLURLMap http://127.0.0.1:8000/welcome/ /welcome
   </Location>
   LogFormat "%h %l %u %t "%r" %>s %b" common
   CustomLog /var/log/apache2/access.log common
</VirtualHost>

El script presentado arriba expone sólo la aplicación “welcome”. Para exponer otras aplicaciones, usted necesita agregar la correspondiente <Location>...</Location> con la misma sintaxis como se hace con la aplicación “welcome”.

El script asume que ya hay un servidor web2py corriendo en el puerto 8000. Antes de reiniciar Apache, asegúrese que este sea el caso:

1
nohup python web2py.py -a '<recycle>' -i 127.0.0.1 -p 8000 &

Usted puede especificar una clave con la opción -a o use el parámetro “<recycle>” en vez de una clave. En el último caso, la clave previamente almacenada es reusada y la clave no es almacenada en la historia del terminal.

Usted también puede usar el parámetro “<ask>”, para que se le pida una clave.

El comando nohup se asegura que el servidor no cierre cuando se cierra el terminal. nohups registra todas las salidas en nohup.out.

Para forzar admin y appadmin sobre HTTPS use en cambio el siguiente archivo de configuración de Apache:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
NameVirtualHost *:80
NameVirtualHost *:443
### deal with requests on port 80
<VirtualHost *:80>
   Alias / /usres/www-data/web2py/applications
   ### admin requires SSL
   <LocationMatch "^/admin">
     SSLRequireSSL
   </LocationMatch>
   ### appadmin requires SSL
   <LocationMatch "^/welcome/appadmin/.*">
     SSLRequireSSL
   </LocationMatch>
   ### serve static files directly
   <LocationMatch "^/welcome/static/.*">
     Order Allow,Deny
     Allow from all
   </LocationMatch>
   ### proxy all the other requests
   <Location "/welcome">
     Order deny,allow
     Allow from all
     ProxyPass http://localhost:8000/welcome
     ProxyPassReverse http://localhost:8000/
   </Location>
   LogFormat "%h %l %u %t "%r" %>s %b" common
   CustomLog /var/log/apache2/access.log common
</VirtualHost>
<VirtualHost  *:443>
   SSLEngine On
   SSLCertificateFile /etc/apache2/ssl/server.crt
   SSLCertificateKeyFile /etc/apache2/ssl/server.key
   <Location "/">
     Order deny,allow
     Allow from all
     ProxyPass http://localhost:8000/
     ProxyPassReverse http://localhost:8000/
   </Location>
   LogFormat "%h %l %u %t "%r" %>s %b" common
   CustomLog /var/log/apache2/access.log common
</VirtualHost>

La interfaz administrativa debe ser deshabilitada cuando web2py corre en un host compartido con mod_proxy, o será expuesto a otros usuarios.

Iniciar como un Demonio de Linux

A menos de que esté usando mod_wsgi, usted debería configurar el servidor web2py para que pueda ser iniciado/detenido/reiniciado como cualquier otro demonio Linux, y así poder iniciar automáticamente en la fase de arranque del computador.

El processo para configurar esto es específico a varías distribuciones Linux/Unix.

En la carpeta web2py hay dos scripts que pueden ser usados para este fin:

1
2
scripts/web2py.ubuntu.sh
scripts/web2py.fedora.sh

En Ubuntu, o cualquier otra distribución Linux basada en Debian, edite “web2py.ubuntu.sh” y reemplace la ruta “/usr/lib/web2py/ con la ruta de su instalación web2py, luego escriba los siguientes comandos de terminal para mover el archivo a la carpeta correcta, registrarlo como un servicio de arranque, e iniciarlo:

1
2
3
sudo cp scripts/web2py.ubuntu.sh /etc/init.d/web2py
sudo update-rc.d web2py defaults
sudo /etc/init.d/web2py start

En Fedora, o cualquier otra distribución basada en Fedora, edite “web2py.fedore.sh” y reemplace la ruta “/usr/lib/web2py” con la ruta de su instalación web2py, luego escriba los siguientes comandos para mover el archivo a la carpeta correcta, registrarlo como un servicio de arranque, e iniciarlo:

1
2
3
sudo cp scripts/web2py.fedora.sh /etc/rc.d/init.d/web2pyd
sudo chkconfig --add web2pyd
sudo service web2py start

Lighttpd

Usted puede instalar Lighttpd en Ubuntu o cualquier otra distribución basada en Debian con el siguiente comando de terminal:

1
apt-get -y install lighttpd

Una vez instalado, edite /etc/rc.local y cree un proceso fcgi web2py en segundo plano

1
cd /var/www/web2py && sudo -u www-data nohup python fcgihandler.py &

Luego, usted necesita editar el archivo de configuración de Lighttpd

/etc/lighttpd/lighttpd.conf

para que pueda encontrar el socket creado por el anterior proceso. En el archivo de configuración, escriba algo como:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
server.modules              = (
        "mod_access",
        "mod_alias",
        "mod_compress",
        "mod_rewrite",
        "mod_fastcgi",
        "mod_redirect",
        "mod_accesslog",
        "mod_status",
)

server.port = 80
server.bind = "0.0.0.0"
server.event-handler = "freebsd-kqueue"
server.error-handler-404 = "/test.fcgi"
server.document-root = "/users/www-data/web2py/"
server.errorlog      = "/tmp/error.log"

fastcgi.server = (
  "/handler_web2py.fcgi" => (
      "handler_web2py" => ( #name for logs
         "check-local" => "disable",
         "socket" => "/tmp/fcgi.sock"
      )
   ),
)

$HTTP["host"] = "(^|\.)example\.com$" {
 server.document-root="/var/www/web2py"
    url.rewrite-once = (
      "^(/.+?/static/.+)$" => "/applications$1",
      "(^|/.*)$" => "/handler_web2py.fcgi$1",
    )
}

Ahora busque errores de sintaxis:

1
lighttpd -t -f /etc/lighttpd/lighttpd.conf

y re(inicie) el servidor web con:

1
/etc/init.d/lighttpd restart

Note que FastCGI une el servidor web con un socket Unix, no con un socket IP:

1
/tmp/fcgi.sock

Aquí es donde Lighttpd reenvía las solicitudes y recibe respuestas de HTTP. Los sockets Unix son más ligeros que los sockets de internet, y ésta es una de las razones por las que Lighttpd+FastCGI+web2py es rápido. Como en el caso de Apache, es posible configurar Lighttpd para que se encargue directamente de los archivos estáticos, y para forzar algunas aplicaciones sobre HTTPS. Revise la documentación Lighttpd para detalles.

Los ejemplos en esta sección fueron tomados de la nota de John Heenan en web2pyslices.

La interfaz administrativa debe estar deshabilitada cuando web2py corre en un host compartido con FastCGI, o de lo contrario será expuesto a otros usuarios.

Host Compartido con mod_python

Hay veces, específicamente en hosts compartidos, donde uno no tiene el permiso para configurar los archivos de configuración de Apache de manera directa. Al momento de escribir el libro, la mayoría de estos hosts todavía usan mod_python aún cuando ya no es mantenido en vez de usar mod_wsgi.

Usted aún puede correr web2py. Aquí mostramos un ejemplo de como configurarlo.

Coloque el contenido de web2py en la carpeta “htdocs”.

En la carpeta de web2py cree un archivo “web2py_modpython.py” con el siguiente contenido:

1
2
3
4
5
6
from mod_python import apache
import modpythonhandler

def handler(req):
    req.subprocess_env['PATH_INFO'] = req.subprocess_env['SCRIPT_URL']
    return modpythonhandler.handler(req)

Cree/Actualice el archivo ”.htaccess” con el siguiente contenido:

1
2
3
SetHandler python-program
PythonHandler web2py_modpython
#PythonDebug On

Este ejemplo fue proporcionado por Niktar.

Cherokee con FastCGI

Cherokee es un servidor web muy rápido y, como web2py, proporciona para su configuración una interfaz web habilitada para Ajax. Su interfaz web está escrita en Python. Además, no se requiere reinicio para que hagan efecto la mayoría de los cambios.

Estos son los pasos necesarios para configurar web2py con Cherokee:

Descargue Cherokee[86]

Descomprima, compile e instale:

1
2
3
4
tar -xzf cherokee-0.9.4.tar.gz
cd cherokee-0.9.4
./configure --enable-fcgi && make
make install

Inicie web2py como siempre al menos una vez para asegurarse que crea la carpeta “applications”.

Escriba un script de terminal llamado “startweb2py.sh” con el siguiente código:

1
2
3
#!/bin/bash
cd /var/web2py
python /var/web2py/fcgihandler.py &

de al script permisos de ejecución y ejecútelo. Ésto iniciara web2py bajo el manejador FastCGI.

Inicie Cherokee y cherokee-admin:

1
2
sudo nohup cherokee &
sudo nohup cherokee-admin &

Por defecto, cherokee-admin solo escucha en la interfaz local en el pueto 9090. Éste no es un problema si usted tiene total acceso físico a esa máquina. Si éste no es el caso, usted puede forzar que se relacione a una dirección IP y puerto usando las siguientes opciones:

1
2
-b,  --bind[=IP]
-p,  --port=NUM

o hacer un SSH directo a puerto (más seguro, recomendado):

1
ssh -L 9090:localhost:9090 remotehost

Abra “http://localhost:9090” en su buscador. Si todo está correcto, verá cherokee-admin.

En la interfaz web de cherokee-admin, haga click en “info sources”. Elija “Local Interpreter”. Escriba el siguiente código, luego haga click en “Add New”.

1
2
3
Nick: web2py
Connection: /tmp/fcgi.sock
Interpreter: /var/web2py/startweb2py.sh

Finalmente, realice los siguientes pasos faltantes:

  • Haga click en “Virtual Servers”, luego en “Default”.
  • Haga click en “Behavior”, luego, al estar ahí, click en “Default”.
  • Elija “FastCGI” en vez de “List and Send” en el campo de lista.
  • Al final, seleccione “web2py” como “Application Server”.
  • Coloque un vistobueno en todas las casillas de verificación (puede obviar Allow-x-sendfile). Si se produce una advertencia, deshabilite y habilite una de las casillas. (Re-enviará de manera automática el parámetro del servidor de la aplicación. Algunas veces no lo hace, lo cual es un error de programación).
  • Apunte su buscador a “http://yoursite”, y “Welcome to web2py” aparecerá.

PostgreSQL

PostgreSQL es una base de datos gratis y de código abierto que es usado en entornos de producción exigentes, por ejemplo, para almacenar la base de datos del nombre de dominio .org, y se ha demostrado que escala bien en cientos de terabytes de datos. Tiene un rápido y sólido soporte de transacciones, y proporciona una característica de auto-vacío que libera al administrador de la mayoría de las tareas de mantenimiento en la base de datos.

En Ubunto o cualquier otra distribución Linux basada en Debian, es muy fácil instalar PostgreSQL y su API Python con:

1
2
sudo apt-get -y install postgresql
sudo apt-get -y install python-psycopg2

Es buena idea correr el/los servidor(es) web y el servidor de base de datos en máquinas diferentes. En este caso, la máquina ejecutando el servidor web debe estar conectado con una red interna (física) segura, o debe establecer túneles SSL para conectarse de manera segura con el servidor de base de datos.

Edite el archivo de configuración PostgreSQL

1
sudo nano /etc/postgresql/8.4/main/postgresql.conf

y asegúrese que contiene las siguientes dos líneas

1
2
3
4
5
...
track_counts = on
...
autovacuum = on   # Enable autovacuum subprocess?  'on'
...

Inicie el servidor de base de datos con:

1
sudo /etc/init.d/postgresql restart

Al reiniciar el servidor PostgreSQL, debe notificar en cual puerto se está ejecutando. A menos que usted tenga múltiples servidores de base de datos, debería ser el puerto 5432.

Los registros de PostgreSQL están en:

1
/var/log/postgresql/

Una vez que el servidor de base de datos está funcionando, cree un usuario y una base de datos de manera que las aplicaciones web2py puedan usarlo:

1
2
3
4
5
sudo -u postgres createuser -PE -s myuser
postgresql> createdb -O myself -E UTF8 mydb
postgresql> echo 'The following databases have been created:'
postgresql> psql -l
postgresql> psql mydb

El primero de los comandos garantizará acceso de super-usuario al nuevo usuario, llamado myuser. Le pedirá una clave.

Cualquier aplicación web2py puede conectarse con esta base de datos con el comando:

1
db = DAL("postgres://myuser:mypassword@localhost:5432/mydb")

donde mypassword es la clave que usted ingreso cuando se le pidió, y 5432 es el puerto donde el servidor de base de datos se está ejecutando.

Normalmente usted usará una base de datos para cada aplicación, y múltiples instancias de la misma aplicación se conectan a la misma base de datos. Tambien es posible que aplicaciones diferentes compartan la misma base de datos.

Para detalles en respaldos de la base de datos, lea la documentación de PostgreSQL; específicamente los comandos pg_dump y pg_restore.

Windows

Apache y mod_wsgi

Instalar Apache y mod_wsgi en Windows requiere de un procedimiento diferente. Aquí asumimos que Python 2.5 está instalado, lo está ejecutando desde la fuente, y web2py está localizado a c:/web2py.

Primero descargue los paquetes requeridos:

  • Apache apache_2.2.11-win32-x86-openssl-0.9.8i.msi desde [79]
  • mod_wsgi desde [80]

Segundo, ejecute apache...msi y siga las pantallas del asistente. En la pantalla de información del servidor

windows_apache

ingrese todos los valores requeridos:

  • Network Domain: ingrese el dominio DNS en el cual su servidor está o estará registrado. Por ejemplo, si el nombre DNS completo de su servidor es server.mydomain.net, usted escribiría mydomain.net.
  • Server Name: El nombre DNS completo de su servidor. Para el ejemplo mostrado arriba, usted escribiría aquí server.mydomain.net. Ingrese un nombre de dominio completamente calificado o una dirección IP de la instalación web2py, no un atajo, para más información revise [82].
  • Administrator’s Email Address: Ingrese aquí la dirección de correo electrónico del administrador del servidor o webmaster. Esta dirección sera mostrada por defecto junto con los mensajes de error al cliente.

Continue con una instalación típica hasta el final a menos que se requiera lo contrario.

El asistente, por defecto, instalará Apache en la carpeta:

1
C:/Program Files/Apache Software Foundation/Apache2.2/

De ahora en adelante nos referiremos a esta carpeta simplemente como Apache2.2.

Tercero, copie el mod_wsgi descargado a Apache2.2/modules

escrito por Chris Travers, publicado por el Laboratorio de Software de Código Abierto en Microsoft, Diciembre 2007.

Cuarto, cree los certificados server.crt y server.key (como se discutió en la anterior sección) y colóquelos en la carpeta Apache2.2/conf. Note que el archivo cnf está en Apache2.2/conf/openss1.cnf.

Quinto, edite Apache2.2/conf/httpd.cong, quite la marca de comentario (el caracter #) de la línea

1
LoadModule ssl_module modules/mod_ssl.so

agregue la siguiente línea después de las líneas de todos los otros LoadModule

1
LoadModule wsgi_module modules/mod_wsgi.so

busque “Listen 80” y agregue esta línea después

Listen 443

agregue las siguientes líneas al final cambiando letra del disco, número de puerto, nombre de servidor, de acuerdo a sus valores

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
NameVirtualHost *:443
<VirtualHost *:443>
  DocumentRoot "C:/web2py/applications"
  ServerName server1

  <Directory "C:/web2py">
    Order allow,deny
    Deny from all
  </Directory>

  <Location "/">
    Order deny,allow
    Allow from all
  </Location>

  <LocationMatch "^(/[\w_]*/static/.*)">
    Order Allow,Deny
    Allow from all
  </LocationMatch>

  WSGIScriptAlias / "C:/web2py/wsgihandler.py"

  SSLEngine On
  SSLCertificateFile conf/server.crt
  SSLCertificateKeyFile conf/server.key

  LogFormat "%h %l %u %t "%r" %>s %b" common
  CustomLog logs/access.log common
</VirtualHost>

Guarde y verifique la configuración usando: [Inicio > Programas > Apache HTTP Server 2.2 > Configure Apache Server > Test Configuration]

Si no hay problemas usted verá abrir y cerrar una pantalla comandos. Ahora usted puede iniciar Apache:

[Inicio > Programas > Apache HTTP Server 2.2 > Control Apache Server > Start]

o mejor aún, inicie el monitor de barra de tareas

[Inicio > Programas > Apache HTTP Server 2.2 > Contro Apache Server]

Ahora usted puede hacer click derecho el el ícono con forma de pluma roja en la barra de tareas y dar click en “Open Apache Monitor” y luego iniciar, detener o reiniciar el servidor Apache, dependiendo de lo que ser necesite.

Esta sección fué creada por Jonathan Lundell.

Iniciar como un Servicio de Windows

Lo que Linux llama demonio, Windows lo llama servicio. El servidor web2py puede ser instalado/iniciado/detenido fácilmente como un servicio de Windows.

Para poder usar web2py como un servicio Windows, usted debe crear un archivo “options.py” con parámetros de arranque:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import socket, os
ip = socket.gethostname()
port = 80
password = '<recycle>'
pid_filename = 'httpserver.pid'
log_filename = 'httpserver.log'
ssl_certificate = "
ssl_private_key = "
numthreads = 10
server_name = socket.gethostname()
request_queue_size = 5
timeout = 10
shutdown_timeout = 5
folder = os.getcwd()

Usted no necesita crear “options.py” desde ccero ya que ya existe un “options_std.py” en la carpeta de web2py que usted podría usar como modelo.

Después de crear “options.py” en la carpeta de instalación de web2py, usted puede instalar web2py como un servicio con:

1
python web2py.py -W install

e iniciar/detener el servicio con:

1
2
python web2py.py -W start
python web2py.py -W stop

Asegurando sesiones y admin

Resulta peligroso exponer de manera pública la aplicación admin y los controladores de appadmin a menos que corran sobre HTTPS. Por otra parte, su clave y credenciales nunca deben ser transmitidas sin ser encriptadas. Ésto es verdad para web2py y para cualquier otra aplicación web.

En sus aplicaciones, si necesitan autenticación, debería hacer seguras las cookies de sesión con:

1
session.secure()

Una manera fácil de configurar un entorno de producción seguro en un servidor es primero detener web2py y luego quitar todos los archivos parameters_*.py de la carpeta de instalación de web2py. Luego inicie web2py sin clave. Ésto deshabilitará completamente admin y appadmin.

Luego, inicie una segunda instancia de Python que sea accesible sólo desde localhost:

1
nohup python web2py -p 8001 -i 127.0.0.1 -a '<ask>' &

y cree un túnel SSH desde la máquina local (desde la que quiere acceder a la interfaz administrativa) al servidor (en donde web2py se está ejecutando, example.com), usando:

1
ssh -L 8001:127.0.0.1:8001 username@example.com

Ahora usted puede acceder a la interfaz administrativa de manera local por medio del buscador en localhost:8001.

Esta configuración es segura porque admin no es alcanzable cuando el túnel se cierra (el usuario cierra sesión).

Esta solución es segura en hosts compartidos si y sólo si otros usuarios no tienen acceso de lectuar a la carpeta que contiene a web2py; de otro modo los usuarios podrían robar cookies de sesión directamente del servidor.

Eficiencia y Escalabilidad

web2py está diseñado para que sea fácil de implemetar y configurar. Ésto no significa que compromete la eficiencia o escalabilidad, pero si significa que usted podría necesitar ajustarlo para hacerlo escalable.

En esta sección asumimos varias instalaciones de web2py detrás de un servidor NAT que proporciona balanceo de carga local.

En este caso, web2py trabaja de manera óptima si algunas condiciones se cumplen. En particular, todas las instancias de cada aplicación web2py deben acceder a los mismos servidores de base de datos y deben ver los mismos archivos. La última condición puede ser implementada compartiendo las siguientes carpetas:

1
2
3
4
applications/myapp/sessions
applications/myapp/errors
applications/myapp/uploads
applications/myapp/cache

Las carpetas compartidas deben soportar bloqueo de archivos. Soluciones posibles son ZFS (ZFS fué desarrollado por Sun Microsystems y es la opción preferida), NFS (con NFS es posible que necesite ejecutar el demonio nlockmgr para permitir bloqueo de archivos), o Samba (SMB).

Es posible compartir toda la carpeta web2py o toda la carpeta de la aplicación, pero no es una buena idea porque esto causaría un incremento innecesario en el uso de ancho de banda.

Nosotros creemos que la configuración discutida anteriormente es muy escalable porque reduce la carga de base de datos al mover a los sistemas de archivos compartidos aquellos recursos que necesitan ser compartidos pero que no necesitan seguridad en las transacciones (se supone que sólo un cliente a la vez tiene acceso al archivo de sesión, el caché siempre necesita un bloqueo global, las cargas y errores son archivos “escribir una sóla vez/leer muchas veces”).

Idealmente, tanto la base de datos como el depósito compartido deben tener capacidad RAID. No cometa el error de almacenar la base de datos en el mismo depósito que las carpetas compartidas, o de lo contrario creara un nuevo cuello de botella aquí.

Caso por caso, usted puede necesitar realizar optimizaciones adicionales que discutiremos más adelante. En particular, discutiremos como deshacernos de estas carpetas compartidas una por una, y como por el contrario almacenar los datos asociados en la base de datos. Mientras ésto es posible, no es necesariamente una buena solución. Sin embargo, puede haber muchas razones para hacerlo. Una de estas razones es que algunas veces no tenemos la libertad de configurar carpetas compartidas.

Trucos de Eficiencia

El código de aplicación de web2py es ejecutado en cada solicitud, así que posiblemente usted querrá minimizar la cantidad de código. He aquí lo que puede hacer:

  • Ejecute una vez con migrate=True y luego configure todas sus tablas con migrate=False.
  • Compile en Bytecode su aplicación usando admin.
  • Use cache.ram tanto como pueda pero asegúrese de usar un conjunto finito de claves, o de lo contrario la cantidad de cache usado crecerá arbitrariamente.
  • Minimice el código en los modelos: no defina ahí las funciones, defínalas en los controladores que las necesitan o - incluso mejor - defina las funciones en los módulos, importelos y use dichas funciones como se requiera.
  • No coloque muchas funciones en el mismo controlador, por el contrario use muchos controladores con pocas funciones.
  • Establezca session.forget() en todos los controladores y/o funciones que no cambian de sesión.
  • Trate de evitar el cron de web2py, y use en cambio un proceso en segundo plano. El cron de web2py puede iniciar muchas instancias de Python y causar un uso excesivo de memoria.

Sesiones en base de datos

Es posible instruit a web2py a almacenar las sesiones en una base de datos en vez de la carpeta de sesiones. Ésto debe hacerse por cada aplicación web2py, aunque pueden usar la misma base de datos para almacenar las sesiones.

Dada una conexión a base de datos

1
db = DAL(...)

usted puede almacenar la sesión en esta base de datos (db) simplemente señalando lo siguiente, en el mismo archivo modelo que estableció la conexión:

1
session.connect(request, response, db)

Si ya no existe, web2py crea, de manera transparente, una tabla en la base de datos llamada web2py_session_appname que contiene los siguientes campos:

1
2
3
4
5
6
Field('locked', 'boolean', default=False),
Field('client_ip'),
Field('created_datetime', 'datetime', default=now),
Field('modified_datetime', 'datetime'),
Field('unique_key'),
Field('session_data', 'text')

“unique_key” es una clave uuid usada para identificar la sesión en la cookie. “session_data” son los datos de la sesión cPickled.

Para minimizar el acceso a la base de datos usted debe evitar almacenar las sesiones cuando no sea necesario, haciendo:

1
session.forget()

Con este ajuste la carpeta “sessions” no necesita ser una carpeta compartida porque ya no sera accesada.

Note que, si las sesiones son deshabilitadas, usted no debe pasar session a form.accepts y no puede usar session.flash o CRUD

HAHProxy y Balanceador de Carga de alta disponibilidad

Si usted necesita múltiples procesos web2py ejecutándose en múltiples máquinas, en vez de almacenar las sesiones en la base de datos o en caché, usted tiene la opción de usar un balaceador de carga con sticky sessions.

Pound[88] y HAHProxy[89] son dos balanceadores de carga HTTP y proxies inversos que proporcionan sticky sessions. Aqui discutimos el segundo porque al parecer su uso es más común en alojamiento VPS comercial.

Por sticky sessions, nos referimos a que una vez que un cookie de sesión ha sido emitido, el balanceador de carga siempre enrutara las solicitudes desde el cliente asociado a la sesión, al mismo servidor. Ésto le permite almacenar la sesión en el sistema de archivos local sin necesidad de un sistema de archivos compartido.

Para usar HAHProxy:

Primero, instale HAHProxy en una máquina de prueba Ubuntu:

1
sudo apt-get -y install haproxy

Segundo, edite el archivo de configuración “/etc/hahproxy.cfg” para que se vea más o menos así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# this config needs haproxy-1.1.28 or haproxy-1.2.1

global
      log 127.0.0.1   local0
      maxconn 1024
      daemon

defaults
      log     global
      mode    http
      option  httplog
      option  httpchk
      option  httpclose
      retries 3
      option redispatch
      contimeout      5000
      clitimeout      50000
      srvtimeout      50000

listen 0.0.0.0:80
      balance url_param WEB2PYSTICKY
      balance roundrobin
      server  L1_1 10.211.55.1:7003  check
      server  L1_2 10.211.55.2:7004  check
      server  L1_3 10.211.55.3:7004  check
      appsession WEB2PYSTICKY len 52 timeout 1h

La directiva listel le dice a HAHProxy en que puerto debe esperar conexión. La directiva server le dice a HAHProxy donde encontrar los servidores detrás de proxy. El directorio appsession hace una sticky session y usa para este propósito la cookie WEB2PYSTICKY.

Tercero, habilite este archivo de configuración e inicie HAHProxy:

1
/etc/init.d/haproxy restart

Usted puede encontrar instrucciones similares para configurar Pound en el URL

http://web2pyslices.com/main/slices/take_slice/33

Limpiar las Sesiones

Si usted elige mantener sus sesiones en el sistema de archivos, debe tener en cuenta que en un ambiente de producción, éstas se acumulan rápidamente. web2py proporciona un script llamado:

1
scripts/sessions2trash.pyp

que al ejecutarse en segundo plano, borra periódicamente todas las sesiones que no han sido accesadas en algún tiempo. Éste es el contenido del script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
SLEEP_MINUTES = 5
EXPIRATION_MINUTES = 60
import os, time, stat
path = os.path.join(request.folder, 'sessions')
while 1:
   now = time.time()t
   for file in os.listdir(path):
      filename = os.path.join(path, file)
      t = os.stat(filename)[stat.ST_MTIME]
      if now - t > EXPIRATION_MINUTES * 60:
         os.unlink(filename)
   time.sleep(SLEEP_MINUTES * 60)

Usted puede ejecutar el script con el siguiente comando:

1
nohup python web2py.py -S myapp -R scripts/sessions2trash.py &

donde myapp es el nombre de su aplicación.

Archivos cargados en la base de datos

Por defecto, todos los archivos cargados manejados por SQLFORMs son renombrados de manera segura y almacenados en el sistema de archivos en la carpeta “uploads”. Es posible instruir a web2py a que en cambio almacene los archivos cargados en la base de datos.

Ahora, considere la siguiente tabla:

1
2
3
db.define_table('dog',
    Field('name')
    Field('image', 'upload'))

donde dog.image es de tipo upload. Para hacer que la imagen cargada vaya en el mismo registro que el nombre del perro, usted debe modificar la definición de tabla agregando un campo blob y enlazarlo al campo upload:

1
2
3
4
db.define_table('dog',
    Field('name')
    Field('image', 'upload', uploadfield='image_data'),
    Field('image_data', 'blob'))

Aquí, “image_data” es sólo un nombre arbitrario para el nuevo campo blob.

La línea 3 instruye a web2py a renombrar de manera segura las imágenes como siempre lo ha hecho, almacenar el nuevo nombre en el campo de imágen, y almacenar los datos en el campo de carga llamado “image_data” en vez de almacenar los datos en el sistema de archivos. Todo ésto es hecho automáticamente por los SQLFORMs y ningún otro código necesita ser cambiado.

Con este ajuste, la carpeta “uploads” ya no es necesaria.

En el Motor de Aplicaciones de Google, los archivos son almacenados por defecto en la base de datos sin necesidad de definir un campo de carga, ya que uno es creado por defecto.

Recogiendo Tickets

Por defecto, web2py almacena los tickets (errores) en el sistema de archivos local. No tendría sentido almacenar los tickets directamente en la base de datos, porque el más común origen de error en un ambiente de producción son las fallas de base de datos.

El almacenamiento de tickets nunca constituye un cuello de botella, porque éste es un evento ordinario muy raro. Por lo tanto, en un entorno de producción con múltiples servidores concurrentes, es más que adecuado almacenarlos en una carpeta compartida. Sin embargo, ya que sólo el administrador necesita recuperar los tickets, es también correcto almacenar los tickets en una carpeta “errors” local no compartida y recojerlos y/o borrarlos de manera periódica.

Una posibilidad es mover periódicamente todos los tickets a una base de datos.

Para este fin, web2py proporciona el siguiente script:

1
scripts/tickets2db.py

que contiene:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import sys
import os
import time
import stat
import datetime

from gluon.utils import md5_hash
from gluon.restricted import RestrictedError

SLEEP_MINUTES = 5
DB_URI = 'sqlite://tickets.db'
ALLOW_DUPLICATES = True

path = os.path.join(request.folder, 'errors')

db = SQLDB(DB_URI)
db.define_table('ticket', SQLField('app'), SQLField('name'),
                SQLField('date_saved', 'datetime'), SQLField('layer'),
                SQLField('traceback', 'text'), SQLField('code', 'text'))

hashes = {}

while 1:
    for file in os.listdir(path):
        filename = os.path.join(path, file)

        if not ALLOW_DUPLICATES:
            file_data = open(filename, 'r').read()
            key = md5_hash(file_data)

            if key in hashes:
                continue

            hashes[key] = 1

        error = RestrictedError()
        error.load(request, request.application, filename)

        modified_time = os.stat(filename)[stat.ST_MTIME]
        modified_time = datetime.datetime.fromtimestamp(modified_time)

        db.ticket.insert(app=request.application,
                         date_saved=modified_time,
                         name=file,
                         layer=error.layer,
                         traceback=error.traceback,
                         code=error.code)

        os.unlink(filename)

    db.commit()
    time.sleep(SLEEP_MINUTES * 60)

Este script debe ser editado. Cambie la cadena DB_URI para que conecte con su servidor de base de datos y ejecútelo con el comando:

1
nohup python web2py.py -S myapp -M -R scripts/tickets2db.py &

donde myapp es el nombre de su aplicación.

Este script se ejecuta en segundo plano y mueve todos los tickets cada 5 minutos a una tabla llamada “ticket” en el servidor de base de datos y borra todos los tickets locales. Si ALLOW_DUPLICATES se establece en False, solo almacenará los tickets que correspondan a distintos tipos de error. Con este ajuste, la carpeta “errors” ya no necesita ser compartida, ya que sólo sera accesada de manera local.

Memcache

Hemos mostrado que web2py provee dos tipos de cache: cache.ram y cache.disk. Ambos trabajan en un ambiente distribuido con múltiples servidores concurrentes, pero no funcionan como se espera. En particular, cache.ram solo hará cache a nivel del servidor; por lo tanto se vuelve inútil. cache.disk también hará cache a nivel de servidor a menos que la carpeta “cache” sea una carpeta compartida que soporta bloqueo; por lo tanto, en vez de darle rapidez a las cosas, se convierte en un gran cuello de botella.

La solución es no usarlos, y al contrario usar memcache. web2py viene con un API memcache.

Para usar memcache, cree un nuevo archivo modelo, por ejemplo 0_memcache.py, y en este archivo escriba (o agregue) el siguiente código:

1
2
3
4
from gluon.contrib.memcache import MemcacheClient
memcache_servers = ['127.0.0.1:11211']
cache.memcache = MemcacheClient(request, memcache_servers)
cache.ram = cache.disk = cache.memcache

La primera línea importa memcache. La segunda línea tiene que ser una lista de los sockets memcache (servidor:puerto). La tercera línea redefine cache.ram y cache.disk en términos de memcache.

Usted podría elegir redefinir sólo uno de ellos para definir un objeto cache totalmente nuevo apuntando al objeto Memcache.

Con este ajuste la carpeta “folder” ya no necesita ser una carpeta compartida, ya no será accesado.

Este código requiere tener servidores memcache corriendo en la red local. Usted debe consultar la documentación memcache para información en cómo configurar esos servidores .

Sesiones en Memcache

Si usted no necesita las sesiones, y no quiere usar un balanceador de carga con sticky sessions, entonces tiene la opción de almacenar las sesiones en memcache:

1
2
from gluon.contrib.memdb import MEMDB
session.connect(request,response,db=MEMDB(cache.memcache))

Removiendo Aplicaciones

En un entorno de producción, puede ser mejor no instlaar las aplicaciones por defecto: admin, examples y welcome. Aunque estas aplicaciones son bastante pequeñas, no son necesarias.

Remover estas aplicaciones es tan fácil como borrar las carpetas correspondientes en las carpetas de las aplicaciones.

Usando Servidores de Base de Datos Replicados

En un entorno de alto rendimiento usted podría tener una arquitectura de base de datos maestro-esclavo con muchos esclavos replicados y quizás unos cuantos servidores replicados. La DAL puede manejar esta situación y conectarse condicionalmente a diferentes servidores dependiendo en los parámetros de las solicitudes. El API para hacer esto fué descrito en el Capítulo 6. He aquí un ejemplo:

1
2
from random import shuffle
db = DAL(shuffle(['mysql://...1','mysql://...2','mysql://...3']))

En este caso, diferentes solicitudes HTTP serán servidas por diferentes bases de datos de manera aleatoria, y cada DB tendrá más o menos la misma probabilidad.

También podemos implementar un simple Round-Robin

1
2
3
4
5
6
def fail_safe_round_robin(*uris):
     i = cache.ram('round-robin', lambda: 0, None)
     uris = uris[i:]+uris[:i] # rotate the list of uris
     cache.ram('round-robin', lambda: (i+1)%len(uris), 0)
     return uris
db = DAL(fail_safe_round_robin('mysql://...1','mysql://...2','mysql://...3'))

Esto es a prueba de fallas en el sentido de que si el servidor de base de datos asignada a la solicitud no se conecta, DAL intentará con la siguiente base de datos en la lista.

También es posible conectarse a diferentes bases de datos dependiendo en la acción o controlador solicitado. En una configuración de base de datos maestro-esclavo, algunas acción solo realizan una lectuar y algunas otras realizan tanto lectura como escritura. El primero puede conectarse a un servidor de base de datos esclavo, mientras que el segundo debe conectarse a un maestro. Así que usted puede hacer:

1
2
3
4
if request.action in read_only_actions:
   db = DAL(shuffle(['mysql://...1','mysql://...2','mysql://...3']))
else:
   db = DAL(shuffle(['mysql://...3','mysql://...4','mysql://...5']))

Donde 1,2,3 son servidores replicados y 3,4,5 son esclavos.

Motor de Aplicaciones de Google

Es posible ejecutar el código de web2py en el Motor de Aplicaciones de Google (GAE)[13], incluyendo código DAL, con algunas limitaciones. La plataforma GAE proporciona muchas ventajas sobre las soluciones normales de alojamiento:

  • Facilidad de implementación. Google abstrae completamente la arquitectura subyacente.
  • Escalabilidad. Google replicará su aplicación tantas veces como sea necesario para servir todas las solicitudes concurrentes.
  • BigTable. En GAE, en vez de una base de datos relacional normal, usted almacena información persistente en BigTable, el almacén de datos por el que Google es famoso.

Las limitaciones son (al momento de escribir el libro):

  • No acceso de lectura o escritura al sistemas de archivos.
  • No transacciones.
  • No solicitudes complejas al almacén de datos. En particular no hay operadores JOIN, LIKE, y DATE/DATETIME.
  • No múltiples sub-solicitudes OR a menos que involucren a una y el mismo campo.
  • No HTTPS a menos que use el dominio appspot.com con un certificado Google.

Esto significa que web2py no puede almacenar sesiones, tickets de error, archivos cache y archivos cargados en el sistema de archivos; estos deben ser almacenados en el almacén de datos y no en el sistema de archivos.

Aquí proporcionamos una rápida visión general de GAE y nos enfocamos en los aspectos específicos de web2py, le recomendamos que revise la documentación oficial en línea de GAE par más detalles.

Atención: Al momento de escribir el libro, GAE soporta sólo Python 2.5. Cualquier otra versión causará problemas. Usted también debe ejecutar la distribución fuente de web2py, no una distribución binaria.

Configuración

Hay dos archivos de configuración con los cuales hay que estar atentos:

1
2
web2py/app.yaml
web2py/index.yaml

“app.yaml” tiene la siguiente estructura (ha sido acortada usando ...):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
application: web2py
version: 1
api_version: 1
runtime: python
handlers:
- url: /_ah/stats.*
  ...
- url: /(?P<a>.+?)/static/(?P<b>.+)
  ...
- url: /_ah/admin/.*
  ...
- url: /_ah/queue/default
  ...
- url: .*
  ...
skip_files:
...

Usted debe reemplazar web2py con el id de la aplicación que usted usó al registrarse con el Motor de Aplicaciones de Google.

url: /_ah/stats.* instruye a GAE a exponer la URL “/stats” con estadísticas e información de perfiles.

url: /(.+?)/static/(.+) instruye a GAE a servir los archivos estáticos de su aplicación de manera directa, sin llamar a la lógica de web2py, esto último por velocidad.

url: /_ah/admin/.* instruye a GAE a exponer la interfaz administrativa de GAE.

url: /_ah/queue/default instruye a GAE a exponer una interfaz a la cola de tareas. Esto es usado por web2py para mantener recargando sus páginas automáticamente manteniendolas en cache para ofrecer un servicio más rápido.

url:.* instruye a web2py a usar gaehandler.py para todas las demás solici tudes.

Los skip_files: session es una lista de expresiones regulares para archivos que no necesitan implementarse en GAE. En particular la línea:

1
((admin|examples|welcome)\.tar)|

le dice a GAE que no implemente las aplicaciones por defecto. Usted puede agregar aquí más aplicaciones a ser ignoradas.

Excepto por el id de la aplicación, probablemente usted no necesitará editar app.yaml.

El archivo index.yaml se genera automáticamente cuando usted ejecuta su aplicación localmente usando el servidor de aplicaciones de GAE (el servidor web que viene con el kit de desarrollo de software de Google). Contiene algo así:

1
2
3
4
5
indexes:
- kind: person
  properties:
  - name: name
    direction: desc

En este ejemplo, le dice a GAE que cree un índice para la tabla “person” que sera usado para ordenar por “name” en order alfabético inverso. Usted no podrá hacer búsquedas y ordenar registros en su aplicación sin los índices correspondientes.

Es importante siempre ejecutar sus aplicaciones de manera local con el servidor de aplicaciones y probar cada funcionalidad de su aplicación antes de implementarla. Ésto será importante para fines de prueba, pero también para generar automáticamente el archivo “index.yaml”. En ocasiones usted puede querer editar este archivo y realizar una limpieza, tal como borrar entradas duplicadas.

Ejecución e Implementación

Aquí asumimos que usted tiene instalado el kit de desarrollo de software de GAE. Usted puede ejecutar su aplicación dentro de la carpeta “web2py” usando el comando de servidor de aplicaciones:

1
python2.5 dev_appserver ../web2py

Ésto iniciará el servidor de aplicaciones y usted podrá ejecutar su aplicación en el URL:

http://127.0.0.1:8080/

Para poder cargar su aplicación en GAE, asegúrese de que ha editado el archivo app.yaml como se explicó antes y configure el id de aplicación correcto, luego ejecute:

1
python2.5 appcfg.py update ../web2py

En Mac y Windows, usted tambien puede usar el ejecutable del Motor de Aplicaciones de Google. Usted puede descargar el software desde la ref.[13]

Elija [File][Add Existing Application], establezca la ruta de la carpeta de más alto nivel de web2py, y presione el botón [Run] en la barra de herramientas. Después de que haya probado que funcione de manera local, usted puede implementarlo en GAE simplemente haciendo click en el botón [Deploy] en la barra de tareas (asumiendo que usted tiene una cuenta).

gae

En GAE, los tickets/errores de web2py también son registrados en la cónsola de administración de GAE con los registros pueden ser accesados y buscados en línea.

gae_log

Configurando el Manejador

El archivo gaehandler.py es responsable por la servida de archivos en GAE y tiene unas cuantas opciones. Aquí están sus valores por defecto:

1
2
3
4
5
KEEP_CACHED = False
LOG_STATS = False
APPSTATS = True
DEBUG = False
AUTO_RETRY = True

KEEP_CACHED le pide a GAE que almacene las tareas en la cola de tareas que sigue llamando a una acción fictícia, por lo tanto forzando a GAE a almacenar en cache sus aplicaciones. Ésto hará que GAE atienda a sus usuarios más rápidamente pero consumirá unos ciclos extra.

LOG_STATS registrará en el registro de GAE el tiempo que toma servir las páginas.

APPSTATS habilitará las estadísticas GAE de aplicaciones que proporcionan estadísticas de perfiles. Estarán disponibles en el URL:

http://localhost:8080/_ah/stats

DEBUG establece el modo de depuración de errores. En la practica no hace ninguna diferencia, a menos que se verifique explícitamente en su código por medio de gluon.settings.web2py_runtime.

AUTO_RETRY instruye a GAE a que, en caso de fallas, trate de confirmar de nuevo.

Evitar el Sistema de Archivos

En GAE usted no tiene acceso al sistema de archivos. Usted no puede abrir ningún archivo para escritura.

Para este fin, en GAE, web2py almacena automáticamente todos los archivos cargados en el almacén de datos, tenga(n) o no el/los campo(s) “upload” el atributo uploadfield.

Usted también debería almacenar las sesiones y los tickets en la base de datos y usted debe ser explícito:

1
2
3
4
5
if request.env.web2py_runtime_gae:
    db = DAL('gae')
    session.connect(request,response,db)
else:
    db = DAL('sqlite://storage.sqlite')

El anterior código verifica si web2py se está ejecutando en GAE, se conecta a BigTable, e instruye a web2py a almacenar allí a las sesiones y tickets. De lo contrario se conecta a una base de datos sqlite. Este código ya está presente en la aplicación de andamiaje en el archivo “db.py”.

Memcache

Si usted así lo prefiere, puede almacenar las sesiones en memcache:

1
2
3
4
5
6
7
from gluon.contrib.gae_memcache import MemcacheClient
from gluon.contrib.memdb import MEMDB
cache.memcache = MemcacheClient(request)
cache.ram = cache.disk = cache.memcache

db = DAL('gae')
session.connect(request,response,MEMDB(cache.memcache))

Note que en GAE, cache.ram y cache.disk no deben ser usados, así que haga que ellos apunten a cache.memcache.

Problemas de Base de Datos

La ausencia de transacciones multi-entidad y funcionalidades típicas de bases de datos relacionales son lo que diferencian a GAE de otros entornos de alojamiento. Éste es el precio que hay que pagar por la alta escalabilidad. GAE es una excelente plataforma si estas limitaciones son toleradas; de lo contrario, se debe considerar una plataforma de alojamiento regular con base de datos relacional.

Si una aplicación de web2py no corre en GAE, es debido a una de las limitaciones discutidas arriba. La mayoría de los problemas pueden resolverse removiendo los JOINs de las solicitudes web2pyy desnormalizando la base de datos.

El Motor de Aplicaciones de Google sporta algunos tipos de campos especiales, tales como ListProperty y StringListProperty. Usted puede usar estos tipos con web2py usando la siguiente sintaxis antigua:

from gluon.contrib.gql import gae
db.define_table('product',
    Field('name'),
    Field('tags', type=gae.StringListProperty())

o la nueva sintaxis equivalente:

db.define_table('product',
    Field('name'),
    Field('tags', 'list:string')

En ambos casos el campo “tags” es un StringListProperty y por lo tanto sus calores deben ser listas de cadenas, de forma compatible con la documentación de GAE. Se prefiere la segunda notación porque web2py tratará el campo de una manera más inteligente en el contexto de los formularios, y porque también trabajará con las bases de datos relacionales.

De manera similar web2py soporta list:integer y list:reference los cuales se mapean en una ListProperty(int).

Los tipos list son discutidos con más detalle en el capítulo 6.

GAE y HTTPS

Si su aplicación tiene como id “myapp”, su dominio GAE es

http://myapp.appspot.com/

y también puede ser accesado por medio de HTTPS

https://myapp.appspot.com/

En este caso usará un certificado “appspot.com” proporcionado por Google.

Usted puede registrar una entrada DNS y usar cualquier otro nombre de dominio de su pertenencia para su aplicación pero no podrá usar HTTPS en él. Al tiempo de escribir el libro, ésta es una limitación del GAE.