Passage de Apache + mod_php vers Nginx + PHP-FPM

Contexte

Afin d'améliorer les performances et limiter la consommation mémoire, on va passer une configuration apache + mod_php en nginx + php-fpm.

Ceci va se faire en plusieurs étapes, qui peuvent être mises en place progressivement :

  • Configuration initiale : apache + module mod_php
  • Changement de mode de fonctionnement PHP : apache + php5-fpm
  • Changement de serveur http : nginx + php5-fpm

Note : toutes les commandes sont données ici pour une plate-forme de souche Debian (Debian / Ubuntu), utilisant les commandes apt-get.

Configuration initiale

Installation

L'installation "par défaut" de apache + php s'effectue simplement via les commandes :

apt-get install apache2 apache2-mpm-prefork apache2-utils 

Ceci installe le serveur http apache2 ainsi que les utilitaires (apache2ctl).

On installe ensuite php5 et quelques modules utiles :

php5 php5-gd php5-mysql php5-curl

Le module php5-mysql n'est utile que pour l'utilisation de php avec MySQL (ce qui est souvent le cas).

Paramétrage

Le VirtualHost apache est défini de la manière suivante :

<Virtualhost *:80 >
 ServerAdmin webmaster@localhost
 ServerName lxc.test.local

 DocumentRoot /var/www/lxc

 <Directory /var/www/lxc>
  Options Indexes FollowSymLinks MultiViews
  AllowOverride None
  Order allow,deny
  allow from all

# Inclusion du .htccess de drupal (si necessaire)
  Include /var/www/lxc/.htaccess

 </Directory>
 ErrorLog ${APACHE_LOG_DIR}/error_lxc.log
 LogLevel info
 CustomLog ${APACHE_LOG_DIR}/access_lxc.log combined

</Virtualhost>

Il s'agit d'un site tournant sous Drupal. On inclut le fichier .htaccess qui se trouve à la racine du site, et on positionne le AllowOverride None pour améliorer les performances.
Ceci évite au serveur Apache de parser chaque répertoire à la recherche d'un fichier .htaccess.

Il faut que le module rewrite soit activé. Ceci est fait via :

a2enmod rewrite

Dans cette configuration avec le module mod_php activé, les process apache consomment entre 32 et 55 Mo de RAM.

Tests de charge / top

Avec la configuration par défaut, si on lance une commande de test : ab -c20 -n1000 http://testgeo.test.local/carte, soit un millier de requêtes avec 20 requêtes concurrentes, on observe le comportement suivant :

