infra: fait tourner PostgreSQL 17 et TimescaleDB sur Docker #81

Merged
lenaic merged 4 commits from olivier/80-postgresql-docker into develop 2026-09-02 11:23:08 +00:00
Member

Ce que ça change

PostgreSQL 17 et TimescaleDB sortent des paquets Debian et tournent dans le conteneur ev-postgres, en réseau hôte, sur le même port 5433. La pile entière est versionnée dans infra/compose/postgres/ : image épinglée, compose, configuration, scripts de sauvegarde. Les rôles et les bases sont mis en conformité avec la cible — quatre rôles, trois bases, chaque rôle sur sa seule base — et l'accès distant est fermé au profit d'un tunnel SSH, conformément à la décision du #65.

Closes #80

Preuve

Accès, depuis la boucle locale du serveur — 16 essais, aucun échec :

== Le superutilisateur n'a AUCUN acces reseau ==
  ✓ postgres            -> postgres            KO
  ✓ postgres            -> enervision_prod     KO
== Chaque role joint sa base ==
  ✓ enervision_prod     -> enervision_prod     OK
  ✓ enervision_preprod  -> enervision_preprod  OK
  ✓ mlflow              -> mlflow              OK
  ✓ grafana             -> enervision_prod     OK
== ... et aucune autre ==
  ✓ enervision_prod     -> enervision_preprod  KO
  ✓ enervision_prod     -> mlflow              KO
  ✓ enervision_preprod  -> enervision_prod     KO
  ✓ enervision_preprod  -> mlflow              KO
  ✓ mlflow              -> enervision_prod     KO
  ✓ mlflow              -> enervision_preprod  KO
  ✓ grafana             -> enervision_preprod  KO
  ✓ grafana             -> mlflow              KO
  ✓ grafana             -> postgres            KO
== Mot de passe faux : refus ==
  ✓ refus sur mot de passe faux
== TLS et adresse source vue par le serveur ==
chiffrement=TLSv1.3 | source vue=127.0.0.1
== Grafana est en LECTURE SEULE sur enervision_prod ==
  ERREUR:  droit refusé pour le schéma public

Fermeture de l'accès distant, éprouvée depuis un poste hors de la machine (172.26.44.15), en interrogeant le décideur pg_hba par le protocole PostgreSQL :

1. En direct, sans tunnel
  enervision_prod     -> enervision_prod     INJOIGNABLE

2. Par le tunnel SSH
  enervision_prod     -> enervision_prod     AUTORISE par pg_hba, demande SASL/SCRAM
  enervision_preprod  -> enervision_preprod  AUTORISE par pg_hba, demande SASL/SCRAM
  mlflow              -> mlflow              AUTORISE par pg_hba, demande SASL/SCRAM
  grafana             -> enervision_prod     AUTORISE par pg_hba, demande SASL/SCRAM
  postgres            -> postgres            REFUSE : pg_hba.conf rejette la connexion
  postgres            -> enervision_prod     REFUSE : pg_hba.conf rejette la connexion
  grafana             -> mlflow              REFUSE : aucune entree dans pg_hba.conf
  mlflow              -> enervision_prod     REFUSE : aucune entree dans pg_hba.conf
  enervision_prod     -> enervision_preprod  REFUSE : aucune entree dans pg_hba.conf

3. Tunnel referme
  enervision_prod     -> enervision_prod     INJOIGNABLE

Reprise du schéma ops d'enervision_preprod — le seul contenu applicatif migré. Empreinte md5 du contenu sérialisé, avant et après :

                   AVANT (natif, gele)                    APRES (conteneur)
auth_events        9  44033ccb6571cca34d0ed8962562edd1     9  44033ccb6571cca34d0ed8962562edd1
auth_sessions      3  1e946d8b94dfa97a11729ea7fb7640c7     3  1e946d8b94dfa97a11729ea7fb7640c7
schema_migrations  3  9ab01ea9239deb3ebc673205344f6639     3  9ab01ea9239deb3ebc673205344f6639
users              1  6f121a757faa54a37104af440306798a     1  6f121a757faa54a37104af440306798a

Sauvegarde et lisibilité :

$ sudo /usr/local/bin/pg-backup.sh; echo "code de sortie : $?"
pg-backup : 3 base(s) sauvegardee(s) dans /var/backups/postgresql
code de sortie : 0
$ sudo cat /var/backups/postgresql/.dernier-etat
2026-09-02T08:49:50+00:00 OK 3 base(s) : enervision_preprod enervision_prod mlflow
$ sudo /usr/local/bin/verifier-sauvegardes.sh --tous     # 16 vidages
  ... lisible sur chaque ligne, code de sortie 0
# contre-epreuve, vidage tronque a 200 octets : ILLISIBLE, code de sortie 1

Restauration à côté des trois bases depuis les vidages d'avant migration : aucune erreur. Inventaires d'objets identiques avant et après. Épreuve de redémarrage passée, conteneur Up (healthy), données intactes.

