Aplicaciones modernas de OpenShift, parte 2: compilaciones encadenadas

¡Hola! Esta es la segunda publicación de nuestra serie, en la que le mostramos cómo implementar aplicaciones web modernas en Red Hat OpenShift.







En la publicación anterior, mencionamos ligeramente las capacidades de la nueva imagen del constructor S2I (fuente a imagen), que está diseñada para construir e implementar aplicaciones web modernas en la plataforma OpenShift. Entonces estábamos interesados ​​en el tema de la implementación rápida de aplicaciones, pero hoy veremos cómo usar una imagen S2I como una imagen de constructor "limpia" y combinarla con ensamblajes OpenShift asociados.



Imagen limpia del constructor



Como mencionamos en la primera parte, la mayoría de las aplicaciones web modernas tienen una etapa de compilación, que generalmente realiza operaciones como la transpilación de código, la concatenación de múltiples archivos y la minificación. Los archivos resultantes (HTML estático, JavaScript y CSS) se agregan a la carpeta de salida. La ubicación de esta carpeta generalmente depende de las herramientas de compilación que se estén utilizando, y para React será la carpeta ./build (volveremos a esto con más detalle a continuación).



Fuente a imagen (S2I)



En esta publicación, no nos ocupamos en absoluto del tema de "qué es S2I y cómo usarlo" (puedes leer más al respecto aquí ), pero es importante tener claro las dos etapas de este proceso para entender qué hace la imagen de Web App Builder.



Fase de montaje



El paso de ensamblaje es inherentemente muy similar a lo que sucede cuando ejecuta la compilación de Docker y termina con una nueva imagen de Docker. En consecuencia, esta etapa ocurre al iniciar una compilación en la plataforma OpenShift.



En el caso de una imagen de Web App Builder, el script de ensamblado es responsable de instalar las dependencias de su aplicación y ejecutar la compilación . De forma predeterminada, la imagen del constructor usa la construcción de construcción de ejecución npm, pero se puede anular mediante la variable de entorno NPM_BUILD.



Como dijimos anteriormente, la ubicación de la aplicación terminada y ya construida depende de las herramientas que utilice. Por ejemplo, en el caso de React, esta será la carpeta. / Build, y para las aplicaciones Angular, la carpeta project_name / dist. Y, como se muestra en la última publicación, la ubicación del directorio de salida, que está configurado para compilar de forma predeterminada, se puede anular a través de la variable de entorno OUTPUT_DIR. Bueno, dado que la ubicación de la carpeta de salida difiere de un marco a otro, simplemente copie el resultado generado en la carpeta estándar de la imagen, a saber, / opt / apt-root / output. Esto es importante para comprender el resto de este artículo, pero por ahora echemos un vistazo rápido a la siguiente etapa: la ejecución (fase de ejecución).



Fase de ejecución



Esta etapa ocurre cuando se invoca la ejecución de la ventana acoplable en una nueva imagen creada durante la etapa de ensamblaje. También sucede cuando se implementa en la plataforma OpenShift. De forma predeterminada, el script de ejecución usa el módulo de servicio para entregar contenido estático ubicado en el directorio de salida estándar anterior.



Este método es bueno para implementar aplicaciones rápidamente, pero generalmente no se recomienda entregar contenido estático de esta manera. Bueno, dado que en realidad solo servimos contenido estático, el Node.js instalado dentro de nuestra imagen no es necesario: un servidor web es suficiente.



En otras palabras, necesitamos una cosa durante el montaje y otra durante la ejecución. Aquí es donde las construcciones encadenadas son útiles.



Construcciones encadenadas



Esto es lo que escriben sobre las compilaciones encadenadas en la documentación de OpenShift:



"Se pueden vincular dos ensamblajes entre sí, uno genera la entidad compilada y el otro aloja la entidad en una imagen separada que se utiliza para ejecutar esa entidad".



En otras palabras, podemos usar la imagen de Web App Builder para ejecutar nuestra compilación y luego usar la imagen del servidor web, NGINX, para servir nuestro contenido.



Por lo tanto, podemos usar la imagen de Web App Builder como un constructor puro y seguir teniendo una pequeña imagen en tiempo de ejecución.



Ahora veamos esto con un ejemplo específico.



Para este tutorial, usaremos una aplicación React simple construida con la herramienta de línea de comandos create-react-app.



El archivo de plantilla de OpenShift nos ayudará a armar todo .



Echemos un vistazo más de cerca a este archivo y comencemos con la sección de parámetros.



