Laisser un commentaire

Installer et configurer PHP pour Apache2

Apache et PHP ont déjà quelques années de vie conjugale. Et comme tout couple qui dure, ils sont très complémentaires et savent se réinventer. Nous allons voir comment configurer PHP de deux manières différentes : en tant que module Apache, mod_php, et et en tant que serveur FastCGI via php-fpm.

Nous allons partir sur la dernière version majeure de PHP : PHP 7. Si vous êtes sous Debian 8 ou Ubuntu 16.04 (ou plus), c’est la version par défaut dans les dépôts. En revanche, si vous avez une version de Linux antérieure, il va falloir utiliser des dépôts alternatifs.

Versions de PHP

Les versions de PHP évoluent régulièrement et leur support est limité dans le temps.

La version 7.0 de PHP n’est pas la dernière en date, elle n’est d’ailleurs même plus supportée. Sachez néanmoins qu’indépendamment du support propre à PHP, les mises à jour de sécurité sont aussi assurées par les distributions Linux.

Ainsi, bien que PHP 7.0 ne soit plus supporté officielement par PHP, c’est la version de PHP par défaut dans Ubuntu 16.04 et les mises à jour de sécurité sont donc automatiquement effectuées par la distribution ‒ dans la mesure où celle-ci est activement supportée.

À moins donc que vous ne souhaitiez installer une version en particulier, votre version de PHP dépendra de celle intégrée à votre distribution.

Toutes les commandes exécutées ci-après sont exécutées en root. Pensez donc à passer en root avec sudo su ou à faire précéder vos commandes de sudo.

# ajout des dépôts alternatifs pour PHP7 au besoin
add-apt-repository ppa:ondrej/php
apt-get update

Voilà, quelle que soit votre version, vous disposez des dépôts qui vont bien. Commençons l’installation.

Installation avec mod_php

On attaque avec le plus simple, la manière legacy d’utiliser PHP avec Apache. Je dis legacy car le moteur de PHP intégré à Apache en tant que module était à l’origine la seule manière de faire jusqu’à Apache 2.4.

Cette méthode est tout à fait fonctionnelle et est plus simple à paramétrer. Elle possède néanmoins deux défauts. Le premier est inhérent au fait que php est intégré en tant que module Apache : les processus ont tendances à gonfler l’usage de la mémoire. Rien d’insurmontable cependant, il suffit de les forcer à se renouvelle de temps à autre. J’explique la marche à suivre dans la partie sur l’optimisation des performances de l’article dédié à la configuration d’Apache.

Le second défaut est que le MPM prefork requis pour utiliser PHP en tant que module Apache n’est pas compatible avec HTTP/2. Si ces deux petits défauts ne vous contrarient pas, alors le mod_php est une solution parfaitement viable.

apt install libapache2-mod-php

Si PHP n’est pas déjà installé, il le sera automatiquement, ainsi que toutes les autres dépendances.

Installation avec php-fpm

php-fpm consiste en un serveur PHP autonome avec lequel Apache communique via fastCGI. On utilisera un socket unix pour la communication.

Nous allons installer php-fpm et configurer Apache afin qu’il transmette les requêtes à PHP lorsqu’elles nécessitent d’être interprétées.

# installation de php-fpm et ses dépendances
apt install php-fpm

# Apache a besoin du module proxy_fcgi pour transmettre les requêtes
a2enmod proxy_fcgi

# on précise de manière globale à Apache (pour tous les vhost)
# de transmettre toutes requête destinée à php
a2enconf php7.0-fpm

Voilà, ce n’est pas plus compliqué que cela. Lors de l’installation de php-fpm, PHP s’installe évidemment avec ses dépendances.

Ubuntu nous a déjà bien mâché le travail en fournissant des fichiers de configuration tout prêts. La configuration de la connexion se trouve dans /etc/php/7.0/fpm/pool.d/www.conf. La partie qui nous intéresse ici est celle qui définit la création du socket d’écoute :

listen = /run/php/php7.0-fpm.sock

C’est cette information qui est reprise dans le fichier de configuration pré-rempli qui nous est automatiquement fournit et que l’on active via a2enconf php7.0-fpm. On peut inspecter son contenu (et le créer s’il n’existe pas). Il se trouve à /etc/apache2/conf-available/php7.0-fpm.conf :

<IfModule !mod_php7.c>
    # Enable http authorization headers
    SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1

    <FilesMatch ".+\.ph(p[3457]?|t|tml)$">
        SetHandler "proxy:unix:/run/php/php7.0-fpm.sock|fcgi://localhost"
    </FilesMatch>
    <FilesMatch ".+\.phps$">
        # Deny access to raw php sources by default
        # To re-enable it's recommended to enable access to the files
        # only in specific virtual host or directory
        Require all denied
    </FilesMatch>
    # Deny access to files without filename (e.g. '.php')
    <FilesMatch "^\.ph(p[3457]?|t|tml|ps)$">
        Require all denied
    </FilesMatch>
</IfModule>