Relecture

  • Un pair a relu et laissé un commentaire, même court
  • Ses remarques sont traitées, ou une réponse explique pourquoi elles ne le sont pas

Ce qui suit le code

  • docs/runbooks/ mis à jour — tous les gestes ont changé de forme : plus de systemctl, plus de sudo -u postgres, plus de fichier dans /var/log/postgresql
  • Une nouvelle variable d'environnement est apparue, elle est dans .env.example (cinq, en fait : les quatre rôles et le superutilisateur)
  • docs/adr/à discuter en relecture : le passage à Docker et la fermeture de l'accès distant méritent-ils une fiche ADR, ou le #65 et ce ticket suffisent-ils ?

Où regarder en priorité

Trois points où j'ai hésité, ou qui demandent un avis qui n'est pas le mien.

1. Le rôle et la base keycloak ne sont pas repris. C'était demandé, et la base était vide (zéro table, aucun conteneur Keycloak déployé). Mais docs/EXIGENCES-collectives.md arbitre au 01/09 « base de Keycloak : schéma dédié dans l'instance PostgreSQL existante », porte les exigences ENF-01 et ENF-02 sur Keycloak, et attribue 3 j.h à Justine et Lénaïc. Je n'ai pas touché au fichier collectif : il n'est pas à moi de le modifier seul. C'est l'écart n°13 de docs/POSTGRESQL.md. Si Keycloak est confirmé, le rôle et la base se recréent au déploiement — le vidage est conservé dans /var/backups/postgresql/avant-docker-2026-09-02-0808/keycloak.dump.

2. Un port publié par Docker échappe entièrement à UFW, et ça vaut pour toute la pile Compose à venir, pas seulement pour la base. Dans la chaîne FORWARD, DOCKER-USER est consultée en position 1 et les chaînes d'UFW en position 3 ; DOCKER-USER est vide. Une règle ufw sur un service en réseau bridge n'a donc aucun effet, tout en affichant le bon masque dans ufw status. C'est l'écart n°14, et c'est la raison principale du réseau hôte ici. À trancher avant MinIO, MLflow et la supervision, pas après.

3. max_worker_processes et timescaledb.max_background_workers ont été corrigés. Le natif journalisait TimescaleDB background worker limit of 4 exceeded seize fois au 1er septembre. Le lanceur démarre un ordonnanceur par base du cluster, y compris celles sans l'extension : les quatre places étaient prises par les ordonnanceurs et aucun travail de fond ne pouvait s'exécuter. Sans correction, les agrégats continus auraient cessé de se rafraîchir — le tableau de bord aurait continué d'afficher des courbes qui ne bougent plus. Passés à 8 et 16, l'avertissement a disparu et les deux ordonnanceurs attendus tournent. Les valeurs méritent un second avis : elles s'écartent du dimensionnement documenté au 1er septembre.

Deux pièges mesurés, notés parce qu'ils reviendront. Un montage de fichier ne suit pas un remplacement par renommage — donc rsync, mv, sed -i et la plupart des éditeurs laissent le conteneur sur l'ancien inode pendant que pg_reload_conf() annonce un succès. La pile monte le répertoire conf/ en entier pour cette raison. Et pg_restore -l /dev/stdin ne fonctionne pas à travers docker exec, le flux d'entrée n'étant pas repositionnable : d'où le script verifier-sauvegardes.sh plutôt qu'une ligne dans le manuel.

Le cluster natif est conservé, arrêté derrière trois verrous (start.conf à disabled, unité masquée, parapluie désactivé). C'est le retour arrière, et c'est délibéré : on ne supprime pas le filet le jour de la bascule. Sa purge est l'écart n°8, à faire après la démonstration.

