L’in­fra­struc­ture tech­nique de Framas­pace

Tout d’abord quelques mots sur Framas­pace

Il s’agit d’un nouveau projet de Frama­soft (disclai­mer : j’y suis employé).
Le plus simple pour le décrire est encore de piquer le chapô de l’article du Frama­blog qui l’ex­plique :

Frama­soft souhaite mettre à dispo­si­tion, d’ici 3 ans, jusqu’à 10 000 espaces « cloud » colla­bo­ra­tifs, gratui­te­ment, à desti­na­tion des asso­cia­tions et collec­tifs mili­tants. 40 Go et de nombreuses fonc­tion­na­li­tés à se parta­ger entre 50 comptes sur un espace de type https://monasso.frama.space.

Ces espaces « cloud » sont des instances de Next­cloud qu’on met à dispo­si­tion avec un certain nombre d’ap­pli­ca­tions vali­dées par Frama­soft.
Nous n’au­to­ri­sons pas d’autres appli­ca­tions tout simple­ment pour éviter d’avoir des cas parti­cu­liers de support à gérer.
Tout le monde aura donc un Next­cloud semblable aux autres, et s’il y a un souci, le résoudre pour une asso­cia­tion le résou­dra pour tout le monde.

J’ai décrit vite fait l’in­fra­struc­ture tech­nique de Framas­pace dans sa FAQ, mais je profite d’avoir mon petit bout de web à moi pour prendre plus de place pour ça ici 🙂

Décou­page chirur­gi­cal

Afin de pouvoir créer une infra­struc­ture flexible que je pour­rais faire évoluer au gré des besoins, il conve­nait d’iden­ti­fier l’en­semble des services néces­saires à Next­cloud de façon à pouvoir les distri­buer sur plusieurs serveurs.

Voici ce qui est néces­saire pour un serveur Next­cloud (pour l’usage de Framas­pace, tout le monde n’a pas forcé­ment besoin de tout ça)

  • une base de données ;
  • un serveur web ;
  • du PHP ;
  • du stockage de fichiers ;
  • du cache mémoire (APCu, Redis ou Memca­ched) ;
  • un serveur Imagi­nary pour la géné­ra­tion de minia­tures d’images afin de déchar­ger la charge du serveur où serait PHP ;
  • un serveur office (Colla­bora ou OnlyOf­fice) ;
  • un serveur de logs pour centra­li­ser les logs de tous les services à un seul endroit.

Choix des morceaux et morceaux de choix

Une fois le décou­page effec­tué, il fallait bien choi­sir quels logi­ciels allaient être utili­sés là où il y avait le choix (en ce qui concerne Imagi­nary par exemple, il n’y avait pas le choix).

