Outils pour utilisateurs

Outils du site


new_php_version

Ajouter une version de php sur telnew

Instances de php multiples sur un seul serveur

Quand plusieurs applications utilisent un même langage il peut y avoir des conflits de configuration, ou des besoins de versions différents. Installer php sur un serveur se fait souvent via les paquets, mais on peut aussi le faire via les sources dans des répertoires isolés du reste du système.

“FastCGI est une technique permettant la communication entre un serveur HTTP et un logiciel indépendant” 1). C'est en FastCGI qu'on va utiliser php pour notre problème. PHP-FPM (FastCGI Process Manager) est une implémentation de ce protocole pour php.

Installation

Choisir sa version de php

C'est la première étape. En essayant d'installer différentes versions je me suis rendu compte qu'il était plus facile d'en installer certaines que d'autres. Les problèmes viennent principalement du fait que les anciennes versions étaient calibrées pour des architectures i386 (32 bits). Il y a aussi des failles comme hearthbleed pour laquelle openssl a été patché et n'est plus rétrocompatible.

C'est plus une question de solutions à trouver et de temps à investir, mais il devrait être possible d'installer n'importe quelle version si on s'en donne les moyens.

Installer la version

Sur telnew, les versions compilées de php se trouvent dans /opt/php5*/ et les sources de ces compilations dans /opt/src/php.

La procédure est en générale simple :

  1. récupérer les sources sur http://museum.php.net/
  2. dézipper
  3. appliquer d'éventuels patchs
  4. configurer la compilation
  5. compiler

Ce genre de guide peut servir d'exemple : http://www.lolart.net/blog/ez-publish/php-fastcgi-fpm-et-apache.

Des procédures déjà écrites

Au lieu d'essayer de trouver soit-même la procédure pour installer une version de php, il y a de fortes chances que quelqu'un l'ait déjà écrite dans un dockerfile. Exemple : https://registry.hub.docker.com/u/tommylau/php-5.2/dockerfile/.

Configurer la compilation

Le .configure est une étape clef puisque c'est d'elle dont dépend le futur interpréteur. Il vaut mieux être exhaustif sur les options de compilation. Ce qui a été utilisé pour les différentes versions sur le serveur :

php5.2 :

./configure \
--with-config-file-path=/opt/php52/conf \
--with-config-file-scan-dir=/opt/php52/conf.d \
--with-fpm-conf="/opt/php52/php-fpm.conf" \
--enable-fastcgi \
--enable-fpm \
--enable-force-cgi-redirect \
--enable-mbstring \
--enable-pdo \
--enable-soap \
--with-curl \
--with-gd \
--with-jpeg-dir=/usr \
--with-png-dir=/usr \
--with-mysql \
--with-mysqli \
--with-openssl \
--with-pdo-mysql \
--with-readline \
--with-zlib \
--prefix=/opt/php52 \
--with-gettext

php5.4+ :

./configure \
--with-config-file-path=/opt/php54/conf \
--with-config-file-scan-dir=/opt/php54/conf.d \
--enable-fpm \
--enable-mbstring \
--enable-pdo \
--enable-soap \
--with-curl \
--with-gd \
--with-jpeg-dir=/usr \
--with-png-dir=/usr \
--with-mysql \
--with-mysqli \
--with-openssl \
--with-pdo-mysql \
--with-readline \
--with-zlib \
--prefix=/opt/php54 \
--with-fpm-user=www-data \
--with-fpm-group=www-data \
--with-gettext \
--enable-sockets \
--with-iconv-dir=/opt/local

php7.0+ :

./configure \
--with-config-file-path=/opt/php70/conf \
--with-config-file-scan-dir=/opt/php70/conf.d \
--prefix=/opt/php70 \
--with-pdo-pgsql \
--with-zlib-dir \
--with-freetype-dir \
--enable-mbstring \
--with-libxml-dir=/usr \
--enable-soap \
--enable-calendar \
--with-curl \
--with-mcrypt \
--with-zlib \
--with-gd \
--with-pgsql \
--disable-rpath \
--enable-inline-optimization \
--with-bz2 \
--with-zlib \
--enable-sockets \
--enable-sysvsem \
--enable-sysvshm \
--enable-pcntl \
--enable-mbregex \
--enable-exif \
--enable-bcmath \
--with-mhash \
--enable-zip \
--with-pcre-regex \
--with-pdo-mysql \
--with-mysqli \
--with-mysql-sock=/var/run/mysqld/mysqld.sock \
--with-jpeg-dir=/usr \
--with-png-dir=/usr \
--enable-gd-native-ttf \
--with-openssl \
--with-fpm-user=www-data \
--with-fpm-group=www-data \
--with-libdir=/lib/x86_64-linux-gnu \
--enable-ftp \
--with-kerberos \
--with-gettext \
--with-xmlrpc \
--with-xsl \
--enable-opcache \
--enable-fpm \
--no-create \

Libs non trouvées

S'il y a des erreurs lors de la configuration type lib.a/lib.o introuvable, il faut installer les paquets correspondants avec le suffixe -dev. Il n'y a pas de méthode ad hock pour les trouver.

Si après l'installation ces bibliothèques ne sont toujours pas trouvées, il faut indiquer leur path lors de la configuration. Exemple :

  1. -with-jpeg-dir=/usr