## Ce que ça change PostgreSQL 17 et TimescaleDB sortent des paquets Debian et tournent dans le conteneur `ev-postgres`, en réseau hôte, sur le même port 5433. La pile entière est versionnée dans `infra/compose/postgres/` : image épinglée, compose, configuration, scripts de sauvegarde. Les rôles et les bases sont mis en conformité avec la cible — quatre rôles, trois bases, chaque rôle sur sa seule base — et l'accès distant est **fermé** au profit d'un tunnel SSH, conformément à la décision du #65. Closes #80 ## Preuve Accès, depuis la boucle locale du serveur — 16 essais, aucun échec : ``` == Le superutilisateur n'a AUCUN acces reseau == ✓ postgres -> postgres KO ✓ postgres -> enervision_prod KO == Chaque role joint sa base == ✓ enervision_prod -> enervision_prod OK ✓ enervision_preprod -> enervision_preprod OK ✓ mlflow -> mlflow OK ✓ grafana -> enervision_prod OK == ... et aucune autre == ✓ enervision_prod -> enervision_preprod KO ✓ enervision_prod -> mlflow KO ✓ enervision_preprod -> enervision_prod KO ✓ enervision_preprod -> mlflow KO ✓ mlflow -> enervision_prod KO ✓ mlflow -> enervision_preprod KO ✓ grafana -> enervision_preprod KO ✓ grafana -> mlflow KO ✓ grafana -> postgres KO == Mot de passe faux : refus == ✓ refus sur mot de passe faux == TLS et adresse source vue par le serveur == chiffrement=TLSv1.3 | source vue=127.0.0.1 == Grafana est en LECTURE SEULE sur enervision_prod == ERREUR: droit refusé pour le schéma public ``` Fermeture de l'accès distant, éprouvée **depuis un poste hors de la machine** (`172.26.44.15`), en interrogeant le décideur `pg_hba` par le protocole PostgreSQL : ``` 1. En direct, sans tunnel enervision_prod -> enervision_prod INJOIGNABLE 2. Par le tunnel SSH enervision_prod -> enervision_prod AUTORISE par pg_hba, demande SASL/SCRAM enervision_preprod -> enervision_preprod AUTORISE par pg_hba, demande SASL/SCRAM mlflow -> mlflow AUTORISE par pg_hba, demande SASL/SCRAM grafana -> enervision_prod AUTORISE par pg_hba, demande SASL/SCRAM postgres -> postgres REFUSE : pg_hba.conf rejette la connexion postgres -> enervision_prod REFUSE : pg_hba.conf rejette la connexion grafana -> mlflow REFUSE : aucune entree dans pg_hba.conf mlflow -> enervision_prod REFUSE : aucune entree dans pg_hba.conf enervision_prod -> enervision_preprod REFUSE : aucune entree dans pg_hba.conf 3. Tunnel referme enervision_prod -> enervision_prod INJOIGNABLE ``` Reprise du schéma `ops` d'`enervision_preprod` — le seul contenu applicatif migré. Empreinte md5 du contenu sérialisé, avant et après : ``` AVANT (natif, gele) APRES (conteneur) auth_events 9 44033ccb6571cca34d0ed8962562edd1 9 44033ccb6571cca34d0ed8962562edd1 auth_sessions 3 1e946d8b94dfa97a11729ea7fb7640c7 3 1e946d8b94dfa97a11729ea7fb7640c7 schema_migrations 3 9ab01ea9239deb3ebc673205344f6639 3 9ab01ea9239deb3ebc673205344f6639 users 1 6f121a757faa54a37104af440306798a 1 6f121a757faa54a37104af440306798a ``` Sauvegarde et lisibilité : ``` $ sudo /usr/local/bin/pg-backup.sh; echo "code de sortie : $?" pg-backup : 3 base(s) sauvegardee(s) dans /var/backups/postgresql code de sortie : 0 $ sudo cat /var/backups/postgresql/.dernier-etat 2026-09-02T08:49:50+00:00 OK 3 base(s) : enervision_preprod enervision_prod mlflow $ sudo /usr/local/bin/verifier-sauvegardes.sh --tous # 16 vidages ... lisible sur chaque ligne, code de sortie 0 # contre-epreuve, vidage tronque a 200 octets : ILLISIBLE, code de sortie 1 ``` Restauration à côté des trois bases depuis les vidages d'avant migration : aucune erreur. Inventaires d'objets identiques avant et après. Épreuve de redémarrage passée, conteneur `Up (healthy)`, données intactes. ## Relecture - [ ] Un pair a relu et laissé un commentaire, même court - [ ] Ses remarques sont traitées, ou une réponse explique pourquoi elles ne le sont pas ## Ce qui suit le code - [x] `docs/runbooks/` mis à jour — **tous** les gestes ont changé de forme : plus de `systemctl`, plus de `sudo -u postgres`, plus de fichier dans `/var/log/postgresql` - [x] Une nouvelle variable d'environnement est apparue, elle est dans `.env.example` (cinq, en fait : les quatre rôles et le superutilisateur) - [ ] `docs/adr/` — **à discuter en relecture** : le passage à Docker et la fermeture de l'accès distant méritent-ils une fiche ADR, ou le #65 et ce ticket suffisent-ils ? ## Où regarder en priorité **Trois points où j'ai hésité, ou qui demandent un avis qui n'est pas le mien.** **1. Le rôle et la base `keycloak` ne sont pas repris.** C'était demandé, et la base était vide (zéro table, aucun conteneur Keycloak déployé). Mais `docs/EXIGENCES-collectives.md` arbitre au 01/09 « base de Keycloak : schéma dédié dans l'instance PostgreSQL existante », porte les exigences ENF-01 et ENF-02 sur Keycloak, et attribue 3 j.h à Justine et Lénaïc. **Je n'ai pas touché au fichier collectif** : il n'est pas à moi de le modifier seul. C'est l'écart n°13 de `docs/POSTGRESQL.md`. Si Keycloak est confirmé, le rôle et la base se recréent au déploiement — le vidage est conservé dans `/var/backups/postgresql/avant-docker-2026-09-02-0808/keycloak.dump`. **2. Un port publié par Docker échappe entièrement à UFW**, et ça vaut pour toute la pile Compose à venir, pas seulement pour la base. Dans la chaîne `FORWARD`, `DOCKER-USER` est consultée en position 1 et les chaînes d'UFW en position 3 ; `DOCKER-USER` est vide. Une règle `ufw` sur un service en réseau bridge n'a donc **aucun effet**, tout en affichant le bon masque dans `ufw status`. C'est l'écart n°14, et c'est la raison principale du réseau hôte ici. À trancher avant MinIO, MLflow et la supervision, pas après. **3. `max_worker_processes` et `timescaledb.max_background_workers` ont été corrigés.** Le natif journalisait `TimescaleDB background worker limit of 4 exceeded` seize fois au 1er septembre. Le lanceur démarre un ordonnanceur **par base du cluster**, y compris celles sans l'extension : les quatre places étaient prises par les ordonnanceurs et aucun travail de fond ne pouvait s'exécuter. Sans correction, les agrégats continus auraient cessé de se rafraîchir — le tableau de bord aurait continué d'afficher des courbes qui ne bougent plus. Passés à 8 et 16, l'avertissement a disparu et les deux ordonnanceurs attendus tournent. **Les valeurs méritent un second avis** : elles s'écartent du dimensionnement documenté au 1er septembre. **Deux pièges mesurés, notés parce qu'ils reviendront.** Un montage de *fichier* ne suit pas un remplacement par renommage — donc `rsync`, `mv`, `sed -i` et la plupart des éditeurs laissent le conteneur sur l'ancien inode pendant que `pg_reload_conf()` annonce un succès. La pile monte le répertoire `conf/` en entier pour cette raison. Et `pg_restore -l /dev/stdin` ne fonctionne pas à travers `docker exec`, le flux d'entrée n'étant pas repositionnable : d'où le script `verifier-sauvegardes.sh` plutôt qu'une ligne dans le manuel. **Le cluster natif est conservé**, arrêté derrière trois verrous (`start.conf` à `disabled`, unité masquée, parapluie désactivé). C'est le retour arrière, et c'est délibéré : on ne supprime pas le filet le jour de la bascule. Sa purge est l'écart n°8, à faire après la démonstration.
Le service tournait en paquets Debian (postgresql@17-main). Il tourne
desormais dans le conteneur ev-postgres, en reseau hote, sur le meme port
5433. Le port, les noms de bases, la collation et les reglages sont
inchanges : un client existant ne voit aucune difference.

