Chapter 13: Possibilités de déploiement

Possibilités de déploiement

Il y a plusieurs façons de déployer web2py dans un environnement de production. Les détails dépendent de la configuration et les services fournis par l'hôte.

Dans ce chapitre, nous considérons les points suivants :

  • Déploiement en production (Apache, Nginx, Lighttpd, Cherokee)
  • Sécurité
  • Scalabilité en utilisant Redis et un load balancer
  • Déploiement sur PythonAnywhere, Heroku, Amazon EC2, et sur la plateforme Google App Engine platform(GAE[gae] )

Apache
CGI
mod_python
mod_wsgi
mod_proxy
WSGI
Nginx
Heroku
PythonAnywhere

web2py est fourni avec un serveur web prêt pour le SSL[ssl] , le Rocket wsgiserver[rocket] . Alors que c'est un serveur web très rapide, il a des possibilités de configuration limitées. Pour cette raison, il est mieux de déployer web2py derrière un Apache[apache] , un Nginx[Nginx], Lighttpd[lighttpd] ou Cherokee[cherokee] . Ce sont des serveurs web libres et open-source qui sont personnalisables et qui ont fait leurs preuves en terme de stabilité dans des environnements en production à fort trafic. Ils peuvent être configurés pour servir des fichiers statiques directement, gérer HTTPS, et passer des contrôles à web2py pour du contenu dynamique.

Jusqu'à il y a quelques années, l'interface standard pour la communication entre les serveurs web et les applications web était le Common Gateway Interface (CGI)[cgi] . Le principal problème avec le CGI est qu'il créé un nouveau processus pour chaque requête HTTP. Si l'application web est écrite dans un langage interprété, chaque requête HTTP servie par les scripts CGI démarre une nouvelle instance de l'interpréteur. Ceci est lent, et devrait être évité dans un environnement de production. De plus, CGI peut seulement gérer des réponses simples. Il ne peut pas gérer, par exemple, le streaming de fichier.

web2py fournit un fichier cgihandler.py pour s'interfacer au CGI.

Une solution à ce problème est d'utiliser le module mod_python pour Apache. Nous en parlons ici car son usage est encore très commun, bien que le projet mod_python ait été officiellement abandonné par la Fondation Apache Software. mod_python démarre une instance de l'interpréteur Python lorsqu'Apache démarre, et sert chaque requête HTTP dans son propre thread sans avoir à redémarrer Python à chaque fois? C'est une meilleure solution que CGI, mais ce n'est pas optimal, puisque mod_python utilise sa propre interface pour la communication entre le serveur web et l'application web. Dans mod_python, toutes les applications hébergées démarrent sous le même user-id/group-id, ce qui présente des problèmes de sécurité.

web2py fournit un fichier modpythonhandler.py pour s'interfacer à mod_python.

Dans les dernières années, la communauté Python s'est rassemblée derrière une nouvelle interface standard pour la communication entre les serveurs web et les applications web écrite en Python. C'est appelé Web Server Gateway Interface (WSGI)[wsgi-w] [wsgi-o] . web2py s'est basé sur WSGI, et il fournit les gestionnaires pour utiliser d'autres interfaces dans le cas où WSGI n'est pas disponible.

Apache supporte WSGI via le module mod_wsgi[modwsgi] développé par Graham Dumpleton.

web2py fournit un fichier wsgihandler.py pour s'interfacer avec WSGI.

Certains services d'hébergement web ne supportent pas mod_wsgi. Dans ce cas, nous devons utiliser Apache comme proxy et transférer toutes les requêtes entrantes au serveur web web2py embarqué (fonctionnant par exemple sur localhost:8000).

Dans les deux cas, avec mod_wsgi et/ou mod_proxy, Apache peut être configuré pour servir les fichiers statiques et gérer l'encryption SSL directement, récupérant le fardeau de web2py.

Nginx utilise uWSGI au lieu de WSGI, un protocole similaire mais différent qui nécessite son propre adaptateur Python.

Le serveur web Lighttpd ne supporte pas l'interface WSGI pour le moment, mais supporte l'interface FastCGI[fastcgi] , qui est une amélioration de CGI. Le but principal de FastCGI est de réduire la surcharge associée avec l'interfaçage du serveur web et des programmes CGI, en autorisant un serveur à gérer plus de requêtes HTTP à la fois.

Selon le site web Lighttpd, "Lighttps exécute de nombreux sites Web 2.0 populaires tels que YouTube et Wikipedia. Son infrastructure optimisée pour des hautes performances IO permet de s'adapter de nombreuses fois mieux que le même matériel avec des serveurs web alternatifs". Lighttpd avec FastCGI est, en fait, plus rapide que Apache avec mod_wsgi.

web2py fournit un fichier fcgihandler.py pour s'interfacer avec FastCGI.

web2py inclut également un gaehandler.py pour s'interfacer avec Google App Engine (GAE). Sur GAE, les applications web fonctionnent "dans le cloud". Cela signifie que le framework est totalement abstrait de tout détail matériel. L'application web est automatiquement répliquée autant de fois que nécessaire pour serveir toutes les requêtes concurrentes. La réplication dans ce cas signifie plus de threads multiples sur une simple serveur ; signifie aussi de multiples processus sur différents serveurs. GAE réussit ce niveau de scalabilité en bloquant l'accès en écriture au système de fichiers, et toutes les informations persistentes doivent être stockées dans le datastore Google BigTable ou en memcache.

Sur les plateformes non-GAE, la scalabilité est un problème qui doit être pris en compte, et il nécessite quelques modifications dans les applications web2py. Le moyen le plus commun de réussir la scalabilité est d'utiliser de multiples serveurs web derrière un load-balancer (un simple round robin, ou quelque chose de plus sophistiqué, en recevant un feedback de heartbeat depuis les serveurs).

Même si il y a plusieurs serveurs web, il doit y avoir un, et seulement un, serveur de base de données. Par défaut, web2py utilise le système de fichiers pour stocker les sessions, les tickets d'erreur, les fichiers uploadés et le cache. Cela signifie que dans la configuration par défaut, les dossiers correspondants doivent être des dossiers partagés.

image

Dans le reste de ce chapitre, nous considérons des moyens différents qui peuvent fournir une amélioration de l'approche basique, incluant :

  • Stocker les sessions dans la base de données, dans le cache ou ne pas stocker les sessions du tout.
  • Stocker les tickets sur les systèmes de fichier locaux et les déplacer dans la base de données dans des batchs.
  • Utiliser memcache au lieu de cache.ram et cache.disk.
  • Stocker les fichiers uploades dans la base de données au lieu du système de fichiers partagé.

Alors que nous recommandons de suivre les trois premières possibilités, la quatrième peut fournir un avantage principalement dans le cas de petits fichiers, mais peut être contre-productif pour des fichiers plus importants.

anyserver.py

anyserver
bjoern
cgi
cherrypy
diesel
eventlet
fapws
flup
gevent
gunicorn
mongrel2
paste
tornado
twisted
wsgiref

Web2py est fourni avec un fichier appelé anyserver.py qui implémente les interfaces WSGI pour les serveurs populaires : bjoern, cgi, cherrypy, diesel, eventlet, fapws, flup, gevent, gunicorn, mongrel2, paste, rocket, tornado, twisted, wsgiref

Vous pouvez utiliser n'importe lequel de ces serveurs, par exemple Tornado, simplement en faisant :

python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P

Ici -l est pour logger et -P est pour le profileur. Pour les informations sur toutes les options de la ligne de commande, utilisez "-h" :

python anyserver.py -h

Linux et Unix

Déploiement en production en une étape

Voici les quelques étapes à suivre pour installer apache+python+mod_wsgi+web2py+postgresql de zéro.

Sur Ubuntu :

wget https://raw.githubusercontent.com/web2py/web2py/master/scripts/setup-web2py-ubuntu.sh
chmod +x setup-web2py-ubuntu.sh
sudo ./setup-web2py-ubuntu.sh

Sur Fedora :

wget https://raw.githubusercontent.com/web2py/web2py/master/scripts/setup-web2py-fedora.sh
chmod +x setup-web2py-fedora.sh
sudo ./setup-web2py-fedora.sh

Deux de ces scripts devraient démarrer directement, mais chaque installation Linux est un peu différente, donc assurez-vous de vérifier le code source de ces scripts avant de les exécuter. Dans le cas d'Ubuntu, la plupart de ce qui est fait est expliqué après. Ils n'implémentent pas les optimisations de scalabilité vues ci-après.

Mise en place d'Apache

Dans cette section, nous utilisons Ubuntu Server Edition comme plateforme de référence. Les commandes de configuration sont très similaires sur toute autre distribution basée sur Debian, mais pourraient différer sur le ssystèmes basés sur Fedora (qui utilisent yum au lieu de apt-get). Vous pouvez aussi bien utiliser la version 2.2.x ou 2.4.x

Assurez-vous d'abord que tous les packages nécessaires Python et Apache sont installés en tapant les commandes Shell suivantes :

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-get -y install libapache2-mod-proxy-html

Ensuite, activez le module SSL, le module proxy, et le module WSGI dans Apache :

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

Créez le dossier SSL, et mettez les certificats SSL à l'intérieur :

sudo mkdir /etc/apache2/ssl

Vous devriez obtenir vos certificats SSL depuis une Autorité de Certification vérifiée telle que verisign.com, mais, pour les tests, vous pouvez générer vos propres certificats auto-signés en suivant les instructions dans ref.[openssl]

Redémarrez ensuite le serveur web :

sudo /etc/init.d/apache2 restart

Le fichier de configuration Apache est :

/etc/apache2/sites-available/default

Les logs Apache sont dans :

/var/log/apache2/

mod_wsgi