On voit bien que la directive SetHandler reprend (en ajoutant une option pour écouter via une connexion TCP au besoin) notre socket UNIX. Il est également possible de ne pas activer la configuration et d’ajouter ceci directement dans le ou les vhost concernés (cela permet de ne pas l’activer pour tous…).

Il y a un autre paramètre très important dans ce fichier de configuration, il s’agit de pm.max_children. Cette valeur défini combien de processus PHP peuvent s’exécuter en paralèlle. Si vous atteingnez le maximum, vous verrez un warning dans les logs de PHP et la requête sera placée en file d’attente.

La valeur est à 5 par défaut. Si vous avez du trafic, il peut être judicieux d’augmenter cette valeur. Évidemment, c’est à mettre en relation avec la charge CPU et la RAM disponible car des processus supplémentaires ont un coût.

L’impact CPU ainsi que mémoire dépend fortement des traitements effectués avec PHP. Cependant, la plupart du temps, la RAM constitue le goulot d’étranglement. On regarde donc quelle est la taille en mémoire d’un processus :

ps aux | grep php-fpm
root     29968  0.0  1.6 357700 32088 ?        Ss   08:48   0:00 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf)
www-data 29973  0.1  4.2 442596 84560 ?        S    08:48   0:02 php-fpm: pool www
www-data 29974  0.1  4.1 440552 82268 ?        S    08:48   0:02 php-fpm: pool www
www-data 30270  0.0  2.8 361248 57264 ?        S    08:58   0:00 php-fpm: pool www

On constate que plusieurs processus enfants sont en cours d’exécution avec plus ou moins de mémoire utilisé. Le plus important consomme 84560 KB de mémoire, soit ≈85MB. Sachant que ce serveur dispose de 2GB de RAM et qu’il doit aussi exécuter MySQL, je peux allouer dans les 800MB maximum. J’augmente donc cette valeur à 10 (ou 9 pour être prudent).

Vous voyez peut-être des warnings dans les logs.

WARNING: [pool www] server reached pm.max_children setting (10), consider raising it

Dans ce cas, vous gagneriez certainement à augmenter pm.max_children. Cependant, si vous n’avez plus beaucoup de RAM disponible, soit vous déchargez votre serveur de la base de données par exemple, soit il faudra envisager un serveur avec plus de RAM.

Réglages du php.ini

Quelle que soit la méthode choisie, PHP est désormais fonctionnel ! Il reste néanmoins quelques paramètres à modifier dans le fichier de configuration de PHP, le php.ini. Vous trouverez ce fichier dans /etc/php/7.0/apache ou /etc/php/7.0/fpm.

Je vous mets ci-dessous, une liste des paramètres intéressants que l’on doit ou que l’on a souvent à changer.

;;;;;;;;;;;;;;;;;;;;
; Language Options ;
;;;;;;;;;;;;;;;;;;;;
# Mettre à Off si possible
# les shorts open tags sont les balises d'ouvertures courtes de php <?
# elles posent des problèmes de compatibilité avec XML
# puisque c'est justement la syntaxe des balises XML.
short_open_tag = Off

# le safe_mode est déprécié dans PHP 5.3 et a été supprimé de 5.4
# par conséquent, laissez cette option à Off
safe_mode = Off

# l'open basedir permet de limiter les dossiers dans lequel php va pouvoir interpréter des données
# cela est plus sur car un attaquant ne pourra pas utiliser php pour lire des données en dehors de dossier spécifié
; http://php.net/open-basedir
open_basedir = /var/www

# désactiver des fonctions de php
# il y en a certaines de désactivées par défaut, toutefois, certaines fonctions sont souvent utilisées pour
# exploiter une faille dans l'application et mettre en place des backdors, ainsi, si votre application ne
# les utilise pas, il est judicieux de les désactiver.
# à celle par défaut listées ci-dessous, on peut ajouter :
# exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signasignal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,

;;;;;;;;;;;;;;;;;
; Miscellaneous ;
;;;;;;;;;;;;;;;;;
# ajoute ou non la signature de php dans les en-tête http
# ce n'est pas primordial, mais personne n'a à savoir
# que nous utilisons php, ni la version utilisée
expose_php = Off

;;;;;;;;;;;;;;;;;;;
; Resource Limits ;
;;;;;;;;;;;;;;;;;;;
# ces options concernent les performances de php
# les réglages par défaut doivent normalement suffire pour la plupart des cas

# temps maximum d'exécution après lequel un script sera arrêté
# sauf cas particulier, vos script ne devraient jamais mettre
# plus de quelques secondes à s'exécuter
max_execution_time = 30

# temps maximum qu'un script peut mettre pour recevoir des données input
# au delà de ce laps de temps, le traitement est stoppé
# il peut-être ici judicieux d'augmenter ce temps si vous proposer l'upload de fichier
max_input_time = 60

# taille de mémoire qu'un script peut consommer
# de même, si vous avez un formulaire d'upload de gros fichiers
# il se peut que vos scripts aient besoin de plus de mémoire
memory_limit = 512M

