TeDomum

toutcasser

C'était un samedi presque comme les autres.

On aurait amplement de quoi romancer les derniers incidents techniques du côté de TeDomum tant les faits sont grandioses et grotesques à la fois, on se contentera de les mettre en scène à fin de propagande. Promis, nous sommes très étrangers au climat politique mais c'est un fait : nous cherchons des bénévoles, et vous pouvez naviguer à la fin de ce billet directement si vous n'avez pas le courage de poursuivre dans les détails.

Tout commençait donc en décembre 2019, attablés en terrasse fermée d'un boulevard parisien nous préparions l'assemblée générale. Tandis que le cortège manifestait dehors, nous débattions de l'avenir de l'association, se professionnaliser et offrir des services payants ou bien réduire la voilure, et nous prenions la direction raisonnable. Voir plus petit c'était d'abord diminuer notre consommation en datacenter, puis rapatrier l'ensemble de nos services sur des machines de récupération : responsable, conforme à nos valeurs, pérenne et techniquement intéressant. Des années devant nous de travaux techniques étaient actées.

Avril 2020, quand la généralisation du télétravail ne mobilisait pas tous nos moyens à soutenir Jitsi Meet et autres services très sollicités, elle nous offrait l'opportunité de mettre le plan en action : un mois plus tard nous avions divisé par 3 nos ressources et d'autant le coût de fonctionnement. Nous préparions dans l'ombre le futur hébergement à la maison sur fonds de ACIDES, Hepto et Kubernetes.