Tasks:  48 total,  23 running,  25 sleeping,   0 stopped,   0 zombie
%Cpu(s): 80,9 us, 18,1 sy,  0,0 ni,  0,0 id,  0,0 wa,  0,0 hi,  1,0 si,  0,0 st
KiB Mem:   4037712 total,  3786172 used,   251540 free,    73920 buffers
KiB Swap:  4881404 total,    40612 used,  4840792 free,   778428 cached

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND                                                               
  305 mysql     20   0 1310m  83m 7636 S   7,0  2,1   0:02.09 mysqld                                                                
  525 www-data  20   0  326m  36m  22m S   6,7  0,9   0:01.74 apache2                                                               
  522 www-data  20   0  325m  32m  20m R   6,3  0,8   0:01.75 apache2                                                               
  358 www-data  20   0  326m  32m  18m R   6,7  0,8   0:01.41 apache2                                                               
  594 www-data  20   0  326m  32m  18m R   7,0  0,8   0:00.91 apache2                                                               
  600 www-data  20   0  326m  32m  18m R   7,0  0,8   0:00.86 apache2                                                               
  604 www-data  20   0  326m  32m  18m R   7,0  0,8   0:00.86 apache2                                                               
  607 www-data  20   0  326m  32m  18m R   7,0  0,8   0:00.86 apache2                                                               
  592 www-data  20   0  326m  31m  18m R   6,7  0,8   0:00.91 apache2                                                               
  582 www-data  20   0  325m  31m  18m R   6,3  0,8   0:01.56 apache2                                                               
  602 www-data  20   0  325m  30m  18m R   6,7  0,8   0:00.84 apache2                                                               
  605 www-data  20   0  325m  30m  18m R   6,0  0,8   0:00.83 apache2                                                               
  359 www-data  20   0  323m  30m  19m R   5,3  0,8   0:01.75 apache2                                                               
  528 www-data  20   0  323m  30m  20m R   6,7  0,8   0:01.72 apache2                                                               
  595 www-data  20   0  324m  30m  18m R   6,3  0,8   0:00.94 apache2                                                               
  360 www-data  20   0  323m  30m  19m S   6,3  0,8   0:01.76 apache2                                                               
  593 www-data  20   0  324m  30m  18m R   6,3  0,8   0:00.95 apache2                                                               
  588 www-data  20   0  323m  29m  18m R   6,0  0,7   0:01.05 apache2                                                               
  527 www-data  20   0  323m  29m  18m R   6,7  0,7   0:01.37 apache2                                                               
  543 www-data  20   0  323m  29m  18m R   7,0  0,7   0:01.34 apache2                                                               
  554 www-data  20   0  323m  29m  18m S   6,7  0,7   0:01.22 apache2                                                               
  555 www-data  20   0  323m  29m  18m R   6,7  0,7   0:01.23 apache2                                                               
  603 www-data  20   0  323m  28m  18m R   6,3  0,7   0:00.83 apache2                                                               
  606 www-data  20   0  322m  27m  18m R   6,0  0,7   0:00.82 apache2                                                               
  610 www-data  20   0  322m  27m  18m R   6,0  0,7   0:00.71 apache2                                                               
  583 www-data  20   0  320m  26m  18m S   6,0  0,7   0:01.19 apache2                                                               
  589 www-data  20   0  320m  26m  18m S   5,3  0,7   0:01.07 apache2                                                               
  601 www-data  20   0  320m  26m  18m S   7,0  0,7   0:00.87 apache2                                                               
  611 www-data  20   0  320m  26m  18m R   6,7  0,7   0:00.43 apache2                                                               
  340 root      20   0  319m  15m 9844 S   0,0  0,4   0:00.05 apache2 

Le mode prefork utilisé par Apache avec le module mod_php implique de lancer un processus pour traiter chaque requête concurrente. Par défaut, le nombre de clients est limité à 150, ce qui explique ici la présence d'une trentaine de processus, qui se réservent chacun environ 30 Mo de mémoire.

Utilisation de PHP en mode FPM

Le mode FPM (Fast Process Management) de PHP permet de séparer le serveur http et le serveur PHP, à la manière de ce qui est fait pour les serveurs d'application Java.

De ce fait, on allège l'empreinte mémoire du serveur http, et on peut contrôler plus facilement les ressources utilisées par PHP.

Installation

L'installation de PHP5-FPM doit également comprendre la désinstallation du module mpm-prefork pour Apache, et la désactivation du module mod_php.

On installe ainsi le package php5-fpm, le module apache mpm-worker, qui utilise des threads plutôt que des process, et la librairie fastCGI pour Apache.

apt-get install php5-fpm apache2-mpm-worker libapache2-mod-fastcgi
apt-get remove apache2-prefork

a2enmod actions fastcgi alias
a2dismod mod_php

Paramétrage

On peut laisser le paramétrage de PHP5-FPM par défaut, celui-ci étant défini dans plusieurs fichiers :

  • /etc/php5/fpm/php.ini : fichier d'initialisation PHP
  • /etc/php5/fpm/php-fpm.conf : fichier de configuration FPM
  • /etc/php5/fpm/pool.d/www.conf : fichier de configuration du pool par défaut

Il faut par contre ajouter un fichier de configuration pour Apache, afin qu'il utilise maintenant FastCGI pour traiter les fichiers PHP.

Ceci peut être fait soit au niveau de chaque VirtualHost, soit globalement. C'est cette option que je présente ici.

Pour ce faire, on crée un nouveau fichier de configuration /etc/apache2/conf.d/php5-fpm.conf, qui contient :

< ifmodule mod_fastcgi.c >
  AddHandler php5-fcgi .php
  Action php5-fcgi /php5-fcgi
  Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
  FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php5-fpm.sock -pass-header Authorization
