[{"data":1,"prerenderedAt":3710},["ShallowReactive",2],{"category-woodstock":3},[4,1651,2930],{"id":5,"title":6,"author":7,"body":8,"category":1554,"categorySlug":1555,"date":1556,"description":14,"excerpt":1557,"extension":1636,"location":1637,"meta":1638,"navigation":1639,"path":1640,"published":1639,"seo":1641,"slug":1642,"stem":1643,"tags":1644,"timeToRead":1649,"__hash__":1650},"posts\u002Fposts\u002FWoodstock\u002F2026-04-26_woodstock_v2.md","Woodstock Backup v2.0.0 - La réécriture complète en Rust","Ulrich Vandenhekke",{"type":9,"value":10,"toc":1517},"minimark",[11,15,18,21,115,118,127,132,137,140,147,151,159,166,169,173,180,188,191,195,202,206,209,212,216,223,227,231,238,245,249,252,340,354,358,366,394,397,404,408,419,429,433,436,439,443,447,453,456,459,462,465,469,472,476,479,485,488,492,502,505,528,531,542,548,552,556,559,781,784,818,822,825,879,882,886,889,896,899,906,909,916,919,926,943,950,953,957,961,964,970,985,988,992,995,999,1002,1021,1444,1447,1450,1453,1459,1466,1472,1479,1490,1494,1497,1500,1514],[12,13,14],"p",{},"Bonjour à tous,",[12,16,17],{},"Six ans. Il m'aura fallu six ans entre la première version de Woodstock Backup et cette v2. Si vous m'aviez dit en 2020\nque je passerais la moitié de la décennie à réécrire trois fois le même logiciel de sauvegarde... j'aurais quand même\nfoncé tête baissée. C'est ma façon de faire. Me voilà donc avec une version 2 stable,\nentièrement réécrite en Rust, qui tourne en production sur ma petite infrastructure depuis plus d'un an. Et je suis\nvraiment content du résultat. 😄",[12,19,20],{},"Pour ceux qui me lisent depuis longtemps, voici un récapitulatif des articles qui ont précédé celui-ci :",[22,23,24,40],"table",{},[25,26,27],"thead",{},[28,29,30,34,37],"tr",{},[31,32,33],"th",{},"Article",[31,35,36],{},"Date",[31,38,39],{},"Sujet",[41,42,43,59,73,87,101],"tbody",{},[28,44,45,53,56],{},[46,47,48],"td",{},[49,50,52],"a",{"href":51},"\u002Fpost\u002Fwoodstock","Woodstock Backup v1.0.0",[46,54,55],{},"2020-09-20",[46,57,58],{},"Présentation du projet, prototype TypeScript + rsync",[28,60,61,67,70],{},[46,62,63],{},[49,64,66],{"href":65},"\u002Fpost\u002Fwoodstock_brtfs","Woodstock Backup - Btrfs",[46,68,69],{},"2021-01-12",[46,71,72],{},"Abandon de Btrfs, écriture d'un pool custom",[28,74,75,81,84],{},[46,76,77],{},[49,78,80],{"href":79},"\u002Fpost\u002Fwoodstock_protocol_language_sauvegarde","Woodstock Backup - Protocole et Langage de sauvegarde",[46,82,83],{},"2021-04-18",[46,85,86],{},"Protocole gRPC maison",[28,88,89,95,98],{},[46,90,91],{},[49,92,94],{"href":93},"\u002Fpost\u002Fwoodstock_rust","Woodstock Backup - Optimiser Node.js avec Rust",[46,96,97],{},"2023-05-10",[46,99,100],{},"NAPI-RS et bindings Rust pour réduire la consommation mémoire",[28,102,103,109,112],{},[46,104,105],{},[49,106,108],{"href":107},"\u002Fpost\u002Fpr_backuppc_pool","Woodstock Backup - Reverse engineering de BackupPC",[46,110,111],{},"2024-05-07",[46,113,114],{},"Migration du pool BackupPC vers Woodstock",[12,116,117],{},"Pour les nouveaux, je résume : Woodstock Backup est mon logiciel de sauvegarde personnel, centralisé, qui sauvegarde\ntoutes les machines de mon réseau local et mes serveurs distants sur un NAS. L'idée de départ était simple. Le résultat\nest... un peu plus complexe. :)",[12,119,120],{},[121,122],"img",{"alt":123,"src":124,"className":125},"Pool of chunks","\u002FWoodstock\u002Fv2_splash.png",[126],"img-center",[128,129,131],"h2",{"id":130},"le-long-chemin-de-2020-à-2026","Le long chemin de 2020 à 2026",[133,134,136],"h3",{"id":135},"prototype-1-typescript-rsync-btrfs-2020","Prototype 1 : TypeScript + rsync + Btrfs (2020)",[12,138,139],{},"Tout a commencé avec un prototype en TypeScript qui utilisait rsync pour copier les fichiers et Btrfs pour les\nsnapshots incrémentaux. L'idée était élégante sur le papier : rsync calcule le delta, Btrfs déduplique, tout le monde\nest content.",[12,141,142,143,146],{},"En pratique, je me suis rapidement retrouvé avec des problèmes de stabilité de Btrfs lorsque le nombre de snapshots\ndevient élevé et que le système de fichiers approche de la saturation. J'en ai ",[49,144,145],{"href":65},"parlé en détail dans mon article sur\nBtrfs",". En résumé : abandonné.",[133,148,150],{"id":149},"prototype-2-typescript-grpc-pool-custom-2021","Prototype 2 : TypeScript + gRPC + pool custom (2021)",[12,152,153,154,158],{},"L'abandon de Btrfs m'a forcé à écrire mon propre pool de stockage basé sur le principe de ",[155,156,157],"strong",{},"Content-Addressable\nStorage"," (CAS) : chaque bloc de fichier (chunk) est identifié par son hash, et si deux fichiers partagent des blocs\nidentiques, ces blocs ne sont stockés qu'une seule fois. La déduplication est donc native.",[12,160,161,162,165],{},"Pour transférer les fichiers entre les machines à sauvegarder et le serveur, j'ai abandonné rsync et développé ",[49,163,164],{"href":79},"mon\npropre protocole basé sur gRPC",". gRPC s'appuie sur HTTP\u002F2, offre une\ncompression et un TLS natifs, et les bibliothèques existent pour tous les langages. Parfait.",[12,167,168],{},"Ce prototype, toujours en TypeScript, fonctionnait. Mais le moteur JavaScript de Node.js a ses limites. En particulier,\nla représentation en mémoire des objets JavaScript est beaucoup plus gourmande que celle d'un langage compilé. Pour un\noutil qui doit gérer des millions de fichiers, c'est un vrai problème.",[133,170,172],{"id":171},"lère-des-bindings-rust-2023","L'ère des bindings Rust (2023)",[12,174,175,176,179],{},"L'idée de cette phase était séduisante : garder toute la partie cœur en Rust pour les performances, et conserver le\nTypeScript pour la couche GraphQL et la logique applicative. Rust pour ce qui est critique, TypeScript pour ce qui est\nlisible et rapide à écrire. C'est ce que j'ai ",[49,177,178],{"href":93},"décrit dans mon article de 2023",".",[12,181,182,183,187],{},"Bien sûr, je me trompais. En pratique, il fallait des bindings d'exposition NAPI-RS pour chaque interface entre les\ndeux langages, des DTOs côté TypeScript qui doublonnaient les structures Rust, et surtout, plus le temps passait, plus\nle cœur métier migrait vers Rust — laissant de moins en moins de code côté TypeScript. La complexité était réelle :\ngérer des équivalents d'",[184,185,186],"code",{},"Observable"," ou du streaming en passant par des bindings NAPI-RS n'est pas trivial, même si\nles dernières versions de NAPI-RS ont depuis apporté des solutions à ces problèmes.",[12,189,190],{},"Le tout en restant performant malgré le coût du passage par les bindings TypeScript. C'était jouable, mais c'était\nde la jonglerie.",[133,192,194],{"id":193},"la-migration-du-pool-backuppc-2024","La migration du pool BackupPC (2024)",[12,196,197,198,201],{},"En parallèle, j'ai travaillé sur la migration de mon pool BackupPC existant vers le format Woodstock. La première\napproche envisagée consistait à monter les sauvegardes BackupPC via FUSE et à les lire comme un système de fichiers\nnormal. J'ai finalement opté pour une approche plus directe : ",[49,199,200],{"href":107},"lire directement le format interne de BackupPC",",\nce qui impliquait un travail de rétro-ingénierie non négligeable. La migration a été un succès, et j'ai pu enfin\nabandonner complètement BackupPC.",[133,203,205],{"id":204},"la-décision-de-tout-réécrire-en-rust-2024","La décision de tout réécrire en Rust (2024)",[12,207,208],{},"À ce stade, la situation était devenue évidente : la partie TypeScript avait tellement rétréci qu'il ne restait plus\ngrand-chose dedans. Un serveur NestJS (framework Node.js) pour l'API, une interface web en Vue.js, et quelques couches\nde glue — le reste était déjà en Rust. Maintenir deux écosystèmes pour si peu de code TypeScript n'avait plus aucun\nsens.",[12,210,211],{},"J'ai donc décidé de sauter le pas : supprimer les bindings, réécrire intégralement le backend en Rust. Plus de\nNAPI-RS, plus de DTOs en double, plus de jonglerie entre deux compilateurs. Du Rust pur, de bout en bout.",[133,213,215],{"id":214},"woodstock-backup-v2-la-version-stable-2026","Woodstock Backup v2 : la version stable (2026)",[12,217,218,219,222],{},"La version ",[184,220,221],{},"2.0.0"," est en production depuis plus d'un an maintenant, et je la considère comme stable. Les sauvegardes tournent tous les jours, les restaurations\nfonctionnent, et je dors tranquille.",[128,224,226],{"id":225},"architecture-de-woodstock-backup-v2","Architecture de Woodstock Backup v2",[133,228,230],{"id":229},"vue-densemble-le-modèle-pull","Vue d'ensemble : le modèle Pull",[12,232,233,234,237],{},"L'architecture reste fondée sur le ",[155,235,236],{},"modèle pull"," : c'est le serveur de sauvegarde qui initie les connexions vers les\npériphériques, et non l'inverse. Cela offre une garantie de sécurité importante : un périphérique compromis ne peut\npas écrire de données arbitraires dans le serveur de sauvegarde.",[239,240,241],"center",{},[121,242],{"alt":243,"src":244},"Architecture Pull - Woodstock v2","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FVLJDRjD04BxxAOPm81M47hZbK9N-g19AfLAZ7b2aQB8UnzkiPvsTtRJSE25nHJm0Hy9h-4tw9E3ysaxAnPPs_hxvlfav5O_EXzn4Btn6EK6Edfp6A1hRH-Z4vEOK72G4Wc5E4tG9TU3bG4yoVsO2HG05Eg-LBf0zYCee2OPSw_tUZaSFratt3CfeOZ_2Ge-agjMsDmm9UXoZ47G-sE0OpP2Vlls0QsITadZg00hShqmDznjh3Po_ZuVSFJCufNUlFujFZfR-XRKc8avWR1_NNT-K2wUBhFhE0a7vgzQystH_vOYuXVP1Hkk64gJSSWD4p5X8Pdq5mhjKZk_YU0L1rfQc-nVnvU-SXfmGf5fbcfmitLFPuMNh2UoSN8qfw4DCpeCX0KUpKFxn970NwEsz3BbxUnb_EhvoM6GV1sz0a0Km-EmeYjhmeNUoBn3q8VyqYE7fwqyGFM4qb1Dxi6mqqx5Dq-eVHTjHgBBiz8S-N9GBPOXLHc2mHksGkqB6CXG6cJLFZY9KNi_HKts0Qhbw9tkKGn_EBJCzQiimkRqvNw8TSbUpzhfLiLPWJxf3P6o4geguSa4GUFlMa7MNTwljDhPt0gb07mQaV71KxPPvXMvi7OaYLhGBJYLA1NhDofl1WDfSH0dLWo9ZRG4tw9GDJY0XsNd2FcMzja83BPuQOT0LxmZpfIe0JGtMA_UFbVAxSdhLLTXk8d6oGMI30vLXjLKg2po577aMaFrUpWEwNb2ErJ8aOHLyi9K6LLkATn4x6PuPdcSpjqcwvBdLzTJD1ggxKgdbhPhYX20f5qaeZ9w5Sj6wG_yXT7tee77dLepM9DyEMUrjRw0FmhlMiZnmLSMg5qUfie4RcMgxTqgSXDpy1G00",[133,246,248],{"id":247},"les-composants-rust","Les composants Rust",[12,250,251],{},"L'ensemble de la solution est écrit en Rust. Côté serveur, quatre microservices ; côté client, un démon déployé sur\nchaque machine à sauvegarder :",[22,253,254,267],{},[25,255,256],{},[28,257,258,261,264],{},[31,259,260],{},"Composant",[31,262,263],{},"Déployé sur",[31,265,266],{},"Rôle",[41,268,269,282,298,313,325],{},[28,270,271,276,279],{},[46,272,273],{},[184,274,275],{},"api_server",[46,277,278],{},"Serveur",[46,280,281],{},"API REST\u002FGraphQL pour l'interface Vue.js — hors du chemin de sauvegarde",[28,283,284,289,291],{},[46,285,286],{},[184,287,288],{},"client_api_server",[46,290,278],{},[46,292,293,294,297],{},"Reçoit les connexions mTLS de ",[184,295,296],{},"ws_client_daemon"," pour le signalement online\u002Foffline",[28,299,300,305,307],{},[46,301,302],{},[184,303,304],{},"job_worker",[46,306,278],{},[46,308,309,310,312],{},"Exécute les sauvegardes : connexion gRPC vers ",[184,311,296],{},", transfert des chunks, déduplication",[28,314,315,320,322],{},[46,316,317],{},[184,318,319],{},"scheduler",[46,321,278],{},[46,323,324],{},"Gère le planning, déclenche les jobs selon les règles définies",[28,326,327,331,334],{},[46,328,329],{},[184,330,296],{},[46,332,333],{},"Chaque périphérique",[46,335,336,337,339],{},"Reçoit les connexions de ",[184,338,304],{},", crée les snapshots, envoie les chunks",[12,341,342,343,349,350,353],{},"La gestion des jobs de sauvegarde est assurée par ",[49,344,348],{"href":345,"rel":346},"https:\u002F\u002Fgithub.com\u002Fgeofmureithi\u002Fapalis",[347],"nofollow","Apalis",", qui s'appuie sur\n",[155,351,352],{},"Redis\u002FValkey"," comme backend de file d'attente. Apalis remplace ici BullMQ, qui remplissait ce rôle dans l'ancienne\nversion NestJS. C'est une architecture simple et fiable, qui permet également de distribuer les workers si un jour\nl'infrastructure venait à grossir.",[133,355,357],{"id":356},"le-pool-de-stockage-cas-blake3zstd","Le pool de stockage : CAS Blake3+Zstd",[12,359,360,361,365],{},"Le cœur du système est le pool CAS (",[362,363,364],"em",{},"Content-Addressable Storage","). Son fonctionnement est le suivant :",[367,368,369,377,384,387],"ol",{},[370,371,372,373,376],"li",{},"Chaque fichier est découpé en ",[155,374,375],{},"chunks"," de taille variable.",[370,378,379,380,383],{},"Chaque chunk est haché avec ",[155,381,382],{},"Blake3"," (un algorithme de hachage moderne, très rapide).",[370,385,386],{},"Si le hash du chunk est déjà présent dans le pool, il n'est pas retransféré ni re-stocké.",[370,388,389,390,393],{},"Si le chunk est nouveau, il est compressé avec ",[155,391,392],{},"Zstd"," avant d'être écrit sur disque.",[12,395,396],{},"La déduplication est donc réalisée au niveau des chunks, et non au niveau des fichiers entiers. Cela signifie que si\nun fichier de 10 Go n'a été modifié qu'à 1 %, seul 1 % sera retransféré et stocké.",[12,398,399,400,403],{},"Le pool maintient également un ",[155,401,402],{},"compteur de références"," (refcount) par chunk : quand une sauvegarde est supprimée,\nles chunks qui ne sont plus référencés sont supprimés du pool. Ce mécanisme de garbage collection permet de garder le\npool propre sans intervention manuelle.",[133,405,407],{"id":406},"sauvegardes-windows-vss-natif","Sauvegardes Windows : VSS natif",[12,409,410,411,414,415,418],{},"Depuis la version ",[184,412,413],{},"alpha.57",", le client Windows utilise le ",[155,416,417],{},"Volume Shadow Copy Service (VSS)"," de Windows pour créer\nun snapshot cohérent du système de fichiers avant la sauvegarde. Cela permet de sauvegarder des fichiers verrouillés\n(comme les bases de données, les fichiers de profil Outlook, etc.) sans erreur.",[12,420,421,422,424,425,428],{},"Plus besoin de rsync, de Cygwin, ou d'outils tiers : le client ",[184,423,296],{}," est un binaire Rust natif pour\nWindows, compilé avec la cible ",[184,426,427],{},"x86_64-pc-windows-msvc",", qui utilise les APIs Win32 directement. C'est\nnettement plus propre que l'ancienne approche.",[133,430,432],{"id":431},"sauvegardes-linux-snapshots-btrfs","Sauvegardes Linux : snapshots Btrfs",[12,434,435],{},"Sur les machines Linux dont le système de fichiers est Btrfs, le client crée un snapshot en lecture seule avant de\nlancer la sauvegarde. Cela garantit la cohérence des données sauvegardées, même si des fichiers sont modifiés pendant\nla sauvegarde.",[12,437,438],{},"Contrairement au premier prototype qui utilisait Btrfs côté serveur (ce qui causait les problèmes évoqués plus haut),\nici les snapshots sont créés côté client et uniquement pour la durée de la sauvegarde. C'est une utilisation beaucoup\nplus conservative de Btrfs.",[128,440,442],{"id":441},"les-défis-techniques","Les défis techniques",[133,444,446],{"id":445},"le-refcounting-un-problème-de-cohérence","Le refcounting : un problème de cohérence",[12,448,449,450,452],{},"Le plus grand défi de cette réécriture a été l'implémentation correcte du ",[155,451,402],{}," du pool CAS.",[12,454,455],{},"Le problème est le suivant : lorsqu'une sauvegarde est en cours, des chunks sont ajoutés au pool et leur refcount est\nincrémenté. Si la sauvegarde est interrompue (coupure réseau, arrêt du serveur, etc.), le pool peut se retrouver dans\nun état incohérent : des chunks présents dans le pool sans être référencés par aucune sauvegarde complète.",[12,457,458],{},"C'est la solution la plus complexe et qui a nécessité le plus de travail pour être implémentée de manière fiable. En\neffet, il faut s'assurer que le comptage de référence est bon si on ne veux pas se retrouver à éliminer des chunks encore référencés.",[12,460,461],{},"Afin de garantir que le comptage de référence est bon, un outil de récupération a été développé : il analyse le pool et les manifestes des sauvegardes, et corrige les refcounts en cas d'incohérence. Il n'est normallement nécessaire qu'en cas de\ncrash du serveur (coupure de courant, etc.) pendant une sauvegarde.",[12,463,464],{},"Actuellement la structure du pool repose sur le système de fichiers. L'inconvénient est à aujourd'hui la durée\nd'execution du fsck qui peut être très longue (taille du pool). En échange les opérations de lecture\u002Fécriture sont très\nrapides.",[133,466,468],{"id":467},"les-breaking-changes","Les BREAKING CHANGES",[12,470,471],{},"Par rapport à la version 1, on est sur une réécriture complète. Vu le peu de monde qui utilise cette version 1, il n'y a\npas de migration prévue. La version 2 est un nouveau projet, avec une nouvelle API, et des changements majeurs dans la façon dont les sauvegardes sont gérées.",[133,473,475],{"id":474},"windows-sans-rsync-binaire-natif-cross-compilé","Windows sans rsync : binaire natif cross-compilé",[12,477,478],{},"Dans la version 1, le client Windows était un serveur rsyncd couplé à Cygwin. C'était fonctionnel, mais peu élégant,\ndifficile à installer, et les permissions NTFS n'étaient pas correctement sauvegardées.",[12,480,481,482,484],{},"Dans la version 2, le client Windows est un binaire Rust compilé en cross-compilation depuis Linux avec la cible\n",[184,483,427],{}," et le linker LLVM\u002FClang. Le binaire est distribué seul, sans dépendance. Il se connecte au\nserveur de sauvegarde via gRPC mTLS, crée un snapshot VSS, parcourt le système de fichiers et envoie les chunks.",[12,486,487],{},"Les ACLs NTFS, les attributs étendus, les points de jonction et les liens symboliques Windows sont correctement\ngérés. C'est un vrai progrès.",[133,489,491],{"id":490},"la-sécurité-mtls-de-bout-en-bout","La sécurité : mTLS de bout en bout",[12,493,494,495,497,498,501],{},"Toutes les communications impliquant ",[184,496,296],{}," sont chiffrées et authentifiées par ",[155,499,500],{},"mutual TLS (mTLS)",".\nChaque périphérique possède un certificat client signé par une autorité de certification interne au serveur Woodstock.",[12,503,504],{},"Deux canaux mTLS distincts :",[506,507,508,518],"ul",{},[370,509,510,517],{},[155,511,512,514,515],{},[184,513,304],{}," ↔ ",[184,516,296],{}," (gRPC) : le canal de sauvegarde, à l'initiative du serveur.",[370,519,520,527],{},[155,521,522,524,525],{},[184,523,296],{}," → ",[184,526,288],{}," (REST) : le canal de présence, à l'initiative du client, qui lui permet\nde signaler son statut online\u002Foffline.",[12,529,530],{},"Cela garantit que :",[506,532,533,536,539],{},[370,534,535],{},"Les données sauvegardées sont chiffrées en transit.",[370,537,538],{},"Seul le serveur Woodstock peut déclencher une sauvegarde sur un périphérique enregistré.",[370,540,541],{},"Un périphérique ne peut pas usurper l'identité d'un autre.",[12,543,544,545,547],{},"À noter : ",[184,546,275],{},", qui sert l'interface Vue.js, ne dispose pas encore d'authentification. C'est prévu, mais pas encore fait (voir plus bas).",[128,549,551],{"id":550},"en-production-depuis-plus-dun-an","En production depuis plus d'un an",[133,553,555],{"id":554},"les-machines-sauvegardées","Les machines sauvegardées",[12,557,558],{},"Voici un tableau récapitulatif de mes neuf machines sauvegardées au 26 avril 2026 :",[22,560,561,582],{},[25,562,563],{},[28,564,565,568,570,573,576,579],{},[31,566,567],{},"Machine",[31,569,266],{},[31,571,572],{},"Sauvegardes",[31,574,575],{},"Fichiers",[31,577,578],{},"Taille brute",[31,580,581],{},"Compressé",[41,583,584,606,628,650,672,694,716,737,759],{},[28,585,586,591,594,597,600,603],{},[46,587,588],{},[184,589,590],{},"localhost",[46,592,593],{},"NAS local (Debian)",[46,595,596],{},"43",[46,598,599],{},"13 346",[46,601,602],{},"1,3 Go",[46,604,605],{},"0,8 Go",[28,607,608,613,616,619,622,625],{},[46,609,610],{},[184,611,612],{},"pc-eve",[46,614,615],{},"PC principal de la famille (Windows)",[46,617,618],{},"211",[46,620,621],{},"563 333",[46,623,624],{},"473 Go",[46,626,627],{},"413 Go",[28,629,630,635,638,641,644,647],{},[46,631,632],{},[184,633,634],{},"pc-m-eve",[46,636,637],{},"Ordinateur portable familial (Windows)",[46,639,640],{},"26",[46,642,643],{},"146 243",[46,645,646],{},"21,6 Go",[46,648,649],{},"13,5 Go",[28,651,652,657,660,663,666,669],{},[46,653,654],{},[184,655,656],{},"pc-m-ulrich",[46,658,659],{},"Mon portable personnel (Linux)",[46,661,662],{},"53",[46,664,665],{},"43 185",[46,667,668],{},"125 Go",[46,670,671],{},"64,5 Go",[28,673,674,679,682,685,688,691],{},[46,675,676],{},[184,677,678],{},"pc-ulrich",[46,680,681],{},"Mon PC de bureau (Linux)",[46,683,684],{},"322",[46,686,687],{},"1 385 001",[46,689,690],{},"108 Go",[46,692,693],{},"76,7 Go",[28,695,696,701,704,707,710,713],{},[46,697,698],{},[184,699,700],{},"pc-alex-linux",[46,702,703],{},"PC Linux secondaire",[46,705,706],{},"4",[46,708,709],{},"210 006",[46,711,712],{},"25 Go",[46,714,715],{},"14,5 Go",[28,717,718,723,726,729,732,735],{},[46,719,720],{},[184,721,722],{},"pc-alex-windows",[46,724,725],{},"PC Windows secondaire",[46,727,728],{},"360",[46,730,731],{},"429 368",[46,733,734],{},"147 Go",[46,736,690],{},[28,738,739,744,747,750,753,756],{},[46,740,741],{},[184,742,743],{},"server",[46,745,746],{},"Serveur dédié OVH principal",[46,748,749],{},"450",[46,751,752],{},"243 074",[46,754,755],{},"810 Go",[46,757,758],{},"752 Go",[28,760,761,766,769,772,775,778],{},[46,762,763],{},[184,764,765],{},"server-ovh-6",[46,767,768],{},"Second serveur OVH",[46,770,771],{},"472",[46,773,774],{},"493 441",[46,776,777],{},"194 Go",[46,779,780],{},"173 Go",[12,782,783],{},"Quelques observations :",[506,785,786,798,805],{},[370,787,788,792,793,797],{},[155,789,790],{},[184,791,743],{}," et ",[155,794,795],{},[184,796,765],{}," ont le plus grand nombre de sauvegardes (450 et 472). Ce sont des serveurs qui\ntournent 24\u002F7, avec des données critiques.",[370,799,800,804],{},[155,801,802],{},[184,803,678],{}," cumule plus de 1,3 million de fichiers sauvegardés, ce qui en fait la machine avec le plus grand\nnombre de fichiers individuels. Mon répertoire de développement est manifestement très fragmenté. 😄",[370,806,807,808,812,813,817],{},"La compression Zstd est particulièrement efficace sur ",[155,809,810],{},[184,811,656],{}," (49 % d'économie) et beaucoup moins\nsur ",[155,814,815],{},[184,816,743],{}," (7 %), ce qui s'explique par la nature des données stockées : données de développement vs.\ndonnées déjà compressées (archives, images Docker, etc.).",[133,819,821],{"id":820},"le-pool-global","Le pool global",[12,823,824],{},"Le pool CAS central agrège toutes les sauvegardes :",[22,826,827,837],{},[25,828,829],{},[28,830,831,834],{},[31,832,833],{},"Statistique",[31,835,836],{},"Valeur",[41,838,839,847,855,863,871],{},[28,840,841,844],{},[46,842,843],{},"Chunks uniques",[46,845,846],{},"3 365 564",[28,848,849,852],{},[46,850,851],{},"Références totales",[46,853,854],{},"60 888 193",[28,856,857,860],{},[46,858,859],{},"Espace pool compressé",[46,861,862],{},"1,95 To",[28,864,865,868],{},[46,866,867],{},"Espace disque total",[46,869,870],{},"9,6 To",[28,872,873,876],{},[46,874,875],{},"Espace disque utilisé",[46,877,878],{},"6,4 To",[12,880,881],{},"Le ratio références\u002Fchunks (60,9 M \u002F 3,4 M ≈ 18x) illustre l'efficacité de la déduplication : chaque chunk unique est\nen moyenne référencé 18 fois par différentes sauvegardes. L'historique long des sauvegardes explique cette valeur\nélevée.",[133,883,885],{"id":884},"interface-web","Interface web",[12,887,888],{},"L'interface web Vue.js 3 \u002F Vuetify permet de visualiser l'état de l'infrastructure, du pool, et de l'historique des\nsauvegardes de chaque machine.",[12,890,891],{},[121,892],{"alt":893,"src":894,"className":895},"Page Devices - liste des machines sauvegardées","\u002FWoodstock\u002Fv2_devices.png",[126],[12,897,898],{},"La page principale liste les neuf machines avec leur état, le nombre de sauvegardes, la taille brute et compressée.",[12,900,901],{},[121,902],{"alt":903,"src":904,"className":905},"Page Pool - statistiques du pool CAS","\u002FWoodstock\u002Fv2_pool.png",[126],[12,907,908],{},"La page pool affiche les statistiques globales du stockage : occupation disque, nombre de chunks, nombre de\nréférences, et l'évolution depuis le mois précédent.",[12,910,911],{},[121,912],{"alt":913,"src":914,"className":915},"Page Backups - détail des sauvegardes d'une machine","\u002FWoodstock\u002Fv2_host_detail.png",[126],[12,917,918],{},"La page de détail d'une machine liste l'historique complet de ses sauvegardes avec la date, la durée, et la liste des\npartitions sauvegardées.",[12,920,921],{},[121,922],{"alt":923,"src":924,"className":925},"Liste des sauvegardes avec état, durée et politique de rétention","\u002FWoodstock\u002Fv2_backup_list.png",[126],[12,927,928,929,932,933,932,936,939,940,179],{},"Chaque entrée de la liste indique le numéro de sauvegarde, la date de démarrage, la durée, le nombre de fichiers\ntotaux et nouveaux, les fichiers modifiés et supprimés, le compteur d'erreurs, et la politique de rétention\nappliquée : ",[155,930,931],{},"Horaire",", ",[155,934,935],{},"Quotidien",[155,937,938],{},"Hebdo"," ou ",[155,941,942],{},"Mensuel",[12,944,945],{},[121,946],{"alt":947,"src":948,"className":949},"Navigation dans les fichiers d'une sauvegarde","\u002FWoodstock\u002Fv2_backup_browse.png",[126],[12,951,952],{},"En cliquant sur une sauvegarde, on accède à la vue détaillée : statistiques complètes (fichiers, tailles, durée,\nvitesse), partitions sauvegardées avec leur type (Btrfs ou VSS), et un explorateur de fichiers permettant de\nnaviguer dans l'arborescence et de télécharger ou restaurer individuellement n'importe quel fichier.",[128,954,956],{"id":955},"perspectives","Perspectives",[133,958,960],{"id":959},"archivage-hors-site-sur-disque-usb","Archivage hors-site sur disque USB",[12,962,963],{},"Une fonctionnalité que je veux mettre en place depuis la v1 est l'archivage de la dernière version des sauvegardes\nsur un disque dur USB, qui est ensuite stocké hors-site. L'idée est d'avoir une copie physique des données dans un\nautre lieu en cas de sinistre (incendie, vol, etc.).",[12,965,966,967,179],{},"Dans la version 1 avec BackupPC, j'avais un script qui utilisait le connecteur FUSE de BackupPC pour monter les\nsauvegardes et les synchroniser avec rsync vers le disque USB. Ce script posait des problèmes avec les gros fichiers\net les permissions Windows, comme je l'avais mentionné ",[49,968,969],{"href":51},"dans le tout premier article",[12,971,972,973,976,977,980,981,984],{},"Dans Woodstock v2, l'outil en ligne de commande ",[184,974,975],{},"ws_console"," dispose d'une commande ",[184,978,979],{},"mount"," qui permet de monter\nune sauvegarde comme un système de fichiers FUSE. Il sera donc possible de faire un ",[184,982,983],{},"rsync"," depuis ce point de\nmontage vers le disque USB, avec une gestion correcte des permissions et des gros fichiers.",[12,986,987],{},"Ce n'est pas encore automatisé, mais c'est la prochaine chose que je veux mettre en place.",[133,989,991],{"id":990},"ajout-dun-format-de-stockage","Ajout d'un format de stockage",[12,993,994],{},"J'envisage également de pouvoir stocker directement le pool sur un bucket S3 ou compatible (SeaweedFS, RustFS). Avant de me lancer dans cette implémentation, je veux d'abord tester\nles performances d'un tel choix.",[128,996,998],{"id":997},"comparaison-avec-les-solutions-existantes","Comparaison avec les solutions existantes",[12,1000,1001],{},"Avant de conclure, voici une comparaison honnête avec les alternatives réalistes. Si un autre outil correspond mieux à vos besoins, utilisez-le. Sans rancune.",[12,1003,1004,1005,932,1008,932,1011,932,1014,792,1017,1020],{},"Les outils couverts : ",[155,1006,1007],{},"Restic",[155,1009,1010],{},"BorgBackup",[155,1012,1013],{},"BackupPC",[155,1015,1016],{},"URBackup",[155,1018,1019],{},"Kopia",". Ils représentent les principales solutions open-source pour une infrastructure self-hostée multi-machines — ce qui correspond grosso modo au problème que Woodstock cherche à résoudre.",[22,1022,1023,1043],{},[25,1024,1025],{},[28,1026,1027,1030,1033,1035,1037,1039,1041],{},[31,1028,1029],{},"Critère",[31,1031,1032],{},"Woodstock v2",[31,1034,1007],{},[31,1036,1010],{},[31,1038,1013],{},[31,1040,1016],{},[31,1042,1019],{},[41,1044,1045,1069,1094,1116,1141,1163,1187,1211,1234,1258,1280,1300,1320,1342,1362,1382,1402,1421],{},[28,1046,1047,1052,1055,1058,1061,1064,1067],{},[46,1048,1049],{},[155,1050,1051],{},"Langage",[46,1053,1054],{},"Rust",[46,1056,1057],{},"Go",[46,1059,1060],{},"Python + C",[46,1062,1063],{},"Perl",[46,1065,1066],{},"C++",[46,1068,1057],{},[28,1070,1071,1076,1079,1082,1085,1088,1091],{},[46,1072,1073],{},[155,1074,1075],{},"Licence",[46,1077,1078],{},"MIT",[46,1080,1081],{},"BSD-2",[46,1083,1084],{},"BSD",[46,1086,1087],{},"GPL v2+",[46,1089,1090],{},"AGPLv3+",[46,1092,1093],{},"Apache 2.0",[28,1095,1096,1101,1104,1107,1109,1112,1114],{},[46,1097,1098],{},[155,1099,1100],{},"Développement actif",[46,1102,1103],{},"✅ 2026",[46,1105,1106],{},"✅ 2025",[46,1108,1103],{},[46,1110,1111],{},"❌ 2020¹",[46,1113,1103],{},[46,1115,1106],{},[28,1117,1118,1123,1126,1129,1132,1135,1138],{},[46,1119,1120],{},[155,1121,1122],{},"Modèle de synchronisation",[46,1124,1125],{},"Pull (initié par le serveur)",[46,1127,1128],{},"Push (le client pousse)",[46,1130,1131],{},"Push (SSH ou local)",[46,1133,1134],{},"Pull (rsync\u002Ftar\u002FSMB)",[46,1136,1137],{},"Pull-like (découverte LAN)",[46,1139,1140],{},"Push \u002F mode serveur",[28,1142,1143,1148,1151,1154,1156,1158,1160],{},[46,1144,1145],{},[155,1146,1147],{},"Agent requis sur le client",[46,1149,1150],{},"daemon",[46,1152,1153],{},"N'a pas de sens",[46,1155,1153],{},[46,1157,983],{},[46,1159,1150],{},[46,1161,1162],{},"serveur optionnel",[28,1164,1165,1170,1173,1176,1179,1182,1184],{},[46,1166,1167],{},[155,1168,1169],{},"Backends cloud\u002Fdistants",[46,1171,1172],{},"❌ self-hosted uniquement",[46,1174,1175],{},"✅ S3, B2, SFTP, Azure, GCS, rclone…",[46,1177,1178],{},"⚠️ SSH \u002F BorgBase",[46,1180,1181],{},"❌ disque local uniquement",[46,1183,1172],{},[46,1185,1186],{},"✅ S3, Azure, GCS, B2, SFTP, rclone…",[28,1188,1189,1194,1197,1200,1203,1206,1209],{},[46,1190,1191],{},[155,1192,1193],{},"Format de stockage",[46,1195,1196],{},"Pool CAS, fichiers sur disque",[46,1198,1199],{},"Pack files CAS",[46,1201,1202],{},"Journal de segments + index",[46,1204,1205],{},"Pool MD5 (niveau fichier) + reverse deltas",[46,1207,1208],{},"Snapshots de fichiers + images de blocs",[46,1210,1199],{},[28,1212,1213,1218,1221,1223,1226,1229,1232],{},[46,1214,1215],{},[155,1216,1217],{},"Granularité de déduplication",[46,1219,1220],{},"Chunk (CDC)",[46,1222,1220],{},[46,1224,1225],{},"Chunk (BUZHASH CDC)",[46,1227,1228],{},"Fichier (MD5 fichier complet, sans hardlinks en v4)",[46,1230,1231],{},"Fichier",[46,1233,1220],{},[28,1235,1236,1241,1244,1247,1250,1253,1256],{},[46,1237,1238],{},[155,1239,1240],{},"Dédup cross-machines",[46,1242,1243],{},"✅ (un pool partagé)",[46,1245,1246],{},"⚠️ uniquement si dépôt partagé",[46,1248,1249],{},"⚠️ uniquement au sein d'un dépôt",[46,1251,1252],{},"✅ (pool MD5 partagé)",[46,1254,1255],{},"✅ (niveau fichier)",[46,1257,1249],{},[28,1259,1260,1265,1268,1271,1274,1276,1278],{},[46,1261,1262],{},[155,1263,1264],{},"Chiffrement au repos",[46,1266,1267],{},"❌ pool en clair",[46,1269,1270],{},"✅ pool chiffré",[46,1272,1273],{},"✅ pool chiffré (optionnel)",[46,1275,1267],{},[46,1277,1267],{},[46,1279,1273],{},[28,1281,1282,1287,1290,1292,1294,1296,1298],{},[46,1283,1284],{},[155,1285,1286],{},"Linux",[46,1288,1289],{},"✅",[46,1291,1289],{},[46,1293,1289],{},[46,1295,1289],{},[46,1297,1289],{},[46,1299,1289],{},[28,1301,1302,1307,1310,1312,1314,1316,1318],{},[46,1303,1304],{},[155,1305,1306],{},"macOS",[46,1308,1309],{},"❌²",[46,1311,1289],{},[46,1313,1289],{},[46,1315,1289],{},[46,1317,1289],{},[46,1319,1289],{},[28,1321,1322,1327,1330,1332,1335,1338,1340],{},[46,1323,1324],{},[155,1325,1326],{},"Windows (natif)",[46,1328,1329],{},"✅ (binaire MSVC)",[46,1331,1289],{},[46,1333,1334],{},"❌ (WSL2 uniquement)",[46,1336,1337],{},"⚠️ (rsync\u002FCygwin ou SMB)",[46,1339,1289],{},[46,1341,1289],{},[28,1343,1344,1349,1351,1353,1356,1358,1360],{},[46,1345,1346],{},[155,1347,1348],{},"Snapshots VSS (Windows)",[46,1350,1289],{},[46,1352,1289],{},[46,1354,1355],{},"❌",[46,1357,1355],{},[46,1359,1289],{},[46,1361,1289],{},[28,1363,1364,1369,1371,1374,1376,1378,1380],{},[46,1365,1366],{},[155,1367,1368],{},"Snapshots Btrfs (Linux)",[46,1370,1289],{},[46,1372,1373],{},"❌ (hooks manuels)",[46,1375,1373],{},[46,1377,1355],{},[46,1379,1355],{},[46,1381,1355],{},[28,1383,1384,1389,1391,1393,1395,1397,1400],{},[46,1385,1386],{},[155,1387,1388],{},"Sauvegarde image \u002F bare-metal",[46,1390,1355],{},[46,1392,1355],{},[46,1394,1355],{},[46,1396,1355],{},[46,1398,1399],{},"✅ (Windows + Linux)",[46,1401,1355],{},[28,1403,1404,1409,1411,1413,1415,1417,1419],{},[46,1405,1406],{},[155,1407,1408],{},"Montage FUSE",[46,1410,1289],{},[46,1412,1289],{},[46,1414,1289],{},[46,1416,1355],{},[46,1418,1289],{},[46,1420,1289],{},[28,1422,1423,1427,1430,1433,1436,1439,1441],{},[46,1424,1425],{},[155,1426,885],{},[46,1428,1429],{},"✅ (sans auth pour l'instant)",[46,1431,1432],{},"❌ (tiers : Restic Browser)",[46,1434,1435],{},"❌ (tiers : Vorta)",[46,1437,1438],{},"✅ (CGI\u002FPerl)",[46,1440,1289],{},[46,1442,1443],{},"✅ (KopiaUI)",[12,1445,1446],{},"¹ La dernière version de BackupPC date de juin 2020. La base de code est stable mais n'est plus maintenue.",[12,1448,1449],{},"² La prise en charge de macOS n'est pas encore implémentée dans Woodstock.",[12,1451,1452],{},"Quelques points à souligner :",[12,1454,1455,1456,1458],{},"Le ",[155,1457,236],{}," (Woodstock, BackupPC, URBackup) signifie qu'un client compromis ne peut pas écrire de données arbitraires sur le serveur de sauvegarde. Le modèle push (Restic, Borg, Kopia) est plus simple à mettre en place, mais accorde davantage de confiance à chaque client.",[12,1460,1461,1462,1465],{},"La ",[155,1463,1464],{},"déduplication au niveau des chunks"," signifie que si un fichier de 10 Go est modifié d'1 Mo, seul cet 1 Mo est transféré et stocké (si la taille du chunk est de 1Ko - sur Woodstock la taille du chunk est de 16Mo). La déduplication au niveau fichier (BackupPC, URBackup) signifie que si un fichier change, le nouveau fichier entier est stocké dans le pool. Pour BackupPC spécifiquement, rsync gère le transfert de manière efficiente — seuls les blocs modifiés transitent sur le réseau — mais comme la correspondance dans le pool est basée sur un MD5 du fichier complet, un fichier modifié génère toujours une nouvelle entrée complète dans le pool.",[12,1467,1461,1468,1471],{},[155,1469,1470],{},"déduplication cross-machines"," est moins courante. Si dix machines possèdent chacune une copie du même installeur de 500 Mo, Woodstock ne le stocke qu'une seule fois. Restic et Borg ne dédupliquent qu'au sein d'un même dépôt — pour obtenir une déduplication cross-machines, il faudrait mettre toutes les machines dans le même dépôt, ce qui crée des problèmes de contention de verrous et de contrôle d'accès.",[12,1473,1474,1475,1478],{},"La principale faiblesse de Woodstock : ",[155,1476,1477],{},"le pool n'est pas chiffré au repos",". Si quelqu'un obtient un accès physique ou système à votre NAS, il peut lire vos données de sauvegarde directement. L'approche de Restic — tout chiffrer, de manière obligatoire — est clairement plus solide si quelqu'un met la main sur votre disque. Sur un NAS de LAN privé que vous contrôlez physiquement, c'est un compromis que j'accepte. Pour un stockage distant ou dans le cloud, c'est une vraie limitation. Il est possible de contrer ce problème en chiffrant le disque lui-même (LUKS, BitLocker, etc.), mais c'est une couche supplémentaire à gérer.",[12,1480,1481,1482,1485,1486,1489],{},"Si vous avez besoin d'une ",[155,1483,1484],{},"restauration bare-metal",", URBackup est le seul outil de cette liste qui le fait nativement. Si vous avez besoin de ",[155,1487,1488],{},"BorgBackup sur Windows",", c'est impossible sans WSL2.",[128,1491,1493],{"id":1492},"conclusion","Conclusion",[12,1495,1496],{},"Ce projet m'a appris énormément de choses : Rust, gRPC, mTLS, la gestion de pools CAS, le VSS Windows, les snapshots\nBtrfs, la gestion de jobs distribués avec Redis, et probablement une dizaine d'autres sujets que j'ai oubliés depuis.",[12,1498,1499],{},"Est-ce que c'était raisonnable de passer six ans à construire son propre logiciel de sauvegarde alors qu'il en existe\ndes dizaines ? Bien sur que oui 😄.",[12,1501,1502,1503,1508,1509,179],{},"Le logiciel est disponible sur ",[49,1504,1507],{"href":1505,"rel":1506},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002Fwoodstock-backup",[347],"mon instance Gitea"," et la\ndocumentation est sur ",[49,1510,1513],{"href":1511,"rel":1512},"https:\u002F\u002Fwoodstock.shadoware.org",[347],"woodstock.shadoware.org",[12,1515,1516],{},"À bientôt !",{"title":1518,"searchDepth":1519,"depth":1519,"links":1520},"",2,[1521,1530,1537,1543,1548,1552,1553],{"id":130,"depth":1519,"text":131,"children":1522},[1523,1525,1526,1527,1528,1529],{"id":135,"depth":1524,"text":136},3,{"id":149,"depth":1524,"text":150},{"id":171,"depth":1524,"text":172},{"id":193,"depth":1524,"text":194},{"id":204,"depth":1524,"text":205},{"id":214,"depth":1524,"text":215},{"id":225,"depth":1519,"text":226,"children":1531},[1532,1533,1534,1535,1536],{"id":229,"depth":1524,"text":230},{"id":247,"depth":1524,"text":248},{"id":356,"depth":1524,"text":357},{"id":406,"depth":1524,"text":407},{"id":431,"depth":1524,"text":432},{"id":441,"depth":1519,"text":442,"children":1538},[1539,1540,1541,1542],{"id":445,"depth":1524,"text":446},{"id":467,"depth":1524,"text":468},{"id":474,"depth":1524,"text":475},{"id":490,"depth":1524,"text":491},{"id":550,"depth":1519,"text":551,"children":1544},[1545,1546,1547],{"id":554,"depth":1524,"text":555},{"id":820,"depth":1524,"text":821},{"id":884,"depth":1524,"text":885},{"id":955,"depth":1519,"text":956,"children":1549},[1550,1551],{"id":959,"depth":1524,"text":960},{"id":990,"depth":1524,"text":991},{"id":997,"depth":1519,"text":998},{"id":1492,"depth":1519,"text":1493},"Woodstock","woodstock","2026-04-26",{"type":9,"value":1558},[1559,1561,1563,1565,1629,1631],[12,1560,14],{},[12,1562,17],{},[12,1564,20],{},[22,1566,1567,1577],{},[25,1568,1569],{},[28,1570,1571,1573,1575],{},[31,1572,33],{},[31,1574,36],{},[31,1576,39],{},[41,1578,1579,1589,1599,1609,1619],{},[28,1580,1581,1585,1587],{},[46,1582,1583],{},[49,1584,52],{"href":51},[46,1586,55],{},[46,1588,58],{},[28,1590,1591,1595,1597],{},[46,1592,1593],{},[49,1594,66],{"href":65},[46,1596,69],{},[46,1598,72],{},[28,1600,1601,1605,1607],{},[46,1602,1603],{},[49,1604,80],{"href":79},[46,1606,83],{},[46,1608,86],{},[28,1610,1611,1615,1617],{},[46,1612,1613],{},[49,1614,94],{"href":93},[46,1616,97],{},[46,1618,100],{},[28,1620,1621,1625,1627],{},[46,1622,1623],{},[49,1624,108],{"href":107},[46,1626,111],{},[46,1628,114],{},[12,1630,117],{},[12,1632,1633],{},[121,1634],{"alt":123,"src":124,"className":1635},[126],"md","Lille, France",{"planet":1639},true,"\u002Fpost\u002Fwoodstock_v2",{"title":6,"description":14},"woodstock_v2","posts\u002FWoodstock\u002F2026-04-26_woodstock_v2",[1555,1645,1646,1647,1648],"backup","sauvegarde","rust","grpc",21,"nIVC1RfQuRDEMq_TSeirXwiPYgaTMOWAcQ1N0Yk1FLc",{"id":1652,"title":1653,"author":7,"body":1654,"category":1554,"categorySlug":1555,"date":69,"description":14,"excerpt":2895,"extension":1636,"location":1637,"meta":2921,"navigation":1639,"path":65,"published":1639,"seo":2922,"slug":2923,"stem":2924,"tags":2925,"timeToRead":2672,"__hash__":2929},"posts\u002Fposts\u002FWoodstock\u002F2021-01-12_woodstock_brtfs.md","Woodstock Backup - Utilisation de Btrfs et son remplacement",{"type":9,"value":1655,"toc":2881},[1656,1658,1661,1664,1691,1699,1702,1705,1708,1711,1715,1719,1722,1725,1733,1736,1826,1829,2077,2080,2083,2087,2090,2093,2096,2107,2110,2113,2116,2119,2130,2133,2136,2139,2143,2146,2155,2169,2172,2176,2185,2195,2198,2201,2204,2207,2210,2213,2263,2266,2270,2273,2277,2280,2283,2318,2321,2476,2479,2482,2489,2492,2496,2499,2502,2505,2508,2517,2848,2855,2863,2866,2869,2871,2877],[12,1657,14],{},[12,1659,1660],{},"La version 1 de mon programme de sauvegarde Woodstock Backup utlise Btrfs et Rsync pour effectuer une sauvegarde. Je\nl'utilise depuis quelques mois pour sauvegarder mes differentes machines (7 machines).",[12,1662,1663],{},"Voici un premier compte-rendu de l'utilisation de la première version de cet outil dont je suis l'auteur:",[506,1665,1666,1669,1679],{},[370,1667,1668],{},"Lors de mon utilisation la sauvegarde fonctionne très bien, et cela c'est cool :). Je suis aux alentours de 200 snapshots.",[370,1670,1671,1672,1675,1676,1678],{},"J'ai eu un problème d'espace disque. Lors du déplacement de plusieurs énormes fichiers sur un serveur. La taille de\nl'espace de stockage à augmenté énormément.",[1673,1674],"br",{},"En effet rsync ne permet pas de détecter les déplacements de fichiers et btrfs ne permet pas de dédupliquer à la volée\nles données.",[1673,1677],{},"Les fichiers ont donc été considérés comme étant nouveau.",[370,1680,1681,1682,1684,1685,1687,1688,1690],{},"L'espace disque étant tombé à zéro, j'ai voulu supprimer la dernière snapshot pour tester un déplacement de fichiers\ndans btrfs (à la main).",[1673,1683],{},"La suppression de la snapshot a commencé à prendre énormément de temps, puis la machine est devenue inaccessible.",[1673,1686],{},"En me connectant en direct sur la machine (KVM), j'ai découvert que la suppression du dernier volume Btrfs remplissait\nla mémore. Les 8Go octets de mémoire ont été remplis. Et le noyaux linux a utilisé OOM Killer pour détruire tous les\nprocessus.",[1673,1689],{},"Bref la machine n'était plus dans un état lui permettant de faire les sauvegardes.",[12,1692,1693,1694,1698],{},"On parle déjà de problème avec btrfs et la suppression de snapshot (",[49,1695,1696],{"href":1696,"rel":1697},"https:\u002F\u002Fwww.spinics.net\u002Flists\u002Flinux-btrfs\u002Fmsg52088.html",[347],").\nOn trouve aussi pas mal d'article si les problèmes de btrfs quand il n'y a plus d'espace.",[12,1700,1701],{},"Cela me fait penser que btrfs ne me permettrait pas de gérer mes sauvegardes sur le long terme (tant sur le nombre de snapshots\nque cela allait créér que sur la déduplication).",[12,1703,1704],{},"Cela m'amène à la conclusion suivante, il faut que je repense la gestion du pool et de la déduplication. C'est d'ailleurs\nce que font beaucoup de logiciel de sauvegarde (UrBackup proposer Btrfs et une version interne ; BackupPC gère son pool interne,\nBorg gère son pool interne ...)",[12,1706,1707],{},"Je vais donc repenser la manière dont je gère la déduplication dans l'application de sauvegarde. Si je réécris la manière\ndont est géré le stockage, je ne pourrais plus me baser sur rsync pour la sauvegarde. Il faudra donc que je développe mon\npropre outil pour générer la sauvegarde.",[12,1709,1710],{},"Par contre cela à l'avantage de ne plus être dépendant d'un système de fichier.",[128,1712,1714],{"id":1713},"le-pool","Le pool",[133,1716,1718],{"id":1717},"diviser-pour-réigner","Diviser pour réigner",[12,1720,1721],{},"Nous allons donc commencer par construire ce qui doit être le pool des données. Permettant de faire la déduplication.",[12,1723,1724],{},"Afin de faire de la déduplication, nous allons découper les fichiers en plusieurs morceaux. Cela permettra de faire des\nsauvegardes de gros fichiers dont seule une partie change:",[506,1726,1727,1730],{},[370,1728,1729],{},"Comme les images de machine virtuelle par exemple",[370,1731,1732],{},"Comme les fichiers de logs par exemple",[12,1734,1735],{},"Quelle est la bonne taille pour un morceau de fichier. Si on découpe le fichier de taille trop petite alors le découpage ne\nsert à rien. J'ai donc pris un échantillon de tous les fichiers que j'ai sur mes différentes machines pour déterminer la taille\nde cet échantillon.",[1737,1738,1742],"pre",{"className":1739,"code":1740,"language":1741,"meta":1518,"style":1518},"language-bash shiki shiki-themes one-dark-pro","find . -type f -print0 | \\\nxargs -0 ls -l | \\\nawk '{ n=int(log($5)\u002Flog(2)); if (n\u003C10) { n=10; } size[n]++ } END { for (i in size) printf(\"%d %d\\n\", 2^i, size[i]) }' | \\\nsort -n | \\\nawk 'function human(x) { x[1]\u002F=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf(\"%3d%s: %6d\\n\", a[1],substr(\"kMGTEPYZ\",a[2]+1,1),$2) }'\n","bash",[184,1743,1744,1775,1793,1805,1818],{"__ignoreMap":1518},[1745,1746,1749,1753,1757,1761,1764,1767,1771],"span",{"class":1747,"line":1748},"line",1,[1745,1750,1752],{"class":1751},"sVbv2","find",[1745,1754,1756],{"class":1755},"subq3"," .",[1745,1758,1760],{"class":1759},"sVC51"," -type",[1745,1762,1763],{"class":1755}," f",[1745,1765,1766],{"class":1759}," -print0",[1745,1768,1770],{"class":1769},"sn6KH"," | ",[1745,1772,1774],{"class":1773},"sjrmR","\\\n",[1745,1776,1777,1780,1783,1786,1789,1791],{"class":1747,"line":1519},[1745,1778,1779],{"class":1751},"xargs",[1745,1781,1782],{"class":1759}," -0",[1745,1784,1785],{"class":1755}," ls",[1745,1787,1788],{"class":1759}," -l",[1745,1790,1770],{"class":1769},[1745,1792,1774],{"class":1773},[1745,1794,1795,1798,1801,1803],{"class":1747,"line":1524},[1745,1796,1797],{"class":1751},"awk",[1745,1799,1800],{"class":1755}," '{ n=int(log($5)\u002Flog(2)); if (n\u003C10) { n=10; } size[n]++ } END { for (i in size) printf(\"%d %d\\n\", 2^i, size[i]) }'",[1745,1802,1770],{"class":1769},[1745,1804,1774],{"class":1773},[1745,1806,1808,1811,1814,1816],{"class":1747,"line":1807},4,[1745,1809,1810],{"class":1751},"sort",[1745,1812,1813],{"class":1759}," -n",[1745,1815,1770],{"class":1769},[1745,1817,1774],{"class":1773},[1745,1819,1821,1823],{"class":1747,"line":1820},5,[1745,1822,1797],{"class":1751},[1745,1824,1825],{"class":1755}," 'function human(x) { x[1]\u002F=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf(\"%3d%s: %6d\\n\", a[1],substr(\"kMGTEPYZ\",a[2]+1,1),$2) }'\n",[12,1827,1828],{},"Voici le resultat de ce test d'échantillon.",[22,1830,1831,1844],{},[25,1832,1833],{},[28,1834,1835,1838,1841],{},[31,1836,1837],{},"File size",[31,1839,1840],{},"Number",[31,1842,1843],{},"Repartition",[41,1845,1846,1857,1868,1879,1890,1901,1912,1923,1934,1945,1956,1967,1978,1989,2000,2011,2022,2033,2044,2055,2066],{},[28,1847,1848,1851,1854],{},[46,1849,1850],{},"1k",[46,1852,1853],{},"29126558",[46,1855,1856],{},"37,36 %",[28,1858,1859,1862,1865],{},[46,1860,1861],{},"2k",[46,1863,1864],{},"8649088",[46,1866,1867],{},"48,45 %",[28,1869,1870,1873,1876],{},[46,1871,1872],{},"4k",[46,1874,1875],{},"7915884",[46,1877,1878],{},"58,60 %",[28,1880,1881,1884,1887],{},[46,1882,1883],{},"8k",[46,1885,1886],{},"6394302",[46,1888,1889],{},"66,81 %",[28,1891,1892,1895,1898],{},[46,1893,1894],{},"16k",[46,1896,1897],{},"4839627",[46,1899,1900],{},"73,01 %",[28,1902,1903,1906,1909],{},[46,1904,1905],{},"32k",[46,1907,1908],{},"3606949",[46,1910,1911],{},"77,64 %",[28,1913,1914,1917,1920],{},[46,1915,1916],{},"64k",[46,1918,1919],{},"3477900",[46,1921,1922],{},"82,10 %",[28,1924,1925,1928,1931],{},[46,1926,1927],{},"128k",[46,1929,1930],{},"5158625",[46,1932,1933],{},"88,72 %",[28,1935,1936,1939,1942],{},[46,1937,1938],{},"256k",[46,1940,1941],{},"3601985",[46,1943,1944],{},"93,34 %",[28,1946,1947,1950,1953],{},[46,1948,1949],{},"512k",[46,1951,1952],{},"971108",[46,1954,1955],{},"94,58 %",[28,1957,1958,1961,1964],{},[46,1959,1960],{},"1M",[46,1962,1963],{},"875574",[46,1965,1966],{},"95,71 %",[28,1968,1969,1972,1975],{},[46,1970,1971],{},"2M",[46,1973,1974],{},"1698194",[46,1976,1977],{},"97,88 %",[28,1979,1980,1983,1986],{},[46,1981,1982],{},"4M",[46,1984,1985],{},"1046430",[46,1987,1988],{},"99,23 %",[28,1990,1991,1994,1997],{},[46,1992,1993],{},"8M",[46,1995,1996],{},"309027",[46,1998,1999],{},"99,62 %",[28,2001,2002,2005,2008],{},[46,2003,2004],{},"16M",[46,2006,2007],{},"105271",[46,2009,2010],{},"99,76 %",[28,2012,2013,2016,2019],{},[46,2014,2015],{},"32M",[46,2017,2018],{},"65211",[46,2020,2021],{},"99,84 %",[28,2023,2024,2027,2030],{},[46,2025,2026],{},"64M",[46,2028,2029],{},"50832",[46,2031,2032],{},"99,91 %",[28,2034,2035,2038,2041],{},[46,2036,2037],{},"128M",[46,2039,2040],{},"33947",[46,2042,2043],{},"99,95 %",[28,2045,2046,2049,2052],{},[46,2047,2048],{},"256M",[46,2050,2051],{},"21338",[46,2053,2054],{},"99,98 %",[28,2056,2057,2060,2063],{},[46,2058,2059],{},"512M",[46,2061,2062],{},"8066",[46,2064,2065],{},"99,99 %",[28,2067,2068,2071,2074],{},[46,2069,2070],{},"> 1G",[46,2072,2073],{},"10068",[46,2075,2076],{},"100,00 %",[12,2078,2079],{},"La conclusion que l'on peut tirer de cela est que si on prend un chunk de 4Mo, alors 99% des fichiers ne seront pas découpés. Dans\nle lot rentrerons la plupart des fichiers textes, des photos, ... . Tous des fichiers qui s'ils ne sont pas des copies, sont normalement\nunique.",[12,2081,2082],{},"Dans une première version, je pense que cette taille ne sera pas paramétrable, mais il faudra la rendre dynamiquement paramétrable dans le\nfutur (si le type des fichiers d'autres personnes a pour conséquence une répartition différente).",[128,2084,2086],{"id":2085},"la-table-de-hashage","La table de hashage",[12,2088,2089],{},"Ces différents morceaux de fichiers (chunk) doivent ensuite pouvoir être identifiés facilement sans pour autant relire le contenu du fichier à\nchaque fois.",[12,2091,2092],{},"L'idée est d'utiliser une table de hashage où avec une clé on peut retrouver le contenu d'un morceau de fichier.",[12,2094,2095],{},"Il faut pouvoir, à partir de la clé:",[506,2097,2098,2101,2104],{},[370,2099,2100],{},"savoir si le morceau de fichier existe,",[370,2102,2103],{},"lire le contenu du morceau de fichier,",[370,2105,2106],{},"écrire le contenu du morceau de fichier.",[12,2108,2109],{},"Quelle clé peut-on utiliser ?",[12,2111,2112],{},"Après quelque recherches je vais partir sur un SHA-256. Quels sont les avantages et les inconvénients d'une telle clé ?",[12,2114,2115],{},"L'avantage est qu'il est possible, quand on connait le hash de faire le lien direct avec le morceau de fichier associé. La clé est\nfacile à générer et le lien entre la clé et le chunk est facile à connaître.",[12,2117,2118],{},"Le plus gros inconvénient est le risque de collision. Le but d'une fonction de hash est de calculer un nombre \"court\" qui permet\nde representer un texte beaucoup plus long. Ce type de fonction de hashage est généralement utilisé pour:",[506,2120,2121,2124,2127],{},[370,2122,2123],{},"quand elle est coupler à une clé public\u002Fprivé, signer un document,",[370,2125,2126],{},"verifier l'intégrité d'un document (en cas d'erreur transfert réseau),",[370,2128,2129],{},"envoyer séparement permet de vérifier que le fichier n'a pas été corrompu.",[12,2131,2132],{},"Ce qui est le propre d'une fonction de hash est sa non réversibilité. En effet s'il était possible de retrouver facilement à partir d'un\nhash le texte contenu associé, les algo de compression n'utiliseraient que ca ;).\nMais la plus grosse conséquence de tout cela est que deux textes (de contenus et de longueurs différentes) peuvent arriver au même hash.",[12,2134,2135],{},"Pour un logiciel de sauvegarde, cela pose alors un gros problème. Si parcequ'il possède le même hash qu'un autre fichier, on ne sauvegarde\npas un document, la restauration ne sera pas faite à l'identique et cela engendrera de la perte de fichier.",[12,2137,2138],{},"Je me suis donc renseigné sur ce qui se faisait ailleurs.",[133,2140,2142],{"id":2141},"borg","Borg",[12,2144,2145],{},"Je trouve borg intéressant dans son fonctionnement, ses performances, ainsi que son stockage interne. Ce qui est dommage, c'est que dans\nmon cas, je n'ai pas une vue centralisée des machines dont j'organise la sauvegarde.",[12,2147,2148,2149,2154],{},"Dans la partie ",[49,2150,2153],{"href":2151,"rel":2152},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Finternals.html",[347],"Internals"," on retrouve la structure interne du pool de Borg.",[12,2156,2157,2158,2163,2164,179],{},"Borg utilise ",[49,2159,2162],{"href":2160,"rel":2161},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Finternals\u002Fdata-structures.html#hashindex",[347],"HashIndex",", qui si j'ai bien compris\ncalcule un hash sur un contenu dont la taille peut varier. Des utilisateurs se sont déjà posés la question du ",[49,2165,2168],{"href":2166,"rel":2167},"https:\u002F\u002Fgithub.com\u002Fborgbackup\u002Fborg\u002Fissues\u002F170",[347],"risque de collision",[12,2170,2171],{},"La réponse faite par l'équipe de Borg a été de rejeter la demande. L'explication qui est faite est que la probabilité qu'une telle\ncollision arrive est faible sur un hash de 256. Très faible.",[133,2173,2175],{"id":2174},"urbackup","UrBackup",[12,2177,2178,2179,2184],{},"Même chose pour UrBackup. Dans le ",[49,2180,2183],{"href":2181,"rel":2182},"https:\u002F\u002Fwww.urbackup.org\u002Fadministration_manual.html",[347],"manuel d'administration, partie 6.3",",\non retrouve l'information suivante:",[2186,2187,2188],"blockquote",{},[12,2189,2190,2191,2194],{},"UrBackup uses SHA512 to hash the files before file deduplication. In comparison ZFS uses SHA256 for block deduplication.\nThe choice of SHA512 is safer. The Wikipedia page for “Birthday attack” has a probability table for SHA512. According to\nit one needs 1.6 ",[362,2192,2193],{},"1068 different files (of same size) to reach a probability of 10-18 of a collision. It also states\nthat 10-18 is the best case uncorrectable bit error rate of a typical hard disk. To have 1.6"," 1068 different files of\n1KB you need 1.4551915 * 1056 EB of hard disk space. So it is ridiculously more likely that the hard disk returns bad\ndata or the data gets corrupted in RAM, rather than UrBackup linking the wrong files to each other.",[12,2196,2197],{},"Bref, on y explique sur la probabilité de collision pour un SHA512 est plus faible que celle utilisé pour la déduplication\ndans ZFS. On y explique également qu'elle est ridicule par rapport au risque de panne d'un disque dur.",[133,2199,1013],{"id":2200},"backuppc",[12,2202,2203],{},"BackupPC utilise un MD5 pour gérer son les clés de son pool. En cas de collision (ce qui est beaucoup plus probable avec un MD5 qu'avec\nun SHA-256) une extension est ajoutée à la fin du fichier.",[12,2205,2206],{},"Cela necessite alors de ré-envoyer le fichier sur le réseau pour comparer, mais permet de s'assurer qu'en cas de collision on ait une\nsolution de repli.",[12,2208,2209],{},"Un MD5 fait 4 octets contrairement à un SHA-256 qui en fait 32.",[12,2211,2212],{},"Du coup, est-ce qu'avec un md5, j'ai beaucoup de collision sur mon instance BackupPC ? Pour un pool qui contient 3 464 397 fichiers, j'ai\n0 collision.",[1737,2214,2216],{"className":1739,"code":2215,"language":1741,"meta":1518,"style":1518},"> find . -type f | wc -l\n3464397\n> find . -type f | awk 'length($0) > 40' | wc -l\n0\n",[184,2217,2218,2234,2239,2258],{"__ignoreMap":1518},[1745,2219,2220,2223,2225,2228,2231],{"class":1747,"line":1748},[1745,2221,2222],{"class":1769},"> find ",[1745,2224,179],{"class":1773},[1745,2226,2227],{"class":1769}," -type f | ",[1745,2229,2230],{"class":1751},"wc",[1745,2232,2233],{"class":1759}," -l\n",[1745,2235,2236],{"class":1747,"line":1519},[1745,2237,2238],{"class":1751},"3464397\n",[1745,2240,2241,2243,2245,2247,2249,2252,2254,2256],{"class":1747,"line":1524},[1745,2242,2222],{"class":1769},[1745,2244,179],{"class":1773},[1745,2246,2227],{"class":1769},[1745,2248,1797],{"class":1751},[1745,2250,2251],{"class":1755}," 'length($0) > 40'",[1745,2253,1770],{"class":1769},[1745,2255,2230],{"class":1751},[1745,2257,2233],{"class":1759},[1745,2259,2260],{"class":1747,"line":1807},[1745,2261,2262],{"class":1751},"0\n",[12,2264,2265],{},"Ce qui est plutôt rassurant sur le risque de collision sur un SHA-256",[133,2267,2269],{"id":2268},"woodstock-backup","Woodstock Backup",[12,2271,2272],{},"Pour mon propre logiciel, je vais partir sur un SHA-256. Je vais partir du principe qu'il n'y a pas de collision (et je ferais un test\npour vérifier cela).\nDans une version future il pourrait être intéressant d'ajouter une extension au fichier dans le cas où il y a une collision, et laisser\nla possibilité à l'utilisateur de demander à retransférer le fichier pour être sûr qu'il n'y a pas de collision.",[128,2274,2276],{"id":2275},"structure-du-pool","Structure du pool",[12,2278,2279],{},"Maintenant que l'on connaît la taille des différents morceau de fichier ainsi que la clé qui nous servira à les classer. Nous allons\nmaintenant voir comment les organiser dans notre système de fichier.",[12,2281,2282],{},"Derrière le stockage j'ai les idées suivantes:",[506,2284,2285,2292,2295,2312,2315],{},[370,2286,2287,2288,2291],{},"Si on stocke tout les chunks (morceau de fichier) dans un seul dossier, nous pourrions au plus avoir ",[184,2289,2290],{},"1.3 x 10^154"," fichiers. Mais\ncela dépendra plus du nombre de fichier (chunk) que du nombre du nombre de possibilités.",[370,2293,2294],{},"Un système de fichier est en lui-même déjà une table de correspondance où la clé est le nom du fichier.",[370,2296,2297,2298],{},"Il y a une limite sur le nombre de fichiers par dossier:\n",[506,2299,2300,2303,2306,2309],{},[370,2301,2302],{},"FAT32: 65 536 (donc non utilisable)",[370,2304,2305],{},"NTFS: à priori pas de limit.",[370,2307,2308],{},"EXT2: ~1.3 x 10^20 (mais problème de perf au delà de 10 000) - (donc je déconseille son utilisation).",[370,2310,2311],{},"EXT4: pas de limit de nombre de fichier par dossier",[370,2313,2314],{},"J'aimerai à terme (un jour peut-être) pouvoir proposer l'utilisation d'un stockage objet (exemple S3, minio, ...) afin de pouvoir\nutiliser ce stockage depuis plusieurs serveurs. Il n'y a pas de limite du nombre de fichier dans un bucket S3.",[370,2316,2317],{},"Sur une structure à deux niveaux (et 255 possibilité par niveau), on peut espérer que les 3,5 millions de fichiers seront répartis\nce qui ferait un peu plus d'une 50 de fichier par dossier. (le nombre de fichier par dossier dépend du nombre de fichier et de leurs\ntaille).",[12,2319,2320],{},"Voici la structure que j'imagine utiliser pour le stockage des chunks.",[1737,2322,2324],{"className":1739,"code":2323,"language":1741,"meta":1518,"style":1518}," pool\n   ├── aa\n   │    ├── aa\n   │    │    ├── aa\n   │    │    │    ├── REFCNT\n   │    │    │    │     ├── sha256 cnt\n   │    │    │    │     ├── sha256 cnt\n   │    │    │    │     └── sha256 cnt\n   │    │    │    ├── LOCK\n   │    │    │    │     └── host backupNumber\n   │    │    │    └── aaaaaacdefghih-sha256.zlib\n",[184,2325,2326,2331,2339,2349,2360,2373,2393,2410,2428,2442,2461],{"__ignoreMap":1518},[1745,2327,2328],{"class":1747,"line":1748},[1745,2329,2330],{"class":1751}," pool\n",[1745,2332,2333,2336],{"class":1747,"line":1519},[1745,2334,2335],{"class":1751},"   ├──",[1745,2337,2338],{"class":1755}," aa\n",[1745,2340,2341,2344,2347],{"class":1747,"line":1524},[1745,2342,2343],{"class":1751},"   │",[1745,2345,2346],{"class":1755},"    ├──",[1745,2348,2338],{"class":1755},[1745,2350,2351,2353,2356,2358],{"class":1747,"line":1807},[1745,2352,2343],{"class":1751},[1745,2354,2355],{"class":1755},"    │",[1745,2357,2346],{"class":1755},[1745,2359,2338],{"class":1755},[1745,2361,2362,2364,2366,2368,2370],{"class":1747,"line":1820},[1745,2363,2343],{"class":1751},[1745,2365,2355],{"class":1755},[1745,2367,2355],{"class":1755},[1745,2369,2346],{"class":1755},[1745,2371,2372],{"class":1755}," REFCNT\n",[1745,2374,2376,2378,2380,2382,2384,2387,2390],{"class":1747,"line":2375},6,[1745,2377,2343],{"class":1751},[1745,2379,2355],{"class":1755},[1745,2381,2355],{"class":1755},[1745,2383,2355],{"class":1755},[1745,2385,2386],{"class":1755},"     ├──",[1745,2388,2389],{"class":1755}," sha256",[1745,2391,2392],{"class":1755}," cnt\n",[1745,2394,2396,2398,2400,2402,2404,2406,2408],{"class":1747,"line":2395},7,[1745,2397,2343],{"class":1751},[1745,2399,2355],{"class":1755},[1745,2401,2355],{"class":1755},[1745,2403,2355],{"class":1755},[1745,2405,2386],{"class":1755},[1745,2407,2389],{"class":1755},[1745,2409,2392],{"class":1755},[1745,2411,2413,2415,2417,2419,2421,2424,2426],{"class":1747,"line":2412},8,[1745,2414,2343],{"class":1751},[1745,2416,2355],{"class":1755},[1745,2418,2355],{"class":1755},[1745,2420,2355],{"class":1755},[1745,2422,2423],{"class":1755},"     └──",[1745,2425,2389],{"class":1755},[1745,2427,2392],{"class":1755},[1745,2429,2431,2433,2435,2437,2439],{"class":1747,"line":2430},9,[1745,2432,2343],{"class":1751},[1745,2434,2355],{"class":1755},[1745,2436,2355],{"class":1755},[1745,2438,2346],{"class":1755},[1745,2440,2441],{"class":1755}," LOCK\n",[1745,2443,2445,2447,2449,2451,2453,2455,2458],{"class":1747,"line":2444},10,[1745,2446,2343],{"class":1751},[1745,2448,2355],{"class":1755},[1745,2450,2355],{"class":1755},[1745,2452,2355],{"class":1755},[1745,2454,2423],{"class":1755},[1745,2456,2457],{"class":1755}," host",[1745,2459,2460],{"class":1755}," backupNumber\n",[1745,2462,2464,2466,2468,2470,2473],{"class":1747,"line":2463},11,[1745,2465,2343],{"class":1751},[1745,2467,2355],{"class":1755},[1745,2469,2355],{"class":1755},[1745,2471,2472],{"class":1755},"    └──",[1745,2474,2475],{"class":1755}," aaaaaacdefghih-sha256.zlib\n",[12,2477,2478],{},"Pour les 3 premiers niveaux, nous allons avoir une structure de dossier qui est constituée des 3 premiers octets du SHA-256. Ce qui permet\nde réduire le nombre de fichiers par dossier, mais aussi de limiter le nombre de LOCK lors de la création du pool.",[12,2480,2481],{},"Ensuite dans chaque dossier, nous aurons un fichier de LOCK qui sera créé lors de l'ajout d'un nouveau morceau de fichier. Il sera également\nutilisé lors de la lecture.",[12,2483,2484,2485,2488],{},"Un fichier ",[184,2486,2487],{},"REFCNT"," sera utilisé pour compter le nombre de référence. Cela permettra de supprimer les morceaux quand ces derniers ne seront\nplus utilisés.",[12,2490,2491],{},"Enfin les morceaux de fichiers sont stockés dans des fichiers dont le nom contient le hash. Ces fichiers peuvent être compressés pour réduire\nl'espace utilisé.",[128,2493,2495],{"id":2494},"structure-des-fichiers-de-sauvegardes","Structure des fichiers de sauvegardes",[12,2497,2498],{},"Une fois les morceaux de fichiers déposés dans le pool, il nous faut un fichier permettant de référencer l'ensemble des fichiers qui composent\nla sauvegarde.",[12,2500,2501],{},"Il y aura un fichier par sauvegarde.",[12,2503,2504],{},"Ces fichiers de sauvegarde seront stockés dans les dossiers des différents serveurs sauvegardés. Ce fichier contient l'ensemble des fichiers\nd'une sauvegarde et pour chaque fichier, le hash de l'enssemble des morceaux de fichiers.",[12,2506,2507],{},"Ce fichier doit avoir un format lisible facilement et rapidement par un programme. Ce fichier sera dans un format binaire (pour que le fichier\nsoit lisible rapidement et qu'il ne prenne pas trop de place).",[12,2509,2510,2511,2516],{},"Afin de simplifier la mise en place de ce fichier, nous allons utiliser ",[49,2512,2515],{"href":2513,"rel":2514},"https:\u002F\u002Fdevelopers.google.com\u002Fprotocol-buffers",[347],"protocol-buffers",". Ce\ndernier permet d'avoir un format de fichier compatible quel que soit le language de l'application. Voici un premier jet de format de fichier qui\nsera utilisé pour stocker chaque fichier d'une sauvegarde.",[1737,2518,2522],{"className":2519,"code":2520,"language":2521,"meta":1518,"style":1518},"language-protobuf shiki shiki-themes one-dark-pro","message FileManifest {\n  message FileManifestStat {\n    int32 ownerId = 1;\n    int32 groupId = 2;\n    int64 size = 3;\n    int64 lastRead = 4;\n    int64 lastModified = 5;\n    int64 created = 6;\n    int32 mode = 7;\n  }\n\n  message FileManifestAcl {\n    string user = 1;\n    string group = 2;\n    int32 mask = 3;\n    int32 other = 4;\n  }\n\n  bytes path = 1;\n  FileManifestStat stats = 2;\n  map\u003Cstring, bytes> xattr = 5;\n  repeated FileManifestAcl acl = 6;\n  repeated bytes chunks = 3;\n  bytes sha256 = 4;\n}\n","protobuf",[184,2523,2524,2537,2547,2565,2579,2594,2608,2622,2636,2650,2655,2660,2670,2685,2699,2713,2727,2732,2737,2752,2767,2795,2812,2829,2842],{"__ignoreMap":1518},[1745,2525,2526,2530,2534],{"class":1747,"line":1748},[1745,2527,2529],{"class":2528},"seHd6","message",[1745,2531,2533],{"class":2532},"sU0A5"," FileManifest",[1745,2535,2536],{"class":1769}," {\n",[1745,2538,2539,2542,2545],{"class":1747,"line":1519},[1745,2540,2541],{"class":2528},"  message",[1745,2543,2544],{"class":2532}," FileManifestStat",[1745,2546,2536],{"class":1769},[1745,2548,2549,2552,2556,2559,2562],{"class":1747,"line":1524},[1745,2550,2551],{"class":2528},"    int32",[1745,2553,2555],{"class":2554},"sVyAn"," ownerId",[1745,2557,2558],{"class":1773}," =",[1745,2560,2561],{"class":1759}," 1",[1745,2563,2564],{"class":1769},";\n",[1745,2566,2567,2569,2572,2574,2577],{"class":1747,"line":1807},[1745,2568,2551],{"class":2528},[1745,2570,2571],{"class":2554}," groupId",[1745,2573,2558],{"class":1773},[1745,2575,2576],{"class":1759}," 2",[1745,2578,2564],{"class":1769},[1745,2580,2581,2584,2587,2589,2592],{"class":1747,"line":1820},[1745,2582,2583],{"class":2528},"    int64",[1745,2585,2586],{"class":2554}," size",[1745,2588,2558],{"class":1773},[1745,2590,2591],{"class":1759}," 3",[1745,2593,2564],{"class":1769},[1745,2595,2596,2598,2601,2603,2606],{"class":1747,"line":2375},[1745,2597,2583],{"class":2528},[1745,2599,2600],{"class":2554}," lastRead",[1745,2602,2558],{"class":1773},[1745,2604,2605],{"class":1759}," 4",[1745,2607,2564],{"class":1769},[1745,2609,2610,2612,2615,2617,2620],{"class":1747,"line":2395},[1745,2611,2583],{"class":2528},[1745,2613,2614],{"class":2554}," lastModified",[1745,2616,2558],{"class":1773},[1745,2618,2619],{"class":1759}," 5",[1745,2621,2564],{"class":1769},[1745,2623,2624,2626,2629,2631,2634],{"class":1747,"line":2412},[1745,2625,2583],{"class":2528},[1745,2627,2628],{"class":2554}," created",[1745,2630,2558],{"class":1773},[1745,2632,2633],{"class":1759}," 6",[1745,2635,2564],{"class":1769},[1745,2637,2638,2640,2643,2645,2648],{"class":1747,"line":2430},[1745,2639,2551],{"class":2528},[1745,2641,2642],{"class":2554}," mode",[1745,2644,2558],{"class":1773},[1745,2646,2647],{"class":1759}," 7",[1745,2649,2564],{"class":1769},[1745,2651,2652],{"class":1747,"line":2444},[1745,2653,2654],{"class":1769},"  }\n",[1745,2656,2657],{"class":1747,"line":2463},[1745,2658,2659],{"emptyLinePlaceholder":1639},"\n",[1745,2661,2663,2665,2668],{"class":1747,"line":2662},12,[1745,2664,2541],{"class":2528},[1745,2666,2667],{"class":2532}," FileManifestAcl",[1745,2669,2536],{"class":1769},[1745,2671,2673,2676,2679,2681,2683],{"class":1747,"line":2672},13,[1745,2674,2675],{"class":2528},"    string",[1745,2677,2678],{"class":2554}," user",[1745,2680,2558],{"class":1773},[1745,2682,2561],{"class":1759},[1745,2684,2564],{"class":1769},[1745,2686,2688,2690,2693,2695,2697],{"class":1747,"line":2687},14,[1745,2689,2675],{"class":2528},[1745,2691,2692],{"class":2554}," group",[1745,2694,2558],{"class":1773},[1745,2696,2576],{"class":1759},[1745,2698,2564],{"class":1769},[1745,2700,2702,2704,2707,2709,2711],{"class":1747,"line":2701},15,[1745,2703,2551],{"class":2528},[1745,2705,2706],{"class":2554}," mask",[1745,2708,2558],{"class":1773},[1745,2710,2591],{"class":1759},[1745,2712,2564],{"class":1769},[1745,2714,2716,2718,2721,2723,2725],{"class":1747,"line":2715},16,[1745,2717,2551],{"class":2528},[1745,2719,2720],{"class":2554}," other",[1745,2722,2558],{"class":1773},[1745,2724,2605],{"class":1759},[1745,2726,2564],{"class":1769},[1745,2728,2730],{"class":1747,"line":2729},17,[1745,2731,2654],{"class":1769},[1745,2733,2735],{"class":1747,"line":2734},18,[1745,2736,2659],{"emptyLinePlaceholder":1639},[1745,2738,2740,2743,2746,2748,2750],{"class":1747,"line":2739},19,[1745,2741,2742],{"class":2528},"  bytes",[1745,2744,2745],{"class":2554}," path",[1745,2747,2558],{"class":1773},[1745,2749,2561],{"class":1759},[1745,2751,2564],{"class":1769},[1745,2753,2755,2758,2761,2763,2765],{"class":1747,"line":2754},20,[1745,2756,2757],{"class":2528},"  FileManifestStat",[1745,2759,2760],{"class":2554}," stats",[1745,2762,2558],{"class":1773},[1745,2764,2576],{"class":1759},[1745,2766,2564],{"class":1769},[1745,2768,2769,2772,2775,2778,2780,2783,2786,2789,2791,2793],{"class":1747,"line":1649},[1745,2770,2771],{"class":2528},"  map",[1745,2773,2774],{"class":1769},"\u003C",[1745,2776,2777],{"class":2528},"string",[1745,2779,932],{"class":1769},[1745,2781,2782],{"class":2528},"bytes",[1745,2784,2785],{"class":1769},"> ",[1745,2787,2788],{"class":2554},"xattr",[1745,2790,2558],{"class":1773},[1745,2792,2619],{"class":1759},[1745,2794,2564],{"class":1769},[1745,2796,2798,2801,2803,2806,2808,2810],{"class":1747,"line":2797},22,[1745,2799,2800],{"class":2528},"  repeated",[1745,2802,2667],{"class":2528},[1745,2804,2805],{"class":2554}," acl",[1745,2807,2558],{"class":1773},[1745,2809,2633],{"class":1759},[1745,2811,2564],{"class":1769},[1745,2813,2815,2817,2820,2823,2825,2827],{"class":1747,"line":2814},23,[1745,2816,2800],{"class":2528},[1745,2818,2819],{"class":2528}," bytes",[1745,2821,2822],{"class":2554}," chunks",[1745,2824,2558],{"class":1773},[1745,2826,2591],{"class":1759},[1745,2828,2564],{"class":1769},[1745,2830,2832,2834,2836,2838,2840],{"class":1747,"line":2831},24,[1745,2833,2742],{"class":2528},[1745,2835,2389],{"class":2554},[1745,2837,2558],{"class":1773},[1745,2839,2605],{"class":1759},[1745,2841,2564],{"class":1769},[1745,2843,2845],{"class":1747,"line":2844},25,[1745,2846,2847],{"class":1769},"}\n",[12,2849,2850,2851,2854],{},"Le fichier sera constitué d'une liste de ",[184,2852,2853],{},"FileManifest",". Ce fichier est de la forme :",[1737,2856,2861],{"className":2857,"code":2859,"language":2860},[2858],"language-text","int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest\nint32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest\n(vous avez compris le principe)\n","text",[184,2862,2859],{"__ignoreMap":1518},[12,2864,2865],{},"où chaque int32 est la taille de chaque FileManifest.",[12,2867,2868],{},"Lors de la sauvegarde, le serveur repartira de la sauvegarde précédente et écrira toute modification dans un journal. Le journal\nindiquera, les fichiers ajoutés, supprimés, et modifiés. Une fois la sauvegarde terminée, le journal sera fusionné dans le fichier de\nsauvegarde (et le comptage de référence sera mis à jour).",[128,2870,1493],{"id":1492},[12,2872,2873,2874,179],{},"Maintenant que j'ai une idée de la forme du nouveau de format de stockage, il ne me reste plus qu'à développer le pool de stockage.\nSi vous avez des retours à me faire sur le format de stockage alors n'hésitez pas à me contacter. Vous pouvez le faire par exemple sur\n",[49,2875,2876],{"href":2268},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fwoodstock-backup",[2878,2879,2880],"style",{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}",{"title":1518,"searchDepth":1519,"depth":1519,"links":2882},[2883,2886,2892,2893,2894],{"id":1713,"depth":1519,"text":1714,"children":2884},[2885],{"id":1717,"depth":1524,"text":1718},{"id":2085,"depth":1519,"text":2086,"children":2887},[2888,2889,2890,2891],{"id":2141,"depth":1524,"text":2142},{"id":2174,"depth":1524,"text":2175},{"id":2200,"depth":1524,"text":1013},{"id":2268,"depth":1524,"text":2269},{"id":2275,"depth":1519,"text":2276},{"id":2494,"depth":1519,"text":2495},{"id":1492,"depth":1519,"text":1493},{"type":9,"value":2896},[2897,2899,2901,2903],[12,2898,14],{},[12,2900,1660],{},[12,2902,1663],{},[506,2904,2905,2907,2913],{},[370,2906,1668],{},[370,2908,1671,2909,1675,2911,1678],{},[1673,2910],{},[1673,2912],{},[370,2914,1681,2915,1684,2917,1687,2919,1690],{},[1673,2916],{},[1673,2918],{},[1673,2920],{},{"planet":1639},{"title":1653,"description":14},"woodstock_brtfs","posts\u002FWoodstock\u002F2021-01-12_woodstock_brtfs",[1645,1646,2926,983,2927,2928,1555],"btrfs","javascript","nodejs","f12bC0YlcreB0kEuvkJiJsi0nYRb0O3yavG9pPjrXUM",{"id":2931,"title":52,"author":7,"body":2932,"category":1554,"categorySlug":1555,"date":55,"description":14,"excerpt":3676,"extension":1636,"location":1637,"meta":3705,"navigation":1639,"path":51,"published":1639,"seo":3706,"slug":1555,"stem":3707,"tags":3708,"timeToRead":2662,"__hash__":3709},"posts\u002Fposts\u002FWoodstock\u002F2020-09-20_woodstock.md",{"type":9,"value":2933,"toc":3661},[2934,2936,2946,2951,2955,2965,2973,2976,2979,2984,2992,2997,3003,3041,3044,3048,3051,3077,3080,3086,3088,3095,3101,3104,3107,3110,3113,3119,3122,3133,3136,3139,3141,3148,3154,3157,3160,3165,3173,3176,3182,3186,3193,3198,3201,3210,3212,3215,3218,3221,3225,3228,3288,3292,3306,3310,3317,3320,3332,3341,3344,3355,3359,3362,3384,3387,3391,3436,3439,3443,3451,3460,3463,3524,3527,3620,3623,3626,3629],[12,2935,14],{},[12,2937,2938,2939,2943,2944,179],{},"Un projet ",[49,2940,2942],{"href":2941},"\u002Fpost\u002F2020-07-27-fin-passprotect\u002F","s'en va"," et un autre commence.\nJe suis heureux de vous présenter ce nouveau projet: ",[155,2945,2269],{},[2947,2948,2950],"h1",{"id":2949},"genèse-du-projet","Genèse du projet",[128,2952,2954],{"id":2953},"mes-problèmes","Mes problèmes",[12,2956,2957,2958,2964],{},"Pour faire des sauvegardes, j'utilisais jusqu'ici ",[155,2959,2960],{},[49,2961,1013],{"href":2962,"rel":2963},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002F",[347],". C'est un très bon logiciel pour effectuer des sauvegardes de plusieurs machines sur une instance centralisée.",[12,2966,2967,2969,2970,2972],{},[155,2968,1013],{}," est écrit en ",[362,2971,1063],{}," avec des partie en C. En effet il se base sur un fork de rsync qui permet d'enregistrer le résultat des sauvegardes dans un format qui lui est propre. Malgré qu'il fonctionne très bien j'ai eu plusieurs problèmes avec récemment.",[12,2974,2975],{},"Il permet de faire des sauvegardes lancées depuis un serveur centralisé. Ce dernier vérifie régulièrement si les différents PC à sauvegarder sont présents sur le réseau et s'y connecte pour faire les sauvegardes sur la base d'un calendrier.",[12,2977,2978],{},"Le serveur a besoin de pouvoir se connecter sur tous les clients (incovénient que je suis prêt à accepter) mais les clients n'ont du coup pas besoin de devoir se connecter au serveur (avec le risque qu'un client compromis, compromette le serveur).",[12,2980,2981,2983],{},[155,2982,1013],{}," est également capable de décider de l'heure de la sauvegarde en fonction de la date de la dernière sauvegarde ainsi que de la présence du PC sur le réseau. Ainsi si un PC est toujours présent, il peut être sauvegardé tout en préservant les heures où la personne a besoin de toute la puissance de son PC, sauf si la sauvegarde date de trop.",[12,2985,2986,2987,2991],{},"Je vous invite fortement à aller à l'adresse ",[49,2988,2962],{"href":2989,"rel":2990},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002Finfo.html",[347]," pour en savoir plus sur BackupPC.",[12,2993,2994],{},[121,2995],{"alt":1013,"src":2996},"\u002FWoodstock\u002Fbackuppc.png",[12,2998,2999,3000,3002],{},"Voici les quelques problèmes que j'ai eu récemment avec les dernières versions de ",[155,3001,1013],{}," :",[506,3004,3005,3014],{},[370,3006,3007,3008,3010,3011,3013],{},"Lors du passage de la version v3 à la version v4, et alors que le pool de sauvegarde était stocké sur un partage NFS, je me suis mis à perdre des fichiers: Impossible de restaurer des fichiers.",[1673,3009],{},"Après quelques recherches j'ai découvert que BackupPC v4 n'était pas compatible avec NFS, et j'ai dû basculer toute mon installation sur un disque iSCSI sur mon NAS.",[1673,3012],{},"J'ai alors perdu mon historique de sauvegarde (je n'avais alors pas assez de place pour faire faire mes sauvegardes dans un coin et des expérimentations\u002Fmigrations de l'autre).",[370,3015,3016,3017,3019,3021,3022,3024,3025,3027,3028,3030,3031,3033,3034,3036,179],{},"Une fois les sauvegardes effectuées je souhaite pouvoir copier le résultat sur un disque dur USB (qui contient donc alors la dernière version). Cette archive est alors déposée régulièrement hors site afin de minimiser le risque de perte de données.",[1673,3018],{},[155,3020,1013],{}," permet de le faire nativement en exportant des archives au format TAR sur ce disque dur USB (moyennant une pré-étape pour monter le disque, et une post-étape pour le démonter).",[1673,3023],{},"Cela me pose problème, en effet je veux pouvoir facilement accéder au contenu de l'archive (donc sans ouvrir le tar), et cette dernière est également très longue à créer.\nDe plus il m'est déjà arrivé d'ouvrir un tar et de me rendre compte que l'archive était corrompue (tronquée) soit par manque de place soit car l'archivage n'avait pas fonctionné correctement mais sans avertissement.",[1673,3026],{},"Je m'emploie donc à créer un script qui à l'aide d'un connecteur Fuse BackupPC me permet de synchroniser les dernières versions des sauvegardes avec rsync vers le disque dur USB.",[1673,3029],{},"Malheureusement le script FUSE n'est pas très maintenu, et pose problème avec des fichiers de plusieurs centaines de gigaoctets, ou avec les permissions de fichiers sauvegardés avec des machines Windows. J'ai donc adapté le script Fuse à mes problèmes pour régler uniquement le problème de droits.",[1673,3032],{},"La modification n'est pas super propre: je squeeze les droits si l'utilisateur à un uid particulier.",[1673,3035],{},[49,3037,3040],{"href":3038,"rel":3039},"https:\u002F\u002Fgist.github.com\u002Fphoenix741\u002F99a5076569b01ba5a116cec24a798d5f",[347],"Vous pouvez retrouver ma modification ici",[12,3042,3043],{},"Le fait est que même si la solution continue de tourner actuellement et fonctionne bien, j'ai perdu une partie de la confiance que j'ai en ce produit.",[128,3045,3047],{"id":3046},"etudes-des-autres-solutions","Etudes des autres solutions",[12,3049,3050],{},"Quelques critères:",[506,3052,3053,3059,3062,3065,3068,3071,3074],{},[370,3054,3055,3056,179],{},"Déjà un premier critère est que la solution doit être ",[362,3057,3058],{},"Open Source",[370,3060,3061],{},"Je ne veux pas de client lourd pour visualiser mes sauvegardes (seul un client léger pour y accéder de n'importe où).",[370,3063,3064],{},"Je veux pouvoir facilement créer des archives sur des disques durs USB à plat (sans format spécial comme zip, tar, ...)",[370,3066,3067],{},"Je dois pouvoir sauvegarder des machines sous Windows ou sous Linux.",[370,3069,3070],{},"Je suis prêt à installer un client lourd sur le client qui est sauvegardé.",[370,3072,3073],{},"Je dois sauvegarder 6 machines (1 serveur dédié, 2 Vieux PC portable, 2 Vieux PC Fixe, 1 Vieux NAS) sans que les utilisateurs (ma femme, moi) n'aient à y penser.",[370,3075,3076],{},"Si le logiciel est facilement contrôlable par API et via une IHM simple d'utilisation, c'est un grand plus.",[12,3078,3079],{},"J'ai regardé plusieurs solutions et voici les deux solutions qui m'ont tapé dans l'oeil (et ça fait mal) en plus de BackupPC que j'utilse déjà.",[12,3081,3082,3083,3085],{},"Je précise que mes tests sont alors limités car je n'ai pas assez d'espace de stockage pour avoir à la fois le pool de ",[155,3084,1013],{}," de environ 2To et le pool d'un autre gestionnaire de sauvegarde en même temps.",[133,3087,2175],{"id":2174},[12,3089,3090,3091,179],{},"J'ai alors installé ",[49,3092,2175],{"href":3093,"rel":3094},"https:\u002F\u002Fwww.urbackup.org\u002F",[347],[12,3096,3097,3098,3100],{},"J'ai apprécié l'utiliser en utilisant le stockage ",[362,3099,2926],{},". Il permet aussi d'utiliser un système de stockage qui lui est propre.",[12,3102,3103],{},"Je n'ai pas trouvé de méthode pour effectuer de l'archivage sur disque dur USB des dernières sauvegardes uniquement (et pas l'ensemble du pool) pour stockage off-site. Ce point pourrait être resolvable en utilisant justement le stockage Btrfs et en écrivant mes propres scripts.",[12,3105,3106],{},"Sous Windows, il permet aussi de créer des Snapshots (équivalent LVM) pour permettre de faire des sauvegardes de fichier en lecture.",[12,3108,3109],{},"Il aussi capable de faire des images disque des machines Windows.",[12,3111,3112],{},"UrBackup utilise un client à installer sur chaque ordinateur à sauvegarder. Cela ne pose pas de problème et peut permettre d'optimiser la vitesse de sauvegarde par rapport à un simple rsync, et d'avoir un format de stockage propre pour optimiser la taille occupé.",[12,3114,3115,3116,3118],{},"Bref, sur le papier ",[155,3117,2175],{}," a tout pour plaire.",[12,3120,3121],{},"Pourquoi ne pas l'utiliser ? Bonne question :).",[12,3123,3124,3125,3128,3129,3132],{},"La migration de mon historique de sauvegarde de BackupPC vers UrBackup n'était pas possible facilement. Il aurait fallu pour cela qu'en plus de générer la structure ",[362,3126,3127],{},"Btrfs"," qui va bien créer la structure dans la base ",[362,3130,3131],{},"Sqlite",", ce que je n'ai pas forcément trouvé pratique.",[12,3134,3135],{},"Il me semble aussi que je ne trouvais pas l'interface très pratique (entre autres pour la restauration de fichiers).",[12,3137,3138],{},"Je n'ai pas plus de raisons que cela de ne pas l'utiliser, donc si vous cherchez un logiciel pour faire des sauvegardes n'hésitez pas à le tester.",[133,3140,2142],{"id":2141},[12,3142,3143,3144,179],{},"J'ai aimé utiliser également ",[49,3145,2142],{"href":3146,"rel":3147},"https:\u002F\u002Fborgbackup.readthedocs.io\u002F",[347],[12,3149,3150,3151,3153],{},"Surtout la possibilité de pouvoir chiffrer les sauvegardes, ainsi que les performances de ",[155,3152,2142],{}," mêmes.",[12,3155,3156],{},"Malheureusement, je souhaite pouvoir facilement déchiffrer le contenu depuis le serveur principal pour archiver plusieurs sauvegardes de plusieurs machines en même temps.",[12,3158,3159],{},"Je souhaite également pouvoir sauvegarder toute sortes d'ordinateurs. Certains appartenant au réseau local, d'autres se trouvant être des dédiés sur Internet. Des PC sous Linux mais aussi un PC sous Windows.",[12,3161,3162,3164],{},[155,3163,2142],{}," m'aurait alors posé quelques soucis sous Windows.",[12,3166,3167,3168],{},"Je ne souhaite pas que mes serveurs dédiés aient accès à mon réseau local pour faire les sauvegardes, ni aient accès aux serveurs de backup directement au risque de compromettre la sécurité. ",[49,3169,3172],{"href":3170,"rel":3171},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Ffaq.html#how-can-i-protect-against-a-hacked-backup-client",[347],"Il faut alors paramétrer le serveur de backup pour gérer cela au niveau de ssh",[12,3174,3175],{},"Il n'est pas possible non plus de mutualiser les sauvegardes dans un seul repo, ce qui au delà des problèmes de sécurité, pose également des problèmes de lock.",[12,3177,3178,3179,3181],{},"Je pense que ",[155,3180,2142],{}," est par contre un très bon logiciel pour faire une sauvegarde de son PC perso quand on ne s'occupe que de soi et qu'on a qu'un seul PC.",[133,3183,3185],{"id":3184},"autres-tests","Autres tests",[12,3187,3188,3189,3192],{},"J'ai également testé rapidement ",[155,3190,3191],{},"Burp"," pour lequel je jette un oeil également régulièrement, mais l'IHM non intégré était alors très lente lors de mes tests, et la ligne de commande me rendait la restauration de fichiers complexe lors du peu de fois que je l'ai utilisé.",[12,3194,3195,3196,179],{},"Il est alors difficile de trouver un concurrent qui me convienne pour remplacer ",[155,3197,1013],{},[12,3199,3200],{},"Si vous pensez que je me suis trompé sur les tests que j'ai fait ci-dessus, ou que vous voyez un autre logiciel de sauvegardes qui pourrait me convenir, n'hésitez pas à m'envoyer un mail.",[12,3202,3203,3204,179],{},"A l'heure actuelle j'ai décidé d'écrire mon propre système de sauvegarde que j'ai nommé ",[155,3205,3206],{},[49,3207,2269],{"href":3208,"rel":3209},"https:\u002F\u002Fwoodstock.shadoware.org\u002F",[347],[2947,3211,2269],{"id":2268},[12,3213,3214],{},"A défaut de tourner en rond et de ne pas être complètement satisfait, j'ai décidé de développer ma propre solution. Ainsi si je n'ai pas toutes les fonctionnalités que je désire, je n'ai qu'à m'en prendre à moi-même et les développer.",[12,3216,3217],{},"Il n'est pas facile de choisir un nom pour un logiciel.",[12,3219,3220],{},"Lors du développement de cette application de sauvegarde, je regardais un épisode d'une série dont l'histoire se passait à Woodstock, d'où le nom :).",[128,3222,3224],{"id":3223},"les-fonctionnalités","Les fonctionnalités",[12,3226,3227],{},"Quels sont les fonctionnalités à l'heure actuelle :",[506,3229,3230,3233,3236,3239,3242,3252,3257,3274,3285],{},[370,3231,3232],{},"Faire des sauvegardes de façon régulière (par exemple une fois par jour) et lors de la dispo de l'ordinateur.",[370,3234,3235],{},"Pour chaque sauvegarde il est possible d'éxecuter des étapes en amont et en aval, voir de faire plusieurs sauvegardes de plusieurs dossiers.",[370,3237,3238],{},"On peut lister les sauvegardes pour chaque hôte.",[370,3240,3241],{},"On peut télécharger les fichiers un par un ou en téléchargeant un fichier Zip.",[370,3243,3244,3245,3247,3248,3251],{},"Les sauvegardes sont stockées sur un système de fichier ",[362,3246,3127],{}," ce qui permet de bénéficier du système de ",[362,3249,3250],{},"Snapshot"," pour la déduplication, et de pouvoir accéder (à des fins d'archivage) directement aux sauvegardes sans contrainte.",[370,3253,3254,3256],{},[362,3255,3127],{}," permet également de compresser les sauvegardes (avec les bonnes options au montage).",[370,3258,3259,3260,3263,3264,179],{},"Comme ",[362,3261,3262],{},"RSync"," est utilisé, il est installé sur tous les clients (même windows), ce qui facilite son utilisation",[3265,3266,3267],"sup",{},[49,3268,3273],{"href":3269,"ariaDescribedBy":3270,"dataFootnoteRef":1518,"id":3272},"#user-content-fn-1",[3271],"footnote-label","user-content-fnref-1","1",[370,3275,3276,3277],{},"L'application possède une interface moderne (à mon goût)",[3265,3278,3279],{},[49,3280,3284],{"href":3281,"ariaDescribedBy":3282,"dataFootnoteRef":1518,"id":3283},"#user-content-fn-2",[3271],"user-content-fnref-2","2",[370,3286,3287],{},"L'application est contrôlable via une API Rest et via une API GraphQL.",[128,3289,3291],{"id":3290},"quelques-captures-décran","Quelques captures d'écran",[12,3293,3294,3298,3302],{},[121,3295],{"alt":3296,"src":3297},"Dashboard","\u002FWoodstock\u002Fdashboard.png",[121,3299],{"alt":3300,"src":3301},"Hosts","\u002FWoodstock\u002Fhosts.png",[121,3303],{"alt":3304,"src":3305},"RunningTask","\u002FWoodstock\u002Frunning_tasks_0.png",[128,3307,3309],{"id":3308},"comment-linstaller","Comment l'installer ?",[12,3311,3312,3313,3316],{},"J'ai écrit un site Internet pour présenter Woodstock: ",[49,3314,3208],{"href":3208,"rel":3315},[347]," et porter la documentation.",[12,3318,3319],{},"Vous pouvez retrouver les liens de téléchargement depuis le repository de code sources :",[506,3321,3322,3327],{},[370,3323,3324],{},[49,3325,1505],{"href":1505,"rel":3326},[347],[370,3328,3329],{},[49,3330,2876],{"href":2876,"rel":3331},[347],[12,3333,3334,3335,3340],{},"L'installation se fait très simplement, j'ai écrit un peu de ",[49,3336,3339],{"href":3337,"rel":3338},"https:\u002F\u002Fwoodstock.shadoware.org\u002Fdoc\u002Finstallation.html",[347],"documentation"," pour expliquer cela.",[12,3342,3343],{},"Il est possible d'effectuer une installation via:",[506,3345,3346,3349,3352],{},[370,3347,3348],{},"les sources",[370,3350,3351],{},"un paquet debian\u002Fubuntu",[370,3353,3354],{},"l'image docker (c'est l'installation que j'utilise moi-même)",[128,3356,3358],{"id":3357},"par-rapport-à-mon-besoin","Par rapport à mon besoin",[12,3360,3361],{},"Reprenons mes critères :",[506,3363,3364,3369,3372,3375,3378,3381],{},[370,3365,3366,3368],{},[362,3367,3058],{},": Check",[370,3370,3371],{},"Client léger: Check",[370,3373,3374],{},"Archivage sur disque dur USB à plat: Partiel (Btrfs me permet de le faire manuellement à l'aide d'un script maison)",[370,3376,3377],{},"Compatible Windows\u002FLinux: Check (via rsync)",[370,3379,3380],{},"Sauvegarde sans y penser (automatique): Check",[370,3382,3383],{},"API ou IHM simple d'utilisation: Milk Check. L'API est là. Il est simple d'utilisation pour moi, mais peut encore être amélioré",[12,3385,3386],{},"Donc pour l'instant il répond à mes besoins mais reste améliorable.",[128,3388,3390],{"id":3389},"roadmap","Roadmap",[506,3392,3393,3396,3399],{},[370,3394,3395],{},"Avoir un outil d'archivage automatique intégré.",[370,3397,3398],{},"Les suppressions automatiques de sauvegardes sont à prévoir également.",[370,3400,3401,3402],{},"Peut-être remplacer Btrfs par autre chose, car actuellement",[506,3403,3404,3407,3413,3416,3419],{},[370,3405,3406],{},"Sans Btrfs, le logiciel ne peut pas fonctionner, donc cela limite les OS du serveur de sauvegarde.",[370,3408,3409,3410,3412],{},"Il y aurait des problèmes de performances avec ",[362,3411,3127],{}," en cas de trop grand nombre de snapshots et de l'utilisation des qgroups. Pour l'instant je n'ai ce genre de problème que lors de la suppression de sauvegarde.",[370,3414,3415],{},"Il n'est pas possible de faire de la déduplication inter-machines simplement.",[370,3417,3418],{},"L'utilisation d'un système de fichier unique empêche l'utilisation de plusieurs serveurs de backups, synchronisés. (Je me dis que sur les grosses infrastructures, il peut-être intéressant de scaler horizontalement)",[370,3420,3421,3422],{},"Par contre, si je remplace Btrfs par autre chose, les questions sont alors",[506,3423,3424,3427,3430,3433],{},[370,3425,3426],{},"par quoi ?",[370,3428,3429],{},"Comment fais-je pour Rsync ?",[370,3431,3432],{},"Dois-je le remplacer aussi ?",[370,3434,3435],{},"Avoir mon propre client de sauvegarde (comme UrBackup) ?",[12,3437,3438],{},"Si vous avez des éléments de réponses, ou des idées là aussi, n'hésitez pas non plus à m'envoyer un mail.\nSi vous aussi n'êtes pas satisfaits de votre solution de sauvegardes, contactez-moi pour me dire ce qu'il vous faudrait, voir même pour contribuer.",[128,3440,3442],{"id":3441},"quelques-chiffres","Quelques chiffres",[12,3444,3445,3446,3448,3449,179],{},"À l'heure actuelle, je suis passé à ",[155,3447,2269],{}," pour mes sauvegardes en ayant migré toutes mes sauvegardes depuis ",[155,3450,1013],{},[12,3452,3453,3454,3456,3457,3459],{},"Je peux donc faire quelques comparaisons rapides (",[155,3455,2269],{}," méritant encore quelques évolutions pour atteindre le niveau de ",[155,3458,1013],{}," dans certains domaines).",[12,3461,3462],{},"Je possède 6 machines dont l'espace est répartit comme suite :",[22,3464,3465,3474],{},[25,3466,3467],{},[28,3468,3469,3471],{},[31,3470,567],{},[31,3472,3473],{},"Stockage",[41,3475,3476,3484,3492,3500,3508,3516],{},[28,3477,3478,3481],{},[46,3479,3480],{},"pc-windows",[46,3482,3483],{},"260.8 Gb ",[28,3485,3486,3489],{},[46,3487,3488],{},"pc-portable-1",[46,3490,3491],{},"148.1 Gb",[28,3493,3494,3497],{},[46,3495,3496],{},"pc-portable-2",[46,3498,3499],{},"41.0 Gb",[28,3501,3502,3505],{},[46,3503,3504],{},"pc-linux",[46,3506,3507],{},"153.3 Gb",[28,3509,3510,3513],{},[46,3511,3512],{},"nas",[46,3514,3515],{},"1.4 Tb",[28,3517,3518,3521],{},[46,3519,3520],{},"server-ovh",[46,3522,3523],{},"189.6 Gb",[12,3525,3526],{},"Certains fichiers (comme les photos de vacances, ...) peuvent être sur plusieurs machines (nas + pc fixe).",[22,3528,3529,3540],{},[25,3530,3531],{},[28,3532,3533,3536,3538],{},[31,3534,3535],{},"Comparaison",[31,3537,1013],{},[31,3539,1554],{},[41,3541,3542,3556,3567,3577,3588,3598,3609],{},[28,3543,3544,3547,3550],{},[46,3545,3546],{},"Pool de sauvegarde",[46,3548,3549],{},"le stockage optimisé 1,88 To",[46,3551,3552,3553,3555],{}," Le stockage ",[362,3554,2926],{}," avec compression prend 2,1 To",[28,3557,3558,3561,3564],{},[46,3559,3560],{},"Compression pc-windows",[46,3562,3563],{},"357 Go non compressé",[46,3565,3566],{},"326 Go non compressé",[28,3568,3569,3571,3574],{},[46,3570],{},[46,3572,3573],{},"246 Go compressé",[46,3575,3576],{},"245 Go compressé",[28,3578,3579,3582,3585],{},[46,3580,3581],{},"Compression pc-portable-1",[46,3583,3584],{},"74 Go non compressé",[46,3586,3587],{},"79 Go non compressé",[28,3589,3590,3592,3595],{},[46,3591],{},[46,3593,3594],{},"70 Go compressé",[46,3596,3597],{},"75 Go compressé",[28,3599,3600,3603,3606],{},[46,3601,3602],{},"Temps de sauvegarde incrémental NAS",[46,3604,3605],{}," 21 minutes en moyenne",[46,3607,3608],{},"15 minutes en moyenne",[28,3610,3611,3614,3617],{},[46,3612,3613],{},"Temps de sauvegarde incrémental pc-linux",[46,3615,3616],{},"30 minutes en moyenne",[46,3618,3619],{},"7 minutes en moyenne",[12,3621,3622],{},"J'ai l'impression que la notion de compress-force n'est pas prise en compte, et que la compression n'est pas des plus efficaces.",[12,3624,3625],{},"Pour la taille du pool de sauvegarde cela ne m'étonne pas vu que la déduplication n'est pas cross-machine.",[12,3627,3628],{},"Pour le temps de sauvegarde c'est une bonne surprise mais là aussi, je pense que c'est normal vu que rsync ne doit pas faire de déduplication à\nson niveau.",[3630,3631,3634,3639],"section",{"className":3632,"dataFootnotes":1518},[3633],"footnotes",[128,3635,3638],{"className":3636,"id":3271},[3637],"sr-only","Footnotes",[367,3640,3641,3652],{},[370,3642,3644,3645],{"id":3643},"user-content-fn-1","A voir si je conserve ce mode de fonctionnement pour le futur. J'envisage de peut-être prendre un client lourd. ",[49,3646,3651],{"href":3647,"ariaLabel":3648,"className":3649,"dataFootnoteBackref":1518},"#user-content-fnref-1","Back to reference 1",[3650],"data-footnote-backref","↩",[370,3653,3655,3656],{"id":3654},"user-content-fn-2","Mais si un UI\u002FUX souhaite améliorer l'interface, je suis preneur. ",[49,3657,3651],{"href":3658,"ariaLabel":3659,"className":3660,"dataFootnoteBackref":1518},"#user-content-fnref-2","Back to reference 2",[3650],{"title":1518,"searchDepth":1519,"depth":1519,"links":3662},[3663,3664,3669,3670,3671,3672,3673,3674,3675],{"id":2953,"depth":1519,"text":2954},{"id":3046,"depth":1519,"text":3047,"children":3665},[3666,3667,3668],{"id":2174,"depth":1524,"text":2175},{"id":2141,"depth":1524,"text":2142},{"id":3184,"depth":1524,"text":3185},{"id":3223,"depth":1519,"text":3224},{"id":3290,"depth":1519,"text":3291},{"id":3308,"depth":1519,"text":3309},{"id":3357,"depth":1519,"text":3358},{"id":3389,"depth":1519,"text":3390},{"id":3441,"depth":1519,"text":3442},{"id":3271,"depth":1519,"text":3638},{"type":9,"value":3677},[3678,3680,3686,3688,3690,3697,3703],[12,3679,14],{},[12,3681,2938,3682,2943,3684,179],{},[49,3683,2942],{"href":2941},[155,3685,2269],{},[2947,3687,2950],{"id":2949},[128,3689,2954],{"id":2953},[12,3691,2957,3692,2964],{},[155,3693,3694],{},[49,3695,1013],{"href":2962,"rel":3696},[347],[12,3698,3699,2969,3701,2972],{},[155,3700,1013],{},[362,3702,1063],{},[12,3704,2975],{},{"planet":1639},{"title":52,"description":14},"posts\u002FWoodstock\u002F2020-09-20_woodstock",[1645,1646,2926,983,2927,2928,1555],"3u7RFr1fMKcgbLywQbn4HX3gySdxxhd7EFlJL3Est34",1777802569521]