Image maison plutot que timescale/timescaledb-ha, pour trois raisons :
aucune image publiee ne porte la collation fr_FR.UTF-8 dont dependent le
tri des libelles et la recherche plein texte ; postgres:17-bookworm est en
17.11 quand Timescale s'arrete a 17.10, et migrer n'a pas a faire reculer
un correctif ; l'image de Timescale pese 4 Go contre 1,5 ici, sur un
disque de 63 Go partage a six groupes. Les trois versions sont epinglees
et verifiees a la construction : l'image ne sort pas si TimescaleDB
n'annonce pas 2.29.2, le toolkit 1.25.0, ou si la locale manque.

Reseau hote et non port publie, apres mesure : dans la chaine FORWARD,
DOCKER-USER est consultee avant les chaines d'UFW et elle est vide, donc
un port publie par Docker echappe entierement au pare-feu. Une regle
`ufw allow from 10.105.200.0/24` aurait laisse la base joignable depuis
tout le reseau de l'ecole en affichant le bon masque. Le mandataire de
Docker reecrivait en outre l'adresse source des clients locaux, ce qui
privait le diagnostic client_addr de tout sens. C'est aussi la convention
de la machine : les trois conteneurs de la forge sont en reseau hote.

Etat cible pose : quatre roles (enervision_prod, enervision_preprod,
mlflow, grafana), trois bases, chaque role sur sa seule base. Le
cloisonnement est double, par paire (base, role) dans pg_hba.conf et par
REVOKE CONNECT ... FROM PUBLIC en base. Le superutilisateur n'a aucun
acces reseau : l'administration passe par le socket du conteneur.
grafana est en lecture seule sur enervision_prod, y compris sur les
tables futures. Le role et la base keycloak ne sont pas repris.

Corrige deux reglages herites : timescaledb.max_background_workers etait
a 4 pour quatre bases, si bien que les ordonnanceurs prenaient toutes les
places et qu'aucun travail de fond ne pouvait s'executer — les agregats
continus auraient cesse de se rafraichir sans que l'ecran le montre. Et
la sonde de sante inscrivait un FATAL au journal toutes les 30 secondes
parce que docker exec entre en tant que root.