</ifmodule>

Le paramétrage utilise ici un socket Unix pour faire la passerelle entre le serveur http et le serveur PHP-FPM.
Le nom du socket est défini dans le fichier du pool php /etc/php5/fpm/pool.d/www.conf :

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /var/run/php5-fpm.sock

Note : on peut également utiliser un port TCP. Si on utilise Apache 2.4, c'est d'ailleurs pour l'instant la seule méthode.

On voit également qu'on pourrait séparer le frontal http et le serveur PHP, en communiquant sur un port réseau entre les 2 machines.

Il faut ensuite redémarrer le serveur PHP et le serveur http :

sudo service php5-fpm start
sudo service apache2 start

L'ordre des commandes n'a pas d'importance, même s'il est préférable de démarrer le serveur PHP en premier, et le "frontal" http par la suite.

Tests de charge / top

Avec le paramétrage par défaut, le serveur http fonctionne avec des threads, beaucoup moins gourmands. On remarque que l'on utilise essentiellement les ressources du serveur php, et très peu celles du serveur http. Au total on a 28 tâches, au lieu des 48 dans la configuration initiale.

Tasks:  28 total,   6 running,  22 sleeping,   0 stopped,   0 zombie
%Cpu(s): 79,9 us, 19,3 sy,  0,0 ni,  0,0 id,  0,0 wa,  0,0 hi,  0,8 si,  0,0 st
KiB Mem:   4037712 total,  3743248 used,   294464 free,    65964 buffers
KiB Swap:  4881404 total,    40632 used,  4840772 free,   893072 cached

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND                                                               
  335 mysql     20   0  804m  84m 7624 S   6,7  2,1   0:10.10 mysqld                                                                
 2471 www-data  20   0  288m  40m  21m R  36,3  1,0   0:06.17 php5-fpm                                                              
 2470 www-data  20   0  281m  33m  21m R  35,9  0,8   0:06.33 php5-fpm                                                              
 2476 www-data  20   0  281m  29m  18m R  35,9  0,7   0:05.29 php5-fpm                                                              
 2477 www-data  20   0  279m  27m  18m R  35,6  0,7   0:04.57 php5-fpm                                                              
 2478 www-data  20   0  276m  24m  18m R  36,9  0,6   0:04.09 php5-fpm                                                              
 2469 root      20   0  273m 5440 1572 S   0,0  0,1   0:00.04 php5-fpm                                                              
 2399 www-data  20   0 1256m 4332 1360 S   1,0  0,1   0:00.21 apache2                                                               
 2398 www-data  20   0 1256m 3824 1208 S   1,0  0,1   0:00.20 apache2                                                               
 2395 root      20   0 78752 3552 1536 S   0,0  0,1   0:00.02 apache2                                                               
  221 root      20   0 10188 2912  612 S   0,0  0,1   0:00.00 dhclient                                                              
  243 root      20   0 50052 2864 2276 S   0,0  0,1   0:00.00 sshd                                                                  
 2396 www-data  20   0 78484 2540  560 S   0,0  0,1   0:00.00 apache2                                                               
 2397 www-data  20   0 78484 2448  464 S   0,0  0,1   0:00.00 apache2  
 

Paramétrage du nombre de clients PHP

Dans le fichier/etc/php-fpm.d/www.conf

pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

On peut calculer la mémoire utilisée par chaque process fils de PHP-FPM :

ps -ylC php-fpm --sort:rss

La valeur de pm.max_children peut être calculée comme suit :

pm.max_children = RAM dédié au serveur web/php / (taille maximale d'un process)

Par exemple si on a 4 Go de RAM pour PHP, et une taille maximale de 80 Mo :

pm.max_children = 4000 / 80 = 50

Le reste du paramétrage est alors (en dynamic) :

pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 20
pm.max_spare_servers = 35
pm.max_requests = 500

Remplacement du serveur apache par nginx

Une fois que php5-fpm est en place et activé, il est assez simple d'installer et de configurer nginx en lieu et place de apache. A compléter !

Installation

Paramétrage

Tests de charge / top

Catégorie