Written by

Le mercredi 17 juillet 2013 à 0h02 (UTC+2), une corruption de système de fichiers s’est produite sur le serveur mysql1. Toutes les bases de données hébergées sur ce serveur ont été indisponibles durant la nuit, et une faible proportion d’entre elles ont été victimes de corruption de données.

Ce billet revient en détails sur cet incident : ce qu’il s’est passé et les enseignements que nous en tirons. Nous avons essayé d’utiliser des termes compréhensibles par tous, tout en gardant autant de détails techniques que possible.

Vous pouvez également relire le compte-rendu en direct de l’incident.

Ce qu’il s’est passé

Utilisation de bcache

Commençons par préciser que le matériel du serveur mysql1 a été changé le 7 juillet afin de l’équiper à la fois de disques SSD et de disques mécaniques. Le serveur était auparavant uniquement pourvu des disques SSD, mais l’augmentation croissante de l’espace disque a justifié cette évolution.

bcache est une technologie intégrée récemment à Linux et qui permet de combiner des disques SSD et mécaniques afin d’obtenir un espace disque important et des performances proches de celles d’un SSD. Nous reviendrons en détails sur le choix de cette technologie dans la seconde partie du billet.

Défaillance d’un disque

Le 16 juillet à 3h29, un des disques du serveur a connu un problème matériel. Le disque a automatiquement été éjecté du RAID1, et le serveur a continué à fonctionner normalement sur le second disque — c’est le principe même du RAID. Ce genre de problème n’est pas rare. Deux possibilités :

  • soit le disque est définitivement hors service, auquel cas il faudra le remplacer ;
  • soit le disque a connu un problème temporaire, et il suffit de le « redémarrer » pour qu’il fonctionne à nouveau normalement.

Notre équipe a pris connaissance de l’incident le matin — aucune astreinte n’a été déclenchée, puisque cela n’a aucun impact sur le bon fonctionnement du serveur. Nous avons décidé de programmer une intervention pour le soir même (c’est une opération qui nécessite de mettre le serveur hors service pendant quelques minutes, nous préférons donc le faire à des heures creuses).

Redémarrage du serveur

À 23h52, nous redémarrons le serveur mysql1. Nous constatons que le disque qui était sorti du RAID fonctionne à nouveau normalement. Mais immédiatement, nous observons une corruption du système de fichiers (XFS) sur lequel les bases de données sont stockées. Après une investigation rapide, nous comprenons ce qu’il vient de se passer.

Les bases de données sont stockées sur un pseudo-disque bcache, combinaison virtuelle de nos disques mécaniques et de nos SSD. Lorsque Linux démarre, bcache scanne l’ensemble des disques physiques à la recherche d’une signature. Cette signature, lorsqu’elle est détectée, signifie que ce disque fait partie d’un pseudo-disque bcache. bcache combine ensuite les disques trouvés et crée le pseudo-disque. Ces signatures sont installées une fois pour toutes à l’installation du système.

Nos deux disques mécaniques étaient en RAID1, ce qui veut dire que les deux disques étaient strictement identiques et synchronisés. C’est, bien entendu, le principe du RAID1 : si un disque tombe en panne, l’autre prend la relève puisqu’il est sa copie conforme. Dans le cas de mysql1, à 23h52, nous avions donc :

  • le disque défaillant qui était resté dans son état de 3h29 (avec des données « périmées ») ;
  • le second disque qui contenait les « vraies » données, à savoir les plus récentes.

Les deux disques avaient donc, du point de vue de bcache, des signatures identiques. Au redémarrage du serveur, bcache a donc assemblé les SSD avec le disque défaillant (le premier trouvé) plutôt que le second disque, à jour. Or les SSD contenaient des données récentes. Nous avions donc un pseudo-disque qui était la combinaison de certaines données de 23h52 (SSD) et de données de 3h29 (disque défaillant).

Évidemment, ce pseudo-disque ne contenait alors pas des données cohérentes, d’où la corruption de fichiers. Nous avons immédiatement démonté le système de fichiers pour recombiner les SSD avec le second disque, avec les données à jour. Mais c’était trop tard : le simple fait d’avoir préalablement monté le pseudo-disque bcache avec le disque défaillant a entrainé des écritures sur les SSD. Les données des SSD n’étaient alors plus cohérentes avec le second disque.

Restauration des données

Malgré les corruptions du système de fichiers, une très grande proportion des données restait lisible. Nous avons donc pris la décision de restaurer autant que possible les données à jour, plutôt que de restaurer les sauvegardes de la veille pour tout le monde.

La première étape a été de copier l’ensemble des données (300 Go) sur un serveur temporaire. Par sécurité, nous avons copié une seconde fois les données, sur un second serveur temporaire. Les données du premier serveur ont été utilisées pour démarrer MySQL (accessible uniquement en interne) et essayer de récupérer le maximum de bases possible, ce qui a entrainé des modifications sur les fichiers. Les données du second serveur n’étaient là que pour nous permettre de recommencer le processus, si besoin.

