fastcgi permet d'allouer un pool de processus php; l'interpréteur sera donc chargé en permanence dans la mémoire, et on gagne donc le temps d'allocation / déallocation de l'exe.

La première étape est d'utiliser PHP-FPM le process manager Fastcgi spécialisé pour PHP. Par chance, dotdeb.org maintient aussi un package php-fpm, mais seulement à partir de la version 5.3 de php (pour les autres version en 5.2, il faudra patcher manuellement).

Dans un autre billet, je vous avais indiqué comment rajouter les sources provenant de dotdeb. Nous allons utiliser le même procédé pour ajouter les packages de php 5.3 (avec support FPM). On édite /etc/apt/sources.list et on rajoute à la fin:

deb http://php53.dotdeb.org stable all
deb-src http://php53.dotdeb.org stable all

On installe ensuite le serveur apache, avec le package mpm-worker, qui permet d'accélérer apache; ce package n'est pas utilisé avec les installations php en module, à cause d'incompatibilités entre le module php et le modèle de thread d'Apache. Toutefois, vu que nous allons installer php en fastcgi, nous pouvons nous permettre d'installer ce module!

Host:/home/User# apt-get install apache2 apache2-mpm-worker libapache2-mod-fastcgi

Une note au sujet de libapache2-mod-fastcgi: ce module existe bien dans les repositories debian, mais celui ci est noté comme étant non-free. Vérifiez donc que dans votre fichier sources.list, vous acceptiez les packages non-free:

deb http://ftp.fr.debian.org/debian/ lenny main non-free contrib
deb-src http://ftp.fr.debian.org/debian/ lenny main non-free contrib

Une fois l'installation d'Apache effectuée, nous allons configurer mod_fastcgi. Le principe est simple: nous allons simuler le comportement d'un programme cgi sur une url spécifique grâce à mod_actions. Tous les appels vers des fichiers php vont être redirigés sur une url spécifique, interne, qui sera le point d'entrée au manager de processus php. On édite donc /etc/apache2/mods-available/fastcgi.conf:

<IfModule mod_fastcgi.c>
AddHandler php5-fcgi .php # Tous les fichiers .php sont processés par "php5-fcgi"
Action php5-fcgi /php-fcgi # Le handler php5-fcgi redirige tout sur /php-fcgi, une url interne
<Location /php-fcgi> # On édite les règles pour cette url /php-fcgi
Order Deny,Allow # Par défaut, on n'accepte aucun client
Deny from All
Allow from env=REDIRECT_STATUS # sauf pour les clients provenant d'url internes
Options ExecCGI # Cette url a l'autorisation d'exécuter des CGI
SetHandler fastcgi-script # Et est gérée par le script de fastcgi
</Location>
</IfModule>

Le dernier SetHandler peut sembler abscons, et c'est normal, la configuration n'est pas terminée. Nous y reviendrons un peu plus loin. Il faut d'abord activer les modules dans apache:

Host:/home/User# a2enmod actions
Host:/home/User# a2enmod fastcgi

