Aplicaciones modernas en OpenShift, parte 2: compilaciones encadenadas

¡Hola a todos! Este es el segundo post de nuestra serie, donde mostramos cómo desplegar aplicaciones web modernas en Red Hat OpenShift.

Aplicaciones modernas en OpenShift, parte 2: compilaciones encadenadas

En el post anterior, tocamos ligeramente las capacidades de la nueva imagen base S2I (source-to-image), diseñada para compilar y desplegar aplicaciones web modernas en la plataforma OpenShift. En aquel momento, nos interesaba el tema del despliegue rápido de aplicaciones; hoy veremos cómo utilizar la imagen S2I como una imagen base «limpia» y combinarla con las compilaciones relacionadas de OpenShift.

Imagen base limpia

Como mencionamos en la primera parte, la mayoría de las aplicaciones web modernas tiene lo que se llama una etapa de construcción, donde generalmente se realizan operaciones como la transpilación de código, la concatenación de varios archivos y la minificación. Los archivos resultantes de estas operaciones – que son HTML estático, JavaScript y CSS – se colocan en la carpeta de output. La ubicación de esta carpeta generalmente depende de las herramientas de construcción que se utilicen, y para React, sería la carpeta ./build (volveremos a este asunto más adelante).

Source-to-Image (S2I)

En este post, no tocamos en absoluto el tema de «qué es S2I y cómo usarlo» (se puede leer más sobre esto aquí), pero es importante tener en claro las dos etapas de este proceso para entender qué hace la imagen Web App Builder.

Etapa de ensamblaje (assemble phase)

La etapa de ensamblaje es muy similar a lo que ocurre cuando ejecutas docker build y obtienes una nueva imagen Docker. Por lo tanto, esta etapa se activa al iniciar la compilación en la plataforma OpenShift.

En el caso de la imagen Web App Builder, la instalación de las dependencias de tu aplicación y la ejecución de la construcción son gestionadas por el script de ensamblaje. Por defecto, la imagen base utiliza la construcción npm run build, pero se puede sobreescribir a través de la variable de entorno NPM_BUILD.

Como mencionamos anteriormente, la ubicación de la aplicación terminada y ya compilada depende de qué herramientas se utilicen. Por ejemplo, en el caso de React, será la carpeta ./build, y para las aplicaciones de Angular, será la carpeta project_name/dist. Y, como ya se mostró en la publicación anterior, la ubicación del catálogo de salida, que por defecto se establece como build, se puede redefinir a través de la variable de entorno OUTPUT_DIR. Y dado que la ubicación de la carpeta de salida difiere de un marco a otro, simplemente copias la salida generada en la carpeta estándar de la imagen, es decir, en /opt/apt-root/output. Esto es importante para entender la siguiente parte de este artículo; por ahora, revisemos rápidamente la siguiente etapa: la fase de ejecución (run phase).

Fase de ejecución (run phase)

Esta etapa ocurre cuando se llama a docker run para una nueva imagen, creada en la etapa de ensamblaje. También sucede durante el despliegue en la plataforma OpenShift. Por defecto run script utiliza serve module para servir contenido estático que se encuentra en el catálogo de salida estándar mencionado anteriormente.

Este método es bueno para desplegar rápidamente aplicaciones, pero en realidad no se recomienda servir contenido estático de esta manera. Como realmente solo estamos sirviendo 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 al construir y otra al ejecutar. En esta situación, las construcciones encadenadas (chained builds) serán útiles.

Construcciones encadenadas (chained builds)

Esto es lo que dicen sobre chained builds en la documentación de OpenShift:

«Se pueden vincular dos construcciones entre sí, donde una genera una entidad compilada y la otra coloca esta entidad en una imagen separada, que se utiliza para ejecutar esta entidad».

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

De este modo, podemos usar la imagen Web App Builder como un constructor ‘limpio’ y tener una imagen de runtime pequeña.

Ahora vamos a desglosarlo con un ejemplo concreto.

Para practicar, utilizaremos una simple aplicación de React, creada con la herramienta de línea de comandos create-react-app.

Ensamblar todo nos ayudará el archivo de plantilla de OpenShift.

Analicemos este archivo en detalle, comenzando con la sección de parámetros.

parameters:
  - name: SOURCE_REPOSITORY_URL
    description: La URL de origen para la aplicación
    displayName: URL de origen
    required: true
  - name: SOURCE_REPOSITORY_REF
    description: El nombre de la rama para la aplicación
    displayName: Rama de origen
    value: master
    required: true
  - name: SOURCE_REPOSITORY_DIR
    description: La ubicación dentro del repositorio de origen de la aplicación
    displayName: Directorio de origen
    value: .
    required: true
  - name: OUTPUT_DIR
    description: La ubicación de los archivos estáticos compilados desde el constructor de sus aplicaciones web
    displayName: Directorio de salida
    value: build
    required: false

Aquí todo es bastante claro, pero vale la pena prestar atención al parámetro OUTPUT_DIR. Para la aplicación React de nuestro ejemplo no hay de qué preocuparse, ya que React utiliza como carpeta de salida el valor por defecto, pero en el caso de Angular o cualquier otra cosa, este parámetro deberá cambiarse adecuadamente.

Ahora echemos un vistazo a la sección de 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'

Mire las tercera y cuarta imágenes. Ambas están definidas como imágenes de Docker, y aquí es claramente visible de dónde provienen.

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

La cuarta es la imagen NGINX (versión 1.12) con la etiqueta latest en Docker hub.