Atten­tion, certains choix sont assez arbi­traires (ques­tion de préfé­rences person­nelles) ou peuvent ne pas être les plus perti­nents : je ne suis qu’un humain, faillible et je ne prétends pas avoir une connais­sance exhaus­tive de tous les compo­sants néces­saires à Next­cloud.

  • pour la base de données, Post­greSQL.
    Post­greSQL, d’après mon expé­rience, ne souffre pas de ralen­tis­se­ments, ou si peu, au fur et à mesure du gros­sis­se­ment de ses bases de données.
    On peut objec­ter que MySQL/MariaDB peuvent avoir de très bonnes perfor­mances. Certes, mais au prix de quels efforts de confi­gu­ra­tion ? Post­greSQL, lui, ne demande aucune modi­fi­ca­tion de confi­gu­ra­tion pour avoir de très bonnes perfor­mances (même si un peu de confi­gu­ra­tion par rapport à la machine qui l’hé­berge ne fait pas de mal).
  • pour le serveur web, la solu­tion choi­sie a été Nginx.
    Plus véloce qu’Apache la plupart du temps, et j’ai bien plus d’ex­pé­rience et d’af­fi­nité avec Nginx ;
  • pour PHP, PHP-FPM.
    Comme au départ, je souhai­tais décou­pler PHP du serveur web, PHP-FPM était à ma connais­sance la seule solu­tion possible (il est capable d’écou­ter sur une socket réseau… et c’est la manière stan­dard de faire du PHP avec Nginx) ;
  • pour le stockage de fichiers, voulant un système exten­sible, il nous a fallu choi­sir un système de stockage S3, et ce fut MinIO.
    Nous avons déjà de l’ex­pé­rience avec Ceph et… disons que le résul­tat est plus que mitigé. Ceph est complexe, trop complexe pour le temps que nous avons à y consa­crer, et la docu­men­ta­tion n’est pas toujours à la hauteur. J’ai aussi regardé Garage, mais je l’ai écarté pour deux raisons : MinIO me semblait plus simple d’ac­cès (déploie­ment, confi­gu­ra­tion…) et la jeunesse du projet (2020 pour le 1er commit, 2016 pour MinIO, ce qui permet d’avoir plus de recul)
  • pour le cache mémoire, Redis.
    Le cache mémoire, pour être le plus effi­cace possible, doit pouvoir être joint par tous les serveurs qui font tour­ner PHP, donc exit APCu et pouvoir être haute­ment dispo­nible, ce qui inclut de la répli­ca­tion de données au sein d’un clus­ter, donc exit Memca­ched. Restait donc Redis, qui permet aussi de faire en sorte que les serveurs PHP se partagent les sessions.
  • pour la géné­ra­tion de minia­tures, Imagi­nary est le seul outil déporté proposé par Next­cloud, donc bon voilà.
  • pour le serveur office, nous lais­sons le choix aux asso­cia­tions.
    Nous utili­sons donc des services Colla­bora ou OnlyOf­fice selon les choix des asso­cia­tions.
  • pour le serveur de logs, le véné­rable Rsys­log.
    J’ai bien essayé de mettre en place des trucs kikoo­lol comme une stack ELK ou équi­valent comme Loki mais j’ai décidé que la complexité de ce genre de bouzin était bien trop impor­tante pour l’in­té­rêt que nous pouvions en tirer. Ça pourra toujours évoluer plus tard, mais en atten­dant, ça fera le job.
  • qui dit infra­struc­ture flexible, dit répar­ti­tion de charge pour pouvoir redi­ri­ger les requêtes entrantes vers les serveurs Nginx, office, etc.
    Nginx peut faire office de répar­ti­teur de charge, mais je préfère les logi­ciels spécia­li­sés dans une tâche : ils la remplissent géné­ra­le­ment mieux que les logi­ciels qui servent aussi à autre chose.
    N’ayant pas d’ex­pé­rience dans ce domaine, j’ai testé et adopté le bien connu, bien docu­menté et réputé HAProxy.

Pour faire tour­ner tout ça, il faut bien évidem­ment un système d’ex­ploi­ta­tion.
Sans surprise, j’uti­lise Debian parce que c’est simple et stable.

Chaque chose à sa place et une place pour chaque chose

Le choix des outils étant fait, il fallait choi­sir où mettre quoi.

Le cahier des charges à ce niveau était (rela­ti­ve­ment) simple : avoir un système évolu­tif avec une bonne tolé­rance aux pannes.
Ça paraît si simple, dit comme ça 😂

Il faut aussi avoir en tête que le budget de Frama­soft n’est pas exten­sible à l’in­fini : si ouvrir le service avec de la redon­dance dans tous les sens serait une excel­lente idée, il a bien fallu faire des choix de non-redon­dance pour certains bouts de l’in­fra­struc­ture pour limi­ter le coût de fonc­tion­ne­ment.
La non-redon­dance totale initiale de l’in­fra­struc­ture est cepen­dant compen­sée par sa flexi­bi­lité et la possi­bi­lité de remon­ter très vite certains bouts de l’in­fra­struc­ture de manière quasi-auto­ma­tique (quasi car il faut quand même lancer la commande qui va bien).

Parcou­rons une nouvelle fois la liste des éléments néces­saires à Next­cloud.

Post­greSQL

Un serveur Post­greSQL.
C’est un bon début.
C’est simple et rapide à mettre en place.

Mais comme nous voulions un système tolé­rant aux pannes et que remon­ter une sauve­garde (voir plus bas pour la solu­tion de sauve­garde employée) peut prendre un certain temps quand les bases de données sont bien remplies (nous avons déjà expé­ri­menté des temps de remon­tage de sauve­garde de plus de 12h), autant avoir de suite un serveur secon­daire, pour répliquer les données.

Deux serveurs Post­greSQL, donc.

J’ai pu me baser sur cet article pour mettre en place la répli­ca­tion entre les deux serveurs sans toucher à rien, seule­ment avec mon logi­ciel de gestion de confi­gu­ra­tion.

