Ziirish's Home :: Blog

Ziirish's Pub

 
 

Certains l'auront peut-être remarqué, d'autres le savent, d'autres non. Remarquer quoi ? Savoir quoi ? Que ce blog a déménagé !

En effet, les nouvelles offres de Online (alias Free) m'ont fait de l'oeil, et je n'ai pas pu résister à l'idée d'avoir une de ces box pour y héberger des services gourmands en bande passante et/ou nécessitant de la plus haute disponibilité possible.

J'y ai donc migré progressivement ce blog. Malheureusement, la bécane est vraiment pas lourde, et ça se sent au niveau des perfs !

"nu", je suis passé de 1,5s sur mon serveur @home à 3s sur la box pour générer la même page PHP ! J'ai donc "tweaké" la box en ajoutant un pooler de connexions SQL et le module PHP xcache pour retomber à peu près à 2s. Correct, mais pas encore idéal ;)

Alors déjà, quelle est mon architecture, et comment faire mieux ?

Je suis assez fan du serveur web nginx , ce dernier contrairement à Apache ne gère pas nativement le PHP, il ne fait que reverse-proxy et refile le bébé à un backend prévu pour. C'est en réalité une bonne chose puisque nous allons pouvoir utiliser les php-fpm .

Qu'est ce que ça change ?

Ça nous permet d'avoir une persistance des sessions PHP et ainsi d'économiser le temps de lancement de ce dernier !

Malheureusement, ce dernier n'est pas disponible sur debian lenny pour PHP 5.2...

L'idée était donc d'utiliser un dépôt alternatif pour installer PHP 5.3 et ainsi profiter de php-fpm.

Cette fois je suis à 1,9s. C'est pas encore le top :(

C'est là que je me souviens avoir discuté en entretien pour une grande société de e-commerce de memcached et de son module PHP memcache .

Problème, je n'ai pas trouvé le moyen de le faire fonctionner avec PHP 5.3... Seule solution, revenir à PHP 5.2, et ainsi perdre php-fpm...

Et là, en explorant un peu le site de ce dernier, je découvre des patchs pour PHP 5.2, YATTA!!!

Mais comme tout admin qui se respecte, je n'aime pas trop installer "salement" mes produits sur un serveur destiné à la production. Les systèmes de packages sont nos amis, mais pour intégrer notre patch au package PHP de debian lenny, nous allons devoir le reconstruire !

Pas de problème :)

On commence par se construire un environnement de compilation pour ne pas encombrer inutilement la "prod" :


% mkdir /build
% debootstrap --arch=amd64 --components=main,contrib,non-free lenny /build/ ftp://ftp.fr.debian.org/debian
% chroot /build
% aptitude install locales
% dpkg-reconfigure locales

Puis on récupère les sources de PHP depuis notre chroot :


% apt-get install devscripts
% apt-get install gcc debhelper fakeroot
% apt-get source php5
% apt-get build-dep php5
% apt-get install libevent-dev

Puis on récupère le patch, on l'applique, on modifie les règles de construction du package pour que nos modifs soient prises en compte, et enfin, on reconstruit nos packages :


% wget http://php-fpm.org/downloads/php-5.2.6-fpm-0.5.9.diff.gz
% gunzip php-5.2.6-fpm-0.5.9.diff.gz
% cd php5-5.2.6.dfsg.1
% patch -p1 <../php-5.2.6-fpm-0.5.9
% vi debian/rules
# on modifie les options de configuration de manière à avoir :
#                --enable-zip \
#                --enable-fastcgi \
#                --enable-fpm \
#                --with-libevent \
#                --with-mime-magic=$(MAGIC_MIME) \
% debuild