Téléchargez et décompressez la source web2py sur la machine où vous avez installé le serveur web précédemment.

Installez web2py sous /home/www-data/, par exemple, et donnez les droits à l'utilisateur www-data et groupe www-data. Ces étapes peuvent être exécutées avec les commandes shell suivantes :

cd /home/www-data/
sudo wget http://web2py.com/examples/static/web2py_src.zip
sudo unzip web2py_src.zip
sudo chown -R www-data:www-data /home/www-data/web2py

Pour mettre en place web2py avec mod_wsgi, créez un nouveau fichier de configuration Apache :

/etc/apache2/sites-available/web2py

et incluez le code suivant :

<VirtualHost *:80>
  ServerName web2py.example.com
  WSGIDaemonProcess web2py user=www-data group=www-data display-name=%{GROUP}
  WSGIProcessGroup web2py
  WSGIScriptAlias / /home/www-data/web2py/wsgihandler.py

#This is Apache 2.2.x permission syntax. See Apache docs for 2.4 syntax
# http://httpd.apache.org/docs/2.4/upgrading.html#run-time

  <Directory /home/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>

Lorsque vous redémarrez Apache, toutes les requêtes devraient être transmises à web2py sans passer par le serveur wsgiserver Rocket.

Déplacez le script de gestion

Finalement, vous avez besoin de relocaliser le script de gestion web2py/handlers/wsgihandler.py. Comme documenté dans le répertoire handlers, le script que vous voulez devrait être déplacé ou copié dans le répertoire parent (i.e. le même répertoire que le script web2py.py). symlinking peut entraîner des problèmes de permission Apache.

Un peu d'arrière-plan wsgi

Voici quelques explications :

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

définit un groupe de processus démon dans le contexte de "web2py.example.com". En définissant cela dans l'hôte virtuel, seul l'hôte virtuel peut y accéder en utilisant WSGIProcessGroup, incluant tout hôte virtuel avec le même nom de serveur mais un port différent. Les options "user" et "group" devraient être définies pour l'utilisateur qui a les droits d'accès au répertoire où web2py a été installé. Vous n'avez pas besoin de définir "user" et "group" si vous avez autorisé le répertoire d'installation web2py en écriture pour l'utilisateur par défaut qui démarre Apache. L'option "display-name" rend le nom de processus en sortie ps en tant que "(wsgi-web2py)" au lieu du nom de l'exécutable du serveur web Apache. Comme aucune option "processes" ou "threads" n'est spécifiée, le processus démon de groupe aura un simple processus avec 15 threads fonctionnant dans ce processus. C'est plus qu'habituellement pour la plupart des sites et devrait être laissé tel quel. Si vous le surchargez, n'utilisez pas "processes=1" car cela engendrerait la désactivation des outils de debug embarqués WSGI des navigateurs qui vérifient le flag "wsgi.multiprocess". Ceci est dû au fait que l'option "processes" entraînera ce flag à être défini à true, même un seul processus, et de tels outils s'attendent à l'avoir à false. Note : si votre code d'application ou un module d'extension tiers n'est pas thread safe, utilisez les options "processes=5 threads=1" à la place. Ceci créera cinq processus dans le group de processus démons où chaque processus est un mono-threadé. Vous pouvez considérer l'utilisation "maximum-requests=1000" si votre application a des pertes d'objets Python car il est impossible d'effectuer du garbage collector correctement.

WSGIProcessGroup web2py

délègue le fonctionnement de toutes les applications WSGI au groupe de processus démon qui a été configuré en utilisant la directive WSGIDaemonProcess.

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

monte l'application web2py. Dans ce cas, elle est montée à la racine du site web.

<Directory /users/www-data/web2py>
  ...
</Directory>

donne la permission à Apache d'accéder au script WSGI.

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

indique à Apache de bypaser web2py lors de la recherche de fichiers statiques.

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

et

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

bloque l'accès publique à admin et appadmin.

Normalement nous autorisons juste la permission à l'ensemble du répertoire où le script WSGI est situé, mais web2py place le script WSGI dans un répertoire qui contient d'autre code source, incluant le mot de passe de l'interface d'administration. Ouvrir tout le répertoire causerait des problèmes de sécurité, car techniquement Apache donnerait la permission de servir tous les fichiers à n'importe quel utilisateur qui a traversé ce répertoire via une URL mappée. Pour éviter des problèmes de sécurité, interdisez explicitement l'accès aux contenus du répertoire, sauf pour le fichier script WSGI, et empêchez un utilisateur de faire des surcharges depuis un fichier .htaccess pour être totalement sûr.

Vous pouvez trouver un fichier de configuration wsgi Apache complet, commenté dans :

scripts/web2py-wsgi.conf

Cette section a été créée avec l'aide de Graham Dumpleton, développeur de mod_wsgi.

Configurer le mot de passe

En production, il peut être nécessaire de définir le mot de passe admin en code. Ceci peut être fait en utilisant le shell Bash avec

sudo -u www-data python -c "from gluon.main import save_password; save_password(raw_input('admin password: '),443)"

mod_wsgi et SSL

Pour forcer certaines applications (par exemple admin et appadmin) à utiliser HTTPS, stockez les certificats SSL et les fichiers de clé :

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

et éditez le fichier de configuration Apache web2py.conf et ajoutez :

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

Redémarrez Apache et vous devriez être capable d'accéder à :

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

mais pas :

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

mod_proxy

Certaines distributions Unix/Linux peuvent démarrer Apache, mais ne supportent pas mod_wsgi. Dans ce cas, la solution la plus simple est de démarrer Apache comme proxy et de laisser Apache gérer les fichiers statiques seulement.

Voici la configuration minimale d'Apache :

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>

Le script ci-dessus présente seulement l'application "welcom". Pour présenter les autres applications, vous avez besoin d'ajouter les <Location>...</Location> correspondants avec la même syntaxe que celle donnée pour l'application "welcome".

Le script suppose qu'il y ait un serveurs web2py fonctionnant sur le port 8000. Avant de redémarrer Apache, assurez-vous que c'est le cas :

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

Vous pouvez spécifier un mot de passe avec l'option -a ou utiliser le paramètre recycle au lieu du mot de passe. Dans le dernier cas, le mot de passe précédemment stocké est ré-utilisé et le mot de passe n'est pas stocké dans l'historique du shell.

Vous pouvez aussi utiliser le paramètre "<ask>", pour obtenir le prompt du mot de passe.

La commande nohup s'assure que le serveur n'est pas mort lorsque vous avez fermé le shell. nohup logge toutes les sorties dans nohup.out.

Pour forcer admin et appadmin à s'exécuter sur HTTPS, utilisez le fichier de configuration Apache suivant à la place :

NameVirtualHost *:80
NameVirtualHost *:443
#### deal with requests on port 80
<VirtualHost *:80>
   Alias / /users/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>

L'interface d'administration doit être désactivée lorsque web2py fonctionne sur un hôte partagé avec mod_proxy, ou il sera présenté aux autres utilisateurs.

Démarrer comme démon Linux

A moins que vous utilisiez mod_wsgi, vous devriez configuer le serveur web2py de sorte qu'il puisse être démarré/stoppé/redémarré comme tout autre démon Linux, afin qu'il puisse démarrer automatiquement au démarrage du serveur.

Le processus à mettre en place est spécifique selon les diverses distributions Linux/Unix.

Dans le dossier web2py, il y a deux scripts qui peuvent être utilisés pour ce cas :

scripts/web2py.ubuntu.sh
scripts/web2py.fedora.sh

Sur Ubuntu, ou sur toute autre distribution Linux basée sur Debian, éditez le fichier "web2py.ubuntu.sh" et remplacez le chemin "/usr/lib/web2py" par le chemin de votre installation web2py, tapez alors les commandes shell suivantes pour déplacer le fichier dans le bon dossier, enregistrez le comme service au démarrage, et démarrez-le :

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

Sur Fedora, ou toute autre distribution basée sur Fedora, éditez "web2py.fedora.sh" et remplacez le chemin "/usr/lib/web2py" avec le chemin de votre installation web2py, tapez alors les commandes shell suivantes pour déplacer le fichier dans le bon dossier, engistrez le comme service au démarrage, et démarrez-le :

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

Nginx

Nginx est un server web, libre et open-source qui a rapidement gagné en popularité pour ses performances impressionnantes.

Contrairement aux serveurs traditionnels, Nginx n'utilise pas de threads. Au lieu de cela, il utilise une architecture asynchrone/événementielle pour gérer la concurrence. Cette architecture résulte en un usage mémoire plus faible et prédictible, même sous haute charge.

Nginx est plus qu'un serveur HTTP et un reverse proxy, c'est aussi un serveur proxy IMAP/POP3.

Nginx est facile à configurer et ses fichiers de configuration sont plus simples et plus compacts que ceux d'Apache.

Nginx ne supporte pas WSGI mais fournit un support natif pour le protocole uWSGI [uwsgi] protocol.

uwsgi

Sur Ubuntu, vous pouvez installer Nginx avec :

apt-get -y install nginx-full

Après vous aurez besoin de créer un fichier de configuration tel que :