D’autres serveurs Post­greSQL pour­ront être ajou­tés en cas de besoin pour mieux répar­tir la charge.
Cette distri­bu­tion de la charge ne concerne cepen­dant que les opéra­tions de lecture : la répli­ca­tion multi-direc­tion­nelle pose des problèmes de perfor­mances non négli­geables.

Nginx + PHP

Lors du maquet­tage de l’in­fra­struc­ture, j’avais initia­le­ment décou­plé (c-à-d mis sur des serveurs diffé­rents) Nginx et PHP-FPM.
Cela néces­si­tait d’avoir les fichiers de Next­cloud sur les deux serveurs (sur le serveur Nginx pour les fichiers statiques, et sur le serveur PHP-FPM pour l’in­ter­pré­ta­tion des fichiers PHP).
Ce n’était pas un problème, tech­nique­ment parlant, mais ça sentait un peu l’inu­ti­lité.

Nginx n’au­rait pas beau­coup fait char­ger la machine si c’était juste pour servir des fichiers CSS, javas­cript et autres ressources (icônes, polices de carac­tè­re…).
C’est ainsi qu’il fut décidé que Nginx et PHP-FPM seraient sur la même machine.

Il n’y a pas grand-chose sur ce serveur :

  • nginx
  • php-fpm
  • le code de next­cloud
  • des fichiers de confi­gu­ra­tion gérés par le logi­ciel de gestion de confi­gu­ra­tion
  • de quoi exécu­ter les tâches cron des instances next­cloud

Si ce serveur tombait, il serait très simple et rapide de remon­ter un autre serveur.
La redon­dance de celui-ci n’est donc pas néces­saire pour l’ins­tant (la ques­tion se repo­sera lorsque la charge augmen­tera) mais elle est prévue : il n’y a que quelques mani­pu­la­tions à faire pour ajou­ter un serveur supplé­men­taire et l’uti­li­ser.

Minio

Combien de serveurs ?

Pour avoir de la tolé­rance de panne, l’outil fourni par la société éditrice de Minio nous indique que nous avons besoin d’au mini­mum 4 serveurs.

Grâce à cet outil, nous avons pu consta­ter qu’u­ti­li­ser 5 serveurs nous assu­rait non seule­ment une meilleure tolé­rance aux pannes, mais égale­ment qu’en modi­fiant légè­re­ment le nombre de disques utili­sés pour les calculs de parité, nous pouvions augmen­ter l’es­pace de stockage dispo­nible pour le prix d’un seul serveur, recu­lant le moment d’avoir besoin d’aug­men­ter la taille du clus­ter MinIO.

En effet, il faut savoir que pour augmen­ter la taille d’un clus­ter MinIO, il faut ajou­ter non pas un serveur supplé­men­taire, mais un pool entier de serveurs.
Nous conten­ter de 4 serveurs n’au­rait fait que rappro­cher le moment où nous aurions eu besoin de rajou­ter 4 serveurs supplé­men­taires.
Il était moins doulou­reux finan­ciè­re­ment de prendre tout de suite 5 serveurs.

Comment sauve­gar­der autant de données ?

Le problème d’un stockage qui se compte comme ici en dizaines de Tio, c’est la sauve­garde.
Impos­sible de sauve­gar­der rapi­de­ment une volu­mé­trie aussi impor­tante que celle que nous prévoyons.

Pour cette raison, nous avons mis en place un deuxième clus­ter MinIO, situé physique­ment loin du premier (en Finlande, quand le premier se trouve en Alle­magne) et avons activé la répli­ca­tion native de MinIO entre les deux clus­ters.

Cette solu­tion, s’il ne s’agit pas d’une sauve­garde stricto sensu, nous permet d’en­vi­sa­ger serei­ne­ment une catas­trophe (on a déjà vu des centres de données entiers partir en fumée) où nous perdrions entiè­re­ment le clus­ter MinIO primaire.
Nous pour­rions alors sans effort bascu­ler sur le clus­ter de répli­ca­tion.

Redis

Il y a deux modes pour faire fonc­tion­ner Redis en haute dispo­ni­bi­lité :

