Set-OPS-Public/roles/serveur_postgresql/tasks/main.yml

358 lines
12 KiB
YAML
Raw Normal View History

---
- name: Installer PostgreSQL
ansible.builtin.apt:
name: "{{ serveur_postgresql_packages }}"
state: present
update_cache: true
cache_valid_time: 3600
- name: Lister les clusters PostgreSQL installes
ansible.builtin.find:
paths: /etc/postgresql
file_type: directory
recurse: false
register: serveur_postgresql_clusters
- name: Determiner la version du cluster
ansible.builtin.set_fact:
serveur_postgresql_version_effective: >-
{{ serveur_postgresql_version
or (serveur_postgresql_clusters.files | map(attribute='path') | map('basename')
| select('match', '^[0-9]+$') | sort | last | default('')) }}
- name: Refuser si aucun cluster PostgreSQL n'est present
ansible.builtin.assert:
that:
- serveur_postgresql_version_effective | length > 0
fail_msg: "Aucun cluster PostgreSQL trouve sous /etc/postgresql."
- name: Definir le repertoire de configuration du cluster
ansible.builtin.set_fact:
serveur_postgresql_conf_dir: "/etc/postgresql/{{ serveur_postgresql_version_effective }}/main"
- name: Assurer le repertoire conf.d
ansible.builtin.file:
path: "{{ serveur_postgresql_conf_dir }}/conf.d"
state: directory
owner: postgres
group: postgres
mode: "0755"
# --- TLS : synchroniser le cert step_ca AVANT d'activer ssl (ordre critique) ---
- name: TLS — assurer le repertoire des certificats (postgres)
ansible.builtin.file:
path: "{{ serveur_postgresql_tls_dir }}"
state: directory
owner: postgres
group: postgres
mode: "0700"
when: serveur_postgresql_tls_actif | bool
- name: TLS — deployer le script de synchronisation du certificat
ansible.builtin.template:
src: setops-postgresql-cert-sync.sh.j2
dest: /usr/local/sbin/setops-postgresql-cert-sync
owner: root
group: root
mode: "0755"
when: serveur_postgresql_tls_actif | bool
- name: TLS — deployer les unites de synchronisation (service + path)
ansible.builtin.template:
src: "{{ item.s }}"
dest: "{{ item.d }}"
owner: root
group: root
mode: "0644"
loop:
- { s: "setops-postgresql-cert-sync.service.j2", d: "/etc/systemd/system/setops-postgresql-cert-sync.service" }
- { s: "setops-postgresql-cert-sync.path.j2", d: "/etc/systemd/system/setops-postgresql-cert-sync.path" }
loop_control:
label: "{{ item.d | basename }}"
when: serveur_postgresql_tls_actif | bool
- name: TLS — activer la surveillance du certificat (path)
ansible.builtin.systemd:
name: setops-postgresql-cert-sync.path
enabled: true
state: started
daemon_reload: true
when:
- serveur_postgresql_tls_actif | bool
- not ansible_check_mode
- name: TLS — synchroniser le certificat maintenant
ansible.builtin.command: /usr/local/sbin/setops-postgresql-cert-sync
changed_when: false
when:
- serveur_postgresql_tls_actif | bool
- not ansible_check_mode
- name: TLS — verifier la presence du certificat synchronise
ansible.builtin.stat:
path: "{{ serveur_postgresql_tls_dir }}/{{ item }}"
register: serveur_postgresql_tls_stat
loop:
- server.crt
- server.key
when:
- serveur_postgresql_tls_actif | bool
- not ansible_check_mode
- name: TLS — garde-fou, refuser d'activer ssl sans certificat en place
ansible.builtin.assert:
that:
- item.stat.exists
fail_msg: "Certificat PG absent ({{ item.item }}) — ssl non active pour ne rien casser."
loop: "{{ serveur_postgresql_tls_stat.results | default([]) }}"
loop_control:
label: "{{ item.item }}"
when:
- serveur_postgresql_tls_actif | bool
- not ansible_check_mode
- name: Deployer la configuration Set-OPS
ansible.builtin.template:
src: 99-setops.conf.j2
dest: "{{ serveur_postgresql_conf_dir }}/conf.d/99-setops.conf"
owner: postgres
group: postgres
mode: "0640"
notify: Redemarrer PostgreSQL
- name: Deployer pg_hba.conf
ansible.builtin.template:
src: pg_hba.conf.j2
dest: "{{ serveur_postgresql_conf_dir }}/pg_hba.conf"
owner: postgres
group: postgres
mode: "0640"
notify: Recharger PostgreSQL
- name: Activer et demarrer PostgreSQL
when: not ansible_check_mode
ansible.builtin.systemd:
name: "{{ serveur_postgresql_service_name }}"
enabled: true
state: started
- name: Appliquer la configuration en attente avant le provisioning
ansible.builtin.meta: flush_handlers
- name: Creer les comptes applicatifs PostgreSQL
community.postgresql.postgresql_user:
name: "{{ item.nom }}"
password: "{{ item.mot_de_passe }}"
role_attr_flags: "{{ item.attributs | default('LOGIN') }}"
state: present
become: true
become_user: postgres
loop: "{{ serveur_postgresql_comptes }}"
loop_control:
label: "{{ item.nom }}"
no_log: true
when: serveur_postgresql_comptes | length > 0
- name: Creer les bases applicatives PostgreSQL
community.postgresql.postgresql_db:
name: "{{ item.nom }}"
owner: "{{ item.proprietaire | default(item.nom) }}"
encoding: "{{ item.encodage | default('UTF8') }}"
state: present
become: true
become_user: postgres
loop: "{{ serveur_postgresql_bases }}"
loop_control:
label: "{{ item.nom }}"
when: serveur_postgresql_bases | length > 0
# --- Provisioning depuis le registre partage instance/plan/bases-donnees.yml ---
- name: Charger le registre des bases applicatives
ansible.builtin.include_vars:
file: "{{ setops_plan_dir }}/bases-donnees.yml"
failed_when: false
- name: Determiner les serveurs BD nommes heberges par ce groupe
ansible.builtin.set_fact:
serveur_postgresql_noms_bd: >-
{{ (serveurs_bd | default({})) | dict2items
| selectattr('value.groupe', 'defined')
| selectattr('value.groupe', 'equalto', serveur_postgresql_groupe)
| map(attribute='key') | list }}
- name: Selectionner les bases hebergees par ce serveur
ansible.builtin.set_fact:
serveur_postgresql_registre: >-
{{ (bases_donnees | default({})) | dict2items
| selectattr('value.serveur', 'defined')
| selectattr('value.serveur', 'in', serveur_postgresql_noms_bd)
| list }}
- name: Creer les comptes proprietaires (registre)
community.postgresql.postgresql_user:
name: "{{ item.value.proprietaire }}"
password: "{{ lookup('vars', item.value.secret) }}"
role_attr_flags: "LOGIN"
state: present
become: true
become_user: postgres
loop: "{{ serveur_postgresql_registre }}"
loop_control:
label: "{{ item.value.proprietaire }}"
no_log: true
when: serveur_postgresql_registre | length > 0
- name: Creer les bases applicatives (registre)
community.postgresql.postgresql_db:
name: "{{ item.value.base }}"
owner: "{{ item.value.proprietaire }}"
encoding: UTF8
state: present
become: true
become_user: postgres
loop: "{{ serveur_postgresql_registre }}"
loop_control:
label: "{{ item.value.base }}"
when: serveur_postgresql_registre | length > 0
sondes : les cinq qui manquaient le plus (autorite, base, annuaire, zones, edge) CORRECTION DE COMPTE D ABORD. J avais annonce cinq groupes sans sonde. La mesure en donne VINGT-SEPT : j avais compte ceux que j avais en tete, pas ceux que le depot contient. Il en reste vingt-deux. LES CINQ, PAR ORDRE DE DEGAT SILENCIEUX. autorite (step_ca) — la plus urgente : nos certificats vivent 24 h, une AC muette ne casse rien aujourd hui et casse TOUT demain, d un coup, sur les 21 machines. Elle surveille aussi l expiration de la RACINE, que personne ne regarde parce qu elle vit des annees (relevee a 3642 jours). base (postgresql) — une VRAIE requete, pas pg_isready : celui-ci dit que le port repond, pas que la base sert. Plus le compte des connexions : a saturation, chaque application tombe sans que la base ait l air morte. annuaire (openldap) — elle COMPTE les entrees. Un annuaire vide repond success a tout : le mensonge des sauvegardes vides, vert et sans contenu. zones (powerdns) — un autoritatif sans zone repond NXDOMAIN a tout, ce qui se lit comme « ce nom n existe pas ». edge (nginx) — elle valide la configuration SUR DISQUE : nginx sert la derniere valide, et une configuration cassee ne se voit qu au prochain demarrage, souvent des mois plus tard. Les cinq eprouvees vertes sur le sain puis rouges PAR PARAMETRE, sans toucher a un service. Etat : 20 sondes sur 19 roles, 23 services distincts, 70 instances, 67 au vert. Les trois autres sont connues : deux sauvegardes sans donnee a emporter, et Loki en delai de stabilisation apres redeploiement. make prouver : CONFORME, 64 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 03:47:03 -04:00
# --- LES BASES D'UNE INCARNATION PRECEDENTE (2026-09-30) ----------------------------
#
# ICI, et pas plus tard : les bases viennent d'etre creees, leurs consommateurs (Keycloak,
# Forgejo...) ne les ont pas encore touchees. Chaque base VIDE recoit sa section du
# `pg_dumpall` d'avant, en une transaction ; une base qui a deja des tables n'est jamais
# ecrasee d'office. Les roles et leurs mots de passe restent ceux de la voute : la section
# d'une base ne les porte pas.
- name: Restauration — quelles bases sont vierges ?
ansible.builtin.command:
argv:
- psql
- -tAX
- -d
- "{{ item }}"
- -c
- >-
select count(*) from information_schema.tables
where table_schema not in ('pg_catalog','information_schema')
become: true
become_user: postgres
loop: "{{ serveur_postgresql_bases_restaurables }}"
register: serveur_postgresql_tables_initiales
changed_when: false
check_mode: false
failed_when: false
- name: Restauration — retenir les bases vierges
ansible.builtin.set_fact:
serveur_postgresql_bases_vierges: >-
{{ serveur_postgresql_tables_initiales.results
| selectattr('rc', 'equalto', 0)
| selectattr('stdout', 'equalto', '0')
| map(attribute='item') | list }}
- name: Restauration — les bases d'une incarnation precedente ?
ansible.builtin.include_role:
name: client_backup
tasks_from: restaurer.yml
vars:
client_backup_restaurer_jeu: serveur_postgresql
client_backup_restaurer_vierge: "{{ serveur_postgresql_bases_vierges | length > 0 }}"
- name: Restauration — rejouer chaque base vierge
changed_when: true
ansible.builtin.command:
argv:
- /usr/local/sbin/setops-restaurer
- base
- --instantane
- "{{ client_backup_restauration.instantane }}"
- "{{ item }}"
loop: "{{ serveur_postgresql_bases_vierges }}"
when: client_backup_restauration.etat == 'a_restaurer'
- name: Restauration — acter les bases remises
ansible.builtin.include_role:
name: client_backup
tasks_from: acter.yml
vars:
client_backup_acter_etat: restaure
when: client_backup_restauration.etat == 'a_restaurer'
sondes : les cinq qui manquaient le plus (autorite, base, annuaire, zones, edge) CORRECTION DE COMPTE D ABORD. J avais annonce cinq groupes sans sonde. La mesure en donne VINGT-SEPT : j avais compte ceux que j avais en tete, pas ceux que le depot contient. Il en reste vingt-deux. LES CINQ, PAR ORDRE DE DEGAT SILENCIEUX. autorite (step_ca) — la plus urgente : nos certificats vivent 24 h, une AC muette ne casse rien aujourd hui et casse TOUT demain, d un coup, sur les 21 machines. Elle surveille aussi l expiration de la RACINE, que personne ne regarde parce qu elle vit des annees (relevee a 3642 jours). base (postgresql) — une VRAIE requete, pas pg_isready : celui-ci dit que le port repond, pas que la base sert. Plus le compte des connexions : a saturation, chaque application tombe sans que la base ait l air morte. annuaire (openldap) — elle COMPTE les entrees. Un annuaire vide repond success a tout : le mensonge des sauvegardes vides, vert et sans contenu. zones (powerdns) — un autoritatif sans zone repond NXDOMAIN a tout, ce qui se lit comme « ce nom n existe pas ». edge (nginx) — elle valide la configuration SUR DISQUE : nginx sert la derniere valide, et une configuration cassee ne se voit qu au prochain demarrage, souvent des mois plus tard. Les cinq eprouvees vertes sur le sain puis rouges PAR PARAMETRE, sans toucher a un service. Etat : 20 sondes sur 19 roles, 23 services distincts, 70 instances, 67 au vert. Les trois autres sont connues : deux sauvegardes sans donnee a emporter, et Loki en delai de stabilisation apres redeploiement. make prouver : CONFORME, 64 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 03:47:03 -04:00
# --- Sonde de supervision (docs/supervision-conception.md) --------------------------
# Le role qui possede la verite depose sa propre sonde ; le porteur (`client_sante`) la
# fait tourner et pousse le verdict, sans savoir ce qu'elle mesure.
- name: Assurer le repertoire des sondes de supervision
ansible.builtin.file:
path: /usr/local/lib/setops/sondes
state: directory
owner: root
group: root
mode: "0755"
- name: Deposer la sonde « base »
ansible.builtin.template:
src: sonde-base.sh.j2
dest: /usr/local/lib/setops/sondes/base.sh
owner: root
group: root
mode: "0750"
# --- L'EXPORTATEUR DE METRIQUES -------------------------------------------------------
#
# CE QU'IL AJOUTE A LA SONDE : la sonde repond « est-ce casse ? » et ne garde aucune
# memoire. L'exportateur publie des SERIES — ce qui derive lentement n'a que le graphe
# pour se faire voir. Voir `meta/metriques.yml` pour ce que le moteur en derive.
- name: Creer le compte de metriques (lecture seule, pg_monitor)
community.postgresql.postgresql_user:
name: "{{ serveur_postgresql_exportateur_utilisateur }}"
password: "{{ serveur_postgresql_exportateur_mot_de_passe }}"
role_attr_flags: "LOGIN"
state: present
become: true
become_user: postgres
no_log: true
when: serveur_postgresql_exportateur_actif | bool
# `pg_monitor` DONNE LES VUES DE STATISTIQUES, ET RIEN D'AUTRE. Pas une ligne de donnee
# applicative. C'est la difference entre superviser une base et l'ouvrir.
- name: Donner a ce compte le role de supervision, et lui seul
community.postgresql.postgresql_membership:
group: pg_monitor
target_role: "{{ serveur_postgresql_exportateur_utilisateur }}"
state: present
become: true
become_user: postgres
when: serveur_postgresql_exportateur_actif | bool
- name: Installer l'exportateur de metriques
ansible.builtin.apt:
name: "{{ serveur_postgresql_exportateur_paquet }}"
state: present
when: serveur_postgresql_exportateur_actif | bool
# LA CHAINE DE CONNEXION NE PASSE PAS PAR LA LIGNE DE COMMANDE. Un `DATA_SOURCE_NAME` en
# argument serait lisible dans `ps` par tout le monde sur la machine ; dans un fichier a
# 0600, il ne l'est que par root et le service.
- name: Poser la chaine de connexion de l'exportateur
ansible.builtin.copy:
content: |
# Gere par Set-OPS (role serveur_postgresql). Ne pas editer a la main.
DATA_SOURCE_NAME="postgresql://{{ serveur_postgresql_exportateur_utilisateur
}}:{{ serveur_postgresql_exportateur_mot_de_passe
}}@127.0.0.1:5432/postgres?sslmode=disable"
ARGS="--web.listen-address=:{{ serveur_postgresql_exportateur_port }}"
dest: /etc/default/prometheus-postgres-exporter
owner: root
group: root
mode: "0600"
no_log: true
notify: Redemarrer l exportateur de metriques
when: serveur_postgresql_exportateur_actif | bool
- name: Activer et demarrer l'exportateur de metriques
ansible.builtin.systemd:
name: "{{ serveur_postgresql_exportateur_service }}"
enabled: true
state: started
when: serveur_postgresql_exportateur_actif | bool