# affiche les erreurs php
# mettre à On pendant le dev, Off en production
display_errors = On

# affiche les erreurs qui ont cours durant la séquence de démarrage de php
# mettre à Off en production. Il peut être intéressant de les mettre
# à On en développement pour le débogage.
display_startup_errors = Off

# consigne les erreurs dans les fichiers de log
# je conseille de toujours laisser à On
log_errors = On

;;;;;;;;;;;;;;;;;
; Data Handling ;
;;;;;;;;;;;;;;;;;
# taille maximale que php accepte pour les données en POST
# dans le cas d'un upload de fichier par post,
# cette valeur sera aussi à modifier si vous voulez uploader des fichiers de plus de 8M
post_max_size = 8M

;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;

# peut-être désactivé si vous ne comptez pas autoriser l'upload de fichiers
file_uploads = On

# définit la taille maximale de fichiers que l'on peut uploader
# cette taille ne peut toutefois pas dépasser celle de post_max_size
upload_max_filesize = 2M

;;;;;;;;;;;;;;;;;;;
; Module Settings ;
;;;;;;;;;;;;;;;;;;;

# règle le fuseau horaire
date.timezone = Europe/Paris

[mysqlnd]
# on désactive les stats de mysqlnd si ce n'est pas déjà le cas
mysqlnd.collect_statistics = Off
mysqlnd.collect_memory_statistics = Off

# On passe ensuite aux réglages du cache opcode

# On l'active évidemment si ce n'est pas déjà fait
opcache.enable=1

# la mémoire totale que vous souhaitez allouer au cache
opcache.memory_consumption=128

# taille en MB dédiée à la sauvegarde des chaînes de caractères répétées dans le code
# si une chaîne est répétées 1000 fois, php la remplace par un pointeur partagé par tous les process FPM
opcache.interned_strings_buffer=8

# ce réglage définit la durée de validité du cache opcode en secondes
# si un fichier est modifié, l'ancien sera utilisé tant que le cache n'est pas renouvelé
# s'il est à 0, le code est vérifié à chaque requête, ce qui génère bcp d'appels système
opcache.revalidate_freq=0

# s'il faut vérifier ou non les timestamps de fichiers pour le réglage précédent
# S'il est désactivé (valeur à 0), il faudra manuellement redémarrer php ou invalider le cache
opcache.validate_timestamps=0

# le nombre total de fichiers qui peuvent être mis en cache (nombre premier compris entre 223 à 130987)
# si le nombre choisi n'est pas premier, alors la valeur sera le nombre premier supérieur
# le mieux est que tous les fichiers puissent tenir
# vous pouvez évaluer le nombre de fichiers de chaque projet avec find : find . -type f -print | grep php | wc -l
# https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.max-accelerated-files
opcache.max_accelerated_files=32531

# permet de conserver ou non les commentaires dans la cache généré
# certains scripts se servent des commentaires,
# si ce n'est pas le cas, autant le mettre à 0
opcache.save_comments=1

Le cache opc est vraiment un élément central dans la performance des applications PHP. Surtout si vous avez de nombreux fichiers (CMS, framework, e-commerce…). Le revalidation du cache nécessite forcément des ressources, c’est pourquoi il est possible de paramétrer aussi finement l’intervale de revalidation ou même de le désactiver.

D’une manière générale, si vous avez une application à fort trafic et qu’il s’agit de code ne recevant pas de mise à jour automatique, vous pouvez désactiver la revalidation et inclure un redémarrage de PHP dans votre processus de deploy (ou le faire manuellement). Cela invalide le cache et vous bénéficiez des meilleures performances.

En revanche, si vous utilisez un CMS tel que WordPress, un moteur de e-commerce tel que PrestaShop ou tout autre application recevant des mises à jour automatiques, il est plus prudent d’avoir une invalidation du cache automatique. Vous pouvez cependant sans problème mettre une valeur assez élevée (10, 30 ou même 60 secondes).

Vous voudrez certainement monitorer l’usage du cache de votre serveur afin d’en optimiser au mieux les réglages. De nombreux dashboards existent à cet effet. J’apprécie pour ma part tout particulièrement opcache-status du créateur même de PHP, Monsieur Lerdorf en personne. Il s’installe d’un simple copier-coller et ne nécessite aucune configuration.

impression d'écran de opcache-status
Impression d'écran de l'interface du dahboard opcache status
# une fois dans le répertoire où vous voulez l'installer
wget https://raw.githubusercontent.com/rlerdorf/opcache-status/master/opcache.php

Vous n’avez ensuite plus qu’à y accéder comme vous le feriez pour n’importe quelle page.

Prise en compte des modifications

Il faut maintenant redémarrer PHP pour que ces modifications soient prises en compte.

service php7.0-fpm restart

# ou si vous avez installé php en tant que module apache,
# c'est ce dernier qu'il faut redémarrer
service apache2 restart

Il n’y a rien de plus à configurer pour PHP. You’re good to go!.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```