En mode Senti­nel, les données sont répliquées du serveur primaire vers des serveurs secon­daires.
En cas de défaillance du serveur primaire, un serveur secon­daire va être élu nouveau serveur primaire.
Dans ce mode, le nombre mini­mal de serveurs est 3.

En mode Clus­ter, les données sont répar­ties au sein du clus­ter : contrai­re­ment au mode Senti­nel, chaque serveur Redis ne contient pas l’in­té­gra­lité des données mais contient une partie des données, dans ce qui est appelé des hash slots (tranche de hachage).
Les données sont cepen­dant répliquées vers des serveurs secon­daires.
Illus­tra­tion avec un exemple d’un clus­ter de 6 serveurs :

  • le serveur A va conte­nir des tranches de hachage de 0 à 5500 ;
  • le serveur B va conte­nir des tranches de hachage de 5501 à 11000 ;
  • le serveur C va conte­nir des tranches de hachage de 11001 à 16383 ;
  • le serveur D va être le répli­cat du serveur A ;
  • le serveur E va être le répli­cat du serveur B ;
  • le serveur F va être le répli­cat du serveur C.

Dans ce mode, le nombre mini­mal de serveurs est 3 serveurs primaires.
La recom­man­da­tion offi­cielle pour une mise en produc­tion est 3 serveurs primaires et 3 serveurs secon­daires.

Nous avons choisi le mode Clus­ter avec 6 serveurs.

En effet, il nous parais­sait plus inté­res­sant de répar­tir la charge de travail sur plusieurs machines que de la faire repo­ser sur un seul serveur avec des serveurs secon­daires ne servant qu’à garan­tir la haute dispo­ni­bi­lité du service.

Imagi­nary

Ne servant qu’à la géné­ra­tion de minia­tures d’images, Imagi­nary est le service le moins critique de tous.
Pour cette raison, nous n’avons qu’un seul serveur Imagi­nary.

Celui-ci étant derrière HAProxy, en cas de charge trop impor­tante, nous pour­rions à loisir ajou­ter de nouveaux serveurs Imagi­nary sans avoir besoin de chan­ger la confi­gu­ra­tion des Next­cloud, qui utili­se­raient toujours la même URL.

Office

Les services Colla­bora et OnlyOf­fice sont four­nis par des conte­neurs Docker, chacun tour­nant sur un port diffé­rent, avec des options (domaine pour Colla­bora, para­mètre JWT_SECRET pour OnlyOf­fice) diffé­rentes.

La confi­gu­ra­tion de ces conte­neurs étant gérée par Salt, il est extrê­me­ment simple de remon­ter un serveur tombé, ce qui nous a conduit à n’uti­li­ser qu’un serveur pour l’ins­tant.

En cas de surcharge, nous pour­rons ajou­ter un serveur supplé­men­taire faci­le­ment. Cepen­dant, au contraire d’Ima­gi­nary, ce serveur ne servira pas pour de la répar­ti­tion de charge d’un même domaine mais pour accueillir de nouveaux domaines de serveurs office.

Les conte­neurs sont lancés avec Podman et non Docker car Podman n’in­tègre pas de dæmon, ce qui a le grand avan­tage d’évi­ter de voir les conte­neurs s’ar­rê­ter et redé­mar­rer lors de la mise à jour de la solu­tion de conte­neu­ri­sa­tion.

Rsys­log

Pas de redon­dance prévue pour ce serveur.

En cas de besoin, le remon­tage du serveur sera rapide, toujours grâce à notre logi­ciel de gestion de confi­gu­ra­tion.

HAProxy

Là encore, nous ne prévoyons pas de redon­dance pour l’ins­tant, mais nous avons toute lati­tude pour ajou­ter un serveur HAProxy supplé­men­taire de façon à répar­tir la charge au besoin.

Schéma de l’infrastructure de Framaspace : plein de serveurs reliés par des flèches

Les logi­ciels supplé­men­taires

Pour lier toute l’in­fra­struc­ture dans un ensemble fonc­tion­nel et sécu­risé, nous avons utilisé plusieurs outils :

WireGuard

L’éta­blis­se­ment de connexions sécu­ri­sées entre les serveurs aurait consommé des ressources que nous souhai­tions écono­mi­ser.
L’uti­li­sa­tion de tunnels WireGuard entre les serveurs devant commu­niquer entre eux permet d’évi­ter cette consom­ma­tion supplé­men­taire, les connexions du VPN étant main­te­nues dans le temps.

