Estrategia para desplegar / alojar sitios web estáticos basados ​​en javascript en contenedores

7

Esto surge de vez en cuando en varios de nuestros equipos de desarrollo, sin que hayamos descubierto la forma "correcta":

Usamos muchas aplicaciones web basadas en reacciones que se "compilan" en sitios web estáticos que son solo unos pocos archivos html, js y css.

Sin embargo, la "construcción" de estas aplicaciones toma una serie de variables que habilitan / deshabilitan las marcas de función, configuran las URL de backend, etc. Esto significa que no podemos "construir" un binario en el sentido tradicional y simplemente aplicar un archivo de configuración en la implementación. tiempo: la "compilación" en sí misma necesita tener estas variables específicas del entorno establecidas y, por lo tanto, el único momento en que podemos "construir" es en el momento de la implementación.

Por ahora resolvemos esto inyectando las variables de entorno requeridas en el contenedor Docker y ejecutamos un cmd de inicio a lo largo de las líneas de

npm build && nginx run

Esto tiene un par de desventajas:

  1. El proceso de compilación toma mucha CPU / memoria en relación con los requisitos de tiempo de ejecución del contenedor. Eso significa que necesitamos escalar el contenedor para el proceso de compilación en lugar de los requisitos de tiempo de ejecución, lo que se siente mal
  2. Las fallas de construcción son difíciles de "rastrear". Podemos usar verificaciones de salud en Kubernetes, pero si una construcción demora 2 minutos, todavía tenemos que esperar 3 minutos (1 extra por seguridad) antes de que podamos comenzar a probar el punto final de verificación de salud del contenedor para ver si está vivo.
  3. Las implementaciones pueden llevar mucho tiempo: si configuramos Kubernetes para que realice una implementación "en serie", iniciará cada pod y esperará el período de "retraso inicial" de 2-3 minutos antes de comenzar el siguiente. Esto significa que estamos viendo fácilmente un tiempo de implementación de 10 minutos si la implementación se escala a 3-4 pods.

Todo esto me parece muy subóptimo. Me interesaría saber cómo la comunidad resuelve el enigma de "compilar en el momento de la implementación" con aplicaciones web modernas de JavaScript.

Me doy cuenta de que para Kubernetes podríamos usar "contenedores de inicio" que realizan la compilación, colocan los artefactos en un almacenamiento persistente y luego hacen que los contenedores de la aplicación simplemente se extraigan del almacenamiento persistente durante el inicio, pero esto todavía parece más como "pasar por alto" el problema que resolviendo el problema raíz.

Trondh
fuente
Lo siento, no entiendo, ¿por qué estás ejecutando npm builden tiempo de ejecución en lugar de durante la construcción del contenedor?
Xiong Chiamiov
@ XiongChiamiov Supongo que la configuración de la interfaz se realiza durante la compilación y no se puede modificar sin la reconstrucción (diría que debería estar separada en un archivo incluido que, en el peor de los casos, podría 'sed' al inicio del contenedor)
Tensibai
exactamente. npm buildes solo un comando arbitrario. Podría ser react buildo sanity buildlo que sea que el marco JS espera. Nos vemos obligados a hacerlo en el despliegue de contenedores porque es cuando conocemos el entorno build.
Trondh

Respuestas:

5

Desde mi punto de vista, el mejor enfoque sería:

  1. Proceso de compilación separado usando Jenkins que construiría el proyecto NodeJS en distribución y lo envolvería en la imagen de Docker
  2. Haga girar el registro Docker que acumularía imágenes Docker de Jenkins (este registro debería ser accesible desde el clúster de Kubernetes)
  3. Mueva las variables de entorno a los secretos de Jenkins o use una herramienta separada para recopilar y combinar configuraciones desde un repositorio externo de Git (usamos Spring Cloud Config a través de REST API para recopilar definiciones json / yml para cada aplicación en cada entorno)

Con Jenkins puede configurar un sistema de entrega continua basado en tuberías genéricas. El flujo posible sería:

  1. Los desarrolladores finalizan su trabajo en correspondencia con GitFlow (la última solicitud de extracción fusionada en la rama de lanzamiento)
  2. Webhook desencadena la tubería de Jenkins:
    • Etapa 1: recopilar la definición del entorno
    • Etapa 2: compile la aplicación NodeJS usando npm
    • Etapa 3: compile la imagen de Nginx Docker con distribución
    • Etapa 4: empuje la imagen de Docker al registro de Docker
    • Etapa 5: implementar / actualizar el servicio en Kubernetes utilizando definiciones estándar
  3. Notificaciones enviadas con respecto a resultados

Este proceso puede visualizarse usando Rancher. Puedo responder tus preguntas en el chat.

Maksim
fuente
¿Entonces propones tener una imagen por entorno? Esa es una solución que consume mucho espacio para algunas variables ...
Tensibai 02 de
@Tensibai está hablando de ~ 15mb de imágenes por cada entorno. Con rotación y servicio de conserje adicional, 1 Gb sería suficiente para manejar todos los proyectos.
Maksim
Basado en esta solución, ¿cómo produciría múltiples "configuraciones" de una aplicación, por ejemplo, una para "puesta en escena" y otra para "producción"? (supongamos que hay una configuración llamada "BackendApi" que difiere, por ejemplo.
Trondh
@Trondh como dije, usamos Spring Cloud Config. Lo que hace es que para cada aplicación obtiene una configuración básica y aplica configuraciones adicionales profiles. Por ejemplo, si lo solicita https://config/backend.yml, recibirá la configuración application.ymly las propiedades adicionales de backend.yml. Para https://config/backend-stage.ymlello responderá con propiedades aplicadas por application.yml<- backend.yml<- backend-stage.yml.
Maksim
@Maksim 15MB para unos pocos bytes de texto es un desperdicio, en algún momento con cientos de microservicios en varios entornos con varias versiones en ellos, se convertirá en TB de datos para los mismos pocos bytes en el origen. En mi opinión, ese es un camino incorrecto si entendí bien lo que está proponiendo, ya que no puede garantizar que la imagen en QA y prod sean realmente las mismas que las de dos compilaciones (incluso con el mismo código, no serán la misma identificación y validación se convierte en un desastre). Pero tal vez lo entendí y solo tienes una imagen y configuración al girar el pod, si es así, no entendí tu publicación correctamente.
Tensibai