parameters:
  - name: SOURCE_REPOSITORY_URL
    description: The source URL for the application
    displayName: Source URL
    required: true
  - name: SOURCE_REPOSITORY_REF
    description: The branch name for the application
    displayName: Source Branch
    value: master
    required: true
  - name: SOURCE_REPOSITORY_DIR
    description: The location within the source repo of the application
    displayName: Source Directory
    value: .
    required: true
  - name: OUTPUT_DIR
    description: The location of the compiled static files from your web apps builder
    displayName: Output Directory
    value: build
    required: false


Todo está bastante claro aquí, pero debe prestar atención al parámetro OUTPUT_DIR. Para la aplicación React de nuestro ejemplo, no hay nada de qué preocuparse, ya que React usa el valor predeterminado como carpeta de salida, pero en el caso de Angular u otra cosa, este parámetro deberá cambiarse según sea necesario.



Ahora echemos un vistazo a la sección ImageStreams.



- apiVersion: v1
  kind: ImageStream
  metadata:
    name: react-web-app-builder  // 1 
  spec: {}
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: react-web-app-runtime  // 2 
  spec: {}
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: web-app-builder-runtime // 3
  spec:
    tags:
    - name: latest
      from:
        kind: DockerImage
        name: nodeshift/ubi8-s2i-web-app:10.x
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: nginx-image-runtime // 4
  spec:
    tags:
    - name: latest
      from:
        kind: DockerImage
        name: 'centos/nginx-112-centos7:latest'


Eche un vistazo a la tercera y cuarta imágenes. Ambos se definen como imágenes de Docker y puede ver claramente de dónde provienen.



La tercera imagen es web-app-builder y proviene de nodeshift / ubi8-s2i-web-app con etiqueta 10.x en Docker Hub .



La cuarta es una imagen NGINX (versión 1.12) con la última etiqueta en el concentrador de Docker .



Ahora echemos un vistazo a las dos primeras imágenes. Ambos están vacíos al inicio y solo se crean en la fase de construcción. La primera imagen, react-web-app-builder, será el resultado de un paso de ensamblaje que fusionará la imagen web-app-builder-runtime y nuestro código fuente. Es por eso que ponemos "-builder" en el nombre de esta imagen.



La segunda imagen, react-web-app-runtime, será el resultado de combinar nginx-image-runtime y algunos archivos de la imagen react-web-app-builder. Esta imagen también se utilizará durante la implementación y solo contendrá el servidor web y el HTML estático, JavaScript, CSS de nuestra aplicación.



¿Confuso? Ahora echemos un vistazo a las configuraciones de compilación y se volverá un poco más claro.



Hay dos configuraciones de compilación en nuestra plantilla. Aquí está el primero, y es bastante estándar:



  apiVersion: v1
  kind: BuildConfig
  metadata:
    name: react-web-app-builder
  spec:
    output:
      to:
        kind: ImageStreamTag
        name: react-web-app-builder:latest // 1
    source:   // 2 
      git:
        uri: ${SOURCE_REPOSITORY_URL}
        ref: ${SOURCE_REPOSITORY_REF}
      contextDir: ${SOURCE_REPOSITORY_DIR}
      type: Git
    strategy:
      sourceStrategy:
        env:
          - name: OUTPUT_DIR // 3 
            value: ${OUTPUT_DIR}
        from:
          kind: ImageStreamTag
          name: web-app-builder-runtime:latest // 4
        incremental: true // 5
      type: Source
    triggers: // 6
    - github:
        secret: ${GITHUB_WEBHOOK_SECRET}
      type: GitHub
    - type: ConfigChange
    - imageChange: {}
      type: ImageChange


Como puede ver, la línea etiquetada como 1 dice que el resultado de esta compilación se colocará en la misma imagen react-web-app-builder que vimos anteriormente en la sección ImageStreams.



La línea etiquetada con 2 le dice de dónde obtener el código. En nuestro caso, este es un repositorio de git, y la ubicación, la referencia y la carpeta de contexto están definidas por los parámetros que ya hemos visto anteriormente.



La línea etiquetada con 3 ya se ve en la sección de parámetros. Agrega la variable de entorno OUTPUT_DIR, que está construida en nuestro ejemplo.

La línea etiquetada 4 dice que use la imagen web-app-builder-runtime que ya vimos en la sección ImageStream.



La línea etiquetada con 5 dice que queremos usar una compilación incremental si la imagen S2I la admite y la imagen de Web App Builder sí. En el primer lanzamiento, después de completar la etapa de ensamblaje, la imagen guardará la carpeta node_modules en un archivo de almacenamiento. Luego, en ejecuciones posteriores, la imagen simplemente descomprimirá esa carpeta para acortar el tiempo de compilación.