Pgpool-II

Afin de pouvoir répar­tir les requêtes vers les serveurs Post­greSQL selon leur type (le serveur secon­daire ne supporte que la lecture, pas l’écri­ture) mais aussi pour répar­tir la charge, j’ai utilisé Pgpool-II.

J’avais regardé PgBoun­cer mais celui-ci ne sait pas gérer plusieurs serveurs pour une même base de données.

Side­kick

MinIO recom­mande l’uti­li­sa­tion du répar­ti­teur de charge side­kick au plus près des clients ayant besoin de se connec­ter au clus­ter de stockage.
Ainsi j’ai installé side­kick sur la machine Nginx + PHP-FPM car c’est Next­cloud qui accède lui-même au clus­ter et non les clients depuis leurs navi­ga­teurs web.

L’avan­tage de ce rappro­che­ment du répar­ti­teur de charge du client est d’évi­ter les points de défaillance unique comme le serait la machine HAProxy si elle avait servi de point d’en­trée vers le clus­ter de stockage comme je l’avais initia­le­ment prévu.

Salt

Salt est un logi­ciel de gestion de confi­gu­ra­tion.
Utilisé chez Frama­soft depuis près de 10 ans, il est une pierre angu­laire du projet Framas­pace : outre la gestion des machines et de leurs confi­gu­ra­tions, il permet aussi la gestion des espaces colla­bo­ra­tifs (créa­tion, désac­ti­va­tion, suppres­sion et chan­ge­ment de solu­tion bureau­tique).

Barman

Barman est la solu­tion de sauve­garde des bases de données Post­greSQL de Frama­soft depuis main­te­nant plusieurs années.
Ses sauve­gardes « au fil de l’eau », sa faci­lité de confi­gu­ra­tion et d’uti­li­sa­tion ainsi que sa fiabi­lité nous séduisent toujours, nous n’avions aucune raison d’en chan­ger.

BorgBa­ckup et Borg­ma­tic

Pour la sauve­garde des fichiers, BorgBa­ckup et son comparse borg­ma­tic qui en faci­lite l’uti­li­sa­tion sont nos outils de sauve­garde préfé­rés depuis long­temps.
Pourquoi chan­ger une équipe qui gagne ?

À noter qu’ils ne sont pas utili­sés pour sauve­gar­der les fichiers dépo­sés par les utili­sa­teurs des espaces colla­bo­ra­tifs mais pour sauve­gar­der les fichiers de confi­gu­ra­tion de nos diffé­rents logi­ciels.
Si nous pouvons aisé­ment remon­ter n’im­porte quel serveur en peu de temps, nous pouvons toujours avoir besoin de consul­ter l’état d’une machine à un instant précé­dent (« Oups, j’ai effacé un truc essen­tiel par inad­ver­tance ! »).

Gotify

Nos diffé­rents outils envoient des noti­fi­ca­tions à diverses occa­sions (demande d’es­pace, mauvais fonc­tion­ne­ment…).
Nous utili­sons géné­ra­le­ment le mail pour de telles noti­fi­ca­tions mais le serveur de listes (sur lequel nous rece­vons les mails de noti­fi­ca­tions) a subi quelques surcharges ralen­tis­sant forte­ment l’en­voi des mails.
J’avais commencé à expé­ri­men­ter Gotify et il me semblait un candi­dat idéal pour nous aver­tir rapi­de­ment des noti­fi­ca­tions urgentes, comme les échecs de créa­tion d’es­paces.

Il remplit parfai­te­ment son rôle 🙂

Les logi­ciels que nous avons déve­loppé

Tous nos déve­lop­pe­ments sont dispo­nibles sur la page Frama­git du projet.

On y retrouve notam­ment :

  • Charon, qui est l’in­ter­face web au projet, écrit en PHP.
    C’est sur Charon que s’ef­fec­tuent les demandes d’es­paces colla­bo­ra­tifs et leur gestion.
    Une inter­face d’ad­mi­nis­tra­tion nous permet d’ac­cep­ter ou de reje­ter les demandes, ainsi que d’ef­fec­tuer des opéra­tions sur les espaces exis­tants.
  • Hermès est un dæmon en Python qui inter­roge régu­liè­re­ment Charon sur son API afin de connaître les tâches à exécu­ter : créa­tion ou suppres­sion d’es­pace ou modi­fi­ca­tion de serveur office.
    Hermès utilise Salt pour effec­tuer les tâches envoyées par Charon.
  • Poséi­don contient toutes nos recettes Salt (Poséi­don, roi des mers, l’eau salée, Salt… vous l’avez ?)
  • Chro­nos est un dæmon, encore une fois en Python, qui se charge d’exé­cu­ter régu­liè­re­ment les tâches cron des espaces Next­cloud.
    Nous ne pouvions pas mettre des centaines de tâches cron en paral­lèle, c’est pourquoi Chro­nos fut déve­loppé.
    Il utilise pour cela une base de données Post­greSQL pour gérer la file d’at­tente et exécute les tâches cron en paral­lèle par paquets de 10 (par défaut)

Nous avons aussi des projets pour les patchs Next­cloud à déployer (nous repor­tons les patchs au projet Next­cloud mais nous n’at­ten­dons pas leur inté­gra­tion pour en béné­fi­cier), les fichiers de ressources (fonds d’écran, fichiers css, modèles, etc), la page d’ac­cueil de Framas­pace et enfin une recette de conte­neur Docker pour Colla­bora (voir plus bas).

Les problèmes rencon­trés

Les conte­neurs office

Le plus gros souci rencon­tré venait de notre volonté de propo­ser un serveur office (Colla­bora ou OnlyOf­fice) à chaque espace colla­bo­ra­tif.
Si l’idée était simple, à savoir lancer un conte­neur office sur un port diffé­rent pour chaque espace, cela a vite posé deux problèmes :

  • la surcharge au démar­rage de la machine.
    Quand on a 2 ou 3 conte­neurs qui se lancent au démar­rage d’une machine, ça passe, mais quand on en a 50 ou 100… ça coince vite et les conte­neurs se lancent très lente­ment, quand ils ne plantent pas.
    Pendant ce temps, la machine est inuti­li­sable.
  • les ressources consom­mées.
    Un conte­neur Colla­bora au repos utilise 500Mio de RAM et un conte­neur OnlyOf­fice en consomme 2Gio.
    Sans rien faire.
    Impos­sible pour le budget de Frama­soft de louer des serveurs monstres à tour de bras.
    Fort heureu­se­ment, nous avons trouvé une recette de conte­neur Docker pour Colla­bora qui fait sauter les limi­ta­tions de celui-ci et nous permet d’ac­cueillir plus de personnes en même temps sur le même conte­neur.
    J’ai créé une diver­gence de ce projet sur Frama­git pour l’adap­ter au mieux à nos besoins et pour contour­ner un bug de la version de Podman de Debian Bull­seye.
    Toutes nos modi­fi­ca­tions ont été propo­sées au projet d’ori­gine mais toutes n’ayant pas été rete­nues, nous main­te­nons notre diver­gence.

La gestion des versions sur MinIO

Utili­sant la fonc­tion de répli­ca­tion de MinIO, le version­nage était auto­ma­tique­ment activé sur les buckets de MinIO.
Nous pensions pouvoir tirer partie de cela en utili­sant l’ap­pli­ca­tion Next­cloud S3 versio­ning, qui permet de restau­rer une version précé­dente d’un fichier en utili­sant les versions des objets dans MinIO plutôt que de lais­ser Next­cloud créer et gérer lui-même des versions.

Malheu­reu­se­ment, nous n’avions pas anti­cipé que lors de l’en­voi d’un fichier de grande taille, Next­cloud le découpe en morceaux de 10Mio, qu’il stocke tempo­rai­re­ment avant de recons­truire le fichier.
Avec le version­nage de MinIO, ces fichiers tempo­raires n’étaient pas réel­le­ment suppri­més avant le délai de 30 jours que nous avions choisi comme temps maxi­mum de conser­va­tion des versions.
Certains usagers remplis­saient donc leur bucket tout en étant bien en-dessous des 40Gio d’es­pace alloué, simple­ment en télé­ver­sant des fichiers de grande taille.

Nous sommes donc reve­nus à une gestion native des versions par Next­cloud et à une suppres­sion la plus rapide possible des versions de fichiers (un jour).

L’ab­sence de tran­sac­tions dans Next­cloud

Si certaines opéra­tions de Next­cloud se font dans des tran­sac­tions, certaines autres sont faites de façon atomiques, avec des écri­tures dans la base de données et des lectures immé­dia­te­ment après.
La répli­ca­tion Post­greSQL n’était au départ pas synchrone : j’ima­gi­nais naïve­ment que Next­cloud utili­sait des tran­sac­tions partout (Pgpool-II dirige les tran­sac­tions vers le serveur Post­greSQL primaire), ou à tout le moins n’al­lait pas véri­fier un enre­gis­tre­ment juste après l’avoir écrit.

Nous avons patché Next­cloud pour qu’il utilise au maxi­mum des tran­sac­tions, mais la taille et la dette tech­nique de Next­cloud rendent cette tâche quasi­ment impos­sible.

En consé­quence, j’ai passé la répli­ca­tion de Post­greSQL en synchrone, et tant pis pour la petite perte de perfor­mance.

Les recettes Salt

Les recettes Salt que nous avons déve­loppé permettent :

  • de confi­gu­rer des machines pour les inté­grer à l’in­fra­struc­ture (on leur applique la recette corres­pon­dant à leurs rôles et on met à jour la confi­gu­ra­tion des autres serveurs qui doivent être en connexion avec elles)
  • de modi­fier la confi­gu­ra­tion des éléments de l’in­fra­struc­ture au besoin (par exemple, j’ai récem­ment modi­fié la confi­gu­ra­tion de Post­greSQL par ce moyen)
  • de déployer les espaces Next­cloud (créa­tion de la base de données et du bucket S3, boots­trap de l’es­pace, ajout dans la confi­gu­ra­tion de Chro­nos)
  • de chan­ger de suite office
  • de mettre à jour Next­cloud et ses appli­ca­tions
  • de mettre à jour les espaces colla­bo­ra­tifs vers une nouvelle version de Next­cloud

La ferme de Next­cloud

La struc­ture

Déployer autant de dossiers Next­cloud que d’es­paces colla­bo­ra­tifs était exclu vu son poids (519Mio pour le dossier de base sans appli­ca­tions autres que celles recom­man­dées !).

J’ai donc modi­fié le fichier de confi­gu­ra­tion de Next­cloud pour qu’il contienne la confi­gu­ra­tion commune à tous les espaces et qu’il inclue un fichier de confi­gu­ra­tion conte­nant la confi­gu­ra­tion parti­cu­lière (base de données, bucket S3, etc.) corres­pon­dant au nom de domaine de la requête ou à la variable d’en­vi­ron­ne­ment NC_INSTANCE (pour que cela fonc­tionne depuis les outils en CLI).

Nginx utilise comme racine du site web un dossier au nom de l’hôte de la requête qui est un lien symbo­lique vers la version de Next­cloud de l’es­pace.

Cette manière de faire nous permet de mettre à jour les espaces de façon séquen­tielle et dimi­nuer le temps d’in­dis­po­ni­bi­lité de chaque espace (entre 40 et 50 secondes d’in­dis­po­ni­bi­lité par espace).

Les mises à jour

Nous mettons à jour un espace dédié à cela, ce qui récu­père la nouvelle base de code, que nous adap­tons en réap­pliquant nos patchs et en modi­fiant le fichier de confi­gu­ra­tion comme évoqué ci-dessus.
Nous dupliquons cette base de code pour servir de base de mise à jour : elle rece­vra un fichier de confi­gu­ra­tion conte­nant toute la confi­gu­ra­tion de l’ins­tance à mettre à jour et nous effec­tuons un php occ upgrade pour mettre à jour la base de données de l’ins­tance.

Ainsi, pour chaque instance, on a :

  • mise en mode main­te­nance
  • copie d’une confi­gu­ra­tion complète dans la base de code de mise à jour
  • php occ upgrade
  • modi­fi­ca­tion du lien symbo­lique du dossier utilisé par Nginx pour poin­ter vers la nouvelle version de Next­cloud
  • sortie du mode main­te­nance.

Conclu­sion

C’est sans doute le projet le plus vaste et complexe dont j’ai eu à m’oc­cu­per.

Mais c’est aussi sans doute le projet le plus fun !
Imagi­ner tout ça et le mettre en place m’a apporté beau­coup d’amu­se­ment, de joie et de fierté.
La joie et la fierté de l’ar­ti­san une fois sa besogne faite, et plutôt bien faite, je trouve.

Merci à mes collègues, tout parti­cu­liè­re­ment Thomas : sans sa connais­sance de Next­cloud et ses propo­si­tions de patchs, je me serais sans aucun doute arra­ché un grand nombre de cheveux.

Moi aussi je peux faire de la merde avec les mails 😑

Après « Le pouvoir de nuisance des silos de mail » (égale­ment paru sur le Frama­blog sous le titre « Être un géant du mail, c’est faire la loi… »), après « Four­nis­seurs d’emails, arrê­tez de faire de la merde ! (#PasMonCaca) » et « I don’t want any spam ! », voici la suite de mes aven­tures avec les spams et les géants du mail.


Atten­tion ! Cet article est erroné ! Tout est de ma faute ! Franck m’a fait remarquer à juste titre que j’avais des erreurs dans mon enre­gis­tre­ment SPF (des adresses IPv6 en /18 à la place de /128). C’était clai­re­ment ça qui a fait que Google a accepté du spam.

Je laisse tout de même l’ar­ticle, par honnê­teté.

Son titre était « Être un géant du mail, c’est faire de la merde avec les spams »


Coucou, vous envoyez du spam

Nous avons reçu récem­ment chez Frama­soft un mail de notre regis­traire de nom de domaine Gandi disant qu’on avait envoyé du spam depuis le domaine frama­listes.org.

Jusque-là, rien de bien étrange, il arrive fréquem­ment que des spam­meurs ciblent des listes de diffu­sions héber­gées chez nous et notre anti­spam n’est pas infaillible (aucun ne l’est).

J’in­ves­tigue donc et me rends sur la page des archives de la liste.
Pas d’ar­chives. Ok, ce n’est pas aber­rant, on a bien le droit de les désac­ti­ver.

Je regarde donc la liste des abon­nées.
Ah, étrange : c’est une liste à nous, l’as­so­cia­tion Frama­soft.
Et pas juste à nous : réser­vée (pour éviter que des gens se fassent passer pour nous avec une adresse qui pour­rait sembler offi­cielle), avec juste 4 membres de l’équipe tech­nique en abon­nés.

Si c’est flou, c’est qu’il y a un loup

Là, je commence à être suspi­cieux.
Je recherche l’adresse mail de la personne qui a reçu le spam dans les fichiers jour­naux des serveurs de mails (nous avons plusieurs serveurs qui envoient du mail, pour répar­tir la charge).

Résul­tat : rien, zéro, nada, que tchi, peau de balle.

Read the headers, Luke

Bon, là, il faut plon­ger dans la bouillie d’en-têtes du spam.
Je n’avais pas commencé par là parce que c’est un méli-mélo pas agréable à lire et que c’est encore forte­ment le matin.
Bref, je m’y mets.

Ah : ça ne vient pas de chez nous.
Mais alors, comment ce spam a-t-il bien pu être accepté ?
On a bien un enre­gis­tre­ment SPF pour dire quels sont les serveurs auto­ri­sés à envoyer des mails venant du domaine frama­listes.org, un enre­gis­tre­ment DKIM pour l’em­preinte publique de la clé qui signe les mails et une poli­tique DMARC qui dit que si un mail n’est pas auto­risé via SPF ou que la signa­ture ne corres­pond pas, il faut le reje­ter sans ména­ge­ment.

Bref, on est norma­le­ment en mode « coupez tout ce qui dépasse ».

Le spam a été accepté pour une raison fort étrange : Google dit que le serveur qui l’a envoyé était bien auto­risé dans notre poli­tique SPF.

Received-SPF: pass (google.com: domain of info@framalistes.org designates 2a01:7c8:bb02:XXXX:XXXX:XXXX:XXX:XXXX as permitted sender) client-ip=2a01:7c8:bb02:XXXX:XXXX:XXXX:XXX:XXXX;

Je ne sais pas si c’est Google qui a fait de la merde ou s’il a été victime d’un empoi­son­ne­ment de son cache DNS mais voilà : si l’en­re­gis­tre­ment SPF qui est dans la zone DNS chez Gandi avait été respecté, ça ne serait pas arrivé.

Conclu­sion

J’ai encore perdu du temps à cause d’un géant du mail et la fusion du titre de mes 3 articles précé­dents était trop tentante pour ne pas en faire un nouvel article 😁