wiki:DebianLampLenny

Version 6 (modified by Vincent Caron, 10 years ago) (diff)

--

Debian LAMP Howto (Lenny)

Note: ce document est adapté de notes internes à Bearstech, il sert de guide précis et concis pour les administrateurs système (du junior à l'expert).

1. Choix module/FastCGI

Il y a deux modèles principaux pour héberger PHP (ou tout autre langage):

  • Embarqué dans Apache, en tant que module
  • Indépendant (standalone)

Module

Quand PHP est embarqué dans Apache, cela implique:

  • Que le modèle Apache est nécessairement le prefork, donc un modèle non threadé ou chaque processus peut traiter une requête à la fois, et donc au plus MaxClients simultanément
  • Qu'il y a un interpréteur PHP dans chaque processus, donc que la consommation mémoire totale de PHP sur le serveur sera au plus [php.ini:]memory_limit * [apache2.conf:]MaxClients. Il est important que ce total soit corrélé avec la mémoire virtuelle (voire physique) disponible, pour éviter un usage trop important du swap

Il y a plusieurs inconvénients:

  • Scalabilité: on peut difficilement monter MaxClients, principalement à cause du contrôle de la consommation max de mémoire (le produit ci-dessus). A comparer avec les milliers de connexions simultanés obtenues facilement en modèle FastCGI
  • Usage mémoire non optimal: il est très peu probable qu'on se retrouve avec précisément MaxClients processus utilisant simultanément memory_limit. Typiquement la limite mémoire PHP est globalement elevée mais seulement pour les besoins de quelques appels particuliers. On se retrouve en général à une sous-utilisation chronique de la mémoire en échange d'une bonne "sécurité anti-swap".

Mais il y a un avantage majeur: c'est très simple à installer et déployer (les fichiers terminant en .php sont magiquement interprétés et c'est tout), et surtout cela permet facilement de mutualiser de très nombreux sites et scripts distincts assez efficacement. Même des développeurs non-PHP envient cet aspect !

FastCGI

