Envoyé par unreal
Article que j'ai initialement écrit pour une mailing list, que je poste aussi, du coup...
A titre personnel, je gère quelques serveurs relativement modestes
hébergeant des sites Web à forte fréquentation. Je devais donc
concevoir une plate-forme à la fois performante, robuste et sécurisée
afin d'héberger des applicatifs PHP (mais on peut facilement
s'inspirer de cet article pour héberger d'autres langages).
La motivation de cet article vient de la constatation que la
configuration par défaut sur tous les systèmes Linux/BSD que j'ai pu
croiser était horriblement inadaptée à des besoins modernes
d'hébergement d'applications "web 2.0" à forte charge.
Choix d'un serveur Web
Un certain nombre d'administrateurs systèmes ont abandonné Apache au
profit Lighttpd pour des raisons de performances, mais la vérité est
que les deux peuvent convenir moyennant optimisation et configuration.
Pour ma part, je préfère utiliser Apache pour servir des pages Web, et
éventuellement ajouter un Lighttpd pour servir du contenu purement
statique (fichiers de style, JS, images...).
Quelques considérations quant à la configuration Apache :
Depuis la version 2.0, Apache est multithreadé, ce qui veut dire qu'un
seul processus peut gérer plusieurs connexions, avec un gain de
mémoire impressionnant. Par ailleurs, il convient aussi de faire le
tri dans les modules Apache, ou mieux, compiler un Apache répondant au
mieux à ses besoins. Ca peut surprendre de vouloir encore compiler
alors qu'il existe des paquetages, mais un autre avantage c'est que
cela permet aussi de migrer de version avec zéro downtime : il suffit
de compiler la nouvelle version en modifiant le '--prefix', de copier
la config, de démarrer sur un autre port pour tester, et de modifier
un symlink quand c'est prêt.
Exemple de compilation Apache :
Pour en finir avec Apache, PHP n'est pas encore "thread safe" donc,
une utilisation en module est à éviter ; nous utiliserons alors le
mode CGI.
Choix d'un connecteur CGI
Bien qu'il soit tout à fait possible d'utiliser PHP comme CGI
directement, cette méthode présente l'inconvénient majeur de lancer le
binaire PHP à chaque accès à une page dynamique. Le coût CPU est donc
prohibitif sur un serveur à fréquentation importante.
Il convient alors d'utiliser un connecteur de type "FastCGI", et j'ai
une très nette préférence pour FCGID pour
plusieurs raisons :
Concrètement, le connecteur FastCGI maintient une pool de "workers"
PHP et transmet à un worker libre quand une requête arrive, tout en
recyclant les processus de manière à éviter qu'un processus reste trop
longtemps en service.
Optimisation PHP
Avant toute chose, il convient d'installer une version de PHP
compatible FastCGI, ou mieux : compiler sa propre version, ce qui
présente un avantage de taille, il est possible de mettre à jour PHP,
sans downtime, et sans même relancer Apache. En effet, FCGID va
recycler périodiquement les "workers" PHP, du coup, il suffit de faire
pointer le symlink vers la nouvelle version et au prochain recyclage,
la nouvelle version sera active.
Exemple de compilation PHP compatible FastCGI :
Pour ce qui est de l'optimisation PHP, outre la possibilité de
fouetter ses développeurs ( ), il convient d'installer un logiciel
de cache. Il en existe plusieurs (eAccelerator, Turck MMCache...), et
ils se présentent sous la forme d'une extension PHP.
Mon préféré à ce jour est Xcache parce
qu'il supporte parfaitement les environnements Linux/BSD que
j'utilise, est assez facile à configurer (quelques lignes dans
php.ini) et offre un excellent niveau de stabilité.
Gestion d'utilisateurs avec Suexec
Suexec est un peu le "sudo" du serveur Web : il permet d'exécuter des
applications CGI sous un autre utilisateur que l'utilisateur du
serveur Web (qui est souvent "www", "www-data" ou "apache").
Cela est surtout intéressant en hébergement mutualisé où il permet
d'isoler les utilisateurs (droits sur le disque, invisibilité de
processus, ainsi de suite), d'identifier rapidement un utilisateur qui
pose problème et d'éviter les casse-tête au niveau des droits.
Pour supporter cette fonctionnalité, Apache doit être compilé en
conséquence :
Ensuite, chaque utilisateur doit avoir son CGI dédié (ici le binaire
"php-cgi" compilé avec support FastCGI), dont il est propriétaire.
Attention : il faut réellement un binaire dédié, pas un symlink.
Par exemple :
Et pour finir, il suffit dans chaque virtual host de déclarer quel
binaire doit être chargé :
Exemple de configuration
Voici un exemple de configuration Apache sans Suexec. Pour la
configuration avec, il suffit de prendre les indications du paragraphe
précédent !
Conclusion
Ce petit article/howto avait comme objectif d'explorer les
améliorations suivantes en hébergement Web :
J'espère qu'il vous a plu !
A titre personnel, je gère quelques serveurs relativement modestes
hébergeant des sites Web à forte fréquentation. Je devais donc
concevoir une plate-forme à la fois performante, robuste et sécurisée
afin d'héberger des applicatifs PHP (mais on peut facilement
s'inspirer de cet article pour héberger d'autres langages).
La motivation de cet article vient de la constatation que la
configuration par défaut sur tous les systèmes Linux/BSD que j'ai pu
croiser était horriblement inadaptée à des besoins modernes
d'hébergement d'applications "web 2.0" à forte charge.
Choix d'un serveur Web
Un certain nombre d'administrateurs systèmes ont abandonné Apache au
profit Lighttpd pour des raisons de performances, mais la vérité est
que les deux peuvent convenir moyennant optimisation et configuration.
Pour ma part, je préfère utiliser Apache pour servir des pages Web, et
éventuellement ajouter un Lighttpd pour servir du contenu purement
statique (fichiers de style, JS, images...).
Quelques considérations quant à la configuration Apache :
- Mode prefork à éviter
- Enlever les modules inutilisés
- Ne plus configurer PHP en module
Depuis la version 2.0, Apache est multithreadé, ce qui veut dire qu'un
seul processus peut gérer plusieurs connexions, avec un gain de
mémoire impressionnant. Par ailleurs, il convient aussi de faire le
tri dans les modules Apache, ou mieux, compiler un Apache répondant au
mieux à ses besoins. Ca peut surprendre de vouloir encore compiler
alors qu'il existe des paquetages, mais un autre avantage c'est que
cela permet aussi de migrer de version avec zéro downtime : il suffit
de compiler la nouvelle version en modifiant le '--prefix', de copier
la config, de démarrer sur un autre port pour tester, et de modifier
un symlink quand c'est prêt.
Exemple de compilation Apache :
export CFLAGS=...
export CXXFLAGS=...
./configure \
--prefix=/usr/local/apache224 \
--enable-log_config=shared \
--enable-mime=shared \
--enable-status=shared \
--enable-autoindex=shared \
--enable-dir=shared \
--enable-cgi=shared \
--enable-alias=shared \
--enable-access=shared \
--enable-auth=shared \
--enable-expires=shared \
--enable-rewrite=shared \
--enable-dav=shared \
--enable-proxy=shared \
--with-mpm=worker
export CXXFLAGS=...
./configure \
--prefix=/usr/local/apache224 \
--enable-log_config=shared \
--enable-mime=shared \
--enable-status=shared \
--enable-autoindex=shared \
--enable-dir=shared \
--enable-cgi=shared \
--enable-alias=shared \
--enable-access=shared \
--enable-auth=shared \
--enable-expires=shared \
--enable-rewrite=shared \
--enable-dav=shared \
--enable-proxy=shared \
--with-mpm=worker
Pour en finir avec Apache, PHP n'est pas encore "thread safe" donc,
une utilisation en module est à éviter ; nous utiliserons alors le
mode CGI.
Choix d'un connecteur CGI
Bien qu'il soit tout à fait possible d'utiliser PHP comme CGI
directement, cette méthode présente l'inconvénient majeur de lancer le
binaire PHP à chaque accès à une page dynamique. Le coût CPU est donc
prohibitif sur un serveur à fréquentation importante.
Il convient alors d'utiliser un connecteur de type "FastCGI", et j'ai
une très nette préférence pour FCGID pour
plusieurs raisons :
- Efficacité : module maintenu et conçu pour Apache 2.x alors que
FastCGI n'est plus maintenu
- Stabilité : FCGID dispose d'un watchdog pour surveiller l'état des
workers
- Configuration : FCGID propose des options de configuration fines
Concrètement, le connecteur FastCGI maintient une pool de "workers"
PHP et transmet à un worker libre quand une requête arrive, tout en
recyclant les processus de manière à éviter qu'un processus reste trop
longtemps en service.
Optimisation PHP
Avant toute chose, il convient d'installer une version de PHP
compatible FastCGI, ou mieux : compiler sa propre version, ce qui
présente un avantage de taille, il est possible de mettre à jour PHP,
sans downtime, et sans même relancer Apache. En effet, FCGID va
recycler périodiquement les "workers" PHP, du coup, il suffit de faire
pointer le symlink vers la nouvelle version et au prochain recyclage,
la nouvelle version sera active.
Exemple de compilation PHP compatible FastCGI :
export CFLAGS=...
export CXXFLAGS=...
./configure \
--prefix=/usr/local/php524 \
--with-mysql=/usr/local/mysql \
[...]
--enable-force-cgi-redirect \
--enable-fastcgi
export CXXFLAGS=...
./configure \
--prefix=/usr/local/php524 \
--with-mysql=/usr/local/mysql \
[...]
--enable-force-cgi-redirect \
--enable-fastcgi
Pour ce qui est de l'optimisation PHP, outre la possibilité de
fouetter ses développeurs ( ), il convient d'installer un logiciel
de cache. Il en existe plusieurs (eAccelerator, Turck MMCache...), et
ils se présentent sous la forme d'une extension PHP.
Mon préféré à ce jour est Xcache parce
qu'il supporte parfaitement les environnements Linux/BSD que
j'utilise, est assez facile à configurer (quelques lignes dans
php.ini) et offre un excellent niveau de stabilité.
Gestion d'utilisateurs avec Suexec
Suexec est un peu le "sudo" du serveur Web : il permet d'exécuter des
applications CGI sous un autre utilisateur que l'utilisateur du
serveur Web (qui est souvent "www", "www-data" ou "apache").
Cela est surtout intéressant en hébergement mutualisé où il permet
d'isoler les utilisateurs (droits sur le disque, invisibilité de
processus, ainsi de suite), d'identifier rapidement un utilisateur qui
pose problème et d'éviter les casse-tête au niveau des droits.
Pour supporter cette fonctionnalité, Apache doit être compilé en
conséquence :
./configure \
[...]
--enable-suexec=shared \
--with-suexec-caller=www \
--with-suexec-docroot=/usr/local/www/cgi \
--with-suexec-uidmin=1000 \
--with-suexec-gidmin=1000
[...]
--enable-suexec=shared \
--with-suexec-caller=www \
--with-suexec-docroot=/usr/local/www/cgi \
--with-suexec-uidmin=1000 \
--with-suexec-gidmin=1000
Ensuite, chaque utilisateur doit avoir son CGI dédié (ici le binaire
"php-cgi" compilé avec support FastCGI), dont il est propriétaire.
Attention : il faut réellement un binaire dédié, pas un symlink.
Par exemple :
/usr/local/www/cgi/user1/php-cgi [user1/user1]
/usr/local/www/cgi/user2/php-cgi [user2/user2]
[...]
/usr/local/www/cgi/userN/php-cgi [userN/userN]
[...]
/usr/local/www/cgi/user2/php-cgi [user2/user2]
[...]
/usr/local/www/cgi/userN/php-cgi [userN/userN]
[...]
Et pour finir, il suffit dans chaque virtual host de déclarer quel
binaire doit être chargé :
<Directory "/home/userX/www">
<IfModule fcgid_module>
FCGIWrapper /usr/local/www/cgi/userX/php-cgi .php
</IfModule>
</Directory>
<VirtualHost *:80>
ServerName domaine.fr
ServerAlias www.domaine.fr
DocumentRoot /home/userX/www
CustomLog /home/userX/logs/access.log combined
SuexecUserGroup userX userX
</VirtualHost>
<IfModule fcgid_module>
FCGIWrapper /usr/local/www/cgi/userX/php-cgi .php
</IfModule>
</Directory>
<VirtualHost *:80>
ServerName domaine.fr
ServerAlias www.domaine.fr
DocumentRoot /home/userX/www
CustomLog /home/userX/logs/access.log combined
SuexecUserGroup userX userX
</VirtualHost>
Exemple de configuration
Voici un exemple de configuration Apache sans Suexec. Pour la
configuration avec, il suffit de prendre les indications du paragraphe
précédent !
LoadModule fcgid_module modules/mod_fcgid.so
[...]
<Directory "/usr/local/www">
Options FollowSymLinks ExecCGI
<IfModule fcgid_module>
FCGIWrapper /usr/local/php5/bin/php-cgi .php
</IfModule>
</Directory>
# fcgid config
<IfModule fcgid_module>
IPCConnectTimeout 10
IPCCommTimeout 40
MaxRequestsPerProcess 500
ProcessLifeTime 600
MaxProcessCount 100
SocketPath sock/fcgidsock
SharememPath sock/fcgid_shm
</IfModule>
[...]
<IfModule mime_module>
[...]
AddHandler fcgid-script .php
</IfModule>
[...]
<Directory "/usr/local/www">
Options FollowSymLinks ExecCGI
<IfModule fcgid_module>
FCGIWrapper /usr/local/php5/bin/php-cgi .php
</IfModule>
</Directory>
# fcgid config
<IfModule fcgid_module>
IPCConnectTimeout 10
IPCCommTimeout 40
MaxRequestsPerProcess 500
ProcessLifeTime 600
MaxProcessCount 100
SocketPath sock/fcgidsock
SharememPath sock/fcgid_shm
</IfModule>
[...]
<IfModule mime_module>
[...]
AddHandler fcgid-script .php
</IfModule>
Conclusion
Ce petit article/howto avait comme objectif d'explorer les
améliorations suivantes en hébergement Web :
- Performances (processus légers, caches)
- Sécurité (Suexec)
- Stabilité (workers séparés avec surveillance)
J'espère qu'il vous a plu !
Posté le 27/03/08 à 11:37