Finalmente, la línea etiquetada como 6 son solo algunos disparadores para que la compilación se inicie automáticamente, sin intervención manual, cuando algo cambia.



Con todo, esta es una configuración de construcción bastante estándar.



Ahora echemos un vistazo a la segunda configuración de compilación. Es muy similar al primero, pero hay una diferencia importante.



apiVersion: v1
  kind: BuildConfig
  metadata:
    name: react-web-app-runtime
  spec:
    output:
      to:
        kind: ImageStreamTag
        name: react-web-app-runtime:latest // 1
    source: // 2
      type: Image
      images:                              
        - from:
            kind: ImageStreamTag
            name: react-web-app-builder:latest // 3
          paths:
            - sourcePath: /opt/app-root/output/.  // 4
              destinationDir: .  // 5
             
    strategy: // 6
      sourceStrategy:
        from:
          kind: ImageStreamTag
          name: nginx-image-runtime:latest
        incremental: true
      type: Source
    triggers:
    - github:
        secret: ${GITHUB_WEBHOOK_SECRET}
      type: GitHub
    - type: ConfigChange
    - type: ImageChange
      imageChange: {}
    - type: ImageChange
      imageChange:
        from:
          kind: ImageStreamTag
          name: react-web-app-builder:latest // 7


Entonces, la segunda configuración de compilación es react-web-app-runtime, y comienza bastante estándar.



La línea etiquetada como 1 no es nada nuevo, solo dice que el resultado de la compilación se coloca en la imagen react-web-app-runtime.



La línea etiquetada con 2, como en la configuración anterior, indica de dónde obtener el código fuente. Pero fíjense que aquí decimos que está tomado de la imagen. Además, a partir de la imagen que acabamos de crear, de react-web-app-builder (indicado en la línea etiquetada como 3). Los archivos que queremos usar se encuentran dentro de la imagen y su ubicación se especifica allí en la línea etiquetada 4, en nuestro caso es / opt / app-root / output /. Si recuerda, aquí es donde se colocan los archivos generados a partir de los resultados de la construcción de nuestra aplicación.



La carpeta de destino, especificada en la línea etiquetada 5, es solo el directorio actual (recuerde, todo esto gira dentro de una cosa mágica llamada OpenShift, no en su computadora local).



La sección de estrategia - línea etiquetada 6 - también es similar a la primera configuración de compilación. Solo que esta vez usaremos el nginx-image-runtime, que ya hemos visto en la sección ImageStream.



Finalmente, la línea etiquetada con 7 es la sección de activadores que activa esta compilación cada vez que cambia la imagen react-web-app-builder.



De lo contrario, esta plantilla contiene una configuración de implementación bastante estándar, así como aspectos relacionados con los servicios y las rutas, pero no entraremos en eso. Tenga en cuenta que la imagen que se implementará es la imagen react-web-app-runtime.



Implementar la aplicación



Entonces, después de echar un vistazo a la plantilla, veamos cómo usarla para implementar la aplicación.



Podemos usar una herramienta cliente de OpenShift llamada oc para implementar nuestra plantilla:



$ find . | grep openshiftio | grep application | xargs -n 1 oc apply -f

$ oc new-app --template react-web-app -p SOURCE_REPOSITORY_URL=https://github.com/lholmquist/react-web-app


El primer comando en la captura de pantalla anterior es una forma deliberada de ingeniería para encontrar la plantilla. / Openshiftio / application.yaml.



El segundo comando simplemente crea una nueva aplicación basada en esta plantilla.



Después de que estos comandos funcionen, veremos que tenemos dos ensamblados:







Y volviendo a la pantalla de Resumen, veremos el pod lanzado:







Haga clic en el enlace y nos dirigiremos a nuestra aplicación, que es la página predeterminada de la aplicación React:







Apéndice 1



Para los amantes de Angular, también tenemos una aplicación de muestra .



La plantilla es la misma aquí, excepto por la variable OUTPUT_DIR.



Apéndice 2



En este artículo, hemos utilizado como servidor web NGINX, pero es bastante fácil de reemplazar con Apache, basta con cambiar la imagen de plantilla de archivo NGINX en el camino de Apache .



Conclusión



En la primera parte de esta serie, le mostramos cómo implementar rápidamente aplicaciones web modernas en la plataforma OpenShift. Hoy analizamos qué hace una imagen de aplicación web y cómo se puede combinar con un servidor web puro como NGINX utilizando compilaciones encadenadas para crear una compilación de aplicaciones más lista para la producción. En el próximo artículo final de esta serie, le mostraremos cómo ejecutar un servidor de desarrollo en OpenShift para su aplicación y mantener sincronizados los archivos locales y remotos.



Contenido de esta serie de artículos










All Articles