Dans le modèle FastCGI, Apache et PHP sont des processus distincts. On en profite pour installer Apache en modèle worker, ce qui permet de gérer un grand nombre de connexions simultanées (testé juqu'à 10,000 ce jour). Il va lui même très bien gérer le service des fichiers statiques à de nombreux clients, et simplement "faire suivre" une requête qui relève de PHP. On peut considérer qu'Apache traite le service des fichiers et des requêtes PHP de la même manière et avec le même coût.

Les processus PHP sont en général gérés (lancés, terminés) par Apache lui-même (ou plus précisément le module fcgid), mais ce n'est pas obligatoire. Il existe un nombre de processus PHP maximum permettant d'exploiter optimalement les ressources d'un serveur, qui se situe en général entre 2x et 8x le nombre de CPU cores disponibles (suivant que les programmes sont plutôt limités en CPU ou I/O).

Dans le cas ou une application web est constituée d'un seul point d'entrée (typiquement /index.php) avec l'aide de mod_rewrite}, on peut alors être économe en mémoire et optimal en performances. Les applications ayant ces propriétés sont Drupal, WordPress, Dotclear, etc. La plupart des frameworks imposent ce modèle (Turbulences, CakePHP, Symfony, etc).

Par contre ce modèle est peu efficace pour les applications PHP "traditionnelles" constituées de nombreux fichiers PHP directement sollicités via leur URL. On va alors lancer plusieurs instances PHP (processus) par programme, ce qui peut finir par un très grand nombre au total. C'est là que l'on retombe sur la nécessité d'un mode mutulalisé de type module.

Il existe une documentation alternative Notes on a LAMP5 setup on Debian Etch, qui fournit beaucoup plus d'explications sur la partie FastCGI (en anglais).

Mode hybride

Soit parce qu'on atteint la limite citée ci-dessus du modèle FastCGI, soit parce qu'on a une installation existante en module difficilement migrable, on peut tenter une solution hybride: mettre devant un Apache prefork avec PHP en module un autre serveur web assurant le service des fichiers statiques de façon "scalable" et faisant suivre le trafic destiné à PHP; on appelle alors ce dernier un reverse proxy.

!NdA: par expérience, ce modèle est souvent délicat à mettre en place. Les problèmes principaux rencontrés viennent du processus de décision qui distingue un fichier statique d'un fichier à exécuter via PHP: il faut le déplacer complètement du serveur Apache prefork vers le reverse proxy. Quand ce processus fait intervenir des customisation via mod_rewrite ce peut être délicat à effectuer (même si le reverse proxy est un autre Apache en mode worker).

2. Installation Apache + PHP

Pour le module, il suffit de désigner le module en question, et Apache (prefork) et sa clique suivent:

# aptitude install libapache2-mod-php5

Pour FastCGI c'est plus explicite:

# aptitude install apache2-mpm-worker libapache2-mod-fcgid php5-cgi

Dans tous les cas, il peut être très pratique de posséder la version "CLI" de PHP: de plus en plus de scripts de maintenance (déploiement, cronjobs, etc) se lancent hors du contexte du serveur web, depuis un shell ou cron. Et on installe d'office les modules les plus incontournables de PHP:

# aptitude install php5-cli php5-curl php5-gd php5-mcrypt php5-mysql

La prochaine étape consiste à récupérer un php.ini sain (voir source:/lamp/). Actuellement il n'existe pas de raison majeure d'utiliser une configuration distincte pour la version module ou FastCGI, mais les deux fichiers de configurations sont maintenus. On s'assure aussi que le "CLI" partage la configuration pour s'éviter d'indebuggables surprises. Pour le module (voir source:/lamp/php.ini):

# cp lamp/php.ini /etc/php5/apache2/php.ini
# rm /etc/php5/cli/php.ini
# ln -s /etc/php5/apache2/php.ini /etc/php5/cli/php.ini

Et pour le FastCGI, un tout petit plus de travail (voir source:/lamp/php-cgi.ini et source:/lamp/php5-fastcgi-wrapper):

# cp lamp/php-cgi.ini /etc/php5/cgi/php.ini
# rm /etc/php5/cli/php.ini
# ln -s /etc/php5/cgi/php.ini /etc/php5/cli/php.ini
# cp lamp/php5-fastcgi-wrapper /usr/local/bin/
# a2enmod fcgid

Les détails et justifications du contenu des php(-cgi).ini et de la configuration de fcgid sont sur le Debian LAMP Etch Howto (anglais).

3. Configuration Apache

Nous avons déjà installé Apache en même temps que PHP. Nous effectuons quelques customisations Bearstech récurrentes, la première consistant à adopter une hiérarchie de logs à un niveau. Il ne faut surtout pas oublier d'ajuster logrotate en conséquence:

# sed -i -e 's:apache2/error.log:apache2/default/error.log:' /etc/apache2/apache2.conf
# sed -i -e 's:^CustomLog:#CustomLog:' /etc/apache2/apache2.conf
# mkdir /var/log/apache2/default
# rm /var/log/apache2/*.log
# sed -i -e 's:log/apache2/:log/apache2/*/:' /etc/logrotate.d/apache2

Si vous êtes en module, il vous faut considérer simultanément:

  • la directive MaxClients dans la section <IfModule mpm_prefork_module> du fichier /etc/apache2/apache2.conf
  • la directive memory_limit du fichier /etc/php5/apache2/php.ini

En FastCGI, vous pourrez contrôler le nombre maximum de processus lancés par script ainsi (le défaut est de 100 avec fcgid) via la directive MaxProcessCount dans le contexte <Directory ...> qui convient.

Certains modules sont populaires voire incontournables, activons-les:

# a2enmod deflate
# a2enmod headers
# a2enmod rewrite

Petit fixes pour Lenny, nous préférons continuer à dédier ports.conf... aux ports:

# sed -i -e 's/^NameVirtualHost/#NameVirtualHost/' /etc/apache2/ports.conf

Ensuite nous avons une configuration globale que nous installons ainsi. Elle ne contient pas de vhost a contrario du ficher Debian livré (voir source:/lamp/sites-default).

# cp lamp/sites-default /etc/apache2/sites-available/default

Enfin il reste à rajouter deux vhosts:

  • le premier est celui par défaut (et normalement un catch-all pour les redirections)
  • le second est le vhost utile, celui accueillant le nom canonique du site web

On les rassemble dans un fichier de conf portant le nom canonique, le "template" est documenté (voir source:/lamp/sites-sample):

# cp lamp/sites-sample /etc/apache2/sites-available/www.mylamp.com
# mkdir /var/log/apache2/www.mylamp.com
# a2ensite www.mylamp.com

A ce stade il vous manque peut être le DocumentRoot, voyez plus loin "Environnement de l'application". Sinon finissez par un restart pour vérifier la séquence de démarrage (en production on privilégiera un apache2ctl configtest suivi d'un reload):

# /etc/init.d/apache2 restart

Diagnostics:

  • # tail -f /var/log/apache2/default/error.log
  • # netstat -tnlp | grep apache
  • # cd ~mylamp/root; echo '<?php phpinfo(); ?>' >info.php; wget -nv -O- http://localhost/info.php; rm info.php

Apache et les 8 000 clients (ou plus)

En mode prefork, vous ne dépasserez en général pas MaxClients = 256 (qui est une limite compilée en dur dans Apache). Par contre en mode worker vous pouvez totaliser un très grand nombre de threads, par exemple voici une répartition en 8192 threads sur 128 processus (consultez la doc du worker pour l'art de la manipulation de ces chiffres):

ServerLimit 128
ThreadLimit 64
<IfModule mpm_worker_module>
    StartServers         8
    MaxClients           8192
    MinSpareThreads      512
    MaxSpareThreads      64
    ThreadsPerChild      64
    MaxRequestsPerChild  10000
</IfModule>

Mais attention: chaque processus dans Debian est limité par défaut à 1024 descripteurs de fichiers (vrai dans la majorité distributions GNU/Linux). Si vous voulez réellement atteindre ces 8,192 connexions simultanées, il suffit de monter la limite:

# echo ulimit -n 64000 >>/etc/default/apache2
# /etc/init.d/apache2 restart

4. Installation MySQL

Aucune surprise particulière, penser toute fois à définir un mot de passe "root" (il n'y en a pas par défaut):

# aptitude install mysql-client mysql-server
# mysqladmin password QSDbq92DNb
# cat >~/.my.cnf
[client]
user = root
pass = QSDbq92DNb
<Ctrl+D>
# chmod 600 ~/.my.cnf

Les bonnes idées concernant mysql.ini:

  • Désactiver le "binlog" (réellement utile que pour la réplication)
  • Activer le "slowlog" (avec les queries sans index)
  • Désactiver InnoDB, on vous le demandera s'il est nécessaire
  • Ne pas toucher les paramètres de "tuning" sans consulter un DBA

Diagnostics:

  • # netstat -tnlp|grep mysql (écoute sur 127.0.0.1 par défaut)
  • # mysql

5. Environnement de l'application

On crée un compte Unix qui a deux rôles:

  • Séparer les droits du serveur HTTP (www-data) de ceux des fichiers de l'application, pour minimiser l'impact des failles de sécu (peut être effectif contre un defacement, mais inopérant contre des botnets qui exploitent les inévitables zones disponibles en écriture pour Apache (voir toutefois mount noexec)
  • Fournir le confort d'un compte Unix et un environnemnt distinct par application aux développeurs (shell, etc)

On crée aussi la base de l'application et on aura la politesse de fournir les credentials dans ~/.my.cnf et fignoler quelques détails agréables:

# mysql
mysql> create database 'mylamp_prod';
mysql> grant all privileges on mylamp_prod.* to mylamp_prod@localhost identified by 'KW1sQ+xb/';
mysql> exit
# adduser --disabled-password --gecos www.mylamp.com --home /var/www/www.mylamp.com mylamp
# su - mylamp
$ sed -i -e 's/^#alias/alias/' .bashrc
$ mkdir .ssh
$ cat >.ssh/authorized_keys
(copier/coller les clés, attention au linewrap)
(Ctrl+D)
$ cat >.my.cnf
[client]
user = mylamp_prod
pass = KW1sQ+xb/
(Ctrl+D)

Fichiers générés par Apache

Le mainteneur ou développeur de l'application va signaler (en général après avoir butté sur l'erreur) que Apache doit pouvoir écrire dans certaines zones du système de fichier. Après vous être battu pour que ces zones se réduisent idéalement à un seul répertoire (et sa descendance):

# chown -R www-data: /var/www/www.mylamp.com/root/files

Par la suite, si on vous demande un accès pour modifier, supprimer ou ajouter des fichiers dans ces zones:

  • S'il s'agit d'un client final qui veut uploader des media, lui fournir un compte FTP (il demandera en général ce protocole), utilisant les droits www-data et chrooté sur la zone adéquate
  • S'il s'agit du développeur, lui expliquer que ce que son application a fait, elle peut sûrement le défaire, puis lui filer un "sudo":
    # aptitude install sudo
    # EDITOR=vim visudo
    ...
    mylamp    ALL= (www-data) NOPASSWD: ALL
    ...
    # su - mylamp
    $ sudo -u www-data bash
    www-data:~$
    

Un dév futé posera un source:/lamp/shell.php qui vaut bien un sudo pour les manips simples.