Atlasingeniería

Incidentes en un servidor propioDeploys que fallan por la mitadTema 1

La migración que corría con una imagen distinta a la de la app

El servicio que aplica migraciones usaba el mismo Dockerfile que la API, y por eso parecía la misma imagen. No lo era. Reconstruir sólo una dejó la base migrada por código viejo y la API devolviendo errores.

Un despliegue de rutina: reconstruir la imagen de la API, levantar, listo. La API arrancó devolviendo errores en todos los endpoints que tocaban la base.

La causa: el servicio que aplica las migraciones tiene su propia imagen, aunque comparta el Dockerfile con la API. Reconstruir una no reconstruye la otra, y la base quedó migrada por la versión anterior del código.

Por qué son dos imágenes si el Dockerfile es uno

En una herramienta de composición, cada servicio que declara una construcción produce su propia imagen etiquetada con el nombre de ese servicio, aunque el contexto y el archivo sean idénticos.

Lo que uno piensaLo que pasa
Mismo Dockerfile, misma imagenUna imagen por servicio, cada una con su etiqueta
Reconstruir la API alcanzaEl servicio de migraciones sigue con la imagen construida la vez anterior
Si algo estuviera mal, fallaría el arranqueLa migración corre bien: es código viejo aplicándose sin error
El fallo no está en la migración sino en el desfasaje entre lo que migró y lo que corre.

Y hay un agravante de tiempos: la migración corre antes, termina bien, y la API arranca después contra un esquema que no es el que su código espera. El error aparece en la primera petición, no en el despliegue.

Cómo se ve desde afuera

La secuencia del incidente

  1. Se reconstruye y levanta sólo el servicio de la API.
  2. El servicio de migraciones se ejecuta con su imagen vieja y aplica el esquema de la versión anterior.
  3. La API nueva arranca, se conecta, y falla al consultar columnas o tablas que no existen.
  4. Los registros muestran errores de base, no de despliegue, y la primera hipótesis es un problema de datos.

Lo que quedó como regla

Cuatro cosas que cambiaron

  1. Construir siempre el conjunto completo de servicios que comparten código: migraciones, API y cualquier trabajador. Nunca uno solo.
  2. Levantar reconstruyendo, en vez de levantar sin construir después de haber construido a mano. Un solo comando que no permite el desfasaje.
  3. Verificar la versión después de desplegar, comparando lo que reporta la aplicación con el commit que se quería desplegar.
  4. Que el arranque verifique el esquema, cuando la herramienta lo permita: fallar al iniciar es mucho mejor que fallar en la primera petición de un usuario.

Antes de seguir, predecí

Un despliegue tiene API, migraciones y un trabajador que comparten el mismo código. ¿Qué es más seguro?

Lo que este incidente enseña sobre migraciones

Más allá de las imágenes, el episodio expone algo estructural: durante un despliegue conviven, por unos segundos o por horas, el esquema viejo y el código nuevo, o al revés.

Las reglas que hacen esa convivencia segura

  1. Migraciones compatibles hacia atrás. Agregar columnas y tablas antes de usarlas; el código viejo tiene que seguir funcionando con el esquema nuevo.
  2. Borrar en un despliegue posterior. Quitar una columna en el mismo despliegue que deja de usarla rompe cualquier instancia vieja que siga viva.
  3. Nunca renombrar en un paso. Agregar, escribir en las dos, migrar datos, dejar de leer la vieja, borrar. Cuatro despliegues, cero caídas.
  4. Migración idempotente y verificable, porque va a correr de nuevo cuando un despliegue falle por la mitad.
Más a fondo · nivel seniorQuién aplica las migraciones

Hacerlas correr al arrancar la aplicación es cómodo y peligroso cuando hay varias instancias: dos arrancando a la vez aplican lo mismo en paralelo. Un servicio dedicado que corre una sola vez, con un bloqueo en la base, es lo que evita la carrera. Y tiene una ventaja operativa: se puede ejecutar y verificar antes de levantar lo demás, en lugar de descubrir el resultado cuando la aplicación ya está sirviendo tráfico.

Las dos imágenes, versión por versión

Estado inicial: las dos imágenes construidas de la misma versión del código. Todo consistente.

1 / 5
Las dos columnas salen del mismo Dockerfile y son dos imágenes distintas, cada una con su etiqueta y su fecha. Reconstruir una no toca a la otra, y como las dos arrancan sin error, la única señal es que la base quedó migrada por una versión del código que ya no es la que corre.

Migraciones que se pueden desplegar

Cierre

Autoevaluación

¿Lo entendiste?

Dos servicios usan el mismo Dockerfile. ¿Comparten la imagen construida?
¿Por qué no conviene borrar una columna en el mismo despliegue que deja de usarla?