La sauvegarde tourne desormais sous root : joindre la base demande
docker exec, et mettre le compte postgres dans le groupe docker
reviendrait a lui donner l'equivalent de root. Elle interroge le
catalogue au lieu d'une liste figee, ecrit son etat dans un fichier
relevable, et un second script verifie que chaque vidage est lisible.
Le repertoire passe en 750 root:root, les vidages en 0640.

Verifie : 16 essais d'acces depuis la boucle locale et depuis l'adresse
reseau, pare-feu eprouve depuis un poste hors du /24, empreintes md5 du
schema ops identiques avant et apres reprise, restauration a cote des
trois bases sans erreur, epreuve de redemarrage passee.

Refs docs/POSTGRESQL.md
Tous les gestes du manuel ont change de forme : plus de systemctl, plus de
sudo -u postgres, plus de fichier dans /var/log/postgresql. Aucun n'a
change de sens. Les commandes ont toutes ete rejouees sur le serveur, y
compris les contre-epreuves.

POSTGRESQL.md decrit l'etat constate au 2 septembre : l'image et ses
versions epinglees, pourquoi une image maison et pourquoi le reseau hote,
l'emplacement de la configuration et le fait que le postgresql.conf du
volume n'est pas lu, les quatre roles et les trois bases, les trois
couches d'acces reseau qui disent toutes 10.105.200.0/24, la sauvegarde,
et le cluster natif conserve comme retour arriere derriere trois verrous.

Consigne aussi ce que la version precedente ne disait pas : le schema ops
d'enervision_preprod porte de vraies donnees — le socle
d'authentification de l'API, quatre tables. C'est le seul contenu
applicatif migre, et sa reprise a ete verifiee par empreinte.

Le tableau des ecarts est repris : cinq sont fermes par la migration
(l'ouverture a 0.0.0.0/0, les droits du repertoire de sauvegarde, le
conf.d imbrique, les bases residuelles, la ligne en doublon). Trois sont
nouveaux : le conflit entre la suppression du role keycloak et
EXIGENCES-collectives.md, qui reste a arbitrer en groupe et pour lequel
ce document ne touche pas au fichier collectif ; le fait qu'un port
publie par Docker echappe a UFW, qui vaut pour toute la pile Compose a
venir ; et le cluster natif a purger apres la demonstration.

L'ecart n°2 est requalifie et il est le plus urgent : les quatre mots de
passe EN SERVICE figurent en clair dans /var/lib/postgresql/.psql_history
et /root/.bash_history, lisibles par quiconque a sudo. Une purge
chirurgicale est prete sur le serveur mais non executee, et purger ne
defait pas la fuite — la rotation des quatre secrets reste a faire.

Le manuel gagne un raccourci qui evite les deux pieges les plus couteux
(l'oubli de -e PGPORT=5433 et de -u postgres), un arbre de decision refait
sur les messages d'erreur reels, une section sur le pare-feu et son
epreuve depuis l'exterieur, et trois pieges mesures pendant la migration :
sudo ne couvre pas la redirection du shell, docker cp depose un fichier
que postgres ne peut pas lire, et pg_restore -l /dev/stdin ne fonctionne
pas a travers docker exec.

Refs infra/compose/postgres/
infra: ferme l'acces distant a PostgreSQL et passe par un tunnel SSH
All checks were successful
Intégration / Qualité du code Python (pull_request) Successful in 12s
Intégration / Tests unitaires et couverture (pull_request) Successful in 13s
Intégration / Dépendances du tableau de bord (pull_request) Successful in 5s
Intégration / Images épinglées par version (pull_request) Successful in 3s
Intégration / Aucun secret commité (pull_request) Successful in 3s
Intégration / Dépendances Python et inventaire applicatif (pull_request) Successful in 3m39s
ecbd9aaa0b
Applique la decision retenue au ticket #65, relu apres coup : le service
n'ecoute plus que sur 127.0.0.1, et les postes du groupe passent par un
tunnel SSH sur le port 22, deja ouvert et deja authentifie par cle.

L'etat precedent — ouverture a 10.105.200.0/24 avec pare-feu resserre —
etait l'option 2 de ce ticket, celle qu'il ecarte explicitement : le
reseau 10.105.200.0/24 est partage avec les quatre autres groupes de la
salle, et une liste d'adresses demande un entretien manuel a chaque bail
DHCP. L'option 1 ne depend d'aucune liste et tient l'ENF-14 telle qu'elle
est ecrite. La regle UFW sur 5433 est retiree, il n'y a plus rien a
ouvrir. Les quatre lignes /24 de pg_hba.conf restent en commentaire avec
leurs motifs, et le fichier dit qu'un retour en arriere demanderait DEUX
changements et pas un.

Une connexion arrivant par le tunnel debouche sur 127.0.0.1 : elle
correspond aux lignes deja en place. Il n'a donc pas fallu elargir
pg_hba.conf pour accueillir le tunnel, il a fallu ne pas l'elargir — et
aucun nouveau poste ne demandera de modification cote serveur.

Corrige au passage un piege trouve en appliquant ce changement : la
configuration etait montee fichier par fichier, et un montage de fichier
ne suit pas un remplacement par renommage. C'est la facon de travailler
de rsync, mv, sed -i et de la plupart des editeurs : l'hote recoit un
nouvel inode et le conteneur continue de lire l'ancien (mesure : hote
3670044, conteneur 3569844 pour le meme chemin). pg_reload_conf() aurait
annonce un succes sans rien changer. La pile monte desormais le
repertoire conf/ en entier, sur /etc/enervision, et le suivi du
renommage est verifie.