Ahora miremos las dos primeras imágenes. Ambas están vacías al inicio y se crean solo en la fase de compilación. La primera imagen, react-web-app-builder, será el resultado de la fase de ensamblaje, que unirá la imagen web-app-builder-runtime y nuestro código fuente. Por eso incluimos en el nombre de esta imagen «-builder».

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

¿Confuso? Ahora miremos las configuraciones de construcción y será un poco más claro.

En nuestra plantilla hay dos configuraciones de construcción. Esta es la primera de ellas, 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 podemos ver, la línea con la etiqueta 1 indica que el resultado de esta construcción se colocará en la imagen react-web-app-builder que vimos anteriormente en la sección de ImageStreams.

La línea con la etiqueta 2 indica de dónde se debe obtener el código. En nuestro caso, se trata de un repositorio git, y la ubicación, ref y carpetas de contexto están definidas por parámetros que ya hemos visto arriba.

La línea con la etiqueta 3 es algo que ya hemos visto en la sección de parámetros. Añade la variable de entorno OUTPUT_DIR, que en nuestro ejemplo es igual a build.
La línea con la etiqueta 4 indica que se debe usar la imagen web-app-builder-runtime, que ya vimos en la sección de ImageStreams.

La línea con la etiqueta 5 indica que queremos utilizar una construcción incremental, si la imagen S2I lo admite, y la imagen Web App Builder sí lo admite. En el primer lanzamiento, después de que finalice la fase de ensamblaje, la imagen guardará la carpeta node_modules en un archivo comprimido. Luego, en lanzamientos posteriores, la imagen simplemente descomprimirá esta carpeta para reducir la duración de la construcción.

Y, por último, la línea con la etiqueta 6 son solo algunos desencadenadores, para que la construcción se inicie automáticamente sin intervención manual, cuando algo cambia.

En general, esta es una configuración de construcción bastante estándar.

Ahora echemos un vistazo a la segunda configuración de construcción. Es muy parecida a la primera, 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 ensamblaje es react-web-app-runtime, y comienza de manera bastante estándar.

En la línea con la etiqueta 1 no hay nada nuevo: simplemente indica que el resultado del ensamblaje se coloca en la imagen react-web-app-runtime.

La línea con la etiqueta 2, al igual que en la configuración anterior, indica de dónde obtener el código fuente. Pero tenga en cuenta que aquí decimos que se obtiene de la imagen. Y de la imagen que acabamos de crear: de react-web-app-builder (indicado en la línea con la etiqueta 3). Los archivos que queremos usar están dentro de la imagen y su ubicación allí se especifica en la línea con la etiqueta 4, en nuestro caso es /opt/app-root/output/. Si recuerdas, es precisamente allí donde se almacenan los archivos generados como resultado de la construcción de nuestra aplicación.

La carpeta de destino, establecida en la línea con la etiqueta 5, es simplemente el directorio actual (todo esto, recordemos, está funcionando dentro de una cosa mágica llamada OpenShift, no en su computadora local).

La sección strategy - línea con la etiqueta 6 - también es similar a la primera configuración de ensamblaje. Solo que esta vez vamos a usar nginx-image-runtime, que ya hemos visto en la sección ImageStream.

Finalmente, la línea con la etiqueta 7 es la sección de desencadenadores que activa este ensamblaje cada vez que se cambia la imagen react-web-app-builder.

Por lo demás, esta plantilla contiene una configuración de implementación bastante estándar, así como elementos relacionados con servicios y rutas, pero no profundizaremos en eso. Tenga en cuenta que la imagen que se desplegará es la imagen react-web-app-runtime.

Despliegue de la aplicación

Entonces, después de revisar la plantilla, veamos cómo utilizarla para desplegar la aplicación.

Podemos usar la herramienta cliente de OpenShift llamada oc para desplegar 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 pantalla anterior es una manera deliberadamente técnica de 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 se ejecuten, veremos que tenemos dos ensamblajes:

Aplicaciones modernas en OpenShift, parte 2: compilaciones encadenadas

Y al volver a la pantalla de Overview, veremos el pod en ejecución:

Aplicaciones modernas en OpenShift, parte 2: compilaciones encadenadas

Haciendo clic en el enlace, accederemos a nuestra aplicación, que es la página de la aplicación React App por defecto:

Aplicaciones modernas en OpenShift, parte 2: compilaciones encadenadas

Complemento 1

Para los aficionados a Angular, también tenemos un ejemplo de aplicación.

El patrón aquí es el mismo, excepto por la variable OUTPUT_DIR.

Complemento 2

En este artículo utilizamos NGINX como servidor web, pero se puede reemplazar fácilmente por Apache, simplemente cambiando en el archivo del patrón imagen de NGINX en imagen de Apache.

Conclusión

En la primera parte de esta serie mostramos cómo desplegar rápidamente aplicaciones web modernas en la plataforma OpenShift. Hoy examinamos lo que hace una imagen de Web App y cómo se puede combinar con un servidor web limpio como NGINX mediante builds encadenados para organizar una construcción más adecuada para entornos de producción. En el siguiente y último artículo de esta serie, mostraremos cómo ejecutar un servidor de desarrollo para su aplicación en OpenShift y sincronizar archivos locales y remotos.

Contenido de esta serie de artículos

  • Parte 1: cómo desplegar aplicaciones web modernas en solo unos pocos pasos;
  • Parte 2: cómo utilizar una nueva imagen S2I junto con una imagen de servidor HTTP existente, como NGINX, utilizando builds encadenados de OpenShift para un despliegue de producción;
  • Parte 3: cómo ejecutar un servidor de desarrollo para su aplicación en la plataforma OpenShift y sincronizarlo con el sistema de archivos local.

Recursos adicionales

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster