Maison >développement back-end >Golang >Maintenir un outil de sauvegarde open source : informations et plus encore
Les stratégies de sauvegarde peuvent sembler un problème résolu, mais les administrateurs système se posent souvent des questions sur la façon de sauvegarder correctement les données, où les stocker et comment standardiser le processus de sauvegarde dans différents environnements logiciels. En 2011, nous avons développé des scripts de sauvegarde personnalisés qui géraient efficacement les sauvegardes des projets Web de nos clients. Ces scripts nous ont bien servi pendant de nombreuses années, stockant les sauvegardes à la fois dans notre stockage et dans des référentiels externes selon les besoins. Cependant, à mesure que notre écosystème logiciel grandissait et se diversifiait, nos scripts n'étaient pas à la hauteur, manquant de prise en charge des nouvelles technologies telles que Redis et MySQL/PostgreSQL. Les scripts sont également devenus lourds, sans aucun système de surveillance autre que les alertes par courrier électronique.
Nos scripts autrefois compacts ont évolué vers un système complexe et ingérable. La mise à jour de ces scripts pour différents clients devenait difficile, en particulier lorsqu'ils utilisaient des versions personnalisées. Au début de l’année dernière, nous avons réalisé que nous avions besoin d’une solution plus moderne.
Dans cet article, nous expliquerons toutes les difficultés que nous avons rencontrées lors du développement de nxs-backup et partagerons nos expériences et nos défis. Vous pouvez également tester l'outil sur votre projet et partager votre expérience, nous serions très intéressés d'avoir de vos nouvelles. Maintenant, commençons !
Nous avons répertorié nos exigences pour un nouveau système :
Nous avons regardé les solutions open source qui existaient déjà avant même de créer notre première version de nxs-backup. Mais ils avaient tous leurs défauts. Par exemple, Bacula est surchargé de fonctions inutiles pour nous, la configuration initiale est — plutôt une tâche laborieuse en raison de beaucoup de travail manuel (par exemple, pour écrire/rechercher des scripts de sauvegarde de bases de données), et pour récupérer des copies, il faut utiliser des utilitaires spéciaux. , etc.
Pas étonnant que nous ayons été confrontés au même problème alors que nous avions l'idée de réécrire notre outil. La possibilité qu'en quatre ans quelque chose ait changé et que de nouveaux outils soient apparus en ligne n'était pas si élevée, mais quand même.
Nous avons étudié quelques nouveaux outils qui n'avaient pas été envisagés auparavant. Mais, comme nous l’avons vu précédemment, ces solutions ne nous convenaient pas non plus. Parce qu'ils ne répondaient pas pleinement à nos exigences.
Nous sommes finalement arrivés à deux conclusions importantes :
Avant d'explorer la nouvelle version, jetons un coup d'œil à ce que nous avions auparavant et pourquoi cela ne nous suffisait pas.
L'ancienne version prenait en charge des bases de données telles que MySQL, PostgreSQL, Redis, MongoDB, la copie discrète et incrémentielle de fichiers, plusieurs stockages distants (S3 ; SMB ; NFS ; FTP ; SSH ; WebDAV) et disposait de fonctionnalités telles que la rotation des sauvegardes, la journalisation. , notifications par e-mail et modules externes.
Maintenant, plus sur ce qui nous préoccupait.
Exécuter un fichier binaire sans redémarrer le fichier source sur n'importe quel Linux
Au fil du temps, la liste des systèmes avec lesquels nous travaillons s'est considérablement allongée. Nous servons désormais des projets qui utilisent des distributions autres que les distributions compatibles Deb et RPM standard telles que Arch, Suse, Alt, etc.
Les systèmes récents ont eu des difficultés à exécuter nxs-backup car nous n'avons collecté que les packages deb et rpm et pris en charge une liste limitée de versions du système. Quelque part, nous avons réédité le paquet entier, quelque part juste du binaire, quelque part nous avons juste dû exécuter le code source.
Travailler avec l'ancienne version était très gênant pour les ingénieurs, en raison de la nécessité de travailler avec la source. Sans oublier que l'installation et la mise à jour dans un tel mode prennent plus de temps. Au lieu de configurer 10 serveurs par heure, vous ne deviez passer qu'une heure sur un seul serveur.
Nous savons depuis longtemps qu'il est bien mieux d'avoir un binaire sans dépendances système que vous pouvez exécuter sur n'importe quelle distribution et de ne pas rencontrer de problèmes avec les différentes versions des bibliothèques et les différences architecturales des systèmes. Nous voulions que cet outil soit le même.
Réduire l'image Docker avec nxs-backup et prendre en charge ENV dans les fichiers de configuration
Dernièrement, de nombreux projets fonctionnent dans un environnement conteneurisé. Ces projets nécessitent également des sauvegardes et nous exécutons nxs-backup dans des conteneurs. Pour les environnements conteneurisés, il est très important de minimiser la taille de l’image et de pouvoir travailler avec des variables d’environnement.
L'ancienne version ne permettait pas de travailler avec des variables d'environnement. Le principal problème était que les mots de passe devaient être stockés directement dans la configuration. Pour cette raison, au lieu d'un ensemble de variables contenant uniquement des mots de passe, vous devez mettre toute la configuration dans une variable. La modification de variables d'environnement volumineuses nécessite plus de concentration de la part des ingénieurs et rend le dépannage un peu plus difficile.
De plus, lorsque nous travaillions avec l'ancienne version, nous devions utiliser une image Debian déjà volumineuse, dans laquelle nous devions ajouter plusieurs bibliothèques et applications pour des sauvegardes correctes.
Même en utilisant une version fine de l'image, nous avons obtenu une taille minimale d'environ 250 Mo, ce qui est beaucoup pour un petit utilitaire. Dans certains cas, cela affectait le processus de démarrage de la collection en raison de la durée pendant laquelle l'image était placée sur le nœud. Nous voulions obtenir une image ne dépassant pas 50 Mo.
Travailler avec le stockage à distance sans fusible
Un autre problème pour les environnements de conteneurs est l'utilisation de fusibles pour monter le stockage à distance.
Pendant que vous exécutez des sauvegardes sur l'hôte, cela est toujours acceptable : vous avez installé les bons packages et activé Fuse dans le noyau, et maintenant cela fonctionne.
Les choses deviennent intéressantes lorsque vous avez besoin d'un fusible dans un conteneur. Sans une mise à niveau des privilèges avec un accès direct au cœur du système hôte, le problème n'est pas résolu, et il s'agit d'une diminution significative du niveau de sécurité.
Cela doit être coordonné, tous les clients n'acceptent pas d'affaiblir les politiques de sécurité. C’est pourquoi nous avons dû faire un nombre effroyable de solutions de contournement dont nous ne voulons même pas nous souvenir. De plus, la couche supplémentaire augmente la probabilité de panne et nécessite une surveillance supplémentaire de l'état des ressources montées. Il est plus sûr et plus stable de travailler avec le stockage distant en utilisant directement leur API.
Surveillance de l'état et envoi de notifications non seulement par e-mail
Aujourd'hui, les équipes utilisent de moins en moins le courrier électronique dans leur travail quotidien. C’est compréhensible car il est beaucoup plus rapide de discuter du problème dans une discussion de groupe ou lors d’un appel de groupe. Telegram, Slack, Mattermost, MS Teams et d'autres produits similaires sont largement distribués par ce biais.
Nous avons également un bot, qui envoie diverses alertes et nous en informe. Et bien sûr, nous aimerions voir des rapports de sauvegardes crashant dans l’espace de travail comme Telegram, et non dans les e-mails, parmi des centaines d’autres e-mails. À propos, certains clients souhaitent également voir des informations sur les échecs dans leur Slack ou un autre messager.
De plus, vous souhaitez depuis longtemps pouvoir suivre l'état et voir les détails des travaux en temps réel. Pour ce faire, vous devez changer le format de l'application, la transformant en démon.
Performances insuffisantes
Une autre douleur aiguë était une performance insuffisante dans certains scénarios.
L'un des clients a un énorme vidage de fichiers de près d'un téraoctet et il s'agit uniquement de petits fichiers - texte, images, etc. Nous collectons des copies incrémentielles de ces éléments et avons le problème suivant : une copie annuelle prend TROIS jours. Ouais, eh bien, l'ancienne version ne peut tout simplement pas digérer ce volume en moins d'une journée.
Compte tenu des circonstances, nous ne sommes en effet pas en mesure de récupérer des données à une date précise, ce qui ne nous plaît pas du tout.
Dans un premier temps, nous avons implémenté notre solution de sauvegarde en Python en raison de sa simplicité et de sa flexibilité. Cependant, à mesure que la demande augmentait, la solution basée sur Python est devenue inadéquate. Après une discussion approfondie, nous avons décidé de réécrire le système dans Go pour plusieurs raisons :
Trouver une solution
Tous les problèmes ci-dessus, dans une plus ou moins grande mesure, ont causé une douleur assez palpable au service informatique, l'obligeant à consacrer un temps précieux à des choses certes importantes, mais ces coûts auraient pu être évités. De plus, dans certaines situations, certains risques étaient créés pour les propriétaires d'entreprise : la probabilité de se retrouver sans données pendant un certain jour, bien qu'extrêmement faible, mais non nulle. Nous avons refusé d'accepter l'état de choses.
Nxs-sauvegarde 3.0
Le résultat de notre travail a été une nouvelle version de nxs-backup v 3.0 qui a récemment eu une mise à jour vers la v3.8.0
Principales fonctionnalités de la nouvelle version :
Nous avons essayé de conserver la plupart des configurations et de la logique applicative, mais certains changements sont présents. Tous sont liés à l'optimisation et à la correction des défauts de la version précédente.
Par exemple, nous mettons les paramètres de connexion aux référentiels distants dans la configuration de base afin de ne pas les prescrire pour des types de sauvegardes différents à chaque fois.
Vous trouverez ci-dessous un exemple de configuration de base pour les sauvegardes. Il contient des paramètres généraux tels que les canaux de notification, le stockage distant, la journalisation et la liste des tâches. Il s'agit de la configuration principale de base avec notification par courrier électronique, nous vous recommandons fortement d'utiliser les notifications par courrier électronique comme méthode par défaut. Si vous avez besoin de plus de fonctionnalités, vous pouvez voir la référence dans la documentation.
server_name: wp-server project_name: My Best Project loglevel: info notifications: mail: enabled: true smtp_server: smtp.gmail.com smtp_port: 465 smtp_user: j.doe@gmail.com smtp_password: some5Tr0n9P@s5worD recipients: - j.doe@gmail.com - a.smith@mail.io webhooks: [] storage_connects: [] jobs: [] include_jobs_configs: [ "conf.d/*.conf" ]
Quelques mots sur les pièges
Nous nous attendions à faire face à certains défis. Il serait insensé de penser autrement. Mais deux problèmes ont causé le plus grand mal.
Fuite de mémoire ou algorithme non optimal
Même dans la version précédente de nxs-backup, nous utilisions notre propre implémentation d'archivage de fichiers. La logique de cette solution était d'essayer d'éviter d'utiliser des outils externes pour créer des sauvegardes, et travailler avec des fichiers était l'étape la plus simple possible.
En pratique, la solution s'est avérée réalisable, même si elle n'est pas particulièrement efficace sur un grand nombre de fichiers, comme le montrent les tests. À l’époque, nous l’avions pris en compte dans les spécificités de Python et espérions constater une différence significative lorsque nous sommes passés à Go.
Quand nous sommes finalement arrivés aux tests de charge de la nouvelle version, nous avons obtenu des résultats décevants. Il n'y a eu aucun gain de performances et la consommation de mémoire était encore plus élevée qu'auparavant. Nous cherchions une solution. J'ai lu beaucoup d'articles et de recherches sur ce sujet, mais ils ont tous dit que l'utilisation de « filepath.Walk » et « filepath.WalkDir » est la meilleure option. Les performances de ces méthodes n'augmentent qu'avec la sortie de nouvelles versions du langage.
Pour tenter d'optimiser la consommation de mémoire, nous avons même commis des erreurs en créant des copies incrémentielles. À propos, les options cassées étaient en réalité plus efficaces. Pour des raisons évidentes, nous ne les avons pas utilisés.
Finalement, tout s'est limité au nombre de dossiers à traiter. Nous en avons testé 10 millions. Garbage Collector ne semble pas être en mesure d'effacer cette quantité de variables générées.
Finalement, réalisant que nous pourrions perdre trop de temps ici, nous avons décidé d'abandonner notre implémentation au profit d'une solution éprouvée et vraiment efficace : GNU tar.
Nous reviendrons peut-être sur l'idée d'auto-implémentation plus tard lorsque nous trouverons une solution plus efficace pour gérer des dizaines de millions de fichiers.
Un FTP tellement différent
Un autre problème est survenu lors de l'utilisation de FTP. Il s'est avéré que différents serveurs se comportent différemment pour les mêmes requêtes.
Et c'est un problème vraiment sérieux lorsque pour une même demande vous obtenez soit une réponse normale, soit une erreur qui ne semble pas avoir quelque chose à voir avec votre demande, soit vous n'obtenez pas de bug alors que vous l'attendez .
Nous avons donc dû renoncer à utiliser la librairie « prasad83/goftp » au profit d'un « jlaffaye/ftp » plus simple, car la première ne pouvait pas fonctionner correctement avec le serveur Selectel. L'erreur était que lors de la connexion, le premier essayait d'obtenir la liste des fichiers dans le répertoire de travail et obtenait l'erreur des droits d'accès au répertoire supérieur. Avec « jlaffaye/ftp » un tel problème n'existe pas, car il est plus simple et n'envoie aucune requête au serveur.
Le problème suivant était une déconnexion alors qu'il n'y avait aucune demande. Tous les serveurs ne se comportent pas de cette façon, mais certains le font. Il a donc fallu vérifier avant chaque demande si le connecteur était tombé et rebranché.
La cerise sur le gâteau était le problème d'obtention de fichiers du serveur, ou pour être clair, une tentative d'obtenir un fichier qui n'existait pas. Certains serveurs donnent une erreur en essayant d'accéder à un tel fichier, d'autres renvoient un objet d'interface io.Reader valide qui peut même être lu, seulement vous obtenez une coupe d'octets vide.
Toutes ces situations ont été découvertes empiriquement et doivent être gérées de leur propre côté.
Conclusions
Plus important encore, nous avons résolu les problèmes de l'ancienne version, les éléments qui affectaient le travail des ingénieurs et créaient certains risques pour les entreprises.
Nous avons encore des « souhaits » non réalisés de la dernière version, tels que :
Cette liste est désormais étendue avec de nouveaux :
Et bien sûr, nous serons heureux de connaître l’avis de la communauté. Quelles autres opportunités de développement voyez-vous ? Quelles options ajouteriez-vous ?
Vous pouvez lire la documentation et en savoir plus sur nxs-backup sur son site Web, il y a également une section de dépannage sur notre site Web si vous souhaitez laisser des problèmes.
Nous avons déjà réalisé un sondage sur notre chaîne Telegram sur les fonctionnalités à venir. Suivez-nous pour participer à de telles activités et contribuer au développement de l'outil !
À la prochaine fois !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!