L'objectif de l'article est de partager avec vous une méthode qui permet d'analyser les problèmes qui se posent lors de pics de charge "anormaux" (non dûs à l'affluence de visiteurs) sur un système complexe à fort traffic basé sur Drupal sous technologie LAMPP avec Debian comme système d'exploitation.
Nous nous baserons sur notre expérience sur notre environnement qui accueille plus de 220'000 visiteurs uniques par jour maintenant.
Petit topo sur l' environnement WebFactory pour les sites Edipresse
Les sites gérés par la webfactory sont hébergés par nos soins sur un environnement système composé de 2 racks et demi dont nous assurons avec notre prestataire système - SCRT - la gestion au quotidien.
Je ne parlerai dans cet article que de l'environnement dit "media" à savoir des sites LeMatin.ch, Tdg.ch et 24 heures.ch, Edicom.ch ainsi que l'ensemble des autres sites sous Drupal comme Femina.ch par exemple à savoir environ une douzaine de sites.
A cela s'ajoute les sites réalisés pour des entreprises tels que Tamedia avec Schweizer Familie et Annabelle.
Nos Drupals accueillent plus de 220'000 visiteurs uniques par jour pour un peu moins de 2 millions de pages vues par jour en moyenne.
Ces visiteurs sont accueillis par 8 frontaux QuadCore avec 8 GB de RAM avec un load balancing associé.
En terme de base de donnée nous avons 4 serveurs de bases de donnée composés d'1 seul master et de 2 slaves.
Le quatriéme sert pour notre site de classified.
Toutes les bases sont à 8GB de RAM sauf le master central à 64GB qui héberge l'ensemble des masters des gros sites.
Nous n'avons pas de reverse proxy (ni Squid, ni Varnish, ni Apache en mod proxy) par contre nous avons un NFS sur 1 serveur dédié qui assure la délivrance des binaires.
Les webmasters, eux, ont accès à un autre serveur qui n'est pas dans la ferme qui délivre leur propre binaire
Nous dédions un serveur à MemCache comme "serveur de cache" et avons installé X-CACHE qui est 'un accélérateur PHP'.
Chaque gros sites (Le Matin - 100'000 visiteurs uniques jour, Tdg - 60'000 - 24 heures - 40'000 ) se voit doter d'1 master et d'une réplication vers une seul slave sur lequel pointent les 8 frontaux.
Les sites fonctionnant sous Drupal nous sommes sous technologie LAMPP (Linux Apache Php MysQL).
Les trois gros sites sont sous Drupal 5, l'ensemble des autres sites sont sous Drupal 6.
Les sites mobiles (mobile.lematin.ch par exemple) et les applications iPhone sont alimentées à partir des flux RSS issus des Drupals.
Constation du phénomène.
Nous allons chercher à répondre au problème suivant : vous perdez vos serveurs.
En quelques secondes ou minutes, tout les sites hébergés sont inaccessibles et vous perdez aussi la main sur les serveurs eux-même.
Nous allons chercher à mettre en place une procédure visant déceler le problème et à récupérer la main sur vos applications.
Nous n'entrerons que dans des détails pratiques et rapides à mettre en oeuvre et ne ferons pas de théorie.
Tout d'abord il faut mesurer
Sur vos serveurs, vous avez globalement 3 paramétres importants : le CPU utilisé, la RAM utilisée et la charge du serveur.
En faisant un "top" (en ligne de commande tapez 'top') vous arrivez au tableau suivant :

Le load average c'est le 3 premiers chiffres en haut à droite....ce sont des moyennes.
Nous prendrons que le premier chiffre juste après "load average" qui est ce qu'on appelle "la charge" instantanée (ou presque) du serveur. Pour un biQuadCore (8 processeurs) on dira que la charge est à 100 % à 8...et que la limite de fonctionnement "correct" se situe à ce niveau là. Si vous avez un processeur cela sera 1, 2 processeurs 2 ect...
Le "Cpu(s)" avec le chiffre devant %us est important aussi. à 100 % cela veut dire que le CPU (processeur) travaille à 100 %...cela veut dire qu'il n'a plus de temps pour faire autre chose. L'inverse est le "idle" %id qui lui donne "son temps libre".
Bref un serveur à 100 % tout le temps c'est plutôt mauvais, cela va entrainer sûrement une augmentation de la charge du serveur qui entrainera un empilement en attente des requêtes sur ce serveur avec un risque de partir dans une instabilité de plus en plus grande les phénomène s'auto alimentant.
En PHP 5, vous pouvez utiliser la fonction sys_getloadavg(), qui vous renverra les 3 valeurs de charge de votre serveur et ainsi afficher des tableaux de ce type :

Qui vous aideront à mesurer l'instabilité.
En doublant cela de "sondes" qui viendront mesurer toutes les 5 minutes la vision utilisateur - par exemple avec XITI OBSERVER - vous aurez un tableau simple mais suffisamment efficace pour mesurer l'état de votre plateforme.
Vous voici dotez du CPU et de la charge de vos serveurs.
Lorsqu 'un incident arrive tout s'affole : la charge explose...mais que se passe-t-il ?
Les enchainements pathogènes
Différents comportements possibles :
La cascade SQL
La base charge car vous avez des requêtes SQL qui mettent trop de temps à ramener leurs résultats, sa charge augmente .
Vous pouvez voir les requêtes en question dans le Log "Slow Query" qui est accessible théoriquement dans /var/log/mysql/slow_query si vous l'avez activé dans le my.cnf de Mysql.
Reportez vous à Google pour savoir comment activer les "SLOW QUERY". Cet élément est indispensable car il va vous permettre de voir les requêtes qui sont trop lourdes.
Avec Drupal ce sont souvent les vues qui produisent ces requêtes.
En augmentant de charge, la base devient plus lente
En devenant plus lente les requêtes normales deviennent lentes à leur tour et les lentes deviennent de plus en plus lentes.
En devenant lentes à leur tour, la base "explose" c'est à dire que les requêtes en attente de leurs résultats respectifs s'accumulent. La base finit par se bloquer au maximum de connexion MySQL permises.
A partir de là, les internautes "qui demandent une page" avec "une requête derriére" s'entassent sur les serveurs car les connexions ne sont pas fermées (en attente de la réponse de MySQL) "ou sont perdues" mais reste active en tout cas pour Apache qui est limite en nombre de place "Nombre maximal de thread apache".
Les serveurs dits "frontaux" où se situent les Apache c'est à dire où les internautes arrivent via HTTP (donc via leur browser classique) chargent alors à leur tour ce qui amplifie le phénomène et la cascade....jusqu'à saturation de ces mêmes serveurs.
Une fois que tout est saturé..c'est fini...l'indisponibilité est là.
Comment réagir face à une "cascade SQL"
0 - Tout dabord logguez vous en SSH sur le serveur de base puis arrêtez MySQL (/etc/init.d/mysql stop généralement )
1 - Faites la même chose avec les Apaches sur vos frontaux (/etc/init.d/apache2 stop). Aidez vous de Cluster SSH si vous en avez plusieurs.
2 - Attendez que tout se calme puis téléchargez et installez MySQLAdministrator tout en activant les Slow_query
3 - Rallumez tout (Apache + MySQL)
4 - Utilisez MySQL Administrator (Gratuit) pour vous connecter à la base de donnée fautive et activer les slow_query (log-slow-queries = [path to the log file]
dans le my.cnf ). Dans MySQL Administrator fourni par MySQL rendez vous simplement sur "Server connection" pour voir les requêtes qui "stackent" (qui s'accumulent). MySQL Administrator peut aussi vous renseigner sur l'état de la base en allant dans "Health". Là vous verrez comment la base réagit.

5 - Entre les Slow_query et en regardant MySQLadministrator vous verrez certainement passer les requêtes fautives : répétition des mêmes requêtes qui mettent du temps, empilement de mêmes petites requêtes. Vous regardez les slow_query en répérant le log que vous avez configuré dans le my.cnf et en faisant un "tail -f [path to the logfile]" qui vous permets de voir en direct les requêtes slow_query arriver.
6 - Une fois que vous avez vos candidates, il vous faut quand même valider si :
- Vous avez une augmentation de trafic importante : il va vous falloir vérifier que les cartes de 100 Mb sortant de vos serveurs de base de donnée sont suffisantes (est ce que vous ne dépassez pas ces 100 Mb ? il faudra alors passer en Gb (1 Gb)). Cela signifirait que vos trafics entre la base de donnée et vos apaches connaissent "un étranglement".
- Est ce qu'en augmentant la limite de connexion cela ne passerait pas ?
voir
http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysv...
7 - Faites un test en jouant en augmentant de maniére importante la limite haute du nombre de connections autorisées pour MySQL en la changeant via les options Mysql (mysql -uroot -p
8 - Si vous avez un défaut applicatif (cas le plus fréquent), le point 7 n'a pas du fonctionner. C'est même pire. Dans ce cas redescendez la limite à 151 par défaut.
9 - Vérifiez quand même que vous avez toujours de la place sur votre disque dur pour écrire vos données MySQL
En vous logguant faites un "df -all"
Il doit vous rester de l'espace dans le /var/ (regardez le use , s'il n'est pas à 100% c'est bon). Si c'est à 100 % dans le /var/ et que vous n'avez pas changé le lieu où MySQL stocke ses fichiers. Il vous faut d'urgence arrêter MySQL et faire de la place ou déplacez le /var/ ailleurs.
10 - Vous en êtes toujours au même point....passez alors vos slow_query ou requêtes tueuses du point 5 au crible pour savoir d'où elles viennent. En particulier...ajoutez le module "devel" (avec Drupal 5 et 6) sur une copie de vos sources en local sur votre ordinateur et essayez de reconnaitre les requêtes qui passent.
En ordre de priorité, regardez :
- Vos vues (module view dans Drupal). Les vues sur Drupal 6 ont un système de cache. Vérifiez bien que ces caches sont bien actifs et configurés
- Vos modules personnels : c'est parfois là où vous faites du mal à Drupal
- Les mécanismes d'imports externes réguliers (feed agregator par exemple). Dans ce cas vérifiez l'absence de charge en coupant l'appel au cron de drupal.
- Vos triggers si vous en avez (module trigger)
Vous devriez arriver à voir d'où cela vient. Je vous conseille alors de couper et de retirer immédiatement de la production ces éléments.
Tout arrêter (apache + mysql) puis tout relancer pour valider le problème.
Si cela repart sans monter de charge, remettez l'élément problématique pour bien vérifier que c'est lui qui provoque l'emballement. Une fois le test effectué - si c'est positif - retirez-le puis corrigez tranquillement dans les environnements de développement.
La cascade Apache.
Un autre problème peut arriver: vos frontaux vous lachent avant la base de donnée. Un certain eZpublish en était coutumier à cause du langage de template qui faisait exploser la mémoire mais tout applicatif peut y arriver sans souci.
Le symptôme est une augmentation du % de CPU utilisé jusqu'à saturation (100 %) puis une augmentation de la charge associée. On peut avoir le même phénomène avec la mémoire qui part alors en swap (entendez qui écrit sur le disque dur les informations qu'elle ne peut plus mémoriser). Les bases suivront ensuite car les demandes aux bases ne seront pas fermées les serveurs frontaux "saturant".
C'est le cas le plus compliqué et le moins facile à résoudre car un tout petit script pour faire vaciller votre serveur même s'il n'est pas souvent appelé.
Sur Drupal 5 j'ai vu ces comportements avec le module vues (view) associé à une nodequeue. Je vous conseille donc d'activer une à une toutes vos vues jusqu'à trouver la bonne qui fait tout exploser.
Si vous avez des grosses consommations d'XML cela peut aussi arriver. Il faudra alors les segmenter en les divisant en de plus petits XML.
Le traitement d'image (via imagemagick mais aussi GD) peut aussi amener à des consommations importantes de CPU et de RAM. Retraiter en même des milliers d'image qui font plusieurs Mo chacunes peut être une mauvaise idée....
Les méta-languages c'est à dire du PHP "processant" du PHP pour créer d'autres fichiers comme un language de template par exemple peuvent amener aussi à avoir des problèmes de mémoire.
Tout traitement important de tableau d'objet (ou d'objet de tableau) avec des programmations récursives peuvent amener si les garbages "volontaires" (unset des tableaux par exemple quand on ne les utilise plus) ne sont pas réalisés.
Après il vous faut éliminer le DDOS (multiplication des solliciations de vos serveurs de maniére automatique et répétée) ou le DOS involontaire ou volontaire. Quand on parle de DDOS involontaire cela peut simplement être une application iPhone "cliente" qui prend vos flux RSS toutes les 5 secondes. Multipliez le nombre d'applications et vous aurez un petit problème. Cela peut être aussi un partenaire ou un concurrent qui vient vous "pomper" votre contenu en lançant un robot sur vos pages de manière très agressive. Google peut aussi très bien jouer ce rôle si vous avez un grand nombre de page à indexer.
Le DOS et DDOS (Denial Of Service) et (Distribued Denial Of Service) volontaires et organisés sont rares : à moins d'être une cible designée comme un serveur de la future HADOPI française (paix à son âme) ou les serveurs de l'armée américaine vous ne rencontrerez pas ou très rarement ce cas de figure.
Il est donc inutile de crier au DDOS ou DOS dès que vos serveurs flanchent mais plutôt aller chercher les erreurs commises dans le code.
C'est en effet dans 99% bien ce problème et non un problème hardware (très rare).
Le problème "réseau" c'est à dire carte réseau trop juste (passez du 100 mb au 1 Gb) peut arriver si vos trafics augmentent beaucoup tout comme l'absence d'espace disque dans la bonne partition sur vos serveurs (le /var/ de base de MysQL en particulier).
Du coté de Drupal allez voir systématiquement les caches de vues dans Drupal 6 et le cache tout court (rubrique 'performance') dans Drupal 5 et 6.
Après vous pouvez configurer mieux vos Apache et MySQL. La configuration par défaut est en effet "moyenne" en terme de performance et on peut faire beaucoup mieux.
Pour Mysql :
http://www.mysqlperformanceblog.com/
et
http://www.mysql.fr/consulting/packaged/performance.html
Pour Apache :
http://httpd.apache.org/docs/2.3/fr/misc/perf-tuning.html
Pour MemCache:
http://memcached.org/
Pour X-Cache :
http://xcache.lighttpd.net/