Code source wiki de Sauvegarde
Masquer les derniers auteurs
| author | version | line-number | content |
|---|---|---|---|
![]() |
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 | |||
| 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## ; | ||
![]() |
3.1 | 17 | * sauvegarde les données des conteneurs, la bibliothèque Immich et le contenu de Joplin, en excluant les fichiers bruts des bases ; |
![]() |
1.1 | 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 | |||
![]() |
4.1 | 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 | |||
![]() |
1.1 | 102 | == Script == |
| 103 | |||
| 104 | {{code language="bash"}} | ||
| 105 | #!/bin/bash | ||
| 106 | set -euo pipefail | ||
| 107 | umask 077 # Ajout : dumps lisibles par root seul | ||
| 108 | |||
| 109 | REPO="sftp://<UTILISATEUR>@<HOTE_DEPOT>:<CHEMIN_DEPOT>" | ||
| 110 | KEYFILE="<FICHIER_PHRASE_SECRETE>" | ||
| 111 | DUMPS="<APPDATA>/dumps" | ||
| 112 | mkdir -p "$DUMPS" | ||
| 113 | |||
| 114 | # Dumps MariaDB/MySQL | ||
| 115 | for c in xwiki-mariadb-db passbolt-db castopod-test-db; do | ||
| 116 | docker exec "$c" sh -c 'exec mysqldump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \ | ||
| 117 | > "$DUMPS/$c.sql" 2>/dev/null \ | ||
| 118 | || docker exec "$c" sh -c 'exec mariadb-dump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \ | ||
| 119 | > "$DUMPS/$c.sql" 2>/dev/null \ | ||
| 120 | || echo "ATTENTION: dump $c échoué" >&2 | ||
| 121 | done | ||
| 122 | |||
| 123 | # Nextcloud : dump via l'utilisateur applicatif | ||
![]() |
2.1 | 124 | docker exec "$(docker ps -q -f name=nextcloud_db)" sh -c 'exec mariadb-dump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' \ |
![]() |
1.1 | 125 | > "$DUMPS/nextcloud.sql" 2>/dev/null \ |
| 126 | || echo "ATTENTION: dump nextcloud échoué" >&2 | ||
| 127 | |||
| 128 | # Dumps PostgreSQL | ||
| 129 | for c in joplin-db flus-db wallabag-db immich_postgres vikunja-db jellystat-db n8n-postgres dawarich_db; do # Ajout : dawarich_db | ||
| 130 | docker exec "$c" sh -c 'exec pg_dumpall -U "$POSTGRES_USER"' \ | ||
| 131 | > "$DUMPS/$c.sql" 2>/dev/null \ | ||
| 132 | || echo "ATTENTION: dump $c échoué" >&2 | ||
| 133 | done | ||
| 134 | |||
| 135 | # Sauvegarde, sans core dumps, binlogs ni fichiers bruts des bases | ||
| 136 | plakar --concurrency 2 -keyfile "$KEYFILE" at "$REPO" backup \ | ||
| 137 | -ignore "**/joplin-app/core.*" \ | ||
| 138 | -ignore "**/Nextcloud/DB/**" \ | ||
| 139 | -ignore "**/log/mysql/**" \ | ||
| 140 | -ignore "**/xwiki/mariadb/**" \ | ||
| 141 | -ignore "**/store.bak.*/**" \ | ||
| 142 | <APPDATA> \ | ||
![]() |
3.1 | 143 | <BIBLIOTHEQUE_IMMICH>\ |
| 144 | <CONTENU_JOPLIN> | ||
![]() |
1.1 | 145 | |
| 146 | # Rétention : 30 jours glissants | ||
| 147 | plakar -keyfile "$KEYFILE" at "$REPO" rm -before 30d -apply | ||
| 148 | {{/code}} | ||
| 149 | |||
| 150 | Tâche cron, ##/etc/cron.d/backup-plakar## : | ||
| 151 | |||
| 152 | {{code}} | ||
| 153 | 30 3 * * * root /root/backup-plakar.sh >> /var/log/backup-plakar.log 2>&1 | ||
| 154 | {{/code}} | ||
| 155 | |||
| 156 | == Ajouter une base == | ||
| 157 | |||
| 158 | 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>##. | ||
| 159 | |||
| 160 | == Vérifier == | ||
| 161 | |||
| 162 | {{code language="bash"}} | ||
| 163 | tail -n 50 /var/log/backup-plakar.log | ||
| 164 | grep ATTENTION /var/log/backup-plakar.log | ||
| 165 | sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> ls | ||
| 166 | {{/code}} | ||
| 167 | |||
| 168 | == Restaurer == | ||
| 169 | |||
| 170 | Restaurer un instantané complet dans un dossier temporaire : | ||
| 171 | |||
| 172 | {{code language="bash"}} | ||
| 173 | sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> restore -to /tmp/restore <ID_INSTANTANE> | ||
| 174 | {{/code}} | ||
| 175 | |||
| 176 | Réinjecter une base MariaDB : | ||
| 177 | |||
| 178 | {{code language="bash"}} | ||
| 179 | docker exec -i <CONTENEUR> sh -c 'exec mariadb -uroot -p"$MARIADB_ROOT_PASSWORD"' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql | ||
| 180 | {{/code}} | ||
| 181 | |||
| 182 | Réinjecter une base PostgreSQL (dump complet de l'instance) : | ||
| 183 | |||
| 184 | {{code language="bash"}} | ||
| 185 | docker exec -i <CONTENEUR> sh -c 'exec psql -U "$POSTGRES_USER" -d postgres' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql | ||
| 186 | {{/code}} | ||
| 187 | |||
| 188 | {{warning}} | ||
| 189 | 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. | ||
| 190 | {{/warning}} | ||
| 191 | |||
| 192 | == Incidents connus == | ||
| 193 | |||
| 194 | |=Symptôme|=Cause|=Solution | ||
| 195 | |Verrou restant après une exécution interrompue|Le processus a été coupé en cours|Supprimer le verrou orphelin avant de relancer | ||
| 196 | |Avertissements VFS|Cache local incohérent|Vider ##~~/.cache/plakar## de root | ||
| 197 | |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 | ||
| 198 | |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]] |
