<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Geekfault &#187; wsgi</title>
	<atom:link href="http://geekfault.org/tag/wsgi/feed/" rel="self" type="application/rss+xml" />
	<link>http://geekfault.org</link>
	<description>If it doesn&#039;t segfault, you&#039;re doing it wrong.</description>
	<lastBuildDate>Tue, 08 May 2012 17:13:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>nginx et python &#8211; le perfect setup</title>
		<link>http://geekfault.org/2010/03/01/nginx-et-python-le-perfect-setup/</link>
		<comments>http://geekfault.org/2010/03/01/nginx-et-python-le-perfect-setup/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 11:47:32 +0000</pubDate>
		<dc:creator>pistache</dc:creator>
				<category><![CDATA[internet]]></category>
		<category><![CDATA[Logiciel]]></category>
		<category><![CDATA[fastcgi]]></category>
		<category><![CDATA[gunicorn]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[planet-libre]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[serveur]]></category>
		<category><![CDATA[unix]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[wsgi]]></category>

		<guid isPermaLink="false">http://geekfault.org/?p=2272</guid>
		<description><![CDATA[Python est devenu assez à la mode, pour le développement web, ces dernières années avec l&#8217;arrivée de frameworks web comme Django, Pylons et web.py. On le voit souvent utilisé sur apache2, avec mod_python, ou sur des serveurs Python dediés à ça (CherryPy, etc) Je cherche depuis longtemps un moyen de faire du Python sur HTTP. [...]
<h3>Si vous avez aimé ce post...</h3><ol>
<li><a href='http://geekfault.org/2011/03/24/devbox-kvmlibvirt-perfect-setup/' rel='bookmark' title='Devbox KVM+Libvirt perfect setup.'>Devbox KVM+Libvirt perfect setup.</a></li>
<li><a href='http://geekfault.org/2010/04/09/maildir-mbox-la-migration/' rel='bookmark' title='Maildir mbox &#8211; La migration'>Maildir mbox &#8211; La migration</a></li>
<li><a href='http://geekfault.org/2010/04/09/filtrer-ses-mails-un-cauchemard/' rel='bookmark' title='Filtrer ses mails ! Un cauchemard !'>Filtrer ses mails ! Un cauchemard !</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><a href="http://geekfault.org/2010/03/01/nginx-et-python-le-perfect-setup/nginx_logo/" rel="attachment wp-att-2406"><img style=' float: left; padding: 4px; margin: 0 7px 2px 0;' align="left"   src="http://geekfault.org/wp-content/uploads/2010/03/nginx_logo-300x206.gif" alt="" title="nginx_logo" width="300" height="206" class="alignleft size-medium wp-image-2406" /></a></p>
<p><strong>Python est devenu assez à la mode, pour le développement web, ces dernières années avec l&#8217;arrivée de frameworks web comme </strong><a title="Django" href="http://djangoproject.com/"><strong>Django</strong></a><strong>, </strong><a title="Pylons" href="http://pylonshq.com/"><strong>Pylons</strong></a><strong> et </strong><a title="webpy" href="http://webpy.org"><strong>web.py</strong></a><strong>. On le voit souvent utilisé sur apache2, avec mod_python, ou sur des serveurs Python dediés à ça (CherryPy, etc)</strong></p>
<p>Je cherche depuis longtemps un moyen de faire du Python sur HTTP. J&#8217;ai mis pas mal de temps mais je pense avoir trouvé une configuration sympa.</p>
<p>Je vous propose ici d&#8217;essayer <a title="nginx" href="http://nginx.org/">nginx</a>, un serveur HTTP (entre autres), très performant et  <strong>beaucoup</strong> plus léger/rapide que apache2. Il est très efficace pour faire du reverse-proxy (c&#8217;est à dire être utilisé en frontal/load-balancer devant un serveur HTTP, et lui transmettre les requêtes en faisant du cache et le cas échéant du load-balancing).</p>
<p><!--more--></p>
<p>La configuration de nginx est très simple, et la documentation est très complète : <a href="http://wiki.nginx.org/Main">wiki.nginx.org</a>. L&#8217;installation de nginx est facile, il est inclus dans la plupart des distributions actuelles, donc à vos emerge/apt-get.</p>
<p> <code>apt-get install nginx</code>
<p>nginx ne gère pas le python nativement. Il y a plusieurs solutions cependant :</p>
<ul>
<li>Faire du proxy vers un serveur qui gère le python, comme apache2. On perd la majeure partie de l&#8217;intêret de nginx, cependant.</li>
<li>Faire du FastCGI vers un serveur FastCGI. C&#8217;est la solution la plus fréquente, en utilisant fcgi.py ou <a title="Flup" href="http://trac.saddi.com/flup">flup</a></li>
<li>Faire du WSGI. C&#8217;est un protocole de communication entre serveurs HTTP et applications Python. Solution aussi assez fréquente.</li>
<li>Faire du proxy vers un serveur qui gère le python de manière <strong>native</strong>. C&#8217;est une solution moins fréquente mais très pratique.</li>
</ul>
<p>Avant même de penser à choisir une des solutions ici présentes, il faut les comparer entre elles.</p>
<p><strong><br /> </strong></p>
<p><strong> </strong></p>
<p><strong> </strong></p>
<p><strong> </strong></p>
<h4>nginx =&gt; apache2 =&gt; python</h4>
<p>L&#8217;ennui de cette solution c&#8217;est : apache2. Utiliser nginx pour forwarder à apache2 est presque inutile. Presque ? Oui, car on peut dire à nginx de garder les fichiers statiques pour lui. Cependant, le gain de cette méthode est assez minime, et ce n&#8217;est intéressant que pour faire du load balancing sur plusieurs apaches.</p>
<p><strong><br /> </strong></p>
<p><strong> </strong></p>
<p><strong> </strong></p>
<p><strong> </strong></p>
<h4>le FastCGI</h4>
<p>FastCGI c&#8217;est une norme, pour faire traiter du contenu dynamique par une application externe. Donc le support du FastCGI tout seul est inutile, il faut aussi quelque chose pour gérer derrière.</p>
<p>J&#8217;ai testé <a href="http://trac.saddi.com/flup">flup</a>, un framework Python léger. Le résultat : ça marche, méééé c&#8217;est lent. Je tournais autour de 35/50 requêtes par seconde, et l&#8217;utilisation en mémoire vive était phénoménale, 150 mégas pour 1000 requêtes. (bien sur ces chiffres n&#8217;auront de valeur que lorsqu&#8217;ils seront comparés aux autres).</p>
<p>FastCGI est sympa pour faire une configuration &#8220;vite&#8221;. C&#8217;est d&#8217;ailleurs la seule solution viable, à ma connaissance pour faire du PHP sur nginx, à part le proxy vers un autre serveur HTTP.</p>
<p>Bref, FastCGI ne m&#8217;a pas convaincu. Et mes tests avec les autres solutions m&#8217;ont donné raison. Toujours avec moi ? On passe au WSGI !</p>
<p><strong><br /> </strong></p>
<p><strong> </strong></p>
<p><strong> </strong></p>
<p><strong> </strong></p>
<h4>le WSGI</h4>
<p> <em>WSGI is the Web Server Gateway Interface. It is a specification for web servers and application servers to communicate with web applications (though it can also be used for more than that). It is a Python standard, described in detail in PEP 333.</em>
<p>le WSGI, c&#8217;est le standard pour le Web Python. Il n&#8217;y a qu&#8217;à consulter <a title="WSGI" href="http://wsgi.org">wsgi.org</a> pour se rendre compte que c&#8217;est tout un monde. Il consiste en un ensemble de normes (nom de variables, par exemple) pour permettre au serveur d&#8217;exécuter  un fichier codé en Python.</p>
<p>Le standard pour le WSGI sur les serveurs HTTP grand public, c&#8217;est le mod_wsgi de apache2. Il a été porté sur nginx, mais c &#8216;est une #@!## infame, qui ne marche que sur les vieilles versions et qui n&#8217;a pas été mise à jour depuis bientôt 2 ans et demi (cf. <a href="http://hg.mperillo.ath.cx/nginx/mod_wsgi/">hg.mperillo.ath.cx/nginx/mod_wsgi/</a>). J&#8217;ai obtenu des performances d&#8217;environ 133 requêtes par seconde avec, ce qui est déjà mieux que FastCGI, pour ceux qui suivent <img src='http://geekfault.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Par contre, dès que j&#8217;ai voulu avancer un peu avec (toucher à la configuration, et surtout, passer à une version supérieure de nginx), j&#8217;ai été confronté à ses limites (et aux #@!## de faute de frappe dans le code source)</p>
<p>D&#8217;autant plus que l&#8217;auteur de mod_wsgi pour apache2 et l&#8217;auteur de mod_wsgi pour nginx reconnaissent ses limitations, respectivement <a href="http://blog.dscpl.com.au/2009/05/blocking-requests-and-nginx-version-of.html">ici</a> et <a href="http://blogg.ingspree.net/blog/2007/11/24/nginx-mod-wsgi-vs-fastcgi/">ici</a>. Le premier document est très intéressant pour comprendre un peu l&#8217;architecture de nginx et ses différences avec apache2. En effet, le mod_wsgi pour nginx a été codé à partir de celui pour apache2, et il subit les différences d&#8217;architecture entre apache2 et nginx.</p>
<p>Attention, je ne remet absolument pas à cause le WSGI ici, juste l&#8217;implémentation de mod_wsgi dans nginx.</p>
<p>Parce que justement, un projet super récent, c&#8217;est&#8230;. uWSGI ! uWSGI sert à combler le manque de support du WSGI dans nginx, en rajoutant un module, meilleur que mod_wsgi. Il nécessite donc de recompiler nginx. Il y a encore une autre solution, nommée gunicorn, qui ne nécessite pas de recompiler nginx. C&#8217;est un serveur HTTP minimal qui gère le WSGI nativement. uwsgi offre plus de fonctionalitées que gunicorn, mais gunicorn a l&#8217;air assez dynamique (dans le développement), et est plus facile à utiliser (pas de recompilation, support de Django natif). Au niveau des performances, je me suis amusé à faire cette comparaison :</p>
<p> </p>
<p><div class="wp-caption alignnone" style="width: 434px"><img title="Comparaison des performances entre uwsgi et gunicorn en fonction du niveau de parallélisme" src="http://pistaches.net/~pistache/stuff/patch1.png" alt="Comparaison des performances entre uwsgi et gunicorn en fonction du niveau de parallélisme" width="424" height="313" /><p class="wp-caption-text">Comparaison des performances entre uwsgi et gunicorn en fonction du niveau de parallélisme</p></div>
<p> </p>
<p>On constate plusieurs choses :</p>
<ul>
<li>Les performances augmentent en utilisant 2 workers à la place d&#8217;un, mais au delà, le gain est ridicule, puis inexistant. Le nombre 2 est dépendant du nombre de coeurs de la machine, et le nombre de workers à utiliser ne sera pas le même sous un bi-socket octocore, bien évidemment. Les meilleures performances étaient obtenues en utilisant gunicorn+3workers.</li>
<li>La différence est vraiment minime, gunicorn est en moyenne légèrement au dessus de uwsgi.</li>
<li>Ces tests sont réalisés sur une application <strong>Django</strong>. Les performances augmentent en utilisant des frameworks plus légers comme <strong>web.py</strong>, bien évidemment.</li>
</ul>
<p>uwsgi dispose de plus de fonctionalitées, comme par exemple une interface d&#8217;administration de serveur wsgi dans Django. Je vais ici expliquer l&#8217;installation et la configuration des deux solutions.</p>
<p>Ici je vais couvrir l&#8217;installation et la configuration de uwsgi et gunicorn avec <strong>Django</strong> et <strong>web.py</strong>, même si c&#8217;est bien sur adaptable avec d&#8217;autres frameworks supportant le <strong>wsgi </strong>(liste sur <a href="http://wsgi.org/wsgi/Frameworks">wsgi.org/wsgi/Frameworks</a>).</p>
<h3>Installation et configuration de <strong>uwsgi</strong></h3>
<p><strong><br /> </strong></p>
<h4>Sous Debian :</h4>
<p>1) Rajouter les dépots source, par exemple :</p>
<p> <code>deb-src http://ftp.gr.debian.org/debian/ squeeze main contrib non-free</code>
<p>2) Télécharger la source</p>
<p> <code>apt-get source nginx<br />
tar -zxvf nginx_*.orig.tar.gz<br />
cd nginx-*<br />
tar -zxvf nginx_*.debian.tar.gz</code>
<p>2) Télécharger uwsgi</p>
<p> <code>cd ..</p>
<p>wget http://projects.unbit.it/downloads/uwsgi-0.9.4.2.tar.gz</p>
<p>tar -zxvf uwsgi-0.9.4.2.tar.gz</code>
<p>3) Modifier le paquet source de nginx pour y ajouter uwsgi. Il faut éditer le fichier nginx-*/debian/rules, trouver le bloc qui commence par ./configure et ou se trouvent toutes les options de compilation, et y ajouter</p>
<p> <code>--add-module=$(CURDIR)/../uwsgi-0.9.4.2/nginx/</code>
<p>$(CURDIR)/../uwsgi-0.9.4.2/nginx/ est le chemin vers le dossier du module dans la source de uwsgi</p>
<p>4) maintenant, dans le dossier de la source de nginx, faites (pas besoin de root) :</p>
<p> <code>dpkg-buildpackage</code>
<p>5) installer le .deb généré ainsi (en tant que root maintenant)</p>
<p> <code>dpkg -i ../nginx-*.deb</code>
<p>6) Dernière étape : compiler uWSGI (le binaire en lui-même, qui va communiquer avec nginx par le biais du module)</p>
<p> <code>cd ../uwsgi-0.9.4.2/ &amp;&amp; make &amp;&amp; make install &amp;&amp; cp uwsgi_params /etc/nginx/</code>
<p>Cette dernière commande (le cp uwsgi_params&#8230;) sert à placer un fichier de configuration du module uwsgi exemple dans le repertoire de nginx.</p>
<h4>Sous les autres distributions :</h4>
<p>Il faut télécharger la source de nginx et de uwsgi, compiler nginx avec le module uwsgi (dans le répertoire nginx de la source de uwsgi), puis compiler uwsgi.﻿</p>
<p>On a maintenant un serveur uWSGI et un nginx avec  le module uwsgi. Il va maintenant falloir dire à nginx de transférer les requêtes au serveur uWSGI, avec une commande uwsgi_pass. Par exemple, dans la section server d&#8217;un de vos vhost (/etc/nginx/sites-enabled/default, par exemple, sous Debian) :</p>
<p> <code>location / {<br />
include uwsgi_params;<br />
uwsgi_pass unix:///tmp/uwsgi.sock;<br />
}</code>
<p>Il faut ensuite créer une application WSGI, par exemple, pour le framework web.py :</p>
<p> <code>import web</p>
<p>urls = (<br />
    '/.*', 'hello',<br />
    )</p>
<p>class hello:<br />
    def GET(self):<br />
        return "Hello, world."</p>
<p>application = web.application(urls, globals()).wsgifunc()<br />
applications = {'/': application }</code>
<p>Pour Django :</p>
<p> <code>import django.core.handlers.wsgi<br />
application = django.core.handlers.wsgi.WSGIHandler()<br />
applications = {'/': application }</code>
<p>Nommez ce fichier wsgi.py et placez le dans le repertoire ou vous allez placer votre code (pour Django, dans le repertoire du projet, évidemment).  Si vos nerfs n&#8217;ont pas encore lâché ici, allez prendre une petite pause de 5 minutes à titre préventif, et reprenez.  Maintenant, on va démarrer un serveur uWSGI. Placez vous dans le répertoire de votre fichier wsgi.py, et faites :</p>
<p> <code>uwsgi -s /tmp/uwsgi.sock -C -w wsgi -p2 -L</code>
<p>Explications : le -s indique ou placer le socket (ça pourrait aussi être une adresse au format hôte:port, pour un usage distant). Le -w wsgi indique de charger le fichier wsgi(.py), le -p2 indique de démarrer 2 processus, et le -L désactive l&#8217;envoi des requêtes à stdout (=&gt;flood).</p>
<p>Si vous allez à l&#8217;adresse de votre serveur web, vous devriez voir votre application Python en face de vous ! Vous voilà en train d&#8217;utiliser <strong>nginx+uwsgi+python</strong>.</p>
<p>En annexe, je rappelle que uwsgi est aussi utilisable avec apache et lighttpd.</p>
<p>Aussi, dans le dossier source de uwsgi, vous trouverez des templates pour un module d&#8217;administration de Django, à installer dans le repertoire des templates de l&#8217;administration de Django (si, si).</p>
<h3>Installation et configuration de gunicorn</h3>
<p>gunicorn est <strong>carrèment</strong> plus facile à installer. Il est codé en python, donc pas de compilation, seulement un <strong>easy_install</strong> :</p>
<p> <code>easy_install gunicorn</code>
<p>La configuration est tout aussi facile, il suffit de cela dans la configuration du vhost dans nginx :</p>
<p> <code>location / {<br />
proxy_pass http://unix:/tmp/gunicorn.sock<br />
}</code>
<p>On démarre ensuite gunicorn. Pour Django, gunicorn dispose d&#8217;un support intégré, il vous suffit de vous placer dans le répertoire de votre projet, puis :</p>
<p> <code>gunicorn_django -b unix:/tmp/gunicorn.sock --workers=2</code>
<p>Pour web.py, il faut créer un module :</p>
<p> <code>import web</p>
<p>urls = (<br />
    '/.*', 'hello',<br />
    )</p>
<p>class hello:<br />
    def GET(self):<br />
        return "Hello, world."</p>
<p>application = web.application(urls, globals()).wsgifunc()</code>
<p>Il faut ensuite démarrer le serveur Gunicorn. En étant dans le repertoire ou se trouve le module web.py (le fichier donc le code vient avant, là, au-dessus, oui), :</p>
<p> <code>gunicorn -b unix:/tmp/gunicorn.sock --workers=2 wsgi</code>
<p>Je vous invite à consulter ce lien : <a href="http://gunicorn.org/tuning.html"><span style="color: #000000;">gunicorn.org/tuning.html</span></a>, pour vous permettre d&#8217;augmenter les performances de gunicorn.</p>
<h3>Annexe</h3>
<h4><span style="font-weight: normal;">Fichiers statiques</span></h4>
<p> <code>location /admin-media {alias /usr/lib/pymodules/python2.5/django/contrib/admin/media;}</p>
<p>location /static {root /var/www/byteflow;}</code>
<p>Ces deux lignes, à rajouter après le &#8220;location /&#8221; avec uwsgi_pass ou proxy_pass (selon votre configuration) indiquent à nginx de traiter les fichiers statiques directement. Il faut bien évidemment adapter à votre configuration.</p>
<h4><span style="font-weight: normal;">Munin</span></h4>
<p><span style="font-weight: normal;">Vous pouvez trouver des plugins nginx pour munin <a href="http://muninexchange.projects.linpro.no/?search=&amp;cid=54&amp;pid=51">ici</a>. Je n&#8217;ai pas trouvé de plugin munin pour uwsgi/gunicorn, mais je pourrais peut-être en faire un si je trouve le temps.</span></p>
<h4><span style="font-weight: normal;">Plusieurs serveurs</span></h4>
<p><span style="font-weight: normal;">Si par hasard vous avez plusieurs serveurs gunicorn/uwsgi répartis sur plusieurs machines , vous pouvez les utiliser en déclarant une section </span></p>
<p> <code><span style="font-weight: normal;">upstream <em>nom</em> {} </span></code>
<p><span style="font-weight: normal;">dans nginx, contenant les adresses des serveurs distant (hôte:port ou emplacement du socket) et en utilisant &#8220;<em>nom</em>&#8221; comme argument à <em>uwsgi_pass</em> et <em>proxy_pass</em>.</span></p>
<h3>Conclusion</h3>
<p>Ces deux architectures sont très pratiques pour faire tourner du python dans un serveur web, ici nginx. uwsgi dispose de plus de fonctionalités, mais la plupart (template d&#8217;admin Django, memory profiling) pourraient facilement être ajoutées à gunicorn, qui est beaucoup plus léger à installer/configurer/maintenir (2 commandes et 3 lignes de configuration dans nginx). J&#8217;ai les deux personellement sur mon serveur, j&#8217;ai eu à alterner entre les deux pour écrire cet article, et c&#8217;est pas bien dur, un daemon à arrêter, un autre à démarrer, et changer une ligne dans la configuration de nginx.</p>
<h4>Liens</h4>
<p><a href="http://wsgi.org">wsgi.org</a></p>
<p><a href="http://gunicorn.org/">gunicorn.org/</a></p>
<p><a href="http://projects.unbit.it/uwsgi/">projects.unbit.it/uwsgi/</a></p>
<p><a href="irc://irc.freenode.org/#gunicorn">irc.freenode.org/#gunicorn</a></p>
<p><a href="http://nginx.org/">nginx.org/</a></p>
<p><a href="http://djangoadvent.com/">djangoadvent.com/</a></p>
<p><a href="http://www.django-fr.org/">www.django-fr.org/</a></p>
<p><a href="http://www.djangoproject.com/">www.djangoproject.com/</a></p>
<p><a href="http://webpy.org/">webpy.org/</a></p>
<p>Je tiens à remercier la communauté Geek{node|fault}, le chan irc #gunicorn, les développeurs de gunicorn qui sont très aimables, ainsi que Amandarn qui m&#8217;a aidé pour certains détails.</p>
<p><h3>Si vous avez aimé ce post...</h3><ol>
<li><a href='http://geekfault.org/2011/03/24/devbox-kvmlibvirt-perfect-setup/' rel='bookmark' title='Devbox KVM+Libvirt perfect setup.'>Devbox KVM+Libvirt perfect setup.</a></li>
<li><a href='http://geekfault.org/2010/04/09/maildir-mbox-la-migration/' rel='bookmark' title='Maildir mbox &#8211; La migration'>Maildir mbox &#8211; La migration</a></li>
<li><a href='http://geekfault.org/2010/04/09/filtrer-ses-mails-un-cauchemard/' rel='bookmark' title='Filtrer ses mails ! Un cauchemard !'>Filtrer ses mails ! Un cauchemard !</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://geekfault.org/2010/03/01/nginx-et-python-le-perfect-setup/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