Verifie depuis un poste hors de la machine, en interrogeant le decideur
pg_hba par le protocole PostgreSQL : injoignable en direct ; par le
tunnel, les quatre roles autorises sur leur base et le superutilisateur
refuse ; chaque croisement de base refuse ; injoignable des le tunnel
referme. Et 16 essais sur 16 depuis la boucle locale du serveur.

Closes #80
docs: consigne la purge du cluster natif et corrige trois imprecisions
All checks were successful
Intégration / Qualité du code Python (pull_request) Successful in 11s
Intégration / Tests unitaires et couverture (pull_request) Successful in 11s
Intégration / Dépendances du tableau de bord (pull_request) Successful in 4s
Intégration / Images épinglées par version (pull_request) Successful in 2s
Intégration / Aucun secret commité (pull_request) Successful in 2s
Intégration / Dépendances Python et inventaire applicatif (pull_request) Successful in 3m37s
5af3c30b5e
Purge du cluster natif autorisee le 2 septembre 2026, et volontairement
differee : on ne retire pas le retour arriere le jour de la bascule. Le
document porte desormais sa sequence, ses trois conditions de
declenchement et ce qu'elle coute.

La sequence evite deux erreurs que le reflexe dicterait. Elle ne
redemarre PAS le natif pour en prendre un dernier vidage : cela
echouerait, le cluster etant regle sur le port 5433 que le conteneur
occupe, et ce serait inutile puisqu'il est gele depuis la bascule et que
les vidages d'avant migration couvrent ses cinq bases, ses roles et
leurs empreintes — verifies lisibles. Et elle passe par
`pg_dropcluster`, qui emporte configuration et donnees d'un seul geste
en refusant d'agir sur un cluster qui tourne, plutot que par un rm -rf.

La purge fermera aussi l'ecart n°7, le conf.d/conf.d imbrique partant
avec /etc/postgresql/17, et la moitie de l'ecart n°2, l'historique psql
partant avec /var/lib/postgresql. Ce qu'elle coute est ecrit aussi :
plus aucun client PostgreSQL sur l'hote, donc une base injoignable ET
inexaminable en cas de panne de Docker, et plus aucun retour arriere au
sens strict — d'ou la condition d'une restauration deja eprouvee.

Trois imprecisions corrigees apres confrontation de chaque affirmation
chiffree du document a l'etat reel du serveur :

- la taille du volume passe de « 80 Mo » a « ~80 Mo », un chiffre exact
  derivant a chaque ecriture ;
- les deux racines de configuration sont expliquees. La configuration est
  sous /etc/enervision et le certificat sous /etc/postgresql/tls, parce
  que ce sont deux regimes de droits distincts : conf/ est lisible par
  tous, la cle privee est en 0600 pour l'uid 999 seul. Les imbriquer
  ferait dependre les droits de la cle de ceux du repertoire de
  configuration ;
- l'ecart n°8 nomme les paquets reellement installes, dont
  postgresql-common, et la taille reelle du repertoire de donnees.

Tout le reste concordait : versions, bornes du conteneur, chemins,
dimensionnement, locales, bases, roles, droits de grafana, contenu du
schema ops, droits de la sauvegarde, verrous du natif, absence de regle
UFW, echeance du certificat.

Refs #80
Author
Member

Ajout : 5af3c30 — purge du cluster natif consignée, et audit de la doc

Deux choses dans ce commit, toutes deux documentaires.

1. La purge du cluster natif est autorisée, et volontairement différée. Le document porte maintenant sa séquence, ses trois conditions de déclenchement et ce qu'elle coûte. Deux points valent une relecture attentive, parce que le réflexe naturel est faux dans les deux cas :

  • Ne pas redémarrer le natif pour en prendre un dernier vidage. Cela échouerait — il est réglé sur le port 5433 que le conteneur occupe, donc Address already in use — et ce serait inutile : il est gelé depuis la bascule (son pg_hba.conf refuse tout accès réseau) et les vidages d'avant-docker-2026-09-02-0808/ couvrent déjà ses cinq bases, ses rôles et leurs empreintes. Vérifiés lisibles :
