L'hébergement virtuel proposant des "tranches" de serveur devient de plus en plus
populaire. Vous trouverez un peu partout des offres comparables à celles proposées
par Gandi ou Slicehost et, de mon point de vue,
elles représentent l'avenir de l'hébergement de sites web.
En regardant les offres de près, le premier réflexe est généralement
d'être effrayé par les tarifs. Surtout en France, où certaines
sociétés laissent croire qu'on peut fournir un hébergement de qualité
pour 2 EUR par mois.
Pour une somme allant de 14 à 20 EUR par mois, vous pourrez trouver un hébergement
disposant de 256 Mo de RAM, un puissance CPU raisonnable et une flexibilité que vous ne
trouverez pas ailleurs. Et vous serez administrateur de votre machine ce qui n'est pas le dernier
des avantages.
256 Mo
Il y a de cela 8 ans, j'avais un vieux PC dans ma cuisine, avec 192 Mo de RAM, un CPU poussif et
un débit montant de 128 kbits/s. Ça ne m'empêchait pas de faire
tourner un serveur web, un serveur de mail. Les début de dotclear.net se sont faits sur
cette machine, à côté du frigo.
Du coup, je me suis dit, qu'il n'y avait aucune raison de ne pas faire fonctionner correctement
un serveur web avec 256 Mo de RAM, un meilleur CPU et une bande passante bien plus confortable.
Afin de tester mon idée, j'ai créé une machine virtuelle avec vmware et une
image Ubuntu server. Cette liste propose un grand nombre d'images de VM
prêtes à l'emploi, ça vous fera gagner du temps si vous voulez faire des
essais.
Au démarrage la machine virtuelle n'utilise que 16% de ses 256 Mo de mémoire. (Avec
uniquement un serveur SSH).
MySQL
Sur Ubuntu, la configuration par défaut de MySQL est réputée pour consommer
un peu trop de ressources. Nous pouvons améliorer ceci en modifiant un peu le fichier
/etc/mysql/my.cnf. Faites en sorte d'avoir ces lignes dans le fichier :
key_buffer = 16K max_allowed_packet = 1M thread_stack = 64K thread_cache_size = 8
sort_buffer_size = 64K read_buffer_size = 256K read_rnd_buffer_size = 256K net_buffer_length = 2K
Si vous n'avez pas l'intention d'installer Dotclear ou plus généralement, ne
comptez pas utiliser InnoDB, désactivez le en ajoutant la ligne :
skip_innodb
Cette configuration me donne une utilisation de mémoire d'environ 30 Mo. Ça
dépendra, bien sûr, de vos applications.
Oublions Apache
De l'ensemble LAMP, nous voulons conserver Linux, MySQL et PHP. Nous allons remplacer Apache par
un autre serveur : Nginx.
Nginx (on dit Engine X) est ce qu'on pourrait appeler la nouvelle génération de
serveurs web. Il peut remplacer Apache dans de nombreuses circonstances. Je vous laisse lire la
présentation du serveur ainsi que ce billet.
Pour installer Nginx, apt, yum ou votre gestionnaire de paquet feront l'affaire. Assurez vous
simplement d'avoir une version 0.7.x. Nous verrons ensuite comment le configurer.
PHP et PHP Xcache
Nginx, contrairement à Apache, ne propose pas d'équivalent à mod_php. Vous
ne pouvez pas non plus utiliser PHP simplement en CGI (tant mieux). Vous allez devoir utiliser
FastCGI et indiquer à Nginx comment y accéder.
Pour faire simple, nous allons installer spawn-fcgi et écrire un script pour lancer nos
processus PHP. Sur Ubuntu/Debian :
apt-get install php5-cgi spawn-fcgi
Le script permettant de lancer les processus PHP est le suivant, vous devez le créer dans
/etc/init.d/php5-fcgi :
#!/bin/sh ### BEGIN INIT INFO # Provides: php5-fcgi # Required-Start: $remote_fs $syslog #
Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 #
Short-Description: PHP5 FastCgi Spawned processes ### END INIT INFO COMMAND=/usr/bin/spawn-fcgi
ADDRESS=127.0.0.1 PORT=9000 USER=www-data GROUP=www-data PHPCGI=/usr/bin/php5-cgi
PIDFILE=/var/run/fastcgi-php.pid RETVAL=0 PHP_FCGI_MAX_REQUESTS=500 PHP_FCGI_CHILDREN=2 start() {
export PHP_FCGI_MAX_REQUESTS PHP_FCGI_CHILDREN $COMMAND -a $ADDRESS -p $PORT -u $USER -g $GROUP -f
$PHPCGI -P $PIDFILE } stop() { /usr/bin/killall -9 php5-cgi } case "$1" in start) start RETVAL=$?
;; stop) stop RETVAL=$? ;; restart|reload) stop start RETVAL=$? ;; *) echo "Usage: fastcgi
{start|stop|restart}" exit 1 ;; esac exit $RETVAL
Pour activer votre script (avec Ubuntu ou Debian) :
update-rc.d php5-fcgi defaults
Maintenant vos processus PHP se lanceront au démarrage. Un petit mot sur deux
paramètres importants. Les processus PHP en CGI ont une tendance connue à planter
de manière régulière. C'est à ceci que sert à la variable
PHP_FCGI_MAX_REQUESTS que nous avons mis à 500. Tous les 500 cycles, chaque processus PHP
sera relancé. Enfin, PHP_FCGI_CHILDREN=2 indique de lancer deux processus PHP. Vous pouvez
en mettre plus mais n'oubliez pas que plus il y en a plus vous consommerez de mémoire.
Une fois en production, vous pourriez constater que c'est un peu lent. L'installation du paquet
php5-xcache améliore grandement les performances de PHP. Consultez le site de Xcache pour en savoir plus. J'ai
doublé la taille du cache (xcache.size dans /etc/php5/conf.d/xcache.ini) mais ne perdez
pas de vue que cette taille est appliquée à chaque processus PHP (soit 2 fois 32M
dans mon cas).
Maintenant, vous pouvez lancer votre processus à l'aide de /etc/init.d/php5-fcgi start.
Notez également que PHP FPM
peut remplacer spawn-fcgi. J'en ai lu le plus grand bien mais ne l'ai pas testé.
Configuration de Nginx
Vous avez bien entendu installé Nginx avec un classique apt-get install nginx.
Nginx pour Ubuntu ou Debian fourni un fichier de configuration pour FastCGI. Nous allons le
compléter. Le fichier est /etc/nginx/fastcgi_params et doit contenir ceci :
fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param
SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param
SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param
REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT
$server_port; fastcgi_param SERVER_NAME $server_name; # PHP only, required if PHP was built with
--enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200; fastcgi_split_path_info
^(.+.php)(.*)$; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED
$document_root$fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
Les derniers paramètres permettent d'utiliser PHP en mode PATH_INFO (à condition de
bien configurer Nginx). Le tout dernier paramètre est le plus important, c'est celui qui
va dire à PHP quel script exécuter.
Maintenant, testons ceci. Créez un fichier /var/www/nginx-default/test.php dans lequel
vous pouvez par exemple mettre un appel à phpinfo() ou ce que vous voulez.
Ensuite, modifiez le fichier /etc/nginx/site-enabled/default (il peut se trouver ailleurs si vous
n'êtes pas sur Ubuntu/Debian). Vous pouvez aussi créer un nouveau fichier, vous
faites comme vous voulez.
server { listen 80; server_name localhost; root /var/www/nginx-default; index index.php
index.html; access_log /var/log/nginx/localhost.access.log; location ~ ^(.+.php)(/.*)?$ {
fastcgi_pass localhost:9000; include /etc/nginx/fastcgi_params; } }
Relancez le serveur avec /etc/init.d/nginx restart et rendez vous sur votre nouveau site pour
pointer sur le fichier test.php. Votre script doit s'exécuter.
Ça ne marche pas ? Vous obtenez le très informatif "No input file
specified" ? Quelque chose est mal configuré. Ça peut être un
problème de permission. Commencez par essayer d'enlever la partie se chargeant
d'interpréter PHP et de charger votre script. Vous pouvez également utiliser strace
pour repérer quelle valeur de SCRIPT_FILENAME est passée à PHP.
Petit bonus : installation de Dotclear
Dotclear, avec la configuration que nous venons de voir, s'installe très bien. Vous
pourrez même l'utiliser en PATH_INFO avec une URL du genre /index.php/...
Peut-on faire mieux ? Yes we can.
Avec Apache, vous aviez la possibilité d'utiliser Mod Rewrite pour lui dire quelque chose
comme : "Si ce n'est pas un fichier ou un répertoire transforme l'URL vers tel script".
Nginx propose des options de rewrite du même type avec une limite de taille ; on ne peut
pas imbriquer les tests. On peut donc tester si l'URL demandée pointe vers un fichier, ou
vers un répertoire mais pas les deux. C'est gênant. On peut s'en sortir en
écrivant une configuration très complexe et difficile à maintenir. Sinon on
peut se rappeler que Nginx n'est pas Apache et qu'il existe peut-être un autre moyen.
Nginx fournit une directive appelée try_files qui va faire exactement ce
que nous ferions avec Apache rewrite en testant si le fichier et le répertoire n'existe
pas.
Donc, pour votre Dotclear à la racine pointant vers /dotclear/index.php, voici la recette
:
location / { try_files $uri $uri/ /dotclear/index.php$uri?$args; }
La même chose est possible pour Wordpress, Drupal et consort.
Quelques tests et conclusion
Ma configuration est une machine virtuelle VMWare avec 256 Mo de RAM utilisant un seul core de
CPU sur un MacBook Pro. C'est sans doute un peu plus que ce dont dispose une part
d'hébergement mais ça donne une idée.
Voici quelques chiffres :
Requêtes (simultanées) Temps par requête Utilisation Mémoire 500 65ms
52% 500 (5) 342ms 56% 500 (20) 1330ms 56% 500 (50) 3436ms 56%
C'est tout à fait honorable pour du PHP avec aussi peu de ressources. L'autre aspect
intéressant est que même avec 50 visiteurs au même moment, vos fichiers
statiques sont servis en moins d'une demie-seconde. Et contrairement à Apache,
l'utilisation mémoire n'augmente pas avec le nombre de requêtes simultanées.
La conclusion est que, oui, avec 256 Mo de RAM, vous pouvez disposer d'un serveur web tout
à fait performant et vous disposez même d'une marge de manoeuvre permettant
d'installer un serveur SMTP et IMAP.
N'étant pas non plus un expert sur le sujet, n'hésitez pas à signaler les
erreurs. Et si vous avez des astuces concernant PostgreSQL avec une telle configuration,
ça m'intéresse.