Et on attend que ça termine (sur mon serveur plus véloce que la box, ça m'a pris 1h). À la fin on a une erreur à cause des signatures inconnues, mais les packages sont bien générés.

Il ne nous reste plus qu'à les installer :


% dpkg -i php5-common_5.2.6.dfsg.1-1+lenny9_amd64.deb php5-cgi_5.2.6.dfsg.1-1+lenny9_amd64.deb php5_5.2.6.dfsg.1-1+lenny9_all.deb

Enfin, on crée notre fichier de configuration pour php-fpm (attention, contrairement à la version pour PHP 5.3 qui est un fichier de conf ini, il s'agit ici d'un xml), puis notre fichier init :


<?xml version="1.0" ?>
<configuration>

   All relative paths in this config are relative to php's install prefix 
   <section name="global_options">

       Pid file
       <value name="pid_file">/var/run/php5-fpm.pid</value>

       Error log file
       <value name="error_log">/var/log/php5-fpm.log</value>

       Log level
       <value name="log_level">notice</value>

       When this amount of php processes exited with SIGSEGV or SIGBUS ...
       <value name="emergency_restart_threshold">10</value>               
       ... in a less than this interval of time, a graceful restart will be initiated.
       Useful to work around accidental curruptions in accelerator's shared memory.
       <value name="emergency_restart_interval">1m</value>                
       Time limit on waiting child's reaction on signals from master
       <value name="process_control_timeout">5s</value>

       Set to 'no' to debug fpm
       <value name="daemonize">yes</value>

   </section>

   <workers>

       <section name="pool">

           Name of pool. Used in logs and stats.
           <value name="name">default</value>

           Address to accept fastcgi requests on.
           Valid syntax is 'ip.ad.re.ss:port' or just 'port' or '/path/to/unix/socket'
           <value name="listen_address">127.0.0.1:9000</value>            
           <value name="listen_options">

               Set listen(2) backlog
               <value name="backlog">-1</value>

               Set permissions for unix socket, if one used.
               In Linux read/write permissions must be set in order to allow connections from web server.
               Many BSD-derrived systems allow connections regardless of permissions.
               <value name="owner">www-data</value>
               <value name="group">www-data</value>
               <value name="mode">0666</value>
           </value>

           Additional php.ini defines, specific to this pool of workers.
           <value name="php_defines">
       <!--        <value name="sendmail_path">/usr/sbin/sendmail -t -i</value>        -->
       <!--        <value name="display_errors">0</value>                              -->
           </value>

           Unix user of processes
            <value name="user">www-data</value>               

           Unix group of processes
           <value name="group">www-data</value>     

           Process manager settings
           <value name="pm">

               Sets style of controling worker process count.
               Valid values are 'static' and 'apache-like'
               <value name="style">static</value>

               Sets the limit on the number of simultaneous requests that will be served.
               Equivalent to Apache MaxClients directive.
               Equivalent to PHP_FCGI_CHILDREN environment in original php.fcgi
               Used with any pm_style.
               <value name="max_children">5</value>
               Settings group for 'apache-like' pm style
               <value name="apache_like">

                   Sets the number of server processes created on startup.
                   Used only when 'apache-like' pm_style is selected
                   <value name="StartServers">20</value>

                   Sets the desired minimum number of idle server processes.
                   Used only when 'apache-like' pm_style is selected
                   <value name="MinSpareServers">5</value>

                   Sets the desired maximum number of idle server processes.
                   Used only when 'apache-like' pm_style is selected
                   <value name="MaxSpareServers">35</value>

               </value>

           </value>

           The timeout (in seconds) for serving a single request after which the worker process will be terminated
           Should be used when 'max_execution_time' ini option does not stop script execution for some reason
           '0s' means 'off'
           <value name="request_terminate_timeout">0s</value>

           The timeout (in seconds) for serving of single request after which a php backtrace will be dumped to slow.log file
           '0s' means 'off'
           <value name="request_slowlog_timeout">0s</value>

           The log file for slow requests
           <value name="slowlog">logs/slow.log</value>

           Set open file desc rlimit
           <value name="rlimit_files">1024</value>

           Set max core size rlimit
           <value name="rlimit_core">0</value>
           Chroot to this directory at the start, absolute path
           <value name="chroot"></value>

           Chdir to this directory at the start, absolute path
           <value name="chdir"></value>

           Redirect workers' stdout and stderr into main error log.
           If not set, they will be redirected to /dev/null, according to FastCGI specs
           <value name="catch_workers_output">yes</value>

           How much requests each process should execute before respawn.
           Useful to work around memory leaks in 3rd party libraries.
           For endless request processing please specify 0
           Equivalent to PHP_FCGI_MAX_REQUESTS
           <value name="max_requests">500</value>

           Comma separated list of ipv4 addresses of FastCGI clients that allowed to connect.
           Equivalent to FCGI_WEB_SERVER_ADDRS environment in original php.fcgi (5.2.2+)
           Makes sense only with AF_INET listening socket.
           <value name="allowed_clients">127.0.0.1</value>

           Pass environment variables like LD_LIBRARY_PATH
           All $VARIABLEs are taken from current environment
           <value name="environment">
               <value name="HOSTNAME">$HOSTNAME</value>
               <value name="PATH">/usr/local/bin:/usr/bin:/bin</value>
               <value name="TMP">/tmp</value>
               <value name="TMPDIR">/tmp</value>
               <value name="TEMP">/tmp</value>
               <value name="OSTYPE">$OSTYPE</value>
               <value name="MACHTYPE">$MACHTYPE</value>
               <value name="MALLOC_CHECK_">2</value>
           </value>

       </section>

   </workers>

</configuration>

#! /bin/sh

### BEGIN INIT INFO
# Provides:          php5-fpm
# Required-Start:    $remote_fs $network
# Required-Stop:     $remote_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts php5-fpm
# Description:       starts the PHP FastCGI Process Manager daemon
### END INIT INFO

prefix=/usr
exec_prefix=${prefix}

php_fpm_BIN=${exec_prefix}/bin/php-cgi
php_fpm_CONF=/etc/php5/fpm/php5-fpm.conf
php_fpm_PID=/var/run/php5-fpm.pid


php_opts="--fpm --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 php5-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 php5-fpm "

                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php5-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-exit"
                        exit 1
                else
                        echo " done"
                fi
        ;;

        force-quit)
                echo -n "Terminating php5-fpm "

                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php5-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 php5-fpm "

                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php5-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

Enfin, on modifie légèrement le code PHP afin de tirer partie de memcache, et là, on tombe à 0,05s toujours sur la même page ! (à la deuxième vision, c'est à dire une fois cachée).