Mais ce chemin ne doit pas être le dossier dans lequel est contenu la bibliothèque. Il doit être le répertoire parent de ce dossier. Avec cette option de configuration, libjpeg.a peut se trouver dans /usr/lib.

Autre possibilité : faire un lien symbolique de la bibliothèque à l'endroit où elle devrait se trouver (typiquement au même niveau que le dossier x86_64-linux-gnu).

Scripts d'init

À chaque changement de configuration, on ne va pas tuer le processus à la main, puis relancer la versions de php. Même problème lorsque le serveur boot : il faut que les versions soient lancées aussi.

Pour ça on va créer des scripts d'init qui start, stop, reload, etc. chaque version indépendamment.

Exemple avec le fichier /etc/init.d/php-56-fpm (honteusement pompé sur internet) :

#! /bin/sh
### BEGIN INIT INFO
# Provides:          php-56-fpm
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts php-56-fpm
# Description:       starts the PHP FastCGI Process Manager daemon
### END INIT INFO
php_fpm_BIN=/opt/php56/sbin/php-fpm
php_fpm_CONF=/opt/php56/etc/php-fpm.conf
php_fpm_PID=/opt/php56/var/run/php-fpm.pid
php_opts="--fpm-config $php_fpm_CONF"
wait_for_pid () {
        try=0
        while test $try -lt 35 ; do
                case "$1" in
                        'created')
                        if [ -f "$2" ] ; then
                                try=''
                                break
                        fi
                        ;;
                        'removed')
                        if [ ! -f "$2" ] ; then
                                try=''
                                break
                        fi
                        ;;
                esac
                echo -n .
                try=`expr $try + 1`
                sleep 1
        done
}
case "$1" in
        start)
                echo -n "Starting php-fpm "
                $php_fpm_BIN $php_opts
                if [ "$?" != 0 ] ; then
                        echo " failed"
                        exit 1
                fi
                wait_for_pid created $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed"
                        exit 1
                else
                        echo " done"
                fi
        ;;
        stop)
                echo -n "Gracefully shutting down php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -QUIT `cat $php_fpm_PID`
                wait_for_pid removed $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed. Use force-quit"
                        exit 1
                else
                        echo " done"
                       echo " done"
                fi
        ;;
        force-quit)
                echo -n "Terminating php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -TERM `cat $php_fpm_PID`
                wait_for_pid removed $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed"
                        exit 1
                else
                        echo " done"
                fi
        ;;
        restart)
                $0 stop
                $0 start
        ;;
        reload)
                echo -n "Reload service php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -USR2 `cat $php_fpm_PID`
                echo " done"
        ;;
        *)
                echo "Usage: $0 {start|stop|force-quit|restart|reload}"
                exit 1
        ;;
esac

Il ne reste plus qu'à le déclarer auprès du système pour qu'il lance / arrête automatiquement quand il le faut :

# update-rc.d /etc/init.d/php-56-fpm

Ensuite, ce genre de commandes devraient suffire après un changement de configuration :

# /etc/init.d/php-56-fpm reload

Les pools ou comment gérer plusieurs configuration

Si tout s'est bien passé jusqu'à maintenant, on a résolu le problème de plusieurs installations php sur un même serveur, mais pas encore celui des configurations différentes pour une version donnée.

Pour ce faire, on va décommente la ligne

include=/opt/php56/etc/pool.d/*.conf

du fichier /opt/php56/etc/php-fpm.conf.

Ensuite il suffit de rajouter ces lignes dans un fichier, disons /opt/php56/etc/pool.d/treso.conf :

[treso]

user = treso
group = treso

chdir = /
security.limit_extensions = .php

listen = /opt/php56/var/run/treso.sock
listen.owner = www-data
listen.group = www-data
;listen.mode = 0660

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

En plus, cela permet d'isoler les exécutions de php les unes par rapport aux autres. Si un CMS vulnérable est infecté, celui qui aura la main sur le serveur ne pourra a priori lancer des commandes qu'avec les droits de l'utilisateur et donc ne pas endommager les autres sites.

Par contre, il faut toujours bien penser à donner les droits de lecture/écriture à l'utilisateur sur ses fichiers (et non pas à www-data).

En n'oubliant pas de :

# /etc/init.d/php-56-fpm reload

L'utilisation de ce socket est décrit dans la partie suivante.

Configuration avec Apache

Normalement, ce sont ces lignes dans un vhost qui doivent renseigner à Apache quelle version utiliser :

Alias /virtualpath /home/apps/treso/www
FastCgiExternalServer /home/apps/treso/www -socket /opt/php56/var/run/treso.sock
AddHandler php-fcgi .php
Action php-fcgi /virtualpath

Cependant, je n'ai pas réussi à faire en sorte que cela fonctionne sans que les autres fichiers (css, js, png…) ne passent aussi par les sockets. cf : http://whocares.de/fastcgiexternalserver-demystified/8/ et http://stackoverflow.com/questions/29827527/php-fpm-apache2-403-error-on-css-js-files.

Du coup, on utilisera ces lignes là :

<FilesMatch \.php$>
    SetHandler "proxy:unix:/opt/php56/var/run/treso.sock|fcgi://localhost"
</FilesMatch>
# /etc/init.d/apache2 reload
1)
wikipedia
new_php_version.txt · Dernière modification: 2017/10/10 11:43 par deldel