Aller au contenu
Sylvain Joffraud l4w
Retour

Backup générique multi-DB : Postgres et ClickHouse

En transformant un backup Plausible codé en dur en script de mirror générique capable de dumper tous les conteneurs DB d’un serveur (pour un standby de failover), deux hypothèses tranquilles ont sauté, et un troisième détail de quoting m’a coûté dix minutes.

Piège 1 — pg_dumpall -U postgres n’est pas universel

Le réflexe : docker exec <pg> pg_dumpall -U postgres. Marche pour Plausible… échoue pour le conteneur postgis voisin :

pg_dumpall: error: ... FATAL: role "postgres" does not exist

Les images Postgres respectent POSTGRES_USER : si le déploiement l’a fixé (ici appuser), le rôle postgres n’existe pas. Le superuser n’est pas une constante, c’est une variable du conteneur. On la lit dans son environnement :

pguser=$(docker exec "$c" printenv POSTGRES_USER | tr -d '\r'); pguser="${pguser:-postgres}"
docker exec "$c" pg_dumpall -U "$pguser"

Le superuser Postgres d’un conteneur, c’est $POSTGRES_USER, pas « postgres ». Codé en dur, ton backup générique rate une base sur deux.

Piège 2 — ClickHouse n’a pas de dump-all

Pas d’équivalent pg_dumpall : on dumpe table par table en FORMAT Native. Coder en dur events_v2, sessions_v2, schema_migrations (les tables « connues » de Plausible) marche… jusqu’à ce qu’une migration ajoute sessions, location_data, les imported_*, etc. La liste codée en dur dérive en silence et tu backupes une vue partielle sans le savoir.

La parade : découvrir les tables de données à dumper via system.tables, en se limitant aux moteurs MergeTree (on exclut vues et dictionnaires, qui sont dérivés) :

SELECT database||'.'||name FROM system.tables
WHERE database NOT IN ('system','INFORMATION_SCHEMA','information_schema')
  AND engine LIKE '%MergeTree%'

La découverte a trouvé 15 tables là où le code en dur en sauvegardait 3.

Une liste de tables codée en dur est une bombe à retardement : la prochaine migration la périme sans erreur. Découvre ce qui existe, ne présume pas ce que tu connais.

Piège 3 — le quoting ssh → docker exec → clickhouse-client --query

Trois niveaux de shell imbriqués, et la requête SQL contient des apostrophes ('system') et des guillemets. Le --query "..." se fait broyer. Deux sorties propres :

Lessons learned

#sysadmin #docker #backup #postgres #clickhouse #bash

Questions fréquentes

Pourquoi pg_dumpall -U postgres renvoie FATAL: role postgres does not exist dans un conteneur Docker ?
Parce que l’image Postgres respecte la variable POSTGRES_USER : si le déploiement l’a fixée (par exemple appuser), le rôle « postgres » n’existe tout simplement pas. Le superuser n’est pas une constante mais une variable propre au conteneur. Lisez-la avec docker exec $c printenv POSTGRES_USER et utilisez cette valeur (avec « postgres » en repli) pour pg_dumpall -U.
Comment sauvegarder toutes les tables ClickHouse alors qu’il n’existe pas de pg_dumpall ?
ClickHouse n’a pas de dump-all : on dumpe table par table en FORMAT Native. Plutôt que de coder en dur la liste des tables, découvrez-les dynamiquement via system.tables en filtrant sur engine LIKE '%MergeTree%' et en excluant les bases système. Dans l’article, cette découverte a trouvé 15 tables là où la liste codée en dur n’en sauvegardait que 3.
Comment gérer le quoting d’une requête SQL passée à travers ssh puis docker exec puis clickhouse-client ?
Avec trois couches de shell imbriquées, les apostrophes du SQL (comme 'system') se font broyer. Deux parades : échapper les guillemets externes passés à ssh (--query \"SELECT ...\") pour que les apostrophes SQL survivent ; ou supprimer tout guillemet du SQL en utilisant char() (char(46) pour le point, char(115,121,115,116,101,109) pour « system »), illisible mais increvable à travers n’importe quelle couche de shell.
Pourquoi éviter une liste de tables ou de bases codée en dur dans un script de backup ?
Parce qu’une liste codée en dur dérive en silence : la prochaine migration ajoute des tables sans lever la moindre erreur, et vous backupez une vue partielle sans le savoir jusqu’au restore incomplet le jour J. La règle est d’interroger l’environnement à l’exécution ($POSTGRES_USER, system.tables, volumes attachés) et de journaliser chaque conteneur ou volume non couvert plutôt que de l’ignorer.

Partager cet article :

Article précédent
RAM d’un NAS de 2011 : dmidecode dément la fiche commerciale
Article suivant
Répliquer une prod Coolify sans Coolify avec Traefik