On doit maintenant installer PHP-FPM et toutes les librairies annexes nécessaires (cette liste varie selon les besoins, j'ai mis les plus communs ici:

Host:/home/User# apt-get install php5-fpm php5-mysql php5-mysql

Les fichiers de configurations de FPM se situent dans le dossier /etc/php5/fpm/. Nous allons légèrement modifier la configuration par défaut, pour la remplacer par celle ci:

;;;;;;;;;;;;;;;;;;;;;
; FPM Configuration ;
;;;;;;;;;;;;;;;;;;;;;
; All relative paths in this configuration file are relative to PHP's install
; prefix.
; Include one or more files. If glob(3) exists, it is used to include a bunch of
; files from a glob(3) pattern. This directive can be used everywhere in the
; file.
;include=/etc/fpm.d/*.conf
;;;;;;;;;;;;;;;;;;
; Global Options ;
;;;;;;;;;;;;;;;;;;
[global]
; Pid file
; Default Value: none
pid = /var/run/php5/fpm.pid
; Error log file
; Default Value: /var/log/php5-fpm.log
error_log = /var/log/php5/fpm.log
; Log level
; Possible Values: alert, error, warning, notice, debug
; Default Value: notice
;log_level = debug
; If this number of child processes exit with SIGSEGV or SIGBUS within the time
; interval set by emergency_restart_interval then FPM will restart. A value
; of '0' means 'Off'.
; Default Value: 0
;emergency_restart_threshold = 0
; Interval of time used by emergency_restart_interval to determine when
; a graceful restart will be initiated.  This can be useful to work around
; accidental corruptions in an accelerator's shared memory.
; Available Units: s(econds), m(inutes), h(ours), or d(ays)
; Default Unit: seconds
; Default Value: 0
;emergency_restart_interval = 0
; Time limit for child processes to wait for a reaction on signals from master.
; Available units: s(econds), m(inutes), h(ours), or d(ays)
; Default Unit: seconds
; Default Value: 0
;process_control_timeout = 0
; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging.
; Default Value: yes
;daemonize = yes
;;;;;;;;;;;;;;;;;;;;
; Pool Definitions ;
;;;;;;;;;;;;;;;;;;;;
include=/etc/php5/fpm/pools/*.conf

Cela nous permet d'avoir des configurations spécialisées pour chaque pool de serveur (et idéalement, on souhaiterait avoir un pool différent par virtual host, ce qui nous permet aussi de séparer les processus par utilisateur; toutefois étant donné que l'installation est pour un serveur personnel, je ne m'attacherai pas trop à la sécurité des process php, et mettrait tout dans le même pool pour les besoins de l'exemple).

On édite notre première configuration de notre premier pool de process, que l'on va mettre dans /etc/php5/fpm/pools/www-data.conf. Le fichier est le suivant:

[www-data]
listen = /var/run/php5/pools/www-data.sock
listen.owner = www-data
listen.group = www-data
listen.mode  = 0666
user         = www-data
group        = www-data
pm                   = dynamic
pm.max_children      = 50
pm.start_servers     = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests      = 0
request_slowlog_timeout = 0
slowlog                 = /var/log/php5/pools/www-data.slow.log
catch_workers_output = yes
php_flag[display_errors]      = off
php_admin_value[error_log]    = /var/log/php5/pools/www-data.log
php_admin_flag[log_errors]    = on
php_admin_value[memory_limit] = 32M

Note: N'oubliez pas de créer les dossiers /var/run/php5/pools/, /var/log/php5/ et /var/log/php5/pools/ afin que php-fpm puisse écrire les sockets et les logs.

On constate plusieurs choses dans ce fichier de configuration de pool. Premièrement (et le plus important) c'est la directive listen, qui va indiquer sur quel file socket le pool de process sera disponible.

On remarque aussi qu'on peut choisir avec quel utilisateur exécuter les scripts php: c'est un gage de sécurité supplémentaire, il est donc ainsi possible de faire tourner php sur plusieurs utilisateurs différents (un pour chaque site).

Les variables commençant par pm sont les variables du process manager: elles indiquent le nombre de processus php à démarrer, le nombre maximum de process acceptables, etc...

Enfin, pour chaque pool, vous pouvez surcharger certaines variables de la configuration de php. Pour plus d'informations sur les variables de configurations des pools, je vous conseille de lire la documentation de PHP-FPM.

Il ne nous reste plus qu'à faire le lien entre le pool de process créés et le site web qui va les utiliser: pour cela nous avons la directive "FastCgiExternalServer", que nous allons mettre au niveau de la configuration d'un virtual host:

Host:/home/User# vim /etc/apache2/sites-available/default

Il ne nous reste plus qu'à inscrire la ligne suivante dans le virtual host:

FastCgiExternalServer /var/www/php-fcgi -socket /var/run/php5/pools/www-data.sock

L'url en /var/www/php-fcgi n'est pas anodine (rappelez vous la configuration de fastcgi.conf précédente). L'option -socket permet d'indiquer quelle socket (et donc le pool) fastcgi devra utiliser pour ce site.

Nous en avons fini avec la configuration. Il ne nous reste plus qu'à vérifier que tout fonctionne; on va créer ce bon vieux /var/www/info.php avec, comme contenu:

<?php phpinfo(); ?>

On redémarre les serveurs:

Host:/home/User# /etc/init.d/php5-fpm start
Host:/home/User# apache2ctl restart

Si tout va bien vous devriez obtenir la page php info sur votre navigateur. Après avoir fait un petit Apache Bench entre une Dédibox V1 avec php en module et une Dédibox V3 avec php en fastcgi, j'obtiens un gain de performance de l'ordre de 30 a 50%, alors que la dédibox V3 dispose d'environ 25% de moins de puissance brute.