Maintenance terminée

Texte alternatif non fourni

Illustration par DALL·E

  1. Contexte
    1. Mac mini M1
    2. Awow AK-34
    3. Minisforum U-820
  2. Implications
  3. C'est la cata
  4. Résolution du problème
  5. Post-mortem
  6. Conclusion

Anticiper les problèmes ne signifie pas que l'on connaisse systématiquement à l'avance le genre de problèmes que l'on va rencontrer. Et, évidemment, j'ai connu un problème majeur pendant ma dernière maintenance planifiée.

Contexte

Mon réseau

Blog

Le but ultime de la maintenance était d'intégrer mon Mac mini M1 au réseau en tant que serveur principal, en remplacement de mon minisforum U-820. Du coup, j'ai vu une opportunité d'utiliser ce dernier en tant que routeur à la place du Awow AK-34, qui deviendrait mon reverse-proxy.

Mac mini M1

Depuis que je dispose du M2, je me gardais le M1 au chaud pour l'intégrer au réseau en tant que serveur principal, c'est-à-dire celui qui stocke tous mes fichiers dans ma forge Gitea, en plus d'héberger tous mes services. Remplacer le minisforum U820 par le Mac mini m'offrirait un gain de performances (même si le U820 était loin d'être surmené) sans compromettre ma consommation d'énergie.

En outre, je voulais tester deux configurations : l'une où nix serait installé dans macOS, et l'autre où j'effectuerais une installation native de NixOS. Contre toute attente, la première configuration s'est révélée peu pratique à l'usage, alors que la seconde a été plus facile que prévu... Il m'a suffi de suivre consciencieusement la documentation pour que je me retrouve avec un environnement serveur familier.

Je dispose d'un Mac mini M2 en plus du M1 : si j'avais fait une connerie, j'aurais pu la rattraper. Je décline toute responsabilité si vous décidez d'installer NixOS sur un Mac avec cette méthode.

Awow AK-34

Cette machine a toujours été mon routeur ; je l'ai achetée pour ça. Mais avec le temps (et mes envies du moment), je me suis rendu compte que c'était le "maillon faible" de mon réseau. En effet, activer l'IPS/IDS (Suricata) faisait considérablement chuter mon débit maximal atteignable sur le réseau.

L'exemple le plus frappant est lors de l'installation d'un jeu : sans Suricata, je sature le lien gigabit. Avec Suricata, je plafonne au tiers (environ 300Mb/s). Je n'aime pas l'idée de ne pas pouvoir utiliser mon matériel "à fond"...

Son petit Celeron N3450 se trouve bien plus à l'aise dans son nouveau rôle de reverse-proxy. La création de ce rôle est peut-être l'élément le plus significatif de cette maintenance (après l'intégration du Mac mini évidemment) : jusqu'à présent, puisque le serveur principal centralisait cette fonctionnalité spécifique, il suffisait d'un reboot pour que plusieurs services dépendant d'un serveur web soient momentanément inaccessibles, même si je ne changeais rien au serveur web.

Le plus embêtant était sans doute Home Assistant, hébergé sur une autre machine physique mais proxifié pour un accès extérieur, et, ma station météo qui a besoin d'envoyer ses données à un nom de domaine et non à une adresse IP. Donc, si, en pleine nuit, je bidouillais mon serveur web, je risquais de me retrouver dans le noir parce que ma station météo devenait incapable de contacter Home Assistant, qui en retour était incapable de connaître la luminance extérieure et ainsi éteignait les lumières...

Du coup, insérer un reverse-proxy dans la chaîne me permet de faire toutes les bidouilles que je veux sur le serveur principal, sans risquer un effet de bord qui entraînerait la panne d'un service qui n'a rien à voir avec ma bidouille. Je touche moins souvent au reverse-proxy : une fois configuré, je peux l'oublier.

Minisforum U-820

Je le désignais jusqu'alors comme mon "serveur préféré", notamment en raison de sa puissance (Core i5 8259U, 4 cœurs/8 threads), et il s'est montré largement à la hauteur de mes attentes (et de son prix puisqu'à l'époque, je l'ai payé plus de 500€).

Il est doté d'un port ethernet 2.5Gb/s. En pratique, ça ne me sert absolument à rien : c'est le seul élément de mon réseau avec la freebox Pop à pouvoir atteindre ces vitesses. Mais c'est surtout son CPU qui m'intéresse pour le rôle de routeur. Et là encore, il se montre redoutable puisque, même avec Suricata actif, je peux saturer tranquillement mon lien réseau en toute circonstance, et sans entraîner de charge CPU. C'est assez bluffant de voir à quel point, dans ce contexte précis, le Core i5 surpasse le Celeron.

Une telle puissance va m'ouvrir la porte à davantage d'expérimentations avec OPNsense. Je rêve déjà à l'installation d'un pare-feu applicatif et autres améliorations en termes de sécurité.

J'ai eu la surprise, à la réinstallation d'OPNsense, de voir qu'un paquet était disponible pour installer mon serveur Web fétiche : Caddy. Malheureusement, mes tentatives de configuration se sont révélées infructueuses pour le moment. Trop de paramètres modifiés en une seule fois : je verrai ça plus tard. L'essentiel étant que ce Minisforum va vivre une deuxième vie en tant que routeur, et vu les performances et sa consommation contenue, il va durer longtemps à ce poste.

Implications

L'intégration d'une seule machine va m'imposer d'en (ré)installer trois en même temps. Inutile d'être un expert en sysadmin pour comprendre que c'est risqué et déconseillé. Mais il faut bien comprendre que :

  • je ne gagne pas d'argent avec mon réseau
  • si je fais une connerie, je perds essentiellement du temps (et parfois des données)
  • quoi qu'il arrive, j'apprends de nouvelles choses

Je ne ferai jamais quelque chose d'aussi "tête-brûlée" en entreprise, mais à la maison, je m'en fous. Je m'en fous d'autant que j'ai des backups dans tous les coins.

Oui, sauf que, si mes backups sont foutues...

C'est la cata

Tout content que je suis d'avoir tout installé et que tout fonctionne extrêmement bien (en apparence), j'ai eu un gros moment de panique en voyant des erreurs quand j'essayais d'accéder à certains fichiers depuis Gitea. De grosses erreurs 500, concernant manifestement LFS. Sauf que dans les copies locales de mes dépôts, celles-là même que j'ai utilisé pour restaurer les dépôts sur Gitea, mes fichiers sont parfaitement lisibles.

Je vous passe les détails parce que je ne me souviens plus de tout ce que j'ai fait, mais mes tentatives de mise à jour de mes dépôts sur Gitea se sont soldées par une corruption de mes dépôts locaux. En gros, certains "vrais" fichiers ont été simplement remplacés par leur pointeur LFS, sauf que dans la panique, les "vrais" fichiers n'existaient plus. Je n'ai pas la moindre idée si j'emploie le bon vocabulaire, et je ne saurai pas vous expliquer plus en détails ce qu'il s'est réellement passé, mais une chose est sûre : j'ai fait une grosse connerie, et j'ai flingué mes dépôts locaux.

Pas de problème, je vais piocher dans une sauvegarde.

Ah, bah non en fait. La connerie que j'ai faite avec LFS ne date pas de ma maintenance, mais d'il y a bien plus longtemps. Trop longtemps.

Les experts de git et de LFS ne comprendront sans doute pas ce que j'ai pu faire pour ne jamais m'en rendre compte, et je ne peux pas les blâmer pour ça. C'est passé inaperçu, c'est tout, c'est comme ça. Et le pire, c'est que plus j'essaye de trouver une solution, plus je risque de corrompre de nouveaux fichiers.

Pour vous donner une idée du volume que ça représente, j'ai plus d'une cinquantaine de dépôts, et plusieurs dizaines de milliers de fichiers stockés avec LFS. Donc à chaque tentative de restauration, je fais monter la pression d'un cran...

À ce stade, on essaye de mesurer l'étendue des dégâts, et, fort heureusement, seuls trois dépôts ont été impactés, dont deux où seuls quelques fichiers ont été perdus (mais il m'est possible de les recréer facilement).

Un dépôt en revanche a morflé : celui de mon blog. 5900 fichiers d'images irrémédiablement corrompus. En vrai, deux tiers de ces fichiers sont simplement des miniatures de différentes tailles, mais pour les re-générer, il me faut les fichiers d'origine...

Résolution du problème

Il m'a fallu quitter l'écran quelques heures pour trouver une solution : il "suffit" de récupérer les fichiers depuis l'instance publiée de mon blog. Non, il ne s'agit pas de faire de simples copier/coller.

Dans le markdown de mes articles, j'utilise un composant Blade pour insérer une pièce jointe, auquel je passe un attribut ref contenant une chaîne aléatoire de six caractères.

Dans le système de métadonnées que j'ai créé, il suffit de déposer un fichier dans le bon dossier pour que mon application génère automatiquement tout ce dont j'ai besoin. Au moment du rendu HTML du markdown source, ce composant Blade est transformé en un autre, approprié au type de fichier. L'instanciation de ce nouveau composant déclenche la copie des fichiers liés (c'est-à-dire, l'image d'origine et les différentes miniatures) dans le répertoire de stockage de Laravel. Mais, afin de ne pas avoir à gérer des chemins relatifs (ce qui me serait difficilement faisable pour différentes raisons) et pour éviter de stocker des fichiers en double, je calcule le md5 de chaque fichier et le place dans une arborescence où chaque dossier correspond à quatre caractères de ce hash.

Ce système est très efficace, ce n'est pas pour rien que je l'utilise depuis des années dans différents projets. Il présente par contre un inconvénient : si le fichier d'origine n'existe plus, on ne peut pas calculer son md5, et donc on ne peut pas retrouver où il est stocké. Note pour plus tard : stocker le md5 dans les métadonnées...

Par contre, j'ai eu la bonne idée d'organiser mes markdown exactement de la même façon que comme ils sont présentés : l'article blog/2024/05/04/maintenance-terminee est généré de sorte que son HTML se trouve dans blog/2024/05/04/maintenance-terminee/index.html. Du coup, si j'inspecte le HTML des pages de mon blog en production, je peux savoir à quel article appartiennent les fichiers qui existent dans cette page. Il me suffit de rechercher les balises <figure>, et d'y chercher les fichiers dont l'URL commence par /storage. DomDocument et xpath ont considérablement aidé pour cette tâche et l'ont rendue presque triviale.

En comparant l'ordre dans lequel ces fichiers apparaissent avec l'ordre dans lequel j'ai créé mes balises x-attachment dans mon markdown, et en prenant en considération que l'image d'en-tête n'y figure pas (pas plus que certaines images additionnelles), j'ai fini par pouvoir associer un fichier spécifique à sa référence dans mes métadonnées. De là, il n'y avait plus qu'à copier le fichier de mon blog en production vers son emplacement attendu dans les sources de l'article correspondant. Ensuite, une "petite" réparation des articles (consistant essentiellement en la recréation des miniatures), et "le tour est joué".

Post-mortem

Maintenant que la crise est passée, je me dis qu'il y a eu plus de peur que de mal. Le moment où l'on constate la perte irrévocable de données est glaçant, terrifiant. Mais après ce moment, on commence à réfléchir. Et, souvent, on arrive à faire preuve d'astuce.

J'ai abondamment utilisé ChatGPT au cours de cette "crise". Il m'a été d'une aide précieuse, pertinente et adaptée. Il m'a permis d'écrire une grosse partie du code "trivial" qui allait me "sauver la vie", un code que j'aurai pu écrire moi-même si la panique n'avait pas été aussi forte. Il m'a aussi permis de sauver une grosse partie des données que je pensais corrompues, afin que je n'aie à déplorer que la perte de quelques fichiers de moindre importance dans seulement deux dépôts. Sans ChatGPT, j'aurai peut-être tellement paniqué que j'aurai perdu bien plus que ça. Sans ChatGPT et donc sans les gens qui ont produit les documents qui l'ont alimenté.

Mais il m'a aussi avoué ses limites : l'idée de créer un script de récupération à partir de mon blog en production m'est venue au moment où il se confondait en excuses devant son incapacité de me suggérer autre chose que l'acceptation de la perte définitive de mes données. Un moment en pleine vallée de l'étrange d'ailleurs, un instant suspendu entre l'appréciation de sa compassion et le rappel que ce n'est qu'un algorithme.

Je ne vais pas me faire donneur de leçon parce que, moi-même, je suis incapable de suivre ce conseil simple : ne jamais travailler sur plusieurs machines en même temps. Toutefois, il aurait été difficile de faire autrement dans ce cas précis, où l'on interverti les rôles d'au moins deux machines. On pourra toujours me suggérer des choses : virtualiser, dockeriser, héberger dans le cloud, etc. Acheter une nouvelle machine ou utiliser une vieille machine de façon temporaire, ou que sais-je encore. Oui, dans un contexte pro. À la maison, on fait avec ce qu'on a.

Un autre mémo que je pourrais faire concerne les sauvegardes. On ne le répètera jamais assez, et, pourtant, c'est plus compliqué qu'il n'y parait : il faut tester ses sauvegardes. Et, là encore, on me donnera l'argument de la virtualisation, de la dockerisation ou de la cloudification, afin d'avoir une infra qu'on peut péter à volonté. Oui, dans un contexte pro. À la maison, je suis un bidouilleur. Si, un jour, je gagne de l'argent avec mon blog, on verra pour quelque chose de plus robuste. En attendant, je bidouille parce que ça me plaît, pas parce que j'en ai l'obligation.

Donc, oui, je m'expose à plein de choses qui empêcheraient certains de dormir. Oui, je fais des choses que certains considèrent comme de l'overengineering. Non, je ne respecte pas toujours les bonnes pratiques en termes de sysadmin (essentiellement par manque de moyens techniques). Mais je m'amuse, parfois je panique, mais je trouve des solutions, je progresse, j'apprends. Je fais des choses que personne ne fait, je tombe sur des problèmes que personne ne rencontre. Je résous des énigmes. C'est ça qui me plaît.

Conclusion

Je suis passé par un moment assez chargé en émotion, puis par un travail de longue haleine pour tout restaurer correctement. Mais je suis content du résultat.

En termes de consommation d'énergie, l'intégralité du réseau consomme moins de 60W en charge. En termes de performances, rien à dire : c'est parfait. Et en termes de maintenabilité, je gagne en flexibilité : je peux héberger temporairement un service sur le reverse-proxy le temps que je tripatouille le serveur principal, sans engendrer de coupure.

J'ai encore des petites choses à faire, comme rendre quelques dépôts git publics (mais je veux d'abord vérifier qu'ils soient présentables), m'assurer du paramétrage de quelques services un peu obscurs, etc.

En outre, il y a encore quelques trucs que j'aimerais mettre en place un jour :

  • IPv6 : j'ai encore profité de l'occasion de tout réinstaller pour tenter de le mettre en place, sans succès. Ça fait partie des choses que je ne m'explique pas : comment peut-on à la fois être dans l'urgence de passer à IPv6 et rendre ça aussi compliqué...
  • Reverse-proxy sur OPNsense : pareil, c'est tellement sur-compliqué... C'est hyper-paramétrable, mais franchement pas accessible...
  • Miroir de Gitea : j'aimerais mettre en place sur une deuxième machine un miroir exhaustif de mon instance de Gitea pour parer à toute éventualité. Je dispose désormais de l'infra matérielle pour ça, mais j'aimerais que ce soit propre et que cela nécessite le minimum de maintenance en cas de création/suppression de dépôt

Enfin, il faudrait que je me mette sérieusement à la newsletter, mais depuis le premier envoi, je bosse non-stop sur un gros refactoring de mon blog, et là sur le réseau depuis trois jours. Je manque donc un peu de temps pour la newsletter.

Mais, la bonne nouvelle, c'est que le refactoring de mon blog est terminé, et la maintenance de mon réseau aussi. Je vais donc pouvoir m'y mettre...