0 Votes

Code source wiki de Sauvegarde

Version 5.1 par john le 2026/09/19 19:59

Afficher les derniers auteurs
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
3 == Fonctionnement ==
4
5 |=Élément|=Valeur
6 |Machine sauvegardée|[[Serveur principal>>Wiki Batcave John.Infrastructure.ServeurPrincipal.WebHome]]
7 |Dépôt|SSD USB du [[Raspberry Pi>>Wiki Batcave John.Infrastructure.RaspberryPi.WebHome]]
8 |Transport|SFTP
9 |Déclenchement|Cron, chaque nuit à 3 h 30
10 |Rétention|30 jours glissants
11 |Script|##/root/backup-plakar.sh##
12 |Journal|##/var/log/backup-plakar.log##, avec rotation par logrotate
13
14 Chaque exécution :
15
16 * exporte toutes les bases en SQL dans ##<APPDATA>/dumps## ;
17 * sauvegarde les données des conteneurs, la bibliothèque Immich et le contenu de Joplin, en excluant les fichiers bruts des bases ;
18 * supprime les instantanés de plus de 30 jours.
19
20 {{info}}
21 Les fichiers bruts d'une base en fonctionnement ne sont pas fiables pour une restauration. Ce sont les dumps SQL qui font foi.
22 {{/info}}
23
24 == Mise en place du dépôt ==
25
26 === Sur le Raspberry Pi (dépôt) ===
27
28 Utilisateur dédié, sans shell :
29
30 {{code language="bash"}}
31 sudo useradd -m -d /srv/plakar -s /usr/sbin/nologin plakar
32 sudo mkdir -p <CHEMIN_DEPOT>
33 sudo chown plakar:plakar <CHEMIN_DEPOT>
34 sudo install -d -m 700 -o plakar -g plakar /srv/plakar/.ssh
35 {{/code}}
36
37 Limitation au SFTP, dans ##/etc/ssh/sshd_config.d/plakar.conf## :
38
39 {{code}}
40 Match User plakar
41 ForceCommand internal-sftp
42 AllowTcpForwarding no
43 X11Forwarding no
44 {{/code}}
45
46 {{code language="bash"}}
47 sudo systemctl reload ssh
48 {{/code}}
49
50 Même en cas de vol de la clé, elle ne donne ni shell ni tunnel : seulement un accès SFTP.
51
52 === Sur le serveur principal (source) ===
53
54 Clé SSH dédiée, sans phrase de passe puisqu'elle sert à la tâche cron :
55
56 {{code language="bash"}}
57 sudo ssh-keygen -t ed25519 -f /root/.ssh/plakar_matrix -N ""
58 {{/code}}
59
60 Contenu de ##/root/.ssh/plakar_matrix.pub## à copier dans ##/srv/plakar/.ssh/authorized_keys## sur le Pi (propriétaire ##plakar##, droits ##600##).
61
62 ##/root/.ssh/config## :
63
64 {{code}}
65 Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com
66
67 Host <HOTE_DEPOT>
68 User plakar
69 IdentityFile /root/.ssh/plakar_matrix
70 {{/code}}
71
72 La ligne ##Ciphers## limite, pour toutes les connexions SSH de root, les algorithmes de chiffrement à des variantes modernes.
73
74 Première connexion, pour valider l'empreinte du Pi (sinon la tâche cron échouera) :
75
76 {{code language="bash"}}
77 sudo sftp <HOTE_DEPOT>
78 {{/code}}
79
80 Phrase secrète du dépôt et création :
81
82 {{code language="bash"}}
83 sudo sh -c 'umask 077; openssl rand -base64 12 > /root/.plakar-pass'
84 sudo plakar -keyfile /root/.plakar-pass at "sftp://plakar@<HOTE_DEPOT>:<CHEMIN_DEPOT>" create
85 {{/code}}
86
87 {{warning}}
88 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.
89 {{/warning}}
90
91 == Restaurer si le serveur principal est perdu ==
92
93 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 :
94
95 {{code language="bash"}}
96 sudo plakar at <CHEMIN_DEPOT> ls
97 sudo plakar at <CHEMIN_DEPOT> restore -to /tmp/restore <ID_INSTANTANE>
98 {{/code}}
99
100 Plakar demande alors la phrase secrète de façon interactive.
101
102 == Script ==
103
104 {{code language="bash"}}
105 #!/bin/bash
106 set -euo pipefail
107 umask 077
108
109 # Alertes Telegram
110 . /root/.backup-telegram
111 notifier() {
112 curl -fsS -m 10 "https://api.telegram.org/bot$TG_TOKEN/sendMessage" \
113 -d chat_id="$TG_CHAT" --data-urlencode text="$1" >/dev/null || true
114 }
115 attention() { echo "ATTENTION: $1" >&2; notifier "⚠️ Sauvegarde : $1"; }
116 trap 'notifier "❌ Sauvegarde interrompue à la ligne $LINENO"' ERR
117
118 REPO="sftp://<UTILISATEUR>@<HOTE_DEPOT>:<CHEMIN_DEPOT>"
119 KEYFILE="<FICHIER_PHRASE_SECRETE>"
120 DUMPS="<APPDATA>/dumps"
121 mkdir -p "$DUMPS"
122
123 # Dumps MariaDB/MySQL
124 for c in xwiki-mariadb-db passbolt-db castopod-test-db; do
125 docker exec "$c" sh -c 'exec mysqldump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \
126 > "$DUMPS/$c.sql" 2>/dev/null \
127 || docker exec "$c" sh -c 'exec mariadb-dump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \
128 > "$DUMPS/$c.sql" 2>/dev/null \
129 || attention "dump $c échoué"
130 done
131
132 # Nextcloud : dump via l'utilisateur applicatif
133 docker exec "$(docker ps -q -f name=nextcloud_db)" sh -c 'exec mariadb-dump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' \
134 > "$DUMPS/nextcloud.sql" 2>/dev/null \
135 || attention "dump nextcloud échoué"
136
137 # Dumps PostgreSQL
138 for c in joplin-db flus-db wallabag-db immich_postgres vikunja-db jellystat-db n8n-postgres dawarich_db; do
139 docker exec "$c" sh -c 'exec pg_dumpall -U "$POSTGRES_USER"' \
140 > "$DUMPS/$c.sql" 2>/dev/null \
141 || attention "dump $c échoué"
142 done
143
144 # Sauvegarde, sans core dumps, binlogs ni fichiers bruts des bases
145 plakar --concurrency 2 -keyfile "$KEYFILE" at "$REPO" backup \
146 -ignore "**/joplin-app/core.*" \
147 -ignore "**/Nextcloud/DB/**" \
148 -ignore "**/log/mysql/**" \
149 -ignore "**/xwiki/mariadb/**" \
150 -ignore "**/store.bak.*/**" \
151 <APPDATA> \
152 <BIBLIOTHEQUE_IMMICH> \
153 <CONTENU_JOPLIN>
154
155 # Rétention : 30 jours glissants
156 plakar -keyfile "$KEYFILE" at "$REPO" rm -before 30d -apply
157 notifier "✅ Sauvegarde terminée"
158 {{/code}}
159 {{/code}}
160
161 Tâche cron, ##/etc/cron.d/backup-plakar## :
162
163 {{code}}
164 30 3 * * * root /root/backup-plakar.sh >> /var/log/backup-plakar.log 2>&1
165 {{/code}}
166
167 == Ajouter une base ==
168
169 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>##.
170
171 == Vérifier ==
172
173 {{code language="bash"}}
174 tail -n 50 /var/log/backup-plakar.log
175 grep ATTENTION /var/log/backup-plakar.log
176 sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> ls
177 {{/code}}
178
179 == Restaurer ==
180
181 Restaurer un instantané complet dans un dossier temporaire :
182
183 {{code language="bash"}}
184 sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> restore -to /tmp/restore <ID_INSTANTANE>
185 {{/code}}
186
187 Réinjecter une base MariaDB :
188
189 {{code language="bash"}}
190 docker exec -i <CONTENEUR> sh -c 'exec mariadb -uroot -p"$MARIADB_ROOT_PASSWORD"' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql
191 {{/code}}
192
193 Réinjecter une base PostgreSQL (dump complet de l'instance) :
194
195 {{code language="bash"}}
196 docker exec -i <CONTENEUR> sh -c 'exec psql -U "$POSTGRES_USER" -d postgres' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql
197 {{/code}}
198
199 {{warning}}
200 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.
201 {{/warning}}
202
203 == Incidents connus ==
204
205 |=Symptôme|=Cause|=Solution
206 |Verrou restant après une exécution interrompue|Le processus a été coupé en cours|Supprimer le verrou orphelin avant de relancer
207 |Avertissements VFS|Cache local incohérent|Vider ##~~/.cache/plakar## de root
208 |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
209 |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]]