Blue-Green et retours en arrière
Comment sh0 remplace une version en service sans l'interrompre, ce que consigne l'historique des déploiements, et ce que fait réellement le bouton de retour en arrière dans la version 1.10.2.
Comment un déploiement remplace la version en service
Chaque déploiement démarre la nouvelle version à côté de celle déjà en ligne et ne déplace le trafic qu'une fois que le nouveau conteneur a prouvé qu'il répond. Aucun réglage n'est nécessaire. Pour une application déployée depuis Git, la séquence est la suivante :
- Construction de l'image à partir du dépôt cloné (les hooks
pre_buildetpost_buildl'encadrent). - Démarrage du nouveau conteneur pendant que l'ancien continue de servir. Les deux montent les mêmes volumes pendant le chevauchement.
- Contrôle de santé du nouveau conteneur (détails ci-dessous).
- Hooks
pre_deploy, puis répliques supplémentaires si l'application en a plus d'une. - Bascule de la route : le proxy inverse Caddy est dirigé vers le nouveau conteneur.
- Arrêt de l'ancien conteneur : il reçoit un signal d'arrêt, dispose de 30 secondes pour se terminer, puis est supprimé.
- Hooks
post_deploy, puis une dernière vérification que le nouveau conteneur tourne toujours et ne redémarre pas en boucle.
Les applications déployées par envoi d'archive, image Docker ou Dockerfile collé suivent le même ordre, sans les étapes de hooks. Si une étape échoue avant la bascule de la route, le nouveau conteneur est retiré et la version précédente continue de servir ; le journal l'indique : A failed deploy leaves nothing running.
Contrôle de santé
sh0 attend jusqu'à 300 secondes que le nouveau conteneur soit jugé en bonne santé :
- L'image déclare un
HEALTHCHECK: sh0 attend que Docker le déclare sain, et vérifie en plus que le port répond depuis l'extérieur du conteneur. - Pas de
HEALTHCHECK: sh0 ouvre une connexion TCP vers le port de l'application. Aucun chemin HTTP n'est demandé. - Worker sans port : le conteneur doit rester en marche 15 secondes sans redémarrer.
Un redémarrage en boucle ou un arrêt pour manque de mémoire fait échouer le déploiement aussitôt, sans attendre les 300 secondes.
Ce que « zéro interruption » ne couvre pas
- Les connexions encore ouvertes sur l'ancien conteneur quand il s'arrête (WebSocket, longs téléchargements, réponses en flux) sont coupées après le délai de grâce de 30 secondes. Elles ne sont pas transférées au nouveau conteneur.
- Pendant le chevauchement, deux conteneurs de la même application tournent sur les mêmes volumes. Une application qui ne supporte pas de tourner deux fois à la fois (fichier SQLite verrouillé en exclusif, tâche qui doit être unique) doit tolérer ces quelques secondes.
- Les migrations de base de données lancées par un hook
pre_deploys'appliquent pendant que l'ancienne version sert encore le trafic. Écrivez-les de sorte que l'ancien code continue de fonctionner avec le nouveau schéma.
Historique des déploiements
L'onglet Déploiements de chaque application liste ses déploiements, du plus récent au plus ancien, avec le déclencheur (upload, cli-push, cli, rollback…), le commit pour les applications Git, la durée et le journal de construction complet. La même liste est disponible via GET /api/v1/apps/<app-id>/deployments.
Un déploiement réussi garde le statut running même après avoir été remplacé par un plus récent : la version en ligne est l'entrée running la plus récente, pas la seule.
Retour en arrière
Dans l'onglet Déploiements, chaque déploiement au statut running porte un bouton Revenir à ce déploiement, qui demande une confirmation (Confirmer le retour en arrière). L'équivalent dans l'API est POST /api/v1/deployments/<deployment-id>/rollback ; il exige le rôle développeur sur l'application et répond 202 avec le nouveau déploiement mis en file. La version 1.10.2 de la CLI n'a pas de commande de retour en arrière.
Ce que déploie un retour en arrière
Observé en version 1.10.3 sur une application déployée par envoi d'archive : version v1, puis v2, puis retour en arrière vers le déploiement v1. Le déploiement de retour porte l'image de v1, son journal indique que rien n'a été construit, et l'application répond v1 :
$ curl -s https://panel.example.com/api/v1/apps/<app-id>/deployments \
-H "Authorization: Bearer $SH0_TOKEN" \
| jq -c '.data[] | {id: .id[0:8], status, triggered_by, commit_msg, image_id: .image_id[7:19]}'
{"id":"c97f6268","status":"running","triggered_by":"rollback","commit_msg":"Rollback to deployment 0c5abcdc","image_id":"44da57c73080"}
{"id":"a36c7932","status":"running","triggered_by":"cli-push","commit_msg":"Push update","image_id":"916d35592fa5"}
{"id":"0c5abcdc","status":"running","triggered_by":"upload","commit_msg":"File upload","image_id":"44da57c73080"}
$ curl -s https://my-site.example.com
version v1Un retour en arrière est refusé en 400 quand le déploiement choisi n'a pas réussi, et en 409 quand son image ne peut plus être exécutée : image construite par sh0 puis supprimée par le nettoyage décrit plus bas, ou, pour une application déployée depuis une image Docker, étiquette mobile comme nginx:latest que le déploiement en ligne utilise aussi. Une application réglée sur plusieurs répliques revient avec un seul conteneur.
Un retour en arrière restaure l'image, pas la configuration : variables d'environnement, volumes et domaines restent ceux d'aujourd'hui. Pour revenir à une version dont l'image a disparu, déployez-la de nouveau : renvoyez l'archive précédente avec sh0 push, ou annulez le commit et poussez-le pour une application Git.
Conservation des images
Toutes les 6 heures, sh0 supprime les anciennes images d'application et garde les 3 plus récentes par application. Les deux valeurs sont des options de sh0 serve, pas des réglages du tableau de bord : --cleanup-interval-hours (0 désactive le nettoyage) et --cleanup-keep-images.