Blue-Green y reversiones
Cómo sh0 sustituye una versión en servicio sin interrumpirla, qué registra el historial de despliegues y qué hace realmente el botón de reversión en la versión 1.10.2.
Cómo un despliegue sustituye la versión en servicio
Cada despliegue inicia la nueva versión junto a la que ya está en línea y solo mueve el tráfico cuando el nuevo contenedor ha demostrado que responde. No hace falta ningún ajuste. Para una aplicación desplegada desde Git, la secuencia es:
- Construcción de la imagen a partir del repositorio clonado (los hooks
pre_buildypost_buildse ejecutan antes y después). - Inicio del nuevo contenedor mientras el anterior sigue sirviendo. Ambos montan los mismos volúmenes durante la superposición.
- Comprobación de salud del nuevo contenedor (detalles más abajo).
- Hooks
pre_deploy, y luego réplicas adicionales si la aplicación tiene más de una. - Cambio de ruta: el proxy inverso Caddy pasa a apuntar al nuevo contenedor.
- Parada del contenedor anterior: recibe una señal de parada, dispone de 30 segundos para terminar y luego se elimina.
- Hooks
post_deployy, por último, una comprobación de que el nuevo contenedor sigue en marcha y no se reinicia en bucle.
Las aplicaciones desplegadas por envío de archivo, imagen Docker o Dockerfile pegado siguen el mismo orden, sin las etapas de hooks. Si algún paso falla antes del cambio de ruta, el nuevo contenedor se elimina y la versión anterior sigue sirviendo; el log lo indica: A failed deploy leaves nothing running.
Comprobación de salud
sh0 espera hasta 300 segundos a que el nuevo contenedor se considere sano:
- La imagen declara un
HEALTHCHECK: sh0 espera a que Docker lo declare sano y, además, comprueba que el puerto responde desde fuera del contenedor. - Sin
HEALTHCHECK: sh0 abre una conexión TCP al puerto de la aplicación. No se solicita ninguna ruta HTTP. - Worker sin puerto: el contenedor debe seguir en marcha 15 segundos sin reiniciarse.
Un reinicio en bucle o una terminación por falta de memoria hace fallar el despliegue de inmediato, sin esperar los 300 segundos.
Lo que «cero interrupciones» no cubre
- Las conexiones todavía abiertas en el contenedor anterior cuando se detiene (WebSocket, descargas largas, respuestas en streaming) se cortan tras el periodo de gracia de 30 segundos. No se trasladan al nuevo contenedor.
- Durante la superposición, dos contenedores de la misma aplicación funcionan sobre los mismos volúmenes. Una aplicación que no admite ejecutarse dos veces a la vez (un archivo SQLite bloqueado en exclusiva, una tarea que debe ser única) tiene que tolerar esos segundos.
- Las migraciones de base de datos lanzadas por un hook
pre_deployse aplican mientras la versión anterior todavía sirve el tráfico. Escríbalas de modo que el código anterior siga funcionando con el nuevo esquema.
Historial de despliegues
La pestaña Despliegues de cada aplicación muestra sus despliegues, del más reciente al más antiguo, con el origen (upload, cli-push, cli, rollback…), el commit en las aplicaciones Git, la duración y el log de construcción completo. La misma lista está disponible en GET /api/v1/apps/<app-id>/deployments.
Un despliegue correcto conserva el estado running incluso después de que otro más reciente lo sustituya: la versión en línea es la entrada running más reciente, no la única.
Reversión
En la pestaña Despliegues, cada despliegue con estado running tiene un botón Revertir a este despliegue, que pide confirmación (Confirmar reversión). El equivalente en la API es POST /api/v1/deployments/<deployment-id>/rollback; requiere el rol de desarrollador en la aplicación y responde 202 con el nuevo despliegue encolado. La versión 1.10.2 de la CLI no tiene comando de reversión.
Qué despliega una reversión
Observado en la versión 1.10.3 con una aplicación desplegada mediante archivo: versión v1, luego v2, luego reversión al despliegue v1. El despliegue de reversión lleva la imagen de v1, su registro indica que no se construyó nada y la aplicación responde 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 v1Una reversión se rechaza con 400 cuando el despliegue elegido no tuvo éxito, y con 409 cuando su imagen ya no puede ejecutarse: una imagen construida por sh0 y eliminada después por la limpieza descrita abajo, o, para una aplicación desplegada desde una imagen Docker, una etiqueta móvil como nginx:latest que también usa el despliegue activo. Una aplicación configurada con varias réplicas vuelve con un solo contenedor.
Una reversión restaura la imagen, no la configuración: las variables de entorno, los volúmenes y los dominios siguen siendo los de hoy. Para volver a una versión cuya imagen ya no existe, despliéguela de nuevo: envíe el archivo anterior con sh0 push, o revierta el commit y súbalo en una aplicación Git.
Conservación de imágenes
Cada 6 horas, sh0 elimina las imágenes antiguas de las aplicaciones y conserva las 3 más recientes por aplicación. Ambos valores son opciones de sh0 serve, no ajustes del panel: --cleanup-interval-hours (0 desactiva la limpieza) y --cleanup-keep-images.