La restauration des données (qui consistait essentiellement à exécuter “mysqldump” sur chaque base) a pris du temps : la corruption a entraîné de nombreux plantages de MySQL, ralentissant l’opération. Une fois que nous avions un dump SQL pour la grande majorité des bases (96 %), nous avons pu démarrer la restauration finale sur mysql1 (reformaté entre temps). Les 4 % de bases corrompues ont ensuite été restaurées depuis les sauvegardes de la veille.

Dans certains rares cas, les données récupérées que nous pensions saines étaient finalement en partie corrompues. Les clients concernés ont pu nous contacter pour que nous restaurions alors une sauvegarde.

Diminuer les risques à l’avenir

Commençons d’emblée par dire que la perte de données est un risque qu’il est impossible de réduire à zéro. Les causes peuvent être multiples : bug, erreur humaine, problème matériel, accident majeur, etc. C’est la raison pour laquelle les sauvegardes existent. Cela ne veut toutefois pas dire qu’on ne peut pas réduire davantage les risques.

Utilisation de technologies jeunes

L’incident a pour origine un bug dans bcache. Il est donc légitime de se demander si l’utilisation de bcache, une technologie relativement jeune, était judicieux.

En règle générale, plus une technologie est jeune, plus le risque qu’elle présente des dysfonctionnements (mineurs ou majeurs) est élevé. Il faut donc juger si les avantages qu’apportent la technologie justifient la prise de risque. Naturellement, ce jugement évolue au fil du temps : plus la technologie mûrit, plus les risques diminuent.

En ce qui concerne bcache, les avantages sont importants : permettre d’augmenter considérablement les performances sans sacrifier l’espace disque. Rappelons que contrairement à la majorité des hébergeurs, nous n’imposons aucune limite sur la taille des bases de données.

Nous suivons l’évolution des technologies hybrides SSD/disque mécanique depuis déjà longtemps. Il en existe de nombreuses, au-delà de bcache : dm-cacheEnhanceIOFlashCacheCacheCadebtier pour n’en citer qu’une partie. En début d’année, nous avons testé et comparé « en laboratoire » plusieurs de ces solutions. Au terme de ces tests, nous avons décidé d’utiliser bcache sur un serveur de pseudo-production : http11, sur lequel nous avons migré une poignée de clients volontaires. Pendant 4 mois, ce serveur a tourné avec bcache sans présenter le moindre problème, tout en tenant ses promesses de performances accrues.

Précisons que bcache, développé par un ingénieur de Google, existe depuis 2010, et est considéré comme stable par son auteur depuis 2011 — date de la dernière découverte d’un bug pouvant provoquer des corruptions de données. Utilisé en production par de nombreuses personnes et organisations depuis, il a été intégré à Linux en 2013 au terme d’une longue et sévère revue de code par plusieurs développeurs seniors de Linux.

Notre décision d’utiliser bcache en production est parfaitement assumée et nous n’estimons pas qu’il s’agisse d’une faute. Le bug, que nous avons depuis remonté aux développeurs, a été corrigé moins de 2h après, un dimanche soir. Une telle réactivité n’est qu’un atout supplémentaire pour bcache.

Améliorer la fiabilité de notre matériel

Deux éléments matériels ont, indirectement, conduit au bug :

  • le fait que le disque ait rencontré un problème temporaire qui a provoqué son éjection du RAID ;
  • le fait que nos châssis ne soient pas équipés de hot swap, nous obligeant à redémarrer le serveur pour relancer le disque (ou à débrancher un serveur pour remplacer un disque).

Nous allons remplacer l’ensemble de nos serveurs au cours des prochains mois. Parmi leurs avantages, ils seront encore plus fiables qu’actuellement. Nous aurons l’occasion de revenir en détails sur cette migration dans quelques semaines.

Ajouter une option de redondance

Plusieurs clients souhaiteraient que nous proposions une option de redondance master/slave qui permettrait de pallier certains types de pannes, dont l’incident que nous avons eu. Cette option a nécessairement un coût non négligeable.

Dites-nous dans les commentaires si vous seriez intéressé par une telle option, qui augmenterait le prix d’un pack d’environ 50 %.

En conclusion

Environ 300 bases de données MySQL ont été corrompues et ont dû être restaurées depuis les sauvegardes de la veille, ce qui représente moins de 4 % des bases stockées sur mysql1, et moins de 1,5 % de l’ensemble des bases MySQL. Chaque client concerné a été contacté par mail, et nous leur réitérons nos excuses.

Nous préparons de gros changements internes destinés à améliorer encore davantage notre fiabilité, déjà très élevée. Vous en saurez plus très bientôt.

N’hésitez pas à nous faire vos retours et suggestions en commentaires. Nous sommes toujours preneurs. Nous remercions au passage tous ceux qui nous ont apporté leur soutien durant cet incident, c’est toujours apprécié.

Nous souhaitons enfin d’excellentes vacances à tous ceux qui s’apprêtent à en prendre — et bon courage à ceux qui en reviennent :)