Le Projet Médiapart
Présentation métier
• Le projet Mediapart
• Volumétrie
• Taille de la Team
Médiapart & Drupal
• Historique
• Version : Drupal 6 non Pressflow
Les spécificités du projet
Web
•Trafic très variable,
•Cycle de développement très court,
Presse
•Outil de travail de la rédaction,
•Pics d’actualité non anticipables,
Médiapart
•Forte activité connectée,
•Un e-commerçant d’un produit
dématérialisé,
Drupal
•Riche fonctionnellement,
•Travail permanent sur la performance.
Critères
pour bien dimensionner son infrastructure
• Volumétrie & répartition du trafic
• Performance & indicateurs
• Pré-requis de disponibilité (SLA)
• Différentes catégories de service
• Provenance des visiteurs/utilisateurs : FR
3 silos
Public Rédaction Pré-production
FORTE Charge FAIBLE Charge FAIBLE Charge
FORTE Variation FAIBLE Variation ZERO Variation
FORTE Disponibilité FORTE Disponibilité FAIBLE Disponibilité
Silo: Silo :
-Multicouches -Mono Frontal
-N-Frontal Silo autonome
-Isolé
-Actif-Actif multisites -Mono site
-Haute disponibilité
Infrastructure
Site A
PA-2 backoffice preprod
ACTIF
LVS backend
FW & Répartition Cache Front Back
de charge
Site B
PA-3
ACTIF LVS backend
Répartition de charge &
HA
Répartition de charge de layer 3
• vers les serveurs Varnish,
• vers les services internes Actif/Actif (MySQL Slaves)
Répartition de charge layer 7
• Varnish vers les frontaux Drupal
Réplication Master/Master MySQL
• Vip + HeartBeat
Haute disponibilité des services Back (SolR&memcache)
• Heartbeat + réplication native master/slave (SolR) + Switch de
redémarrage sur la conf
• Memcache vide
Segmenter vos règles de
caching
Varnish Mise en cache systématique avec une
Règles générales rétention importante pour vos médias ou
Fréquence de modification faible pages statiques (images, js, css, 404, etc.)
Supprimer les cookies « inutiles »
(Analytics) & Lazy session (<7)
Apache Surcharger les headers « cache-control »
Règles un peu plus fines depuis votre conf ou .htaccess pour
Fréquence de modification épisodique certaines rubriques publiques « one-to-all »
de votre site (max-age=X, public|private)
Applicatif – cache-control Piloter directement depuis l’applicatif (code
Règles fines ou backend)
Modification en « temps réel »
Varnish
Implémenter des stratégies « métiers » de
caching
• Cache anonyme : la
version du site publique
sans auth
• Cache par groupe & rôle
• Cache par user
ESI pour déterminer les
blocs cachables et
accessibles
Varnish
Implémenter des stratégies « métiers » de
caching
sub vcl_recv
{
[...]
# Test d'exclusion du cache (pass = "pas de cache", lookup="accès au cache")
if (req.http.Cookie)
{
# Test sur l'url pour déterminer si il s'agit d'une page avec des tags ESI ou un appel ESI directement
if (req.url ~ "^/esi/get" || req.url ~ "^/$" || req.url ~ "^/whatever/(a|b|c)/")
{
# Extraction de la stratégie de cache depuis les variables GET de l'url
if (req.url ~ "^.*&cache=[^&]+$")
{
# assignation de la strategie de cache a un en-tete HTTP (trick varnish pour créer des variables)
set req.http.X_CACHE_MODE = regsub(req.url, "^.*&cache=([^&]+).*$", "1");
}
}
else
{
return (pass)
}
# assignation de l'ID de session (extrait du cookie) à un en-tete HTTP.
set req.http.X_SESS_COOKIE = "session-" + regsub(req.http.Cookie, "^.*?SESS[0-9a-zA-Z]{32}=([^;]*);*.*$", "12");
# C embarqué pour gérer le retour d'erreur de la fonction Vmod_Func_memcached.get
# On verifie ici si une session actuellement valide correspond ‡ l'ID extrait du cookie.
# Pour cela on utilise le vmod memcached de varnish.
C{
if (Vmod_Func_memcached.get(sp, &vmod_priv_memcached, VRT_GetHdr(sp, HDR_REQ, "016X_SESS_COOKIE:")) ==
NULL)
{
/*Création d'un en-tete HTTP pour positioner un flag en cas de session invalide*/
VRT_SetHdr(sp, HDR_REQ, "015X_SESS_VALID:", "Not Found", vrt_magic_string_end);
}
}C
# exclusion du cache en cas de session invalide
if (req.http.X_SESS_VALID ~ "^Not Found$")
{
return (pass);
}
}
return (lookup)
}
Varnish
Implémenter des stratégies « métiers » de
caching
sub vcl_hash
{
# Si la requête est acheminée ici, alors la présence du cookie autorisé l'accès au cache authentifié.
if (req.http.Cookie)
{
hash_data("authenticated");
}
# Extraction de la strategie de cache depuis le en-tête http créé précédemment
if (req.http.X_CACHE_MODE)
{
# Stratégie de cache au niveau "user".
# on hash donc la ressource sur le nom du user extrait du cookie.
if (req.http.X_CACHE_MODE == "user")
{
if (req.http.Cookie ~ "user=")
{
set req.http.X-CACHE-USER = regsub( req.http.Cookie, "^.*?user=([^;]*);*.*$", "12" );
hash_data(req.http.X-CACHE-USER);
}
}
# Stratégie de cache au niveau "rôles"
# on hash donc la ressource sur le nom
# du group (un groupe étant commun à plusieurs utilisateur)
if (req.http.X_CACHE_MODE == "roles")
{
if (req.http.Cookie ~ "roles=")
{
set req.http.X-CACHE-ROLES = regsub( req.http.Cookie, "^.*?roles=([^;]*);*.*$", "12" );
hash_data(req.http.X-CACHE-ROLES);
}
}
}
[...]
}
Memcache
Affiner la stratégie de caching applicatif
• Une instance par type de
Front
Front Front
Front
données,
• Flush partiel vs flush global
DRUPAL
DRUPAL DRUPAL
DRUPAL • 3 containers pour Médiapart :
MC HTML actif MC HTML actif
– HTML,
MC HTML actif MC HTML actif
– Data diverse,
MC Data actif
MC Data actif MC Data actif
MC Data actif – Session.
… possible d’aller plus loin
(pages, vues, blocs, menus,
BackEnd
BackEnd BackEnd
BackEnd filtres, etc.)
MC Data actif
MC Data actif MC Session actif
MC Session actif
MC Session passif
MC Session passif MC Data passif
MC Data passif • Bien choisir la place dans
MC Lock actif
MC Lock actif MC Lock passif
MC Lock passif l’infrastructure (local vs
remote)
• Warning sur la charge réseau
Optimiser Drupal
• Utiliser l’API !
• Attention à l’abus de modules
• Dans les modules custom : penser cache.
• Compilation des assets css et js minifiés
(agrégation et compression)
• Développer en E_STRICT
• DBlog en pré-production, syslog en production
• Pas de requêtes dans les thèmes
PHP & memcache
Optimiser l’environnement d’exécution
• Apache : KeepAlive, MaxClients, MaxRequestsPerChild
• Nginx + FPM : pour la faible empreinte applicative (et + FPM)
• PHP Web <> PHP CLI
• Memcache : memory limit
Nb de req par sec -> nombre de connexions simultanées -> lié au
nombre de FD par user et espace mémoire
Nb d'évictions -> ajuster le max memory
Optimisation de l’utilisation des pages des slabs
MySQL
Optimiser l’environnement d’exécution
• Utiliser innoDB pour :
– rester en transactionnel
– minimiser les locks,
– plus de tolérance à la concurrence.
• innoDB tout en RAM (innodb buffer pool size au max)
• Avec les empreintes MySQL Report & Tuner, on ajuste le nombre
de connexions maxi en fonction de la mémoire,
• Les tables ne sont jamais très grosses
• Pb sur les tables de commentaires
• Sharder et/ou partitionner si nécessaire (par année ou par mois)
• Répartition lecture et faire une partie des lectures dans SolR.
• Pensez au NoSQL ou indexation pour vos nouveaux modules
Monitorer pour optimiser
• Check HTTP
• XHProf :
– Toutes les « n » requêtes,
– Sur un max_execution_time
• Xdebug/Webgrind en mode
trigger pour troubleshooter avec
un watchdog qui simule une visite
via lib curl
• Pinba (mod PHP et mod nginx =>
pour faire remonter le monitoring
applicatif au niveau du serveur
web),
• Newrelic,
• Plugin munin
• Log Watchdog dans mongoDB
pour les logs
Monitorer
l’environnement
• Varnish : Hit ratio,
Remplissage des
différentes instances, etc.
• Memcache : Sessions, %
hits/sec, %req/sec,
%evictions/sec; etc.
• MySQL : Template
Percona
Chargez !
JMeter pour voir si ça tient
1/ Home (anonyme)
2/ Authentification
⇒Génération dynamique du form_build_id pour les variables
du POST http
Utilisation d’un pré-processeur beanshell (un composant
Jmeter) pour l’exécution d’un script php avecla logique Drupal
pour générer des id de formulaire unique.
⇒Extraction des login/password depuis un .csv contenant
l’ensembles des comptes.
3/ Articles
⇒Boucle sur un ensemble d’url d’article extraites depuis
un .csv
Affichage de la page1, page2 et des commentaires.
⇒Post d’un commentaire
4/ Blog
⇒Boucle sur un ensemble d’url de blog extraite depuis un
.csv
⇒Post d’un billet de blog
Parsing du formulaire html pour l’extraction d’un id de
formulaire unique. (via l’utilisation d’un regexp sur la page
html).
5/ Déconnexion
Piloter vos déploiements
• Versionner :
CVS/SVN/Git
• Déployer : Capistrano
• Drush : Mise à jour du
schéma des bases en
fonction du modèle, etc.
• Piloter : Drush & Varnish
– Warning bcp de choses en
base => Coupler
Capistrano pour activer
Drush : Déploiement de
fichiers + commande Drush
(versioné dans Scripts
shell)