Français
Mises à jour
Mettre à jour Mankomail, c’est remplacer l’image de l’application et la redémarrer. Le schéma de la base suit tout seul : les migrations s’appliquent au démarrage de l’application. Cette page décrit ce mécanisme, la procédure, et le retour à la version précédente.
Le fonctionnement des migrations
- Elles s’appliquent au démarrage. Quand un processus applicatif démarre, il applique les migrations que contient son image et que la base n’a pas encore, puis commence à servir. Il n’y a pas de commande de migration séparée.
- Sous un verrou. Les migrations s’exécutent sous un verrou consultatif PostgreSQL. Plusieurs processus peuvent démarrer en même temps : un seul migre, les autres attendent, puis constatent qu’il ne reste rien à faire.
- Une transaction par migration. Une migration qui échoue est annulée entièrement, celles d’avant restent appliquées, et le processus s’arrête avec
migration <fichier> faileddans sa sortie d’erreur. - Uniquement vers l’avant. Il n’y a pas de migration « descendante ». Une base migrée par une version plus récente ne peut pas revenir à un schéma plus ancien, sauf en restaurant une sauvegarde.
- Immuables. La base enregistre une empreinte de chaque migration appliquée. Si une image livre une migration dont le contenu diffère de celle déjà appliquée, le processus refuse de démarrer (
was modified after being applied). - Sans limite de durée. Les migrations ne sont pas concernées par
DATABASE_STATEMENT_TIMEOUT_MS: une longue migration sur une grosse base n’est pas annulée.
Vous pouvez les suivre dans les journaux : une ligne applying migration par migration, puis database schema is up to date.
DATABASE_RUN_MIGRATIONS=false désactive les migrations pour un processus : il écrit migrations are disabled et démarre sans migrer. Son /readyz répond alors 503 avec la liste des migrations en attente, jusqu’à ce qu’un autre processus les ait appliquées.
Avant la mise à jour
Faites une sauvegarde, et gardez-la jusqu’à ce que la nouvelle version ait fait ses preuves :
bash./scripts/backup.shVoir Sauvegardes. Le manifeste enregistre la version du schéma : c’est cette sauvegarde que vous restaurerez en cas de retour arrière.
Notez la version qui tourne aujourd’hui, d’après
/healthz:bashcurl -s localhost:3000/healthzGardez un moyen de revenir à l’image actuelle : le commit ou le tag à partir duquel vous l’avez construite, ou la référence de l’image publiée.
Mettre à jour une instance construite depuis les sources
Récupérez la nouvelle version du code source et placez-vous dessus, par exemple sur un tag :
bashgit fetch --tags git checkout <version>Renseignez
APP_VERSION=<version>dans.env. Elle devient la version affichée par/healthzet sur chaque ligne de journal, et celle qu’enregistrent les manifestes de sauvegarde.Construisez l’image et recréez les conteneurs :
bashdocker compose -f compose.reference.yaml up -d --build
Lisez les modifications de compose.reference.yaml et de .env.example entre les deux versions : une nouvelle version peut ajouter une variable ou changer un service. Si vous gardez votre propre copie du fichier Compose, reportez-y ces modifications.
Mettre à jour avec une image publiée
Si vous faites tourner une image publiée au lieu de construire la vôtre :
Renseignez dans
.envAPP_IMAGEavec la référence de la nouvelle image, etAPP_VERSIONavec sa version.Récupérez l’image et recréez le conteneur applicatif :
bashdocker compose -f compose.reference.yaml pull app docker compose -f compose.reference.yaml up -d app
Vérifier la mise à jour
bash
docker compose -f compose.reference.yaml logs app | grep -E 'applying migration|schema is up to date|configuration:'
curl -s localhost:3000/healthz
curl -s localhost:3000/readyz- Les journaux montrent les migrations appliquées, puis
database schema is up to date. /healthzrend la nouvelleversion./readyzrépond200avec"status":"ready".- Aucun avertissement
configuration:n’est apparu : une variable renommée ou retirée par la nouvelle version se signalerait ici.
Si le processus s’arrête sur une migration en échec, le conteneur redémarre et échoue de nouveau, en boucle. Lisez l’erreur dans les journaux, puis corrigez sa cause ou revenez en arrière.
Plusieurs processus applicatifs
Quand vous faites tourner des processus api et worker séparés, ou plusieurs répliques :
- Démarrez d’abord un processus de la nouvelle version et attendez que son
/readyzréponde200: les migrations sont alors appliquées. Remplacez ensuite les autres processus. - Des processus qui démarrent ensemble ne risquent rien de toute façon : le verrou ne laisse migrer qu’un seul d’entre eux.
- Pour choisir précisément le processus qui migre, réglez
DATABASE_RUN_MIGRATIONS=falsesur tous les autres. - Ne laissez pas tourner côte à côte des processus de deux versions plus longtemps que le remplacement ne l’exige.
Le travail en cours pendant une mise à jour
Arrêter un processus ne perd aucun travail, car tout l’état vit dans la base.
- À la réception de
SIGTERM, le processus cesse d’accepter des requêtes, arrête son planificateur, puis laisse les tâches de fond qu’il tient se terminer pendant 30 secondes au plus. Les tâches encore en cours au-delà sont abandonnées. - Une tâche abandonnée garde son bail jusqu’à son expiration (60 secondes), puis un processus qui fait tourner le travail de fond (
allouworker) la rend à la file, et elle s’exécute de nouveau. - Les exécutions en attente d’un délai, d’une approbation ou d’un signal sont enregistrées en base. Le redémarrage ne les touche pas.
Docker laisse par défaut 10 secondes à un conteneur pour s’arrêter avant de le tuer. Pour laisser aller au bout l’attente de 30 secondes, augmentez le délai de grâce du service applicatif dans votre fichier Compose :
yaml
services:
app:
stop_grace_period: 40sRevenir en arrière
Comme les migrations ne vont que vers l’avant, revenir en arrière consiste à restaurer la sauvegarde faite avant la mise à jour, avec l’image précédente :
Revenez à la version précédente :
git checkout <version précédente>puis reconstruisez, ou remettez dansAPP_IMAGEl’image précédente. Remettez aussi l’ancienneAPP_VERSION.Restaurez la sauvegarde faite avant la mise à jour :
bash./scripts/restore.sh backups/<sauvegarde faite avant la mise à jour>Le script redémarre l’application avec l’image désormais configurée.
Vérifiez
/healthzet/readyz.
Tout ce qui a été créé entre la sauvegarde et le retour arrière est perdu : exécutions, modifications de workflows, tables, réglages. Les boîtes rattrapent seules leurs fournisseurs.
WARNING
Ne démarrez pas une image plus ancienne sur une base déjà migrée par une version plus récente. L’ancienne image n’applique que les migrations qu’elle connaît et démarre, mais son code ne correspond pas au schéma plus récent.
Mettre à jour PostgreSQL
Le fichier Compose de référence fixe la version majeure de PostgreSQL (POSTGRES_IMAGE, postgres:16 par défaut). Récupérer une nouvelle image de la même version majeure est une mise à jour mineure ordinaire. Un changement de version majeure ne se fait pas en changeant seulement l’image : les fichiers de données d’une version majeure ne sont pas lisibles par une autre. Passez par un export et une restauration.
Avant de commencer, lisez les notes de l’image PostgreSQL de la nouvelle version majeure : l’emplacement du répertoire de données dans le conteneur peut changer d’une version majeure à l’autre, et le volume du fichier Compose doit y correspondre.
- Arrêtez l’application, pour que rien ne change après la sauvegarde :
docker compose -f compose.reference.yaml stop app. Faites ensuite une sauvegarde avec./scripts/backup.sh. - Arrêtez la pile :
docker compose -f compose.reference.yaml down(sans-v). - Supprimez uniquement le volume PostgreSQL. Son nom est le nom du projet Compose suivi de
_postgres-data; listez les volumes avecdocker volume lspour le trouver, puisdocker volume rm <nom>. - Renseignez dans
.envPOSTGRES_IMAGEavec la nouvelle version majeure. - Restaurez la sauvegarde avec
./scripts/restore.sh. Il démarre le nouveau PostgreSQL sur un volume vide, puis y restaure l’export.
Répétez d’abord cette procédure sur une copie, comme décrit dans Tester une restauration sans toucher à la production.