¿Por qué está "rebuild" tan estrechamente acoplado al estado de ejecución del contenedor?

En justicia, debo empezar diciendo que soy nuevo en la plataforma y en la base de código, por lo que no tengo idea de cómo funciona actualmente el proceso de reconstrucción por dentro. Sin embargo, mi comprensión actual es que la reconstrucción:

  1. detiene el contenedor actual
  2. construye un nuevo contenedor con los datos del árbol de origen
  3. espera a que inicies el nuevo contenedor

Desde una perspectiva orientada a DevOps, ¿por qué no se puede construir el nuevo contenedor (quizás en otra rama o directorio temporal) mientras el anterior sigue en ejecución? Eso parecería hacer mucho más rápido el intercambio del contenedor nuevo por el antiguo (al menos en términos de tiempo de inactividad), quizás en el orden de segundos en lugar de minutos.

Si los contenedores utilizan volúmenes de almacenamiento que no se destruyen al reconstruir el contenedor, ni siquiera estoy seguro de que los cambios de configuración o de base de datos (por ejemplo, mensajes nuevos) necesiten un manejo especial para este caso de uso, lo que significa que la construcción del contenedor no debería estar tan estrechamente acoplada al estado del contenedor.

¿Se trata simplemente de un problema que nadie ha abordado aún, o existe una decisión arquitectónica previa que requiere que se detenga un contenedor antes de poder construir otro?

Rebuild es una actualización integral que puede:

  • Actualizar el código fuente de Discourse
  • Actualizar dependencias a nivel de sistema operativo, como la versión principal de Ruby
  • Actualizar a versiones más recientes e incompatibles de PostgreSQL, encargándose de actualizar el formato del disco de datos para la nueva versión
  • Actualizar la imagen de Docker. Solo como ejemplo, a principios de este año pasamos de Ubuntu 16.04 a la última versión de Debian y todo es transparente para el usuario; simplemente escribe ./launcher rebuild app.

Las reconstrucciones no son necesarias en todo momento; son obligatorias solo unas pocas veces al año cuando ocurre una actualización masiva de dependencias. Para todas las demás actualizaciones, puedes realizar actualizaciones sin tiempo de inactividad haciendo clic en el actualizador web en la interfaz de administración.

Para más puntos relacionados con “devops”, puedes consultar:

y mucho más en #howto:sysadmin

Puede que me equivoque, pero siento que la pregunta de @CodeGnome sobre las reconstrucciones sin tiempo de inactividad aún merece más investigación.

Si entiendo correctamente Docker, los siguientes aspectos de Discourse podrían reconstruirse en segundo plano en un nuevo contenedor mientras el contenedor existente sigue en ejecución:

  • Actualizar el código fuente de Discourse
  • Actualizar las dependencias a nivel del sistema operativo, como la versión principal de Ruby
  • Actualizar la imagen de Docker. Solo como ejemplo, a principios de este año cambiamos de Ubuntu 16.04 a la última versión de Debian y todo es transparente para el usuario; solo hay que escribir ./launcher rebuild app.

En cuanto a los cambios que rompen la compatibilidad en PostgreSQL, esto es más complicado debido al volumen de datos, el cual, supongo, se comparte entre el contenedor antiguo y el nuevo.

Quizás el sitio podría ponerse en modo de solo lectura al inicio de la reconstrucción, manteniendo el contenedor antiguo su volumen existente, mientras que la base de datos en construcción podría operar dentro de un nuevo volumen de Docker.

Sobre la actualización del código fuente de Discourse, las dependencias a nivel de sistema operativo, la imagen base de Docker, las gemas de Ruby y cosas similares, es posible hacerlo construyendo en dos pasos y ejecutando las tareas mencionadas anteriormente en el primer paso.

Este primer paso es agnóstico al entorno y podría ejecutarse incluso en un entorno de CI (de modo que podrías usar una imagen casi idéntica en los entornos de staging y producción, evitando posibles errores debidos a reconstrucciones en fechas diferentes, sin mencionar la reducción del tiempo de inactividad).

Las tareas de migración de la base de datos y assets:precompile aún tendrían que ejecutarse en la máquina de destino. La migración de la base de datos, en la mayoría de los casos, sería rápida. Por otro lado, la tarea assets:precompile es un problema porque es el paso que más tiempo toma. Creo que es porque algunos activos necesitan conocer ciertos detalles del entorno definidos en la base de datos, como algunas reglas CSS, para ejecutarse.

Sería excelente si esta tarea se dividiera en dos: primero, todos los activos que no dependen del entorno se ejecutarían y podrían procesarse en un entorno de CI; y en el segundo paso, solo se compilarían los activos que dependen de datos en la base de datos, etc. Dicho esto, no sé qué tan difícil sería implementarlo técnicamente.

Hablo sobre el arranque del contenedor de la aplicación en dos pasos en el siguiente tema:

Los cambios que realicé solo consistieron en dividir la plantilla web de Discourse en tres archivos, pero las tareas son las mismas, aunque sería genial si el equipo de Discourse lo soportara para que no tuviera que actualizarlo debido a cambios futuros en la plantilla web.