# file /etc/nginx/sites-available/web2py
server {
        listen          80;
        server_name     $hostname;
        #to enable correct use of response.static_version
        #location ~* /(\w+)/static(?:/_[\d]+.[\d]+.[\d]+)?/(.*)$ {
        #    alias /home/www-data/web2py/applications/$1/static/$2;
        #    expires max;
        #}
        location ~* /(\w+)/static/ {
            root /home/www-data/web2py/applications/;
            #remove next comment on production
            #expires max;
        }
        location / {
            #uwsgi_pass      127.0.0.1:9001;
            uwsgi_pass      unix:///tmp/web2py.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}
server {
        listen 443 default_server ssl;
        server_name     $hostname;
        ssl_certificate         /etc/nginx/ssl/web2py.crt;
        ssl_certificate_key     /etc/nginx/ssl/web2py.key;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_ciphers ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA;
        ssl_protocols SSLv3 TLSv1;
        keepalive_timeout    70;
        location / {
            #uwsgi_pass      127.0.0.1:9001;
            uwsgi_pass      unix:///tmp/web2py.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }

}

Vous aurez besoin de lier symboliquement le fichier et supprimer celui par défaut

ln -s /etc/nginx/sites-available/web2py /etc/nginx/sites-enabled/web2py
rm /etc/nginx/sites-enabled/default

Vous pouvez aussi avoir besoin de créer le dossier ssl pour les certificats et mettre les certificats dedans :

mkdir /etc/nginx/ssl
cp web2py.key /etc/nginx/ssl
cp web2py.crt /etc/nginx/ssl

Vous aurez ensuite besoin d'installer et configurer uWSGI

sudo mkdir /etc/uwsgi
sudo mkdir /var/log/uwsgi

et créé un fichier de configuration "/etc/uswsgi/web2py.xml" :

<uwsgi>
    <socket>/tmp/web2py.socket</socket>
    <pythonpath>/home/www-data/web2py/</pythonpath>
    <mount>/=wsgihandler:application</mount>
    <master/>
    <processes>4</processes>
    <harakiri>60</harakiri>
    <reload-mercy>8</reload-mercy>
    <cpu-affinity>1</cpu-affinity>
    <stats>/tmp/stats.socket</stats>
    <max-requests>2000</max-requests>
    <limit-as>512</limit-as>
    <reload-on-as>256</reload-on-as>
    <reload-on-rss>192</reload-on-rss>
    <uid>www-data</uid>
    <gid>www-data</gid>
    <no-orphans/>
</uwsgi>

Ce fichier suppose que web2py est installé sous "/home/www-data/web2py", comme dans le cas d'Apache.

Vous avez aussi besoin d'éditer un second fichier de configuration "/etc/init/uwsgi-emperor.conf" :

# Emperor uWSGI script
description "uWSGI Emperor"
start on runlevel [2345]
stop on runlevel [06]
respawn
exec uwsgi --master --die-on-term --emperor /etc/uwsgi --logto /var/log/uwsgi/uwsgi.log

Redémarrez finalement le tout :

start uwsgi-emperor
/etc/init.d/nginx restart

Vous pouvez recharger uwsgi avec

restart uwsgi-emperor

Vous pouvez l'arrêter avec

stop uwsgi-emperor

Vous pouvez recharger seulement web2Py (sans redémarrer uwsgi) avec

touch /etc/uwsgi/web2py.xml

Toutes ces étapes sont effectuées automatiquement pas les scripts fournis :

scripts/setup-web2py-nginx-uwsgi-on-centos.sh
scripts/setup-web2py-nginx-uwsgi-ubuntu.sh

Lighttpd

Lighttpd
FastCGI
fcgihandler

Vous pouvez installer Lighttpd sur une distribution Linux Ubuntu ou basée sur Debian avec la commande shell suivante :

apt-get -y install lighttpd

Une fois installé, éditez /etc/rc.local et créer un processus fcgi web2py en arrière-plan

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

Ensuite, vous avez besoin d'éditer le fichier de configuration Lighttpd

/etc/lighttpd/lighttpd.conf

afin qu'il puisse trouver la socket créée par le processus au-dessus. Dans le fichier de configuration, écrivez quelque chose comme :

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

Vérifiez maintenant les erreurs de syntaxe :

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

et (re)démarrez le serveur web avec :

/etc/init.d/lighttpd restart

Notez que FastCGI lie le serveur web2py à une socket Unix, et non une socket IP :

/tmp/fcgi.sock

C'est où Lighttpd transmet les requêtes HTTP et reçoit les réponses. Les sockets Unix sont plus légères que les sockets Internet, et c'est l'une des raisons pour laquelle Lighttpd+FastCGI+web2py est rapide. Comme dans le cas d'Apache, il est possible de configurer Lighttpd pour gérer directement les fichiers statiques, et forcer certaines applications en HTTPS. Se référer à la documentation Lighttpd pour plus de détails.

Les exemples dans cette section sont pris du post de John Heenan dans web2pyslices.

L'interface d'administration doit être désactivée lorsque web2py foncitonne sur une hôte partagé avec FastCGI, ou il sera exposé à tous les autres utilisateurs.

Hébergement partagé avec mod_python

Il y a des fois, spécifiquement sur les hôtes partagés, où l'on n'a pas la permission de configurer les fichiers de config Apache directement. Au jour d'aujourd'hui, la plupart de ces hôtes fonctionnent toujours avec mod_python même s'il n'est plus maintenu en faveur de mod_wsgi.

Vous pouvez toujours démarrer web2py. Voici un exemple de comment le configurer.

Placer les contenus de web2py dans le dossier "htdocs".

Dans le répertoire web2py, créez un fichier "web2py_modpython.py" avec les contenus suivants :

from mod_python import apache
import modpythonhandler

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

Créez/Mettez à jour le fichier ".htaccess" avec les contenus suivants :

SetHandler python-program
PythonHandler web2py_modpython
#PythonDebug On

Cet exemple a été fourni par Niktar.

Cherokee avec FastCGI

Cherokee
FastCGI
Cherokee est un serveur web très rapide, et comme web2py, il fournit une interface web prête pour AJAX pour sa configuration. Son interface web est écrite en Python. En plus, il n'y a pas besoin de redémarrage pour la plupart des changements.

Voici quelques étapes reuqises pour configurer web2py avec Cherokee :

Téléchargez Cherokee[cherokee]

Décompressez, construisez, et installez :

tar -xzf cherokee-0.9.4.tar.gz
cd cherokee-0.9.4
./configure --enable-fcgi && make
make install

Démarrez web2py normalement au moins une fois pour vous assurer qu'il créé le dossier "applications".

Ecrivez un script shell nommé "startweb2py.sh" avec le code suivant :

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

et donnez les droits d'exécution au script et démarrez-le. Ceci démarrera web2py sous le gestionnaire FastCGI.

Start Cherokee and cherokee-admin:

sudo nohup cherokee &
sudo nohup cherokee-admin &

Par défaut, cherokee-admin écoute seulement sur l'interface locale sur le port 9090. Ce n'est pas un problème si vous avez les accès physiques complets sur la machine. Si ce n'est pas le cas, vous pouvez le forcer à lier une adresse IP et un port en utilisant les options suivantes :

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

ou faire une redirection de port SSH (plus sécurisé, recommandé) :

ssh -L 9090:localhost:9090 remotehost

Ouvrez "http://localhost:9090" dans votre navigateur. Si tout est ok, vous obtiendez cherokee-admin.

Dans l'interface web cherokee-admin, cliquez sur "info sources". Choisissez "Local Interpreter". Ajoutez le code suivant, et cliquez sur "Add New".

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

Finalement, effectuez les étapes suivantes :

  • Cliquez sur "Virtual Servers", ensuite cliquez sur "Default".
  • Cliquez sur "Behavior", ensuite, sous cela, cliquez sur "default".
  • Choisissez "FastCGI" au lieu de "List and Send" depuis la liste.
  • En bas, sélectionnez "web2py" comme "Application Server"
  • Cochez toutes les cases (vous pouvez laisser Allow-x-sendfile). S'il y a un warning d'affiche, désactivez et activez l'une des checkbox. (Ceci renverra automatiquement les paramètres du serveur d'application. Parfois ça ne fonctionne pas, c'est un bug).
  • Pointez votre navigateur sur "http://yoursite", et "Welcome to web2py" va apparaître.

Postgresql

PostgreSQL est une base de données libre et open-source qui est utilisée dans de nombreux environnements de production, par exemple, pour stocker la base de données des noms de domaine en .org, et a fait ses preuves pour bien s'adapter avec des centaines de téra-octets de données. Il a un support de transaction très rapide et solide, et fournit une fonctionnalité d'auto-vacuum qui libère les administrateurs de la plupart des tâches de maintenance de la base.

Sur une distribution Ubuntu ou basée sur Debian, il est facile d'installer PostgreSQL et son API Python avec :

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

Il est habituel de démarrer les serveurs web et le serveur de base de données sur des machines différentes. Dans ce cas, les machines démarrant les serveurs web devraient être connectées avec un réseau interne (physique) sécurisé, ou devrait établir des tunnels SSL pour se connecter de façon sécurisée avec le serveur de base de données.

Editez le fichier de configuration PostgreSQL

sudo nano /etc/postgresql/9.1/main/postgresql.conf

et assurez-vous qu'il contient ces lignes

...
listen_addresses = 'localhost'
...
track_counts = on
...
autovacuum = on   # Enable autovacuum subprocess?  'on'
...

Editez le fichier de configuration client d'authentification

sudo nano /etc/postgresql/9.1/main/pg_hba.conf

et changez la méthode de ces lignes à trust

...
# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
...

Démarrez le serveur de base de données avec :

sudo /etc/init.d/postgresql restart

Lors du redémarrage du serveur PostgreSQL, il devrait notifier sur quel port il fonctionne. A moins que vous ayez plusieurs serveurs de base de données, ce devrait être 5432.

Les logs PostgreSQL sont dans :

/var/log/postgresql/

Une fois le serveur de base de données démarré et prêt, créez un utilisateur et une base de données afin que les applications web2py puissent l'utiliser :

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

La première des commandes donnera les accès superuser au nouveau utilisateur, appelé myuser. Il vous demandera un mot de passe.

Toute application web2py peut se connecter à cette base de données avec la commande :

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

mypassword est le mot de passe que vous avez entré lorsqu'il vous a été demandé, et 5432 le port où le serveur de base de données fonctionne.

Normalement, vous utilisez une base de données pour chaque application, et plusieurs instances de la même application se connectent à la même base de données. Il est également possible pour différentes applications de partager la même base de données.

Pour les détails sur le backup de base de données, lisez la documentation de PostgreSQL ; notamment les commandes pg_dump et pg_restore.

Démarrer l'ordonnanceur comme service Linux (démarrage)

Pour installer l'ordonnanceur comme démon permanent sur Linux (dès le démarrage), mettez le code suivant dans /etc/init/web2py-scheduler.conf, en supposant que votre instance web2py est installée dans le répertoire home de <user>, fonctionne comme <user>, avec l'application <myapp>, sur l'interface réseau eth0.

description "web2py task scheduler"
start on (local-filesystems and net-device-up IFACE=eth0)
stop on shutdown
respawn limit 8 60 # Give up if restart occurs 8 times in 60 seconds.
exec sudo -u <user> python /home/<user>/web2py/web2py.py -K <myapp>
respawn

Vous pouvez alors démarrer/arrêter/redémarrer/vérifier le status du démon avec :

sudo start web2py-scheduler
sudo stop web2py-scheduler
sudo restart web2py-scheduler
sudo status web2py-scheduler

Windows

Apache et mod_wsgi

Installer Apache, et mod_wsgi, sous Windows requiert une procédure légèrement différente. Cependant, c'est quasiment similaire à Linux, donc référez-vous aux notes Apache sur Linux précédentes.

Nous supposons ici qu'un binaire Python 2.x Windows est installé, vous démarrez des sources et web2py est situé à c:/web2py.

Les binaires modernes Apache pour Windows (e.g. 2.4.x) ne sont pas téléchargés depuis apache.org. Au lieu de cela, vous les téléchargez depuis les sites partenaires, tels que ApacheHaus. Le site Apache a une liste complète de tels partenaires ; cherchez les binaires Windows pour Apache 2.4.

Cependant, les binaires Windows peuvent ne pas être fournis avec le module wsgi. Dans ce cas, visitez la page modwsgi (actuellement à http://code.google.com/p/modwsgi/) et téléchargez le binaire pré-compilé pour votre version de Python et Apache. Après que vous ayez installé Apache, mettez la librairie .so dans le répertoire des modules.

Vous aurez besoin de modifier le fichier httpd.conf pour charger le module wsgi ; suivez l'exemple de lignes de configuration des autres modules.

LoadModule wsgi_module modules/mod_wsgi.so

Le wiki modwsgi mentionne des spécifications pour Windows : il est recommandé de les lire.

Vous aurez besoin de configurer httpd.conf comme pour toute nouvelle installation Apache.

L'installation de certificat est la même pour Windows que pour Linux.

Les binaires Windows généralement configuré pour charger et configurer les modules SSL 'out of the box' (le binaire Apache Haus est ainsi).

web2py devrait être servi sur https et optionnellement sur http. Typiquement, cela signifie les ports 80 et 443 à moins que votre serveur Windows les utilise déjà s'il y a un serveur IIS d'installé, dans quel cas vous devez choisir des ports alternatifs.

Cependant, nous supposerons que nous utilisons les ports 80 et 443 pour le moment. Cherchez "Listen 80" et ajoutez cette ligne après

Listen 443

ajoutez les lignes suivantes à la fin en changeant la lettre du lecteur, le numéro de port, le ServerName selon vos valeurs

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"
#and don't forget to move the handler script out of the handlers directory

  SSLEngine On
#these cert settings are correct for self-signed certificates
  SSLCertificateFile conf/server.crt
  SSLCertificateKeyFile conf/server.key

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

Sauvez et vérifiez la configuration en utilisant : [Démarrer > Programmes > Apache HTTP Server 2.2 > Configure Apache Server > Test Configuration]

S'il n'y a pas de problème vous verrez une fenêtre de commande s'ouvrir et se fermer. Maintenant vous pouvez démarrer Apache :

[Démarrer > Programmes > Apache HTTP Server 2.2 > Control Apache Server > Start]

ou mieux en démarrant maintenant le moniteur dans la barre des tâches

[Démarrer > Programmes > Apache HTTP Server 2.2 > Control Apache Server]

Maintenant vous pouvez faire un clic droit sur l'icône rouge ressemblant à une plume dans la barre des tâches pour "Open Apache Monitor" et ensuite démarrer, stopper et redémarrer Apache comme nécessaire.

Cette section a été d'abord en contribution avec Jonathan Lundell.

Utiliser nssm pour démarrer comme service Windows

Ce que Linux appelle un démon, Windows l'appelle un service. Le serveur pré-compilé web2py peut facilement être installé/démarré/arrêté comme service Windows. De même pour l'ordonnanceur web2py.

Windows services with nssm

Plutôt que de maintenir le code du service Windows dans web2py.py, les développeurs supportent un outil externalisé 'nssm'.

nssm est un outil Windows bien respecté avec de bonnes fonctionnalités, telles que le redémarrage automatique de services. Il signifie également un moyen consistant de démarrer les services web2py, les services d'ordonnancement et processus de nettoyage comme les suppressions de session. L'utilisation de l'option en ligne de commande précédente -W n'est plus supportée. La méthode nssm n'utilise pas le fichier options.py de l'ancienne méthode. Au lieu de cela, les options sont passée sur la ligne de commande (quelques exemples sont fournis ci-dessous)

Possibilité nssm : démarrer l'ordonnanceur comme service Windows avec nssm
Windows scheduler service

Démarrer l'ordonnanceur comme service Windows prend beaucoup de sens. L'approche la plus simple est de télécharger nssm (depuis http://www.nssm.cc). nssm est un helper open-source d'ordonnancement. Il embarque une commande exécutable pour le rendre en tant que service. La commande pour démarrer l'ordonnanceur est pythonw.exe -K <appname> Nous utilisons nssm pour embarquer cela, et en faire un service. Avant de faire cela, vous avez besoin de choisir un nom pour votre service. Il y a de forts avantages à créer un service spécifique pour chaque application qui a besoin d'un scheduler. Par conséquent, votre convention de nommage pour les sevrices peut être web2py_scheduler_app1

Après avoir extrait le fichier zup nssm, ouvrez une invite de commandes Windows dans le dossier contenant la version pour votre architecture (32-bit ou 64-bit), et tapez

nssm install web2py_scheduler_app1

Ceci montre une boite de dialogue demandant à entrer Application et Options. Application est l'exécutable pythonw.exe depuis le répertoire d'installation de Python. Options est le reste de la ligne de comandes. Vous pouvez avoir besoin de fournir un chemin complet vers votre script web2py.py. Par exemple, le champs d'options dans le dialogue nssm peut être :

c:\web2py\web2py.py -K app1

où app1 est le nom de votre application.

Il est possible d'invoquer l'ordonnanceur avec de multiples application. Cependant, dans ce mode, web2py détache chaque ordonnanceur d'application en sous-processus. Par conséquent, le processus démarré par le service ne mourra pas si l'une des instances de l'ordonnanceur rencontre des problèmes ; plutôt que cela, ce processus fils mourra. Nous ne pouvons alors pas profiter du redémarrage automatique de service Windows en cas de problème. Utiliser une application par service active cette fonctionnalité.

Possibilité nssm : démarrer web2py.py comme service

L'exemple ci-dessu montre comment utiliser nssm. Pour démarrer web2py en mode SSL sur le port 8041, et incluant quelques autres options, vous pourrez donner une ligne de commande nssm (dans le champs des options du dialogue nssm) comme ceci :

c:\web2py.py -p 8041 -i "0.0.0.0" --password="112233" --folder="d:\web2py_internet" --socket-timeout=10 --timeout=120 -c "d:\web2py_internet\applications\example.com.au.crt" -k "d:\web2py_internet\applications\web2py.key

(notez que ce n'est pas recommandé pour stocker les mots de passe, puisqu'un simple gestionnaire de tâches qui montre les lignes de commandes révèlera le mot de passe. Investiguez l'option -a de la ligne de commande web2py "<recycle>")

Sécuriser les sessions et admin

security
admin

Il est très dangereux d'exposer publiquement l'application admin et les contrôleurs appadmin à moins qu'ils fonctionnent sur HTTPS. De plus, votre mot de passe et les identifiants ne devraient jamais être transmis en clair. Ceci est vrai pour web2Py et toute autre application web.

Dans vos applications, si elles nécessitent une authentification, vous devriez rendre les cookies de session sécurisés avec :

session.secure()

Un moyen simple de configurer un environnement de production sécurisé sur un serveur est de d'abord stopper web2py et ensuite retirer tous les fichiers parameters_*.py du répertoire d'installation web2py. Ensuite démarrez web2py sans mot de passe. Ceci désactivera complètement admin et appadmin.

nohup python web2py --nogui -p 8001 -i 127.0.0.1 -a '' &

Ensuite, démarrez une seconde instance de web2py accessible seulement depuis localhost :

nohup python web2py --nogui -p 8002 -i 127.0.0.1 -a '<ask>' &

et créez un tunnel SSH depuis la machine locale (celle depuis laquelle vous souhaitez accéder à l'interface d'administration) au serveur (celui où web2py fonctionne, example.com), en utilisant :

ssh -L 8002:127.0.0.1:8002 username@example.com

Vous pouvez maintenant accéder à l'interface d'administration localement via un navigateur web sur localhost:8002.

Cette configuration est sécurisée car admin n'est pas atteignable lorsque le tunnel est fermé (l'utilisateur est déconnecté).

Cette solution est sécurisée sur les hôtes partagés si et seulement si les autres utilisateurs n'ont pas les accès en lecture au répertoire qui contient web2py ; autrement, les utilisateurs pourraient voler les cookies de session directement depuis le serveur.

Efficacité et scalabilité

scalability

web2py est développé dans une optique d'être simple à déployer et à configurer. Ceci ne signifie pas que cela compromet l'efficacité ou la scalabilité, mais signifie que vous pourriez avoir besoin de forcer pour le rendre scalable.

Dans cette section, nous supposons plusieurs installations web2py derrière un serveur NAT qui fournit du load balancing local.

Dans ce cas, web2py fonctionne out-of-the-box si certaines conditions sont respectées. En particulier, toutes les instances de chaque applications web2py doit accéder aux mêmes serveurs de base de données, et pouvoir voir les mêmes fichiers. Cette dernière condition peut être implémentée en partageant les dossiers suivants :

applications/myapp/sessions
applications/myapp/errors
applications/myapp/uploads
applications/myapp/cache

Les dossiers partagés doivent supporter le verrouillage de fichier. Les solutions possibles sont ZFS (ZFS a été développé par Sun Microsystems et c'est le choix préféré), NFS (avec NFS vous pouvez démarrer le démon nlockmgr pour permettre le verrouillage de fichier), ou Samba (SMB).

Il est possible de partager le dossier entier web2py ou le dossier entier des applications, mais ce n'est pas une bonne idée car ceci causerait une augmentation nécessaire de l'utilisation de la bande passante réseau.

Nous pensons que la configuration présentée au-dessus pour être très scalable car cela réduit la charge de la base de données en déplaçant ces ressources vrs un filesystem partagé qui ait besoin d'être partagé mais qui n'ait pas besoin d'être protégé pour les transactions (seulement un client à la fois est supposé pouvoir accéder à un fichier de session, le cache a toujours besoin d'un verrou global, les uploads et erreurs sont une fois en écriture/lecture sur plusieurs fichiers).

Idéalement, aussi bien la base de données et le stockage partagé devraient avoir les possibilités de RAID. Ne faites pas l'erreur de stocker la base de données sur le même stockage que les dossiers partagés, ou vous créerez un nouveau bloquage ici.

Sur la base du cas par cas, vous pouvez avoir besoin d'exécuter des optimisations additionnelles and nous les présenterons ci-après. En particulier, nous présenterons comment passer outre ces dossiers partagés un à un, et comment stocker les données associées dans la base de données à la place. Même si cela est possible, ce n'est pas nécessairement une bonne solution. Néanmoins, il peut y avoir des raisons de le faire. Une telle raison et que parfois nous n'avons pas la liberté de configurer des dossiers partagés.

Astuces d'efficacité

Le code d'application web2py est exécuté sur toute requête, mais vous voulez minimiser cette quantité de code. Voici ce que vous pouvez faire :

  • Démarrez une fois avec migrate=True ensuite définissez toutes vos tables à migrate=False.
  • Compilez votre application en bytecode en utilisant admin.
  • Utilisez cache.ram autant que vous pouvez mais assurez-vous d'utiliser un ensemble fini de clés, ou sinon le montant de cache utilisé va grandir arbitrairement.
  • Minimisez le code dans les modèles : ne définissez pas de fonctions ici, définissez les fonctions dans les contrôleurs qui ont besoin d'eux ou - même mieux - définissez les fonctions dans des modules, importez-les et utilisez les fonctions selon les besoins.
  • Ne mettez pas plusieurs fonctions dans le même contrôleurs mais utilisez plusieurs contrôleurs avec peu de fonctions.
  • Appelez session.forget(response) dans tous les contrôleurs et/ou fonctions qui ne changent pas la session.
  • Essayez d'éviter les cron web2py, et utilisez un processus d'arrière-plan à la place. Le cron web2py démarre trop d'instances Python et cause un usage excessif de mémoire.

Sessions en base de données

Il est possible d'indiquer à web2py de stocker les sessions dans une base de données au lieu du répertoire de sessions. Ceci doit être fait pour chaque application web2py individuellement, bien qu'ils puissent tous utiliser la même base de données pour stocker les sessions.

Etant donnée une connexion à la base de données

db = DAL(...)

vous pouvez stocker les sessions dans cette base de données (db) en indiquant simplement ce qui suit, dans le même fichier de modèles que celui qui établit la connexion :

session.connect(request, response, db)

Si elle n'existe pas déjà, web2py créé, en arrière-plan, une table dans la base appelée web2py_session_appname contenant les champs suivants :

Field('locked', 'boolean', default=False),
Field('client_ip'),
Field('created_datetime', 'datetime', default=request.now),
Field('modified_datetime', 'datetime'),
Field('unique_key'),
Field('session_data', 'text')

"unique_key" est une clé uuid utilisée pour identifier la session dans le cookie. "session_data" contient les données de session cPickled.

Pour minimiser l'accès à la base de données, vous devriez éviter de stocker les sessions lorsqu'elles ne sont pas nécessaires avec :

session.forget()

Les sessions sont automatiquement oubliées si inchangées.

Avez les sessions dans la base de données, le répertoire "sessions" n'a pas besoin d'être un dossier partagé puisqu'il ne sera plus utilisé.

Notez que, si les sessions sont désactivées, vous ne devez pas passer la session au form.accepts et vous ne pouvez pas utiliser session.flash ni CRUD.

HAProxy un load balancer hautement disponible

HAProxy

Si vous avez besoin de plusieurs processus web2py fonctionnant sur de multiples machines, au lieu de stocker des sessions dans une base de données ou en cache, vous avez l'option d'utiliser un load balancer avec les sticky sessions.

Pound[pound] et HAProxy[haproxy] sont deux load balancers HTTP et Reverse Proxy qui fournissent la gestion de sticky sessions. Nous présentons ici la dernière option car elle semble être très commune sur les offres d'hébergement commercial VPS.

Par sticky sessions, nous voulons dire qu'une fois un cookie de session rendu, le load balancer renverra toujours les requêtes de ce client associé à la session au même serveur. Ceci vous permet de stocker la session sur le système de fichiers local sans besoin de filesystem partagé.

Pour utiliser HAProxy :

D'abord, installez-le, sur notre machine de test Ubuntu :

sudo apt-get -y install haproxy

Ensuite éditez le fichier de configuration "/etc/haproxy.cfg" à quelque chose comme :

## 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 directive listen indique à HAProxy sur quel port il doit attendre une connexion. La directive server indique à HAProxu où trouver les serveurs proxys. Le répertoire appsession fait une session sticky et utilise un cookie appelé WEB2PYSTICKY pour cet usage.

Troisièmement, activez ce fichier de configuration et démarrez HAProxy :

/etc/init.d/haproxy restart

Vous pouvez trouver des instruction similaires pour configurer Pound à l'URL

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

Nettoyage de sessions

Vous devriez être conscient que sur un environnement de production, les sessions s'empilent vite. web2py fournit un script appelé :

scripts/sessions2trash.py

qui lorsqu'il est lancé en arrière-plan, supprime périodiquement toutes les sessions qui n'ont pas été utilisées depuis un certain moment. Web2py fournit un script pour nettoyer ces sessions (il fonctionne aussi bien pour les fichiers de sessions que pour les sessions en base de données).

Voici quelques cas d'usage typiques :

  • Supprimer les sessions expirées toutes les 5 minutes :
nohup python web2py.py -S app -M -R scripts/sessions2trash.py &

ou sous Windows, utiliser nssm comme décrit ci-dessus dans la section sur l'ordonnanceur. Vous aurez probablement besoin d'inclure le chemin complet de web2py.py et le dossier de scripts, et le caractère de fin & n'est pas nécessaire.

  • Supprimer les sessions plus anciennes que 60 minutes peu importe leur expiration, avec une sortie verbeuse, ensuite quitter :
python web2py.py -S app -M -R scripts/sessions2trash.py -A -o -x 3600 -f -v
  • Supprimer toutes les sessions peu importe leur expiration et quitter :
python web2py.py -S app -M -R scripts/sessions2trash.py -A -o -x 0

Ici, app est le nom de votre application.

Uploader des fichiers dans la base de données

Par défaut, tous les fichiers uploadés gérés par SQLFORMs sont proprement renommés et stockés dans le système de fichier sous le dossier "uploads". Il est possible d'indiquer à web2py de stocker les fichiers uploadés dans la base de données à la place.

Maintenant, considérer la table suivante :

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

dog.image est de type upload. Pour faire en sorte que l'image uploadée dans le même enregistrement que le nom du chien, vous devez modifier la définition de la table en ajoutant un champ blob et le lier au champ upload :

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

Ici, "image_data" est juste un nom arbitraire pour le nouveau champ blob.

La ligne 3 indique à web2py de renommer proprement les images uploadées comme d'habitude, stocker le nouveau nom dans le champ image, et stocker les données dans le uploadfield appelé "image_data" au lieu de stocker les données sur le système de fichiers. Tout cela est fait automatiquement par SQLFORMs et aucun autre code n'a besoin d'être changé.

Avec cette astuce, le dossier "uploads" n'est plus nécessaire.

Sur Google App Engine, les fichiers sont stockés par défaut dans la base de données sans le besoin de définir un uploadfield, puisqu'un est créé par défaut.

Collecter les tickets

Par défaut, web2py stocke les tickets (erreurs) sur le système de fichiers local. Cela n'aurait pas de sens de stocker les tickets directement dans la base de données puisque la plupart des erreurs dans un environnement de production provient d'erreur avec la base de données.

Stocker les tickets n'est jamais bloquant, puisque c'est normalement un événement rare. Cependant, dans un environnement de production avec de multiples serveurs concurrents, il est plus adéquat de les stocker dans un dossier partagé. Néanmoins, puisque seul l'administrateur a besoin de récupérer ces tickets, il est également possible de stocker les tickets dans un dossier local non partagé "errors" et les collecter périodiquement et/ou les nettoyer.

Une possibilité est de déplacer périodiquement tous les tickets locaux vers la base de données.

Pour cet usage, web2py fournit le script suivant :

scripts/tickets2db.py

Par défaut, le script obtient l'uri de la base depuis un fichier sauvé dans le dossier private, ticket_storage.txt. Ce fichier devrait contenir une chaîne qui est passée directement à une instance DAL, comme :

mysql://username:password@localhost/test
postgres://username:password@localhost/test
...

Ceci permet de laisser le script tel quel : si vous avez plusieurs applications, il choisira dynamiquement la bonne connexion pour chaque application. Si vous voulez coder en dur l'URI dedans, éditez la seconde référence au db_string, juste après la ligne except . Vous pouvez lancer le script avec la commande :

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

où myapp est le nom de votre application.

Ce script se lance en arrière-plan et déplace tous les tickets toutes les 5 minutes vers une table et supprime les tickets locaux. Vous pouvez plus tard voir les erreurs en utilisant l'application admin, cliquer sur le bouton "switch to: db" en haut, avec exactement la même fonctionnalité que s'ils étaient stockés sur le système de fichiers.

Avec cette astuce, le dossier "errors" n'a plus besoin d'être un dossier partagé, puisque les erreurs seront stockées dans la base de données.

Memcache

memcache

Nous avons montré que web2py fournit deux types de cache : cache.ram et cache.disk. Ils fonctionnent tous les deux sur un environnement distribué avec de multiples serveurs concurrents, mais ils ne fonctionnent pas comme on s'y attend. En particulier, cache.ram mettra uniquement en cache au niveau serveur ; devenant donc inutile. cache.disk mettra également en cache au niveau serveur à moins que le dossier "cache" ne soit un dossier partagé qui supporte les verrous ; donc, au lieu d'accélerer les choses, il devient un véritable goulot.

La solution est de ne pas les utiliser, mais d'utiliser memcache à la place. web2py est fourni avec une API memcache.

Pour utiliser memcache, créez un nouveau fichier modèle, par exemple0_memcache.py, et écrivez dans ce fichier (ou ajouter) le code suivant :

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 première ligne importe memcache. La seconde ligne doit être une liste de sockets memcache (server:port). La troisième ligne définit cache.memcache. La quatrième ligne redéfinit cache.ram et cache.disk en termes de memcache.

Vous pouvez aussi choisir de redéfinir seulement l'un d'eux pour définir un nouvel objet cache pointant sur l'objet Memcache.

Avec cette astuce, le dossier "cache" n'a plus besoin d'être un dossier partagé, puisqu'il ne sera plus utilisé.

Ce code nécessite d'avoir des serveurs memcache fonctionnant sur le réseau local. Vous devriez consulter la documentation memcache pour avoir des informations sur les moyens de configurer ces serveurs.

Sessions en memcache

Si vous avez besoin des sessions et que vous ne voulez pas utiliser un load balancer avec des sticky sessions, vous avez la possibilité de stocker les sessions en memcache :

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

Mise en cache avec Redis

[redis]

Une alternative au Memcache est d'utiliser Redis.

Redis

Supposons que nous avons Redis installé et fonctionnel en localhost sur le port 6379, nous pouvons nous y connecter en utilisant le code suivant (dans un modèle) :

from gluon.contrib.redis_cache import RedisCache
cache.redis = RedisCache('localhost:6379',db=None, debug=True)

où 'localhost:6379' est la chaîne de connexion et db n'est pas un objet DAL mais un nom de base de données Redis.

Nous pouvons maintenant utiliser cache.redis au lieu de (ou en complément de) cache.ram et cache.disk.

Nous pouvons aussi obtenir des statistiques Redis en appelant :

cache.redis.stats()

Le sous-système de cache Redis vous permet d'éviter le fameux "thundering herd problem" : ce n'est pas actif par défaut puisqu'habituellement Redis est choisi pour sa vitesse, mais à un coût négligeable vous pouvez vous assurer que seul un thread/process peut définir une valeur de manière concurrente. Pour activer ce comportement, passez uniquement le paramètre with_lock=True à l'appel RedisCache. Vous pouvez également activer ce comportement "sur-demande" avec value = cache.redis('mykey', lambda: time.time(), with_lock=True)

Sessions dans Redis

Si vous avez Redis dans votre stack, pourquoi ne pas l'utiliser pour les sessions ?

from gluon.contrib.redis_session import RedisSession
sessiondb = RedisSession('localhost:6379',db=0, session_expiry=False)
session.connect(request, response, db = sessiondb)

Le code a été testé avec ~1M sessions. Tant que Redis peut remplir la mémoire, le temps nécessaire pour gérer 1 ou 1M sessions est le même. Alors qu'avec des sessions en fichiers ou en base de données, l'amélioration de vitesse est imperceptible pour ~40K sessions, au-delà, l'amélioration se ressent. Une grande amélioration peut être notée lorsque vous lancer une ferme d'instances web2py, puisque partager le dossier sessions ou avoir de multiples processus connectés à une base de données monopolise souvent le syst-me. Vous finirez avec une clé par sessions, et deux clés, l'une contenant un entier (nécessaire pour assigner différentes clés de session) et l'autre contenant liste de toutes les sessions générées (soit pour 1000 sessions, 1002 clés).

Si session_expiry n'est pas défini, les sessions seront gérées comme d'habiture, vous pourriez avoir besoin de nettoyer les sessions comme d'habitude une fois de temps en temps.

Cependant, lorsque session_expiry est défini pour supprimer automatiquement les sessions après n secondes (e.g. si défini à 3600, la session va expirer exactement une heure après avoir été mise à jour la dernière fois), vous devriez occasionnellement démarrer sessions2trash.py pour effectuer un nettoyage de la clé contenant l'ensemble de toutes les sessions précédemment délivrées (pour ~1M sessions, le nettoyage requiert 3 secondes). Le système de Redis pour les sessions est le seul qui peut éviter les modifications concurrentes de la même session : c'esst particulièrement vrai pour les applications intensives Ajax qui écrivent des sessions souvent de manière semi-concurrente. Pour favoriser la vitesse c'est oar défaut non forcé, cependant si vous voulez mettre en place le comportement de verrou, mettez juste le paramètre with_lock=True pour l'objet RedisSession.

Supprimer des applications

removing application

Dans une configuration de production, il peut être préférable de ne pas installer les applications par défaut : admin, examples et welcome. Bien que ces applications soient un peu petites, elles ne sont pas nécessaires.

Supprimer ces applications est aussi simple que supprimer les dossiers correspondants sous le dossier applications.

Utiliser des bases de données répliquées

Dans un environnement haute-performance vous pouvez avoir une architecture de base de données maître-esclave avec plusieurs esclaves répliqués et peut être un ensemble de serveurs répliqués. La DAL peut gérer cette situation et se connecter aux différents serveurs selon des conditions et des paramètres de requête. L'API pour faire cela a été décrite au Chapitre 6. Voici un exemple :

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

Dans ce cas, les différentes requêtes HTTP seront servies par différentes bases de données de manière aléatoire, et chaque base de données sera attaquée plus ou moins avec la même probabilité.

Nous pouvons également implémenter un simple Round-Robin

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

Ceci est assez sécurisé dans le sens où si le serveur de base de données assigné pour répondre à la requête n'arrive pas à se connecter, la DAL va essayer le suivant dans l'ordre.

Il est également possible de se connecter à différentes bases de données selon l'action demandée ou le contrôleur. Dans une configuration maitre-esclave de base de données, quelques actions n'effectuent qu'une lecture et certaines personnes des lectures/écritures. Le premier peut se connecter proprement au serveur de base de données en tant qu'esclave, alors que le deuxième devrait se connecter au maître. Donc vous pouvez faire :

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

où 1,2,3 sont des esclavec et 3,4,5 sont des maîtres.

Compresser les fichiers statiques

Les navigateurs peuvent décompresser du contenu à la volée, donc compresser du contenu pour ces navigateurs peut vous aider à sauver de la bande passante, et ainsi réduire les temps de réponse. Aujourd'hui, la plupart des serveurs web peuvent compresser votre contenu à la volée et l'envoyer aux navigateurs demandant du contenu compressé. Cependant, pour les fichiers statiques, vous perdez des cycles CPU pour compresser le même contenu plusieurs fois.

Vous pouvez utiliser scripts/zip_static_files.py pour créer des versions compressées de vos fichiers statiques et les envoyer sans gaspiller de CPU. Démarrez python web2py.py -S myapp -R scripts/zip_static_files.py dans un cron. Le script fait attention à créer (ou mettre à jour) les versions compressées et les sauve parmi vos fichiers, en ajoutant une extension .gz à leur nom. Vous avez juste besoin de laisser savoir à votre serveur web quand envoyer ces fichiers [apache-content-negotiation] [nginx-gzipstatic]

Déployer sur PythonAnywhere

PythonAnywhere
PythonAnywhere

PythonAnywhere est le meilleur moyen de déployer des applications web2py.

PythonAnywhere est un développement Python et environnement d'hébergement qui s'affiche dans votre navigateur web et qui fonctionne sur des serveurs cloud. Ils sont déjà configurés avec tout ce dont vous avez besoin pour faire fonctionner Python et ils supportent plus spécifiquement web2py. D'expérience PythonAnywhere est simple à utiliser, rapide et puissant. Ils fournissent également des bases de données MySQL, des shells Python et l'intégration de Dropbox. L'hébergement professionnel est disponible si l'option basique gratuite n'est pas suffisante pour vous.

Afin d'utiliser PythonAnywhere, vous avez besoin de créer un compte, vous connecter, et ensuite utiliser le Dashboard web pour ajouter une nouvelle application de type web2py.

image

image

L'interface vous demande également un mot de passe d'administration.

image

Le dossier web2py sera créé dans votre répertoire utilisateur.

De façon alternative, vous pouvez aussi utiliser le shell BASH en web pour installer web2py comme vous le feriez normalement :

wget http://www.web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip

Toujours depuis le shell, vous devriez créer un mot de passe administrateur pour l'utilisation future :

python -c "from gluon.main import save_password; save_password(raw_input('admin  password: '),433)"

Visitez ensuite le panneau "Web" en utilisant l'interface web et éditer le fichier "/var/www/<username>_pythonanywhere_com_wsgi.py". C'est le point d'entrée pour votre programme (dans notre cas web2py) et, comme vous pouvez l'imaginer, c'est basé sur le protocole WSGI.

Editez le fichier "/var/www/<username>_pythonanywhere_com_wsgi.py" et écrivez dedans :

import sys
path = '/home/<username>/web2py'
if path not in sys.path: sys.path.append(path)
from wsgihandler import application # the web2py handler

Ici "<username>" est votre nom d'utilisateur PythonAnywhere.

Après que vous ayez installé web2py, notez que vous n'avez pas besoin de démarrer ou ocnfigurer un serveur web. PythonAnywhere en fournit un et est rechargé lorsque vous éditez le fichier de configuration ci-dessus ou pressez le bouton "Reload web app" sur le Dashboard. Tout le monde peut immédiatemment y accéder à l'URL :

http://yourusername.pythonanywhere.com/

Ils fournissent également une version sécurisée du site, et vous êtes forcé de l'utiliser pour utiliser l'interface d'Administration web2py :

https://yourusername.pythonanywhere.com/admin/default/index

Nous remercions l'équipe PythonAnywhere pour leur aide et leur support.

Deploiement de Heroku

[heroku]

Heroku

Heroku est une solution d'hébergement moderne et agile multi-plateforme. Cela permet de pousser vos applications vers un serveur cloud utilisant Git. Afin d'utiliser Heroku vous devez avoir Git d'installé et vous devez avoir le SDK Heroku installé. Vous interagissez avec Heroku en utilisant le SDK localement et vos commandes seront poussées et exécutées sur le serveur.

Les applications fonctionnant sur Heroku ne peuvent pas rester sur un système de fichiers persistent puisqu'il est rafraîchi périodiquement. Pour cette raison, seul le code d'application peut être stocké sur le système de fichier. Toutes les données doivent être stockées dans la base de données. Heroku fonctionne sur PostgreSQL. PostgreSQL est aussi configuré en utilisant le SDK Heroku et l'URI pour la base de données est assignée dynamiquement au démarrage et stockée dans une variable d'environnement.

Cela signifie que les applications web2py doivent être modifiées pour fonctionner sur Heroku afin d'utiliser la base de données.

Web2py fournit un script "heroku.sh" pour vous aider. Tout ce dont vous avez besoin est de remplacer :

db = DAL(...)

dans votre code par :

from gluon.contrib.heroku import get_db
db = get_db(name=None, pool_size=10)

Ici name est la variable d'environnement contenant l'URL PostgreSQL Heroku (quelque chose comme HEROKU_POSTGRESQL_RED_URL). Par défaut à None et s'il y a seulement une variable d'environnement HEROKU_POSTGRESQL_*_URL il l'utilisera. pool_size est la taille de pool habituelle pour la DAL.

Lorsque ça ne fonctionne pas sur une plateforme Heroku, get_db utilisera une base de développement "sqlite://heroku.test.sqlite".

Dans les deux cas, les sessions seront stockées dans la base de données.

Web2py fournit un script "scripts/setup-web2py-heroku.sh" pour déployer votre installation web2py sur heroku. Il effectue les étapes suivantes :

Il installe virtualenv et le pilote psycopg2 :

sudo pip install virtualenv
sudo pip install psycopg2

Il créé un active un virtualenv

virtualenv venv --distribute
source venv/bin/activate

Ensuite créé un fichier de requirement :

pip freeze > requirements.txt

Et créé un "Procfile" qui indique à Heroku comment démarrer web2py :

echo "web: python web2py.py -a 'yourpassword' -i 0.0.0.0 -p $PORT" > Procfile

Vous pouvez changer cette ligne pour utiliser un serveur différent. Vous devez l'éditer pour sélectionner votre propre mot de passe admin. $PORT est une variable qui est correctement échappée puisque sa valeur est définie au démarrage. Vous devriez aussi considérer le démarrage de web2py avec gunicorn en utilisant anyserver.py puisque c'est l'un des serveurs web recommandés pour Python.

Finalement le script créé un dépôt Git :

git init
git add .
git add Procfile
git commit -a -m "first commit"

pousse tout sur Heroku, et le démarre :

heroku create
git push heroku master
heroku addons:add heroku-postgresql:dev
heroku scale web=1
heroku open

heroku ici, fait partie des commande shell du SDK Heroku.

Nous remercions Craig Krestiens de Heroku pour cette aide avec ce déploiement.

Déployer sur EC2

Amazon EC2

Amazon Elastic Compute Cloud (Amazon EC2) est un service web qui fournit de la capacité de calcul redimensionnable dans le cloud. C'est l'un des clouds les plus populaires. Beaucoup d'autres plateformes cloud fonctionnent sur EC2. Vous pouvez démarrer n'importe quelle application sur EC2 en créant et déployant une image disque. Amazon fournit alors les API pour répliquer l'image en partageant une partie du système de fichiers.

Une description du process complet est en dehors du scope de ce livre, mais, supposant que vous avez un compte existant Amazon EC2, vous pouvez utiliser le hub Turnkey pour trouver et déployer une image web2py prête à l'emploi :

https://hub.turnkeylinux.org/amazon/launch/web2py/

Une fois votre image déployée, vous pouvez vous y connecter comme un VPS normal et vous pouvez les gérer (backup/restore/copy) via l'interface web EC2.

Déployer sur Google App Engine

Google App Engine

Il est possible d'exécuter du code web2py sur Google App Engine (GAE)[gae] , incluant du code DAL.

GAE supporte deux versions de Python : 2.5 et 2.7. web2py supporte les deux mais utilise 2.5 par défaut (ceci peut changer dans le futur). Regardez dans le fichier "app.yaml" décrit ci-après pour les détails de configuration.

GAE supporte aussi une base de données Google SQL (compatible avec MySQL) et Google NoSQL (référé à "Datastore").

web2py supporte les deux, et en effet, peut se connecter aux deux en même temps, en utilisant les chaînes de connexion détaillées dans le Chapitre 6.

La plateforme GAE fournit de nombreux avantages vis à vis des solutions normales d'hébergement :

  • Facilité de déploiement. Google fait une abstraction complète de l'architecture sous-jacente.
  • Scalabilité. Google va répliquer votre application autant de fois que nécessaire pour déservir toutes les requêtes concurrentes.
  • On peut choisir entre une base de données SQL et NoSQL (ou les deux ensemble).

Mais aussi quelques désavantages :

  • Pas d'accès en lecture ou écriture au système de fichiers.
  • Pas de HTTPS à moins d'utiliser le domaine appspot.com avec un certificat Google.
  • Ce ne sont pas toutes les librairies Python qui sont supportées (scipy est une librairie qui n'est pas supportée aujourd'hui par exemple).

Alors que Google Cloud SQL est une base de données MySQL habituelle, Google Datastore a quelques désavantages spécifiques :

  • Pas de transactions typiques ; eventual_consistency plutôt qu'une consistance forte pour les requêtes.
  • Pas de requêtes complexes au datastore. En particulier, il n'y a pas d'opérateur JOIN, LIKE, et DATE/DATETIME.
  • Pas de sous-requêtes multiples OR à moins qu'elles n'utilisent un seul et même champ.

Nous fournissons ici une vue rapide de GAE en nous axant sur les problèmes spécifiques à web2py, nous vous invitons à aller voir la documentation officielle en ligne de GAE pour de plus amples détails.

Attention : Vous devez exécuter la disctribution source de web2py, pas une distribution binaire.

Configuration

Il y a deux fichiers de configuration dont vous devez faire attention :

web2py/app.yaml
web2py/queue.yaml
web2py/index.yaml

app.yaml et queue.yaml sont plus facilement créés en utilisant les fichiers de template app.example.yaml et queue.example.yaml comme points de départ. index.yaml est créé automatiquement par le logiciel de déploiement de Google.

app.yaml a la structure suivante (a été raccourci en utilisant ...) :

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

app.example.yaml (lorsqu'il a été copié vers app.yaml) est configuré pour déployer l'application web2py welcome, mais pas admin ou example. Vous devez remplacer web2py avec l'id de l'application que vous avez utilisée lors de l'enregistrement avec Google App Engine.

url: /(.+?)/static/(.+) indique à GAE de servir les fichiers statiques de votre application directement, sans appeler la logique web2py, pour optimiser la vitesse.

url:.* indique à web2py d'utiliser gaehandler.py pour toutes les autres requêtes.

La session skip_files: est une liste d'expressions régulières pour les fichiers qui n'ont pas besoin d'être déployés sur GAE. En particulier les lignes :

 (applications/(admin|examples)/.*)|
 ((admin|examples|welcome).(w2p|tar))|

indiquent à GAE de ne pas déployer les applications par défaut, sauf pour l'application de base welcome. Vous pouvez ajouter plus d'applications à ignorer ici.

Sauf pour l'application id et la version, vous n'avez probablement pas besoin d'éditer app.yaml, bien que vous pourriez vouloir exclure l'application welcome.

Le fichier queue.yaml est utilisé pour configurer les listes de tâches GAE.

Le fichier index.yaml est automatiquement généré lorsque vous exécuter votre application localement en utilisant le serveur d'application GAE (le serveur web fourni avec le SDK Google). Il contient quelque chose comme :

indexes:
- kind: person
  properties:
  - name: name
    direction: desc

Dans cet exemple, il indique à GAE de créer un index pour la table "person" qui sera utilisée pour trier en ordre alphabétique inverse par "name". Vous ne pourrez pas chercher et trier les enregistrement dans votre application sans les index correspondants.

Il est important de toujours exécuter vos applications localement avec le serveur d'application et d'essayer toutes les foncitonnalités de votre application, avant le déploiement. Ce sera important pour des raisons de test, mais aussi pour générer automatiquement le fichier "index.yaml". Occasionnellement vous pouvez vouloir éditer ce fichier et effectuer un nettoyage, tel que supprimer les entrées dupliquées.

Exécution et deployment

Linux

Nous supposons ici que vous avez installé le SDK GAE. Au moment où ce livre est écrit, GAE fonctionne sur Python 2.5.2. Vous pouvez exécuter votre application depuis le dossier "web2py" en utilisant la commande appserver :

python2.5 dev_appserver.py ../web2py

Ceci démarrera le serveur d'application et vous pouvez exécuter votre application à l'URL :

http://127.0.0.1:8080/

Pour pouvoir uploader votre application sur GAE, assurez-vous que vous avez édité le fichier "app.yaml" comme expliqué avant et défini le bon id d'application. Ensuite, lancez :

python2.5 appcfg.py update ../web2py
Mac, Windows

Sur Mac et Windows, vous pouvez également utiliser le Google App Engine Launcher. Vous pouvez télécharger le logiciel depuis ref.[gae] .

Choisissez [File][Add Existing Application], définissez le chemin au chemin racine du dossier web2py, et pressez le bouton [Run] dans la barre d'outils. Après que vous ayez testé que cela fonctionne localement, vous pouvez le déployer sur GAE en cliquant simplement sur le bouton [Deploy] sur la barre d'outils (supposant que vous avez un compte).

image

Sur GAE, les tickets/erreurs web2py sont aussi loggés dans la console d'administration de GAE où les logs peuvent être récupérés et parsés en ligne.

image

Configurer le gestionnaire

Le fichier gaehandler.py est responsable de la distribution des fichiers sur GAE et il a quelques options. Voici les valeurs par défaut :

LOG_STATS = False
APPSTATS = True
DEBUG = False

LOG_STATS va logger le temps de distribuer les pages dans les logs GAE.

APPSTATS va activer les appstats GAE qui fournissent des statistiques de profil. Elles seront rendues disponibles à l'URL :

http://localhost:8080/_ah/stats

DEBUG définit le mode debug. En pratique, il n'y a pas de différence, à moins d'être explicitement cochée via gluon.settings.web2py_runtime.

Eviter le système de fichiers

Sur GAE, vous n'avez pas accès au système de fichiers. Vous ne pouvez pas ouvrir n'importe quel fichier pour écrire dedans.

Pour cet usage, sur GAE, web2py stocke automatiquement tous les fichiers uploadés dans le datastore, que le champ "upload" ait ou n'ait pas un attribut uploadfield.

Vous devriez également stocker les sessions et les tickets dans la base de données et vous devez être explicite :

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

Le code ci-dessus vérifie que vous soyez en train de fonctionner sur GAE, se connecte à BigTable, et indique à web2py de stocker les sessions et les tickets dedans. Il se connecte à une base de données sqlite sinon. Ce code est déjà dans l'application de base dans le fichier "db.py".

Memcache

Si vous le préférez, vous pouvez stocker les sessions en memcache également :

from gluon.contrib.gae_memcache import MemcacheClient
from gluon.contrib.memdb import MEMDB
cache.memcache = MemcacheClient(request)
cache.ram = cache.disk = cache.memcache
session.connect(request,response,db=MEMDB(cache.memcache.client))

Notez que sur GAE le cache.ram et cache.disk ne devraient pas être utilisés, donc nous les faisons pointer sur cache.memcache.

Problèmes de Datastore

Alors que Google Cloud SQL fonctionne comme une base de données SQL régulière, et est en effet basé sur mysql (au moment où ce livre est écrit), Google Datastore présente des différences significatives.

Consistence éventuelle

La eventual consistency offerte par Google Datastore mérite une attention spéciale. Dans le Datastore, les transactions qui se basent sur les clés primaires du Datastore, ou un groupe d'entités partagées, offrent une forte consistence : toute transaction ultérieure ou requête verra le résultat de toute transaction antérieure pour la même clé ou groupe d'entités. D'un autre côté, les requêtes qui ne se basent pas sur les clés primaires ou sur les groupes d'entités partagées offrent seulement eventual consistency : les données nouvelles ou modifiées seront éventuellement rendues disponibles aux requêtes, après un délai non spécifié qui est généralement de l'ordre de quelques secondes.

web2py ne se base pas sur les clés primaire du Datastore, si sur les groupes d'entités partagées. La déclaration :

id = db.table.insert(field1=value1, field2=value2)

a l'effet suivant :

  • Une nouvelle ligne est insérée dans la table db.table ; la ligne a un champ id dont la valeur est assignée par web2py. La ligne a également une clé primaire Datastore, mais cette clé primaire n'est pas id et n'est pas présentée.
  • Les indices de db.table sont mis à jour. En particulier, l'index id est mis à jour, donc la nouvelle ligne est eventually disponible pour les requêtes sur db.table.id.

Seulement une fois ces deux étapes terminées, la nouvelle donnée est disponibles pour les requêtes par id. En particulier, le paradigme commun web2py suivant va fail :

def insert():
    form = SQLFORM(db.table)
    if form.process().accepted:
        session.flash = T('The data has been inserted.')
        redirect(URL('view', args=[form.vars.id]))
    return dict(form=form)

def view():
    row = db.table(request.args(0))
    if row is None:
        session.flash = T('Data not found')
        redirect(URL(index))
    form = SQLFORM(db.table, record=row, readonly=True)
    return dict(form=form)

Une fois qu'un utilisateur visite la page insert et insère des données, l'utilisateur est redirigé vers la page view. Lors de l'utilisation du Google Datastore, souvent la donnée juste insérée n'est pas trouvée dans le contrôleur view. Pourquoi ?

Ce qu'il se passe est cela. Dans le contrôleur insert, la donnée est insérée dans la base de donnée et la transaction est complétée. Ensuite, de manière asynchrone et après que la transaction soit complète, Google Datastore reconstruit les indices, incluant l'index sur le champ id de db.table. Lorsque l'utilisateur accède au contrôleur view il n'y a aucune garantie que l'index sur db.table.id inclut déjà l'objet de la nouvelle donnée, et du coup, l'utilisateur se retrouve souvent avec le message "Data not found".

Manque de JOINs

Le manque d'opérations JOIN et généralement de fonctionnalité relationnelle du Datastore requiert de supprimer les JOINs des requêtes web2py et dénormaliser la base de données.

Google App Engine supporte quelques types de champ spéciaux, tels que ListProperty et StringListProperty. Vous pouvez utiliser ces types avec web2py en utilisant l'ancienne syntaxe suivante :

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

ou la nouvelle syntaxe équivalente :

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

Dans les deux cas, le champ "tags" est un StringListProperty donc ses valeurs doivent être des listes de chaînes, en lien avec la documentation GAE. La seconde notation doit être préférée car web2py va traiter le champ de façon plus intelligente dans le contexte de formulaires et car cela fonctionnera avec des bases de données relationnelles également.

De la même manière, web2py supporte list:integer et list:reference qui se mappent en ListProperty(int).

Les types list sont présentés plus en détail dans le Chapitre 6.

Les migrations de base de données

Une bonne pratique pour les migrations utilisant Google AppEngine est la suivante. AppEngine supporte de multiples versions de code. Utiliser une version de code (e.g. version1) pour le site visible par les utilisateurs, et une autre version de code (e.g. version 2) pour le code admin. Dans app.yaml pour la version 2, déclarez le gestionnaire comme suit (supposant que Python 2.7 est utilisé) :

- url: .*
  script: gaehandler.wsgiapp    # WSGI (Python 2.7 only)
  secure: optional
  login: admin

La clause login: admin assure que seuls les admins puissent utiliser la version 2. Dans la chaîne de connexion à la base de données, spécifiez migrate_enabled=False. Pour effectuer une migration, il est mieux de désactiver l'accès concurrent à la base de données pour la migration. Procédez comme suit :

  • Ajoutez un fichier nommé DISABLED au répertoire le plus haut de la verison 1 de l'application (le répertoire parent des répertoires /controllers, /views, etc.), et uploadez la nouvelle version vers GAE. Ceci désactivera la version 1, et affichera un message "Le site est temporairement coupé pour maintenance".
  • Uploadez la version 2 du code avec migrate_enabled=True dans la chaîne de connexion à la base et visitez la depuis un compte admin, en déclenchant la migration.
  • Uploadez la version 2 du code avec migrate_enabled=False pour désactiver les migrations futures.
  • Supprimez le fichier nommé DISABLED de la version 1, et uploadez le code de la version 1. Ceci rend le site de nouveau visible pour tous.

GAE et https

Si votre application a un id "myapp" votre domaine GAE est

http://myapp.appspot.com/

et il peut également être accessible via HTTPS

https://myapp.appspot.com/

Dans ce cas, il utilisera un certificat "appspot.com" fourni par Google.

Vous pouvez enregistrer une entrée DNS et utiliser n'importe quel autre nom de domaine que vous possédez pour votre application mais vous ne serez pas en mesure d'utiliser HTTPS dessus. Au moment où ce livre est écrit, c'est une limitation GAE.

 top