Aller au contenu principal

Haute disponibilité de la plateforme (addendum 20.4)

Modèle​

Chaque cluster et chaque service portent ownership_mode (PLATFORM_MANAGED, CLIENT_MANAGED) et deployment_mode (SINGLE_HOST, NODE_GROUP, K8S_EXISTING, K8S_MANAGED, MULTI_CLUSTER, EXTERNAL_MANAGED), migration server/sql/V050__availability.sql. Un groupe HA (nm.ha_groups) déclare desired_replicas, fault_domains, ha_mode, failover_policy, un profil standalone, ha (2 réplicas, 2 domaines) ou critical-ha (3 réplicas, 3 domaines) et des cibles RTO et RPO fournies par le client (vides par défaut).

Statut ha_validation_status : HA_CONFIGURED tant qu'aucun test de bascule enregistré (nm.ha_test_runs, statut passed) n'est postérieur à la dernière modification du groupe ; HA_VALIDATED uniquement après un tel test. Un membre sans heartbeat récent (90 s) est unknown, jamais sain : le groupe passe DEGRADED. MULTI_CLUSTER est marqué non pris en charge (aucun second site ici).

Routes​

GET /api/v1/ha-modes, POST /api/v1/ha-groups, GET /api/v1/ha-groups, GET /api/v1/ha-groups/{id}, POST /api/v1/ha-groups/{id}/test-failover, GET /api/v1/ha-groups/{id}/operations. Authentifiées, autorisées (cluster:manage, services:operate, cluster:read), auditées. Une action destructive sur CLIENT_MANAGED ou EXTERNAL_MANAGED est refusée (409 ha_client_managed_refused, audit denied). Base ou piste d'audit indisponible : refus (503 ha_fail_closed).

Opérations exclusives​

nm.exclusive_leases : un bail par portée (install, upgrade, failover, restore), pris par INSERT ... ON CONFLICT atomique, jeton de fencing croissant (nm.fencing_token_seq). Le résultat d'un test est refusé si son jeton n'est plus valide. Testé avec deux processus concurrents.

Preuves locales mesurées (binaires officiels, sans Docker)​

ComposantScénarioRTO mesuréPerte mesurée
PostgreSQL 16.2primaire tué (-m immediate), réplica promu par pg_ctl promote, ancien primaire clôturé (pg_rewind, standby, écriture refusée), réintégration0,215 s0 ligne sur 2000
Patroni 4.0.6 + etcd 3.5.21leader tué (Patroni et PostgreSQL), élection, ancien leader réintégré en lecture seule19,878 s (ttl 10 s)0 ligne sur 500
Redis 7.0.15 Sentinel1 primaire, 2 réplicas, 3 sentinelles, primaire tué, client résolu via SENTINEL get-master-addr-by-name2,223 s0 clé sur 2000
NATS 2.15.2 JetStreamflux R3, leader tué, publication et consommation reprises, doublon refusé (Nats-Msg-Id), 10 relivraisons dédupliquées5,569 s0 message sur 100

La réplication PostgreSQL et Redis est asynchrone : une perte nulle n'est pas garantie, seulement mesurée nulle dans ces exécutions. Ces chiffres valent pour ce poste local et ne remplacent pas les cibles du client.

Kubernetes (configuré seulement)​

deploy/helm/enterprise-ai/values-ha.yaml : 3 réplicas minimum, PDB minAvailable: 2, répartition sur hostname et zone, anti-affinité requise par nœud, sondes, arrêt gracieux 60 s. Le manifeste CloudNativePG est validé hors ligne par kubeconform contre le schéma CRD épinglé (tools/ha/fetch.sh, sha256). Aucune bascule n'a été exécutée sur un cluster : BLOQUÉ faute de cluster de test (le contexte disponible est la production). Temporal est modélisé HA_CONFIGURED seulement. Perte d'une salle LiveKit : interruption de session puis reconnexion du client, aucun transfert transparent.

Services du catalogue​

Ce que chaque service du catalogue sait fournir en haute disponibilité (cluster, bascule, quorum, prérequis, documentation officielle, topologie par mode) est déclaré dans son bloc high_availability, servi par GET /api/v1/catalog/high-availability et résumé dans la matrice générée fr/high-availability.md.

Contrôles d'installation et supervision​

Avant une installation (POST /clusters/{cid}/services, POST /clusters/{cid}/blueprint/apply) et avant un changement de topologie d'un service installé (POST .../services/{s}/components ajoute des instances, DELETE .../services/{s}/components/{c}/hosts/{h} en retire une), server/src/navin_manager/services/ha_checks.py contrôle le mode choisi sur les hôtes choisis : ha.distinct_hosts, ha.fault_domains (label zone de l'hôte, sinon sa baie, sinon inconnu), ha.quorum, ha.prerequisites (un prérequis ha: true lié à un service du catalogue en mode autonome échoue, lié à une connexion externe demande de confirmer sa HA), ha.entrypoint (un point d'entrée répartiteur ou VIP exige la paire HAProxy avec Keepalived du cluster, ou un répartiteur externe déclaré), ha.peer_tls, ha.readiness, et ha.not_ha (info) pour un mode autonome. POST .../services/validate et les réponses d'installation renvoient ces vues sous ha.

  • Un fail refuse la demande : 409 ha_requirements_not_met, details.checks.
  • Un warn exige ha_acknowledged: true dans le corps (paramètre de requête sur le DELETE), sinon 409 ha_acknowledgement_required ; l'acquittement est inscrit dans l'événement d'audit. Les corps sans avertissement restent valides tels quels. Un changement de topologie ne compte que les contrôles qui se dégradent par rapport au placement actuel.
  • Les modes d'exploitation (POST .../services/{s}/mode : drainage, arrêt d'urgence, suspension de sécurité...) ne sont jamais refusés pour des raisons HA ; leur réponse porte un impact ha informatif.
  • Le tableau de bord d'un service renvoie ha_status (niveau, instances saines, état du quorum ok, at_risk, lost, état healthy, degraded, quorum_lost ou standalone, docs, commande d'état de la grappe). La console l'affiche sur la fiche du service avec un bouton qui lance la commande healthcheck.cluster_status déclarée. La page Services regroupe les groupes HA, le quorum, les tests de bascule et la capacité du catalogue de chaque service dans son action de ligne « Haute disponibilité (HA) » ; la matrice du catalogue et l'architecture cible sont dans fr/high-availability.md (les pages /availability et /high-availability redirigent vers Services).

Tests​

server/tests/test_availability_api.py, test_availability_local_ha.py, test_availability_helm.py, ui/src/test/services-consoles-ha.test.tsx. Mesures écrites dans /tmp/ha-evidence/*.json.