$ sudo DEST="$D" /usr/local/bin/verifier-sauvegardes.sh
    enervision_preprod.dump        lisible
    enervision_preprod-FINAL.dump  lisible
    enervision_prod.dump           lisible
    enervision_restore_test.dump   lisible
    keycloak.dump                  lisible
    mlflow.dump                    lisible
  • Passer par pg_dropcluster, pas par rm -rf. L'outil Debian emporte /etc/postgresql/17/main et /var/lib/postgresql/17/main d'un seul geste et refuse d'agir sur un cluster qui tourne. Il faut démasquer l'unité d'abord, sinon il ne peut pas en vérifier l'état — c'est écrit dans la séquence.

La purge fermera aussi l'écart n°7 (le conf.d/conf.d/ imbriqué part avec /etc/postgresql/17) et la moitié de l'écart n°2 (l'historique psql part avec /var/lib/postgresql). Ce qu'elle coûte est écrit aussi, parce que ce n'est pas gratuit : plus aucun psql ni pg_dump sur l'hôte, donc en panne de Docker la base sera injoignable et inexaminable ; pg_lsclusters disparaîtra et le piège correspondant devra être retiré du manuel ; et il n'y aura plus de retour arrière au sens strict. D'où la condition n°3 : une restauration déjà éprouvée depuis un vidage produit par la pile Docker, avant d'y toucher.

2. J'ai confronté chaque affirmation chiffrée de docs/POSTGRESQL.md à l'état réel du serveur plutôt que de la supposer à jour. Tout concordait — versions, bornes du conteneur, chemins de configuration, dimensionnement, locales, bases, rôles, droits de grafana, contenu du schéma ops, droits de la sauvegarde, verrous du natif, absence de règle UFW, échéance du certificat — sauf trois points, corrigés :

Ce qui était écrit Ce qui est vrai
Volume « 80 Mo » 81 Mo au relevé. Remplacé par « ~80 Mo » : un chiffre exact dérive à chaque écriture et périme le document tout seul
Rien sur les deux racines de configuration La configuration est sous /etc/enervision, le certificat sous /etc/postgresql/tls. C'est délibéré et c'est maintenant expliqué : deux régimes de droits distincts, conf/ lisible par tous et la clé privée en 0600 pour l'uid 999 seul. Les imbriquer ferait dépendre les droits de la clé de ceux du répertoire de configuration
Écart n°8 : « paquets postgresql-17, postgresql-16 et clients » postgresql-common et postgresql-client-common sont aussi installés, et le répertoire de données pèse 99 Mo. Les deux figurent désormais dans l'écart et dans la séquence

Le document ajoute la commande qui donne les six chemins réellement appliqués, pour que la question « où vit la configuration » se lise au lieu de se déduire.

Ce qui n'a pas changé dans cette demande de fusion

Le service tourne exactement comme décrit plus haut : ev-postgres en réseau hôte, écoute sur 127.0.0.1:5433 seule, 4 rôles, 3 bases, accès distant par tunnel SSH. Aucune modification sur le serveur dans ce commit — il ne touche que docs/.

### Ajout : `5af3c30` — purge du cluster natif consignée, et audit de la doc Deux choses dans ce commit, toutes deux documentaires. **1. La purge du cluster natif est autorisée, et volontairement différée.** Le document porte maintenant sa séquence, ses trois conditions de déclenchement et ce qu'elle coûte. Deux points valent une relecture attentive, parce que le réflexe naturel est faux dans les deux cas : - **Ne pas redémarrer le natif pour en prendre un dernier vidage.** Cela échouerait — il est réglé sur le port 5433 que le conteneur occupe, donc `Address already in use` — et ce serait inutile : il est gelé depuis la bascule (son `pg_hba.conf` refuse tout accès réseau) et les vidages d'`avant-docker-2026-09-02-0808/` couvrent déjà ses cinq bases, ses rôles et leurs empreintes. Vérifiés lisibles : ``` $ sudo DEST="$D" /usr/local/bin/verifier-sauvegardes.sh enervision_preprod.dump lisible enervision_preprod-FINAL.dump lisible enervision_prod.dump lisible enervision_restore_test.dump lisible keycloak.dump lisible mlflow.dump lisible ``` - **Passer par `pg_dropcluster`, pas par `rm -rf`.** L'outil Debian emporte `/etc/postgresql/17/main` et `/var/lib/postgresql/17/main` d'un seul geste et refuse d'agir sur un cluster qui tourne. Il faut démasquer l'unité d'abord, sinon il ne peut pas en vérifier l'état — c'est écrit dans la séquence. La purge fermera aussi l'écart n°7 (le `conf.d/conf.d/` imbriqué part avec `/etc/postgresql/17`) et la moitié de l'écart n°2 (l'historique `psql` part avec `/var/lib/postgresql`). Ce qu'elle coûte est écrit aussi, parce que ce n'est pas gratuit : plus aucun `psql` ni `pg_dump` sur l'hôte, donc en panne de Docker la base sera injoignable **et** inexaminable ; `pg_lsclusters` disparaîtra et le piège correspondant devra être retiré du manuel ; et il n'y aura plus de retour arrière au sens strict. D'où la condition n°3 : **une restauration déjà éprouvée depuis un vidage produit par la pile Docker**, avant d'y toucher. **2. J'ai confronté chaque affirmation chiffrée de `docs/POSTGRESQL.md` à l'état réel du serveur** plutôt que de la supposer à jour. Tout concordait — versions, bornes du conteneur, chemins de configuration, dimensionnement, locales, bases, rôles, droits de `grafana`, contenu du schéma `ops`, droits de la sauvegarde, verrous du natif, absence de règle UFW, échéance du certificat — sauf trois points, corrigés : | Ce qui était écrit | Ce qui est vrai | |---|---| | Volume « 80 Mo » | 81 Mo au relevé. Remplacé par « ~80 Mo » : un chiffre exact dérive à chaque écriture et périme le document tout seul | | Rien sur les deux racines de configuration | La configuration est sous `/etc/enervision`, le certificat sous `/etc/postgresql/tls`. C'est délibéré et c'est maintenant expliqué : deux régimes de droits distincts, `conf/` lisible par tous et la clé privée en `0600` pour l'uid 999 seul. Les imbriquer ferait dépendre les droits de la clé de ceux du répertoire de configuration | | Écart n°8 : « paquets `postgresql-17`, `postgresql-16` et clients » | `postgresql-common` et `postgresql-client-common` sont aussi installés, et le répertoire de données pèse 99 Mo. Les deux figurent désormais dans l'écart et dans la séquence | Le document ajoute la commande qui donne les six chemins réellement appliqués, pour que la question « où vit la configuration » se lise au lieu de se déduire. ### Ce qui n'a pas changé dans cette demande de fusion Le service tourne exactement comme décrit plus haut : `ev-postgres` en réseau hôte, écoute sur `127.0.0.1:5433` seule, 4 rôles, 3 bases, accès distant par tunnel SSH. Aucune modification sur le serveur dans ce commit — il ne touche que `docs/`.
lenaic left a comment

Vérifié depuis mon poste : le port 5433 refuse la connexion. listen_addresses est sur la boucle locale, pg_hba n'autorise plus que 127.0.0.1 paire par paire, et host all postgres all reject en tête ferme le superutilisateur au réseau. Le #65 est réglé par cette demande.

Un point de coordination, et il n'est pas de ton fait. Ton README place la pile dans /opt/enervision/postgres/, et c'est bien là qu'elle tourne. Or le rôle app d'Ansible, dans la #61 de Gabriel, clone le dépôt dans /opt/enervision et lance les composes depuis /opt/enervision/infra/compose. Les deux vont se percuter : soit le clone écrase ton dossier, soit la même pile existe à deux endroits.

À trancher entre vous avant que le rôle app soit implémenté. Le plus simple est que la pile ne vive qu'à un seul endroit, celui du dépôt cloné, et que le README pointe dessus.

Sur l'écart n°13, tu as raison et la correction est à moi. Le document collectif annonce toujours Keycloak comme arbitré le 31 août, alors que la décision réelle est un jeton émis par notre API, Keycloak seulement si le temps le permet. Ce n'est pas ta demande qui contredit la référence, c'est la référence qui n'a pas été mise à jour. Je m'en occupe.

Vérifié depuis mon poste : le port 5433 refuse la connexion. `listen_addresses` est sur la boucle locale, `pg_hba` n'autorise plus que 127.0.0.1 paire par paire, et `host all postgres all reject` en tête ferme le superutilisateur au réseau. Le #65 est réglé par cette demande. Un point de coordination, et il n'est pas de ton fait. Ton README place la pile dans `/opt/enervision/postgres/`, et c'est bien là qu'elle tourne. Or le rôle `app` d'Ansible, dans la #61 de Gabriel, clone le dépôt dans `/opt/enervision` et lance les composes depuis `/opt/enervision/infra/compose`. Les deux vont se percuter : soit le clone écrase ton dossier, soit la même pile existe à deux endroits. À trancher entre vous avant que le rôle `app` soit implémenté. Le plus simple est que la pile ne vive qu'à un seul endroit, celui du dépôt cloné, et que le README pointe dessus. Sur l'écart n°13, tu as raison et la correction est à moi. Le document collectif annonce toujours Keycloak comme arbitré le 31 août, alors que la décision réelle est un jeton émis par notre API, Keycloak seulement si le temps le permet. Ce n'est pas ta demande qui contredit la référence, c'est la référence qui n'a pas été mise à jour. Je m'en occupe.
lenaic requested review from lenaic 2026-09-02 11:22:40 +00:00
lenaic approved these changes 2026-09-02 11:22:57 +00:00
lenaic merged commit 323d29defe into develop 2026-09-02 11:23:08 +00:00
lenaic deleted branch olivier/80-postgresql-docker 2026-09-02 11:23:08 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
g2/enervision!81
No description provided.