Évidemment réduire la cadence n'est pas sans conséquences. Nous devons aussi réduire nos besoins sans supprimer de service jugé utile au plus grand nombre. Au centre de la cible notre consommation de RAM, le débit d'écriture sur les disques et l'espace en général. Nous nous attaquions à la mémoire quelques jours afin de tailler les giga nécessaires pour tout migrer dans les moules plus petits, puis nous concentrions nos efforts sur le gros morceau : les IO disque. C'est un peu plus d'un mois de travail qu'il a fallu, à optimiser les bases de données mais aussi à modifier les applications pour diminuer l'intensité d'utilisation de nos disques (qui aurait cru que TTRSS parcourait plusieurs fois l'ensemble de sa base à chaque mise à jour de flux ?).

Bien entendu nos objectifs ne s'arrêtaient pas là mais les opérations ont laissé la place aux développements de hepto, hiboo et kity que nous vous présentions ce début d'année. Seulement voilà : migrer tout un CHATONS vers un cluster hepto encore balbutiant n'est pas simple. D'abord il faut former les administrateurs à Kubernetes, mais surtout les spécificités d'un cluster réparti géographiquement sont autant d'embûche sur le chemin des déploiements. Aussi le stockage fichiers de la majorité de nos services doit être repensé, mais notre emploi des bases de données aussi sur fond de développement d'un nouveau contrôleur de stockage Kubernetes, hicso. Faisons court : pour espérer maintenir l'ensemble, nos bases relationnelles hétéroclites doivent converger vers une technologie unique où nous concentrerons nos efforts.

Le temps de cette réflexion n'est pas sans son lot de maintenance et de surprises sur notre infrastructure existante. Entre autres pannes et mises à jour, le train réduit approche de sa capacité maximale à mesure que nous rejoignent de nouvelles âmes. Les travaux sur la RAM sont faciles a poursuivre mais l'espace disque est de plus en plus complexe à optimiser. En cause, notre mécanisme de sauvegarde des bases de données. Pour chaque technologie de bases (principalement Mariadb et PostgreSQL), nous sauvegardons les journaux binaires sur disque, découpés en segments que nous intégrons au reste de nos sauvegardes de fichiers. Cette approche ayant le mérite d'être simple de mise en place nous permet, en combinaison avec des copies intégrales régulières, de conserver l'historique détaillé de nos bases et donc de restaurer au besoin à un instant donné dans le passé. Les gens bien appellent ça du PITR, point in time recovery ; c'est bien utile lorsqu'une application fait défaut et corrompt sa base pendant plusieurs heures ou plusieurs jours. Nous bénéficions à ces fins directement de notre sauvegarde automatique de fichiers basée sur restic. Inconvénient majeur : les journaux de transactions binaires, ça prend de la place, près de la moitié de l'occupation sur nos disques que nous compensons par des nettoyages manuels pénibles.

Enters wal-g. Dans un effort commun pour gagner en RAM et en espace disque nous repensons notre usage des bases de données et l'orientons par l'occasion vers une migration dans kity. De nos nombreux serveurs de bases (pas moins de 17 dénombrés !) nous n'en conservons qu'un, sur une technologie unique. C'est un travail de longue haleine juste entamé, qui implique de mettre à jour plusieurs applications pour supporter PostgreSQL. À la clé non seulement des ressources mieux partagées entre les bases, mais aussi l'opportunité de déployer des configurations plus complexes comme nous n'avons pas à les dupliquer. Ainsi, nous avons pris le parti de construire nos propres images Docker PostgreSQL intégrant des composants pour préparer l'avenir : pglogical, repmgr, et... wal-g.

Ré-écriture du fabuleux wal-e en perte de vitesse, nous guettons wal-g depuis quelques mois afin de transférer directement les journaux de transaction (WAL) PostgreSQL chiffrés et compressés vers nos serveurs de sauvegarde. Fini la copie locale sauvegardée par restic, c'est moins de défauts, des sauvegardes plus immédiates, et surtout des centaines de gigas épargnés sur les disques. Notre configuration est simple et publique, notre plan d'attaque l'était tout autant : en parallèle concentrer l'ensemble des bases sur un unique cluster PostgreSQL et développer notre image intégrant wal-g ainsi que sa configuration type.

Le 5 juin tout était prêt pour basculer sur l'image fraîche et activer wal-g. La bascule était testée, et comme l'architecture et la version de PostgreSQL étaient identiques, pas de difficulté anticipée à conserver le dossier de données en l'état. C'est bien entendu sans compter sur un oubli majeur : d'un côté PostgreSQL est linké sur la glibc, de l'autre sur musl libc d'une distribution Alpine. Bien que les interfaces d'accès fichier soient strictement identiques et n'interfèrent donc absolument pas avec le format de stockage des bases, des différences existent dans la manipulation de chaînes UTF-8, impactant le format des index de tables. C'est ainsi, alors que la majorité des services ont repris un fonctionnement nominal, que certains index ont commencé à défaillir, retournant des résultat instables voire faux à des requêtes sur les clés indexées. Le résultat est d'autant plus catastrophique que le défaut a subsisté plusieurs heures : ici une utilisatrice existant en base s'est vu créer un compte dupliqué masquant ses données, là des pouets, messages Matrix ou flux RSS ont été enregistrés en double ou en triple, ou bien encore des tâches planifiées n'ont jamais enregistré leur résultat.

Au bilan, il a fallu pas moins de 48 heures, application par application, pour réparer manuellement ces défauts en fonction de la meilleure approche au cas par cas. À suivre quelques exemples de requêtes PostgreSQL qui nous ont sauvé la vie pour dédupliquer des lignes dans des tables aux index corrompus.

# Identifier les lignes dupliquées
# ctid est une colonne spéciale retournant un identifiant technique de ligne interne à PostgreSQL, toujours différent y compris en cas d'insertion multiple de la même ligne exactement
SELECT ctid
FROM 
  (SELECT ctid, ROW_NUMBER()
   OVER( PARTITION BY id ORDER BY ctid )
   AS cnt 
   FROM table) t
WHERE cnt > 1;

# Identifier les duplicatas pour une clé
SELECT ctid, key
FROM table
WHERE key = 'value';

# Supprimer sur la base du ROW_NUMBER
DELETE FROM table
WHERE ctid IN
(SELECT ctid
FROM 
  (SELECT ctid, ROW_NUMBER()
   OVER( PARTITION BY id ORDER BY ctid )
   AS cnt 
   FROM table) t
WHERE cnt > 1);

Pour satisfaire les curiosités, notre stratégie était la suivante : – comme le mal était largement fait, ne pas interrompre les applications dans l'espoir de sauver quoi que ce soit puisque la plupart (Peertube faisant exception) survivaient très bien sur des index affreusement incomplets ; – pour tous les comptes en doublon, comme aucun n'avait servi réellement, supprimer l'ensemble des comptes créés après le début de l'incident ; – pour les contenus peu critiques comme la fédiverse ou les flux RSS, supprimer les entrées les plus récentes en conflit, c'est ainsi que quelques centaines de pouets ne sont pas correctement reliés à leurs hashtags ou à leur thread ; – pour Matrix, reconstruire les informations critiques (qui a quel rôle dans un salon par exemple) à la main à partir des événements réellement reçus une fois dédoublonnés.

Les leçons de cet exercice périlleux qui aurait pu nous coûter plus cher ? D'abord évidemment tester en conditions réelles comme toujours, bien que nous pensions sincèrement avoir mis l'adage à l'épreuve. Ensuite et surtout nous ne sommes plus en nombre ni pour faire face à ce type d'événement ni pour soutenir le rythme d'évolution de nos infrastructures vers kity, qui a pris près d'un an de retard. Si nous avions la force, nous aurions déjà migré largement et ne chercherions pas sans cesse les optimisations d'une architecture vieillissante. Si nous avions la disponibilité, des 5 personnes privilégiées sur le serveur plusieurs auraient pu intervenir plus tôt et plus vite afin d'atténuer les dégâts.

Ce n'est bien entendu qu'un exemple et nous pourrions narrer encore cette semaine les attaques globales contre le réseau Matrix qui ont impacté notre serveur et présagent quelques jours laborieux de nettoyage de données et autres festivités.

Pour toutes ces raisons nous avons besoin de vous et de votre volonté bénévole. Deux administrateurs peu actifs les derniers mois laissent aujourd'hui leur place de sorte que nous puissions renforcer l'équipe. Aussi, si vous mourrez d'envie de jongler comme nous sur le fil de chantiers et d'incidents tels que ceux relatés plus haut, si vous n'avez pas peur de #toutcasser le front perlant sous la tension, c'est sans hésitation et sans timidité que vous pouvez nous contacter. Comme nos capacités de formation et d'intégrations de nouveaux membres ne sont pas infinies, une petite idée concrète des profils que nous recherchons :

  • un·e administrateur·rice application, entretenant quotidiennement les services, suivant et appliquant les mises à jour et intervenant sur les incidents simples ; nous pouvons vous former si vous avez le goût du numérique et déjà effleuré le monde GNU/Linux ;
  • un·e administrateur·rice système intervenant sur nos serveurs, contribuant aux chantiers de rénovation, menant les maintenances et intervenant en cas d'incident majeur ; votre expérience de l'administration Debian, de Docker, PostgreSQL et du Web en général sont plus que bienvenues car nos disponibilités sont modestes pour vous former, nous pouvons contribuer à financer des formations en ligne si besoin ;
  • un·e administrateur·rice et développeur·se (il paraît même qu'on dit devops !) contribuant à notre migration vers kity, y assurant progressivement la maintenance et intervenant sur les incidents ; il faut pour cela avoir de l'expérience avec Docker, et idéalement quelques bases dans le monde Kubernetes ; nous saurons nous former ensemble à mesure que nous découvrirons ce monde également !

Si vos CV et lettre de motivation sont prêts, vous pouvez les ranger et venir échanger directement sur les salons de messagerie : il n'est pas question de vous mettre à l'épreuve ou de conduire des entretiens, mais bien d'accueillir votre bonne volonté à bras ouverts.