Exploitation (jour 2)
Vue d'ensemble
Vaks PM est une application web de gestion de projet, multi-utilisateurs et auto-hébergée. Une fois en service, la responsabilité quotidienne de son exploitation se ramène à un petit nombre d'activités récurrentes, chacune traitée par une section de ce guide :
- Sauvegarde et restauration : protéger les données contre les erreurs logiques, la corruption et les sinistres, et pouvoir les rétablir. C'est la tâche jour 2 la plus importante, et elle doit être mise en place avant que l'instance ne porte de vraies données.
- Mises à jour : déployer une nouvelle version d'image sans interruption de service.
- Santé : savoir, à tout moment, si l'instance sert correctement le trafic.
- Tenants : ajouter une autre organisation lorsqu'une instance en sert plusieurs.
- Support : rassembler diagnostics et journaux lorsqu'un point demande investigation.
L'architecture compte pour chacune de ces activités. Les services applicatifs, api, web et worker, sont sans état : ils ne détiennent aucune donnée durable et peuvent être démarrés, arrêtés et répliqués librement. Tout l'état durable réside dans trois magasins sous-jacents : PostgreSQL (la base de données relationnelle), Redis (cache et file de jobs) et un stockage objet compatible S3 (Simple Storage Service, les fichiers téléversés). L'exploitation se divise donc nettement entre préoccupations sans état, comme les mises à jour, la mise à l'échelle et la santé, et préoccupations avec état, comme la sauvegarde et la restauration. Voir Architecture pour le tableau complet.
Glossaire
Sigles utilisés dans ce guide, définis une fois ici pour référence.
| Terme | Signification |
|---|---|
| RPO | Recovery Point Objective (objectif de point de reprise) : la quantité maximale de perte de données qu'une organisation est prête à tolérer après un incident, exprimée comme une durée (par exemple « au plus 15 minutes de travail »). Un RPO plus petit signifie des sauvegardes plus fréquentes. |
| RTO | Recovery Time Objective (objectif de temps de reprise) : la durée cible d'une restauration, c'est-à-dire le temps qu'il peut falloir pour remettre le service en route après un incident. Le RPO porte sur combien on perd ; le RTO porte sur combien de temps dure la reprise. |
| PITR | Point-In-Time Recovery (restauration à un instant précis) : restaurer la base de données à un moment choisi (par exemple la seconde précédant une commande malheureuse), plutôt qu'au moment de la dernière sauvegarde complète. |
| WAL | Write-Ahead Log (journal des écritures anticipées) : le journal de PostgreSQL, dans lequel chaque changement est consigné avant que les tables ne soient touchées. L'archivage continu du WAL est ce qui rend la PITR possible. |
| S3 | Simple Storage Service : une interface de stockage objet, d'origine Amazon, devenue un standard de fait de l'industrie. Vaks PM stocke les fichiers téléversés dans un magasin compatible S3 qu'il héberge lui-même. |
| SPA | Single-Page Application (application monopage) : le front-end navigateur (le service web), un ensemble de ressources statiques qui fait tourner l'interface utilisateur dans le navigateur et dialogue avec l'API. |
| JWT | JSON Web Token : un jeton de session signé émis après la connexion ; il porte l'identité de l'utilisateur et du tenant pour que l'API puisse autoriser chaque requête. |
Paliers de sauvegarde
Vaks PM n'impose pas une politique de sauvegarde unique. Il propose un ensemble de paliers, chacun acceptant une quantité différente de perte de données (un RPO différent) à un coût de stockage différent. Une organisation choisit le palier qui correspond à la perte de données qu'elle peut tolérer. Chaque palier repose uniquement sur des outils livrés avec PostgreSQL, donc aucun logiciel de sauvegarde tiers n'est requis.
Le palier base de données est, en production, répliqué sur plusieurs hôtes pour la disponibilité. La réplication n'est pas une sauvegarde : un DELETE erroné, un changement corrupteur ou un rançongiciel se propage à toutes les répliques en quelques millisecondes, donc trois copies saines deviennent trois copies du dommage. Ce sont les vraies sauvegardes, conservées dans le temps avec au moins une copie hors du cluster, qui protègent contre les erreurs logiques. Les paliers ci-dessous fournissent exactement cela.
Les trois paliers sont cumulatifs, chacun s'appuyant sur le précédent. La plupart des organisations démarrent au palier 1 et ne montent que si perdre une journée de travail est inacceptable.
| Palier | Mécanisme | RPO (perte de données max.) | Coût / complexité |
|---|---|---|---|
| Palier 1 Vidage logique | Un instantané SQL compressé (pg_dump) pris chaque nuit et expédié hors cluster. |
~24 heures (depuis le dernier vidage nocturne). | Faible. Un seul fichier autonome par jour ; rétention triviale ; le point de départ recommandé. |
| Palier 2 PITR via archivage WAL | Une sauvegarde de base complète quotidienne (pg_basebackup) plus l'archivage continu du flux WAL. |
~15 minutes, ajustable (fixé par archive_timeout ; une valeur plus petite abaisse le RPO). |
Moyen. Restaure à un instant précis. ~25 Go pour une base de 10 Go sur 7 jours. |
| Palier 2b Incrémentiel (PostgreSQL 17) | Une sauvegarde de base complète hebdomadaire plus des sauvegardes de base incrémentielles quotidiennes (une fonctionnalité native de PostgreSQL 17), toujours avec WAL continu. | ~15 minutes (comme le palier 2). | Moyen+. Même précision de reprise que le palier 2 pour environ un tiers du stockage : à peu près 10 à 12 Go pour la même fenêtre de 7 jours. |
pg_stat_archiver de PostgreSQL. Les procédures palier par palier et la planification sont décrites dans les sections ci-dessous.Dimensionnement
Les chiffres ci-dessous modélisent une instance pour environ 200 utilisateurs (la cible de référence du produit), en supposant à peu près 72 000 écritures en base par jour et une base d'environ 10 Go. Les fichiers téléversés résident dans le stockage objet et ne gonflent ni la base ni le WAL ; ils sont dimensionnés à part (voir Stockage objet).
| Élément | Brut | Compressé | Note |
|---|---|---|---|
| Une sauvegarde de base complète | ~10 Go | ~3 Go | Suit la taille de la base. |
| 7 bases complètes (palier 2) | ~70 Go | ~21 Go | Le coût dominant à cette échelle. |
| WAL par jour | ~2 Go | ~0,5 à 0,7 Go | Les segments forcés, presque vides, se compressent fortement. |
| WAL sur 7 jours | ~14 Go | ~3,5 à 5 Go | Peu coûteux par rapport aux bases complètes. |
| Total palier 2 (7 jours) | s.o. | ~25 Go | Pour une base de 10 Go. |
| Total palier 2b (7 jours) | s.o. | ~10 à 12 Go | Base complète hebdomadaire + incréments quotidiens. |
jours × base_compressée. Palier 2 : jours × (base_compressée + ~0,6 Go WAL/jour). Palier 2b : 1 complète + jours × (incrément + ~0,6 Go). Parce que le WAL est peu coûteux et que les bases complètes sont lourdes, le palier 2b est nettement plus économique à l'échelle de 200 utilisateurs. Toujours provisionner la destination hors cluster avec une marge confortable.Stockage objet
Les fichiers téléversés, y compris les pièces jointes des tâches, le branding de l'organisation, les modèles de documents, les documents de cadrage de projet et les archives d'audit compressées, sont stockés dans le stockage objet compatible S3, hors de PostgreSQL. La base ne contient que des références vers ces fichiers. La conséquence est directe : une restauration limitée à la base perd tous les fichiers téléversés. Le stockage objet doit être sauvegardé comme une préoccupation à part entière.
Deux approches équivalentes conviennent pour sauvegarder le stockage objet :
- Synchroniser les buckets vers une destination hors cluster avec un client S3 standard (par exemple
aws s3 syncpointé sur l'endpoint du magasin). C'est portable et incrémentiel. - Archiver les répertoires de données sur chaque nœud de stockage directement (par exemple un
tardes données sous-jacentes), ce qui est utile quand des copies au niveau du système de fichiers sont préférées.
L'application utilise un petit ensemble fixe de buckets, qui doivent tous être inclus dans la sauvegarde : pièces jointes, branding, modèles de documents, blobs de cadrage de projet et archive d'audit froide. Conserver la sauvegarde du stockage objet sur la même planification et la même rétention que la sauvegarde de la base, afin qu'une restauration rétablisse une paire cohérente : les lignes de la base et les fichiers qu'elles référencent.
Sauvegardes chiffrées
Par défaut, les artefacts de sauvegarde quittent l'hôte en clair. Pour les environnements réglementés, où le chiffrement est requis à la fois au repos et en transit, chaque artefact (le vidage logique, chaque sauvegarde de base et chaque segment WAL) peut être chiffré à la création, avant même d'être expédié hors de l'hôte. Le mécanisme est age, un outil moderne de chiffrement de fichiers, employé de façon asymétrique : l'hôte ne détient que la clé publique (le « destinataire »), de sorte qu'il n'y a rien à voler sur l'hôte, tandis que la clé privée correspondante nécessaire au déchiffrement est conservée en séquestre hors cluster. Chaque artefact chiffré gagne un suffixe .age, et les procédures de restauration déchiffrent avec la clé privée séquestrée comme première étape.
- La clé privée age, qui déchiffre les artefacts de sauvegarde.
- La KEK (Key Encryption Key, clé de chiffrement de clé) de l'application. Vaks PM chiffre les champs sensibles au repos avec une DEK (Data Encryption Key, clé de chiffrement de données) par tenant, elle-même enveloppée par la KEK. Un vidage de base ne contient que les clés enveloppées plus du texte chiffré, donc sans la KEK une instance restaurée fonctionne mais ses secrets chiffrés (secrets multifacteurs, mot de passe SMTP, secrets d'authentification unique et de connecteurs) sont définitivement illisibles.
Restauration
La procédure de restauration dépend du palier auquel la sauvegarde a été prise. Dans tous les cas, si les artefacts étaient chiffrés (un suffixe .age), les déchiffrer d'abord avec la clé privée séquestrée.
| Palier | Comment se déroule une restauration |
|---|---|
| Palier 1 Vidage logique | Vérifier la somme de contrôle du vidage, puis le ré-importer avec pg_restore, dans une base isolée neuve pour un test, ou par-dessus la base en service pour une vraie reprise. Des tables individuelles peuvent être restaurées sélectivement. La base restaurée revient à l'état du dernier vidage nocturne. |
| Palier 2 PITR | Déployer la dernière sauvegarde de base sur la cible, puis configurer PostgreSQL pour rejouer le WAL archivé en avant jusqu'à un instant choisi (par exemple la seconde précédant une commande malheureuse) et s'arrêter là. Une restauration PITR reconstruit le cluster de base de données. Dans une topologie HA, elle s'effectue avec le gestionnaire de cluster arrêté, puis les répliques sont ré-amorcées ensuite. |
| Palier 2b Incrémentiel | Recombiner d'abord la base complète hebdomadaire avec les incréments quotidiens en une seule copie complète (pg_combinebackup), puis suivre le même rejeu WAL à un instant donné que pour le palier 2. |
Mises à jour progressives
Les nouvelles versions d'image sont déployées sans interruption. Parce que les services api, web et worker sont sans état et tournent en plusieurs répliques, l'orchestrateur les remplace une (ou quelques-unes) à la fois pendant que les répliques restantes continuent de servir le trafic, de sorte que les clients ne voient aucune interruption.
| Plateforme | Comment le déploiement est effectué |
|---|---|
| Docker Swarm | Une mise à jour de service progressive demande à Swarm de remplacer les tâches en cours d'un service par la nouvelle image, en respectant le parallélisme et le délai de mise à jour configurés. Les répliques sont renouvelées graduellement ; si une nouvelle tâche échoue à son contrôle de santé, le déploiement peut être suspendu ou annulé. |
| Kubernetes | Un helm upgrade applique la nouvelle image ; la stratégie de mise à jour progressive du Deployment fait monter de nouveaux pods et ne retire les anciens qu'une fois les nouveaux pods déclarés prêts. |
Les changements de schéma de la base sont appliqués automatiquement au démarrage de l'API. Quand une nouvelle version nécessite un changement de schéma, l'api réconcilie le schéma de la base au moment de son amorçage, donc il n'y a pas de commande de migration manuelle séparée à lancer pour une mise à niveau de routine. Combiné au déploiement sans état, cela fait d'une mise à niveau de version une action unique sur l'une ou l'autre plateforme.
Contrôles de santé
Le service api expose deux endpoints HTTP de santé. Les contrôles de santé de l'orchestrateur les appellent, et un système de supervision externe peut aussi les interroger. Tous deux doivent renvoyer une réponse de succès sur une instance saine.
| Endpoint | Indique | Sert à |
|---|---|---|
/health/live liveness | Le processus est vivant et capable de servir des requêtes, c'est-à-dire qu'il n'est ni figé ni en interblocage. | L'orchestrateur redémarre une réplique dont la sonde de vivacité (liveness) échoue. |
/health/ready readiness | La réplique est prête à accepter du trafic : ses dépendances (telles que la base de données) sont joignables. | L'orchestrateur retient le trafic d'une réplique dont la sonde de disponibilité (readiness) échoue (par exemple au démarrage), et n'envoie de requêtes qu'aux répliques prêtes pendant une mise à jour progressive. |
La distinction compte pendant un déploiement : une réplique fraîchement démarrée peut être vivante mais pas encore prête. Ne router le trafic que vers les répliques prêtes est ce qui empêche la mise à jour progressive de perdre des requêtes. Si une réplique est redémarrée de façon répétée, une sonde de vivacité en échec est le premier signal ; si le trafic ne parvient pas aux nouvelles répliques après une mise à jour, vérifier la sonde de disponibilité et la dépendance sur laquelle elle se prononce.
Ajouter une organisation
Vaks PM peut fonctionner selon deux modes de tenancy. En mode dédié, l'instance sert une seule organisation, le cas on-premise typique. En mode pooled (mutualisé), une instance sert plusieurs organisations, chacune isolée dans sa propre base et atteinte sous son propre nom d'hôte.
Ajouter une organisation en mode pooled est une opération additive et isolée : elle provisionne une nouvelle base pour le tenant et route un nouveau nom d'hôte vers elle, sans perturber les tenants existants. Il n'y a aucune interruption pour les organisations déjà présentes sur l'instance, et leurs données ne sont jamais touchées. En résumé, provisionner un tenant implique de :
- Créer la base du tenant (nommée d'après le tenant) et lui appliquer le schéma.
- Router le nom d'hôte public du tenant vers le point d'entrée de l'instance, avec un certificat TLS pour ce nom.
- Amorcer l'organisation et son premier compte administrateur.
Diagnostics et journaux
Quand un point demande investigation, les données de support sont collectées à deux niveaux, reflétant une frontière de sécurité délibérée : l'application tourne comme un conteneur durci et sans état qui écrit ses journaux sur la sortie standard uniquement, n'a aucun accès au démon Docker et est cloisonnée des hôtes. De ce fait, l'application ne peut pas lire les journaux d'infrastructure (ceux du reverse proxy, du cluster de base de données, du cache ou du stockage objet) ; ceux-ci vivent sur les hôtes. Les deux niveaux sont complémentaires.
Niveau 1 : le paquet de diagnostics intégré à l'application
Une personne administratrice d'organisation peut télécharger un paquet de diagnostics en un clic depuis l'espace d'administration (Admin → Sécurité et conformité → Support et diagnostics → Télécharger le paquet). Il est produit par un endpoint réservé aux administrateurs et renvoie un fichier JSON expurgé qui couvre les problèmes au niveau applicatif :
- Santé des dépendances : sondes en direct de la base, de Redis et du stockage objet, plus l'indication que le SMTP (e-mail) est configuré ou non, chacune avec sa latence.
- Files de jobs : la profondeur (en attente / actifs / échoués) des files e-mail, webhook et transfert d'audit.
- Distribution des e-mails : une répartition par statut des envois récents et les derniers échecs avec leur erreur SMTP brute (destinataires masqués), généralement suffisante pour diagnostiquer à elle seule un problème d'e-mail.
- Informations sur l'application : version, version du runtime, durée de fonctionnement, mémoire et mode de tenancy.
- Paramètres effectifs, avec chaque secret masqué.
- Entrées d'audit critiques récentes et les dernières erreurs/avertissements conservés en mémoire par la réplique d'API ayant servi la requête.
Niveau 2 : journaux d'hôte
Les problèmes d'infrastructure exigent des journaux rassemblés sur les hôtes par SSH par une personne opératrice. Sur Docker Swarm, ils se lisent avec des commandes de journaux au niveau du service sur le nœud manager, cadrées à la fenêtre temporelle de l'incident ; sur Kubernetes, l'équivalent est la lecture des journaux de pods. Faire correspondre la source de journaux au symptôme :
| Symptôme | Paquet niveau 1 | Journaux d'hôte à ajouter |
|---|---|---|
| Fonctionnalité cassée, erreurs serveur, données erronées | Signal principal | Journaux des services api (et worker) |
| Échecs de connexion / authentification unique / MFA | Oui (audit + config) | Journaux du service api |
| Erreurs de passerelle, erreurs TLS / certificat | Non couvert | Journaux du reverse proxy (Traefik) |
| Site indisponible, base injoignable, bascule | Oui (sonde DB) | État et journaux du cluster de base de données (Patroni / etcd / HAProxy) |
| E-mails / webhooks non envoyés | Oui (profondeurs de files) | Journaux du worker et de Redis |
| Connecteur de ticketing externe défaillant (ServiceNow, Jira…) : le test échoue, ou la recherche de ticket ne renvoie rien dans la timesheet | Partiel (réglages effectifs) | Journaux du service api — filtrer sur ticketing / servicenow. Le bouton Tester du connecteur affiche aussi l'erreur en direct (401 identifiants invalides, 403 droits Table API insuffisants, délai dépassé, hôte injoignable). |
| Échecs de téléversement / téléchargement des pièces jointes | Oui (sonde S3) | Journaux du stockage objet (Garage / MinIO) |
| Lenteur diffuse / défaillances aléatoires | Oui (mémoire, durée de fonctionnement) | Ressources des hôtes (disque, mémoire) et état des tâches du cluster |
Pour ouvrir une demande de support, télécharger le paquet de niveau 1. Si le problème est lié à l'infrastructure, collecter les journaux d'hôte de niveau 2 correspondants. Noter l'heure approximative de l'incident (avec le fuseau horaire) et ce qui était en cours, puis tout joindre. L'ensemble complet des commandes d'hôte, avec des exemples par service, se trouve dans le guide de support dédié référencé depuis Sécurité.