0 Votes

Code source wiki de Sauvegarde

Version 7.2 par john le 2026/10/03 20:00

Masquer les derniers auteurs
john 1.1 1 Sauvegarde quotidienne du serveur principal vers le Raspberry Pi avec [[Plakar>>https://plakar.io]] : dépôt chiffré et dédupliqué, transport SFTP.
2
john 7.2 3 {{toc/}}
4
john 1.1 5 == Fonctionnement ==
6
7 |=Élément|=Valeur
8 |Machine sauvegardée|[[Serveur principal>>Wiki Batcave John.Infrastructure.ServeurPrincipal.WebHome]]
9 |Dépôt|SSD USB du [[Raspberry Pi>>Wiki Batcave John.Infrastructure.RaspberryPi.WebHome]]
john 7.2 10 |Transport|SFTP, via l'intégration Plakar ##sftp##
john 1.1 11 |Déclenchement|Cron, chaque nuit à 3 h 30
12 |Rétention|30 jours glissants
13 |Script|##/root/backup-plakar.sh##
14 |Journal|##/var/log/backup-plakar.log##, avec rotation par logrotate
15
16 Chaque exécution :
17
18 * exporte toutes les bases en SQL dans ##<APPDATA>/dumps## ;
john 7.1 19 * attend la fin de la sauvegarde Borg de [[Nextcloud>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Nextcloud.WebHome]] AIO (planifiée à 3 h 00), au plus une heure ;
20 * sauvegarde les données des conteneurs, la bibliothèque Immich, le contenu de Joplin et le dépôt Borg de Nextcloud AIO, en excluant les fichiers bruts des bases et les données brutes de Nextcloud ;
john 1.1 21 * supprime les instantanés de plus de 30 jours.
22
23 {{info}}
24 Les fichiers bruts d'une base en fonctionnement ne sont pas fiables pour une restauration. Ce sont les dumps SQL qui font foi.
john 7.1 25
26 Nextcloud AIO fait exception : il n'y a pas de dump, sa base, ses fichiers et sa configuration sont dans son propre dépôt Borg, cohérent car produit conteneurs arrêtés. Plakar copie ce dépôt tel quel, d'où l'attente de la fin de la sauvegarde Borg : copier un dépôt en cours d'écriture donnerait une copie incohérente.
john 1.1 27 {{/info}}
28
john 7.2 29 {{info}}
30 Les dumps sont faits avec ##docker exec##, donc avec le client ##pg_dump## ou ##mysqldump## du conteneur lui-même, toujours à la bonne version. Les intégrations Plakar ##postgresql## et ##mysql## ont été écartées : elles exigent un accès TCP à chaque base et des clients de même version installés sur l'hôte, et produisent un instantané séparé par base.
31 {{/info}}
john 4.1 32
john 7.2 33 == Mise en place ==
34
35 === Installer Plakar (les deux machines) ===
36
37 Installer le paquet ##plakar## (paquet ##.deb## disponible sur la [[page de téléchargement>>https://www.plakar.io/download]]) sur le serveur principal et sur le Raspberry Pi, **dans la même version**.
38
39 {{code language="bash"}}
40 sudo apt install ./plakar_<VERSION>_<ARCH>.deb
41 plakar version
42 {{/code}}
43
44 === Installer l'intégration SFTP (serveur principal) ===
45
46 Depuis Plakar 1.1, le transport SFTP n'est plus inclus dans le binaire : c'est une intégration à installer à part. Sans elle, toute commande sur un dépôt ##sftp:~/~/## échoue.
47
48 Les intégrations sont installées **par utilisateur** (dans ##~~/.local/share/plakar/plugins##). La sauvegarde tournant en root, l'installation se fait avec ##sudo##.
49
50 Le téléchargement des intégrations précompilées demande un compte Plakar connecté (gratuit). Le serveur n'ayant pas de navigateur, utiliser ##-no-spawn## : Plakar affiche une URL à ouvrir sur un autre poste, puis un code à saisir dans le terminal.
51
52 {{code language="bash"}}
53 sudo plakar login -github -no-spawn # ou : sudo plakar login -email <ADRESSE>
54 sudo plakar login -status
55 sudo plakar pkg add sftp
56 sudo plakar pkg list
57 {{/code}}
58
59 {{info}}
60 La connexion au compte ne sert qu'au gestionnaire d'intégrations. Les sauvegardes et restaurations n'en dépendent pas.
61
62 Le Raspberry Pi n'a pas besoin de l'intégration : il ouvre le dépôt en local.
63 {{/info}}
64
john 4.1 65 === Sur le Raspberry Pi (dépôt) ===
66
67 Utilisateur dédié, sans shell :
68
69 {{code language="bash"}}
70 sudo useradd -m -d /srv/plakar -s /usr/sbin/nologin plakar
71 sudo mkdir -p <CHEMIN_DEPOT>
72 sudo chown plakar:plakar <CHEMIN_DEPOT>
73 sudo install -d -m 700 -o plakar -g plakar /srv/plakar/.ssh
74 {{/code}}
75
76 Limitation au SFTP, dans ##/etc/ssh/sshd_config.d/plakar.conf## :
77
78 {{code}}
79 Match User plakar
john 7.2 80 ForceCommand internal-sftp -l VERBOSE
john 4.1 81 AllowTcpForwarding no
82 X11Forwarding no
83 {{/code}}
84
85 {{code language="bash"}}
john 7.2 86 sudo sshd -t && sudo systemctl reload ssh
john 4.1 87 {{/code}}
88
89 Même en cas de vol de la clé, elle ne donne ni shell ni tunnel : seulement un accès SFTP.
90
john 7.2 91 L'option ##-l VERBOSE## journalise chaque opération SFTP (ouverture, renommage, suppression, refus) dans ##journalctl -u ssh##. Sans elle, un refus côté dépôt n'apparaît nulle part sur le Pi.
92
john 4.1 93 === Sur le serveur principal (source) ===
94
95 Clé SSH dédiée, sans phrase de passe puisqu'elle sert à la tâche cron :
96
97 {{code language="bash"}}
98 sudo ssh-keygen -t ed25519 -f /root/.ssh/plakar_matrix -N ""
99 {{/code}}
100
101 Contenu de ##/root/.ssh/plakar_matrix.pub## à copier dans ##/srv/plakar/.ssh/authorized_keys## sur le Pi (propriétaire ##plakar##, droits ##600##).
102
103 ##/root/.ssh/config## :
104
105 {{code}}
106 Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com
107
108 Host <HOTE_DEPOT>
109 User plakar
110 IdentityFile /root/.ssh/plakar_matrix
111 {{/code}}
112
113 La ligne ##Ciphers## limite, pour toutes les connexions SSH de root, les algorithmes de chiffrement à des variantes modernes.
114
115 Première connexion, pour valider l'empreinte du Pi (sinon la tâche cron échouera) :
116
117 {{code language="bash"}}
118 sudo sftp <HOTE_DEPOT>
119 {{/code}}
120
121 Phrase secrète du dépôt et création :
122
123 {{code language="bash"}}
124 sudo sh -c 'umask 077; openssl rand -base64 12 > /root/.plakar-pass'
125 sudo plakar -keyfile /root/.plakar-pass at "sftp://plakar@<HOTE_DEPOT>:<CHEMIN_DEPOT>" create
126 {{/code}}
127
128 {{warning}}
129 Le dépôt est chiffré avec cette phrase secrète. Sans elle, les sauvegardes sont illisibles, même pour leur propriétaire : en conserver une copie dans le gestionnaire de mots de passe, jamais uniquement sur le serveur.
130 {{/warning}}
131
john 7.2 132 == Mettre à jour Plakar ==
133
134 Le paquet ##plakar## et l'intégration ##sftp## se mettent à jour **séparément** : ##apt## ne touche pas aux intégrations. Après chaque mise à jour du paquet :
135
136 {{code language="bash"}}
137 # Sur les deux machines : même version partout
138 plakar version
139
140 # Sur le serveur principal : mise à jour de toutes les intégrations de root
141 sudo plakar login -status # se reconnecter si besoin
142 sudo plakar pkg add -u
143 sudo plakar pkg list
144
145 # Contrôle de l'accès au dépôt
146 sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> ls | head -2
147 {{/code}}
148
149 {{warning}}
150 Une intégration restée sur une ancienne version alors que Plakar a été mis à jour peut provoquer des échecs difficiles à diagnostiquer, sur le verrou du dépôt notamment (voir Incidents connus). Ne jamais mettre à jour le paquet sans mettre à jour l'intégration dans la foulée.
151 {{/warning}}
152
john 4.1 153 == Restaurer si le serveur principal est perdu ==
154
john 7.2 155 Le dépôt peut s'ouvrir directement sur le Raspberry Pi, où Plakar est aussi installé, avec la phrase secrète récupérée dans le gestionnaire de mots de passe. L'accès étant local, aucune intégration ni compte Plakar n'est nécessaire :
john 4.1 156
157 {{code language="bash"}}
158 sudo plakar at <CHEMIN_DEPOT> ls
159 sudo plakar at <CHEMIN_DEPOT> restore -to /tmp/restore <ID_INSTANTANE>
160 {{/code}}
161
162 Plakar demande alors la phrase secrète de façon interactive.
163
john 1.1 164 == Script ==
165
john 7.2 166 {{code language="bash"}}
167 #!/bin/bash
john 1.1 168 set -euo pipefail
john 5.1 169 umask 077
john 1.1 170
john 5.1 171 # Alertes Telegram
172 . /root/.backup-telegram
173 notifier() {
174 curl -fsS -m 10 "https://api.telegram.org/bot$TG_TOKEN/sendMessage" \
175 -d chat_id="$TG_CHAT" --data-urlencode text="$1" >/dev/null || true
176 }
177 attention() { echo "ATTENTION: $1" >&2; notifier "⚠️ Sauvegarde : $1"; }
178 trap 'notifier "❌ Sauvegarde interrompue à la ligne $LINENO"' ERR
179
john 1.1 180 REPO="sftp://<UTILISATEUR>@<HOTE_DEPOT>:<CHEMIN_DEPOT>"
181 KEYFILE="<FICHIER_PHRASE_SECRETE>"
182 DUMPS="<APPDATA>/dumps"
183 mkdir -p "$DUMPS"
184
185 # Dumps MariaDB/MySQL
186 for c in xwiki-mariadb-db passbolt-db castopod-test-db; do
187 docker exec "$c" sh -c 'exec mysqldump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \
188 > "$DUMPS/$c.sql" 2>/dev/null \
189 || docker exec "$c" sh -c 'exec mariadb-dump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \
190 > "$DUMPS/$c.sql" 2>/dev/null \
john 5.1 191 || attention "dump $c échoué"
john 1.1 192 done
193
194 # Dumps PostgreSQL
john 5.1 195 for c in joplin-db flus-db wallabag-db immich_postgres vikunja-db jellystat-db n8n-postgres dawarich_db; do
john 1.1 196 docker exec "$c" sh -c 'exec pg_dumpall -U "$POSTGRES_USER"' \
197 > "$DUMPS/$c.sql" 2>/dev/null \
john 5.1 198 || attention "dump $c échoué"
john 1.1 199 done
200
john 7.1 201 # Attente de la fin de la sauvegarde Borg d'AIO (planifiée à 3h00)
202 attente=0
203 while docker ps --format '{{.Names}}' | grep -qx nextcloud-aio-borgbackup; do
204 if [ "$attente" -ge 3600 ]; then
205 attention "Borg AIO encore en cours après 1 h : dépôt Borg possiblement incohérent"
206 break
207 fi
208 sleep 60
209 attente=$((attente + 60))
210 done
211
212 # Backup avec exclusions : core dumps, binlogs, fichiers bruts des bases
213 # et données brutes de Nextcloud AIO (déjà dans le dépôt Borg, de façon cohérente)
john 1.1 214 plakar --concurrency 2 -keyfile "$KEYFILE" at "$REPO" backup \
215 -ignore "**/joplin-app/core.*" \
john 7.1 216 -ignore "**/Nextcloud-AIO/data/**" \
john 1.1 217 -ignore "**/log/mysql/**" \
218 -ignore "**/xwiki/mariadb/**" \
219 -ignore "**/store.bak.*/**" \
220 <APPDATA> \
john 5.1 221 <BIBLIOTHEQUE_IMMICH> \
john 7.1 222 <CONTENU_JOPLIN> \
223 <DEPOT_BORG_AIO>
john 1.1 224
225 # Rétention : 30 jours glissants
226 plakar -keyfile "$KEYFILE" at "$REPO" rm -before 30d -apply
john 7.1 227 notifier "✅ Sauvegarde terminée"
228 {{/code}}
john 1.1 229
230 Tâche cron, ##/etc/cron.d/backup-plakar## :
231
232 {{code}}
233 30 3 * * * root /root/backup-plakar.sh >> /var/log/backup-plakar.log 2>&1
234 {{/code}}
235
john 6.1 236 == Alertes ==
237
238 Le script envoie un message Telegram à chaque exécution :
239
240 |=Message|=Signification
241 |✅ Sauvegarde terminée|Tout s'est bien passé
242 |⚠️ Sauvegarde : dump … échoué|Un dump de base a échoué, la sauvegarde a continué sans lui
john 7.1 243 |⚠️ Sauvegarde : Borg AIO encore en cours après 1 h …|La sauvegarde Borg de Nextcloud n'était pas finie : la copie du dépôt Borg de cette nuit peut être incohérente, celle des nuits précédentes reste utilisable
john 7.2 244 |❌ Sauvegarde interrompue à la ligne …|Erreur bloquante, la sauvegarde n'a pas eu lieu (la ligne du ##plakar backup## indique un échec de Plakar lui-même : consulter la fin du journal)
john 6.1 245
246 Si aucun message n'arrive un matin, le script n'a pas démarré (serveur éteint, tâche cron cassée).
247
248 Jeton du bot et identifiant du groupe dans ##/root/.backup-telegram##, lisible par root seul :
249
250 {{code language="bash"}}
251 TG_TOKEN="<JETON_BOT>"
252 TG_CHAT="<ID_GROUPE>"
253 {{/code}}
254
255 {{warning}}
256 Dans le script, chaque ligne continuée doit se terminer par ##\## sans rien après, pas même un espace. Une barre oblique inverse suivie d'un espace ne continue plus la ligne : c'est ce qui a interrompu plusieurs sauvegardes en septembre 2026. Contrôle après toute modification : ##sudo bash -n /root/backup-plakar.sh##, et ##cat -A## pour voir les caractères invisibles en fin de ligne.
257 {{/warning}}
258
john 1.1 259 == Ajouter une base ==
260
261 Ajouter le nom du conteneur dans la boucle MariaDB ou PostgreSQL du script, puis exclure ses fichiers bruts avec un ##-ignore## s'ils se trouvent dans ##<APPDATA>##.
262
263 == Vérifier ==
264
265 {{code language="bash"}}
266 tail -n 50 /var/log/backup-plakar.log
267 grep ATTENTION /var/log/backup-plakar.log
john 7.2 268 sudo grep -nE '^(warn|error|plakar):' /var/log/backup-plakar.log | tail -n 20
john 1.1 269 sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> ls
john 7.2 270 sudo plakar pkg list
john 1.1 271 {{/code}}
272
john 7.2 273 La colonne durée de ##ls## donne le temps de chaque sauvegarde : une hausse brutale mérite un coup d'œil au journal.
274
275 Contrôle d'intégrité du dernier instantané, à lancer **sur le Raspberry Pi** (lecture locale du SSD, sans passer par le réseau) :
276
277 {{code language="bash"}}
278 sudo -u plakar plakar at <CHEMIN_DEPOT> check <ID_INSTANTANE>
279 {{/code}}
280
281 Sans identifiant, ##check## vérifie tous les instantanés : c'est très long sur un Pi et doit être terminé avant 3 h 30.
282
john 1.1 283 == Restaurer ==
284
285 Restaurer un instantané complet dans un dossier temporaire :
286
287 {{code language="bash"}}
288 sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> restore -to /tmp/restore <ID_INSTANTANE>
289 {{/code}}
290
291 Réinjecter une base MariaDB :
292
293 {{code language="bash"}}
294 docker exec -i <CONTENEUR> sh -c 'exec mariadb -uroot -p"$MARIADB_ROOT_PASSWORD"' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql
295 {{/code}}
296
297 Réinjecter une base PostgreSQL (dump complet de l'instance) :
298
299 {{code language="bash"}}
300 docker exec -i <CONTENEUR> sh -c 'exec psql -U "$POSTGRES_USER" -d postgres' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql
301 {{/code}}
302
john 7.1 303 Restaurer Nextcloud AIO : restaurer le dossier ##<DEPOT_BORG_AIO>## sur l'hôte, déployer le mastercontainer sur une installation neuve, puis choisir la restauration dans l'interface AIO (chemin du dépôt, mot de passe Borg, archive). Voir la page [[Nextcloud>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Nextcloud.WebHome]].
304
john 1.1 305 {{warning}}
306 Une sauvegarde qui n'a jamais été restaurée n'est pas une sauvegarde vérifiée. Faire un essai de restauration de temps en temps, dans un dossier temporaire.
307 {{/warning}}
308
309 == Incidents connus ==
310
311 |=Symptôme|=Cause|=Solution
john 7.2 312 |Verrou restant après une exécution interrompue|Le processus a été coupé en cours|Vérifier qu'aucun Plakar ne tourne (##pgrep -a plakar##), puis supprimer le fichier signalé dans ##<CHEMIN_DEPOT>/locks/## sur le Pi
john 1.1 313 |Avertissements VFS|Cache local incohérent|Vider ##~~/.cache/plakar## de root
314 |Transfert coupé (EOF SFTP) sur de gros fichiers|Lien Wi-Fi du dépôt instable|Économie d'énergie Wi-Fi désactivée sur le Pi ; passer à ##~-~-concurrency 1## pour une première sauvegarde volumineuse
315 |Déconnexion du SSD pendant la sauvegarde|Pont USB en mode UAS|Voir la configuration SSD sur la page [[Raspberry Pi>>Wiki Batcave John.Infrastructure.RaspberryPi.WebHome]]
john 7.2 316 |Arrêt après une dizaine de minutes, ##failed to refresh the repository lock: sftp: "Failure" (SSH_FX_FAILURE)## répété dans le journal, puis ##lock refresher: failed to refresh the repository lock##|Cause probable : intégration ##sftp## restée sur une ancienne version après une mise à jour de Plakar (octobre 2026). Le verrou est rafraîchi toutes les 5 minutes, les sauvegardes courtes peuvent donc masquer le problème|##sudo plakar pkg add -u##, supprimer le verrou orphelin, consulter ##journalctl -u ssh## sur le Pi (log ##VERBOSE##)
317 |##authentication required. Run `plakar login`## lors d'un ##pkg add##|Le gestionnaire d'intégrations exige un compte connecté|##sudo plakar login -github -no-spawn##