Openshift Enterprise 3: la estrella de los PaaS (1/2)

En la siguiente serie de posts pretendo explicar brevemente mi experiencia con Openshift Enterprise 3 en un proyecto con el que he colaborado recientemente. En concreto voy a  presentar una serie de conceptos básicos de Openshift, analizar la solución utilizada en dicho proyecto, repasar una serie de comandos básicos y evaluar la problemática encontrada al desplegar una arquitectura de microservicios.

Se suponen, por parte del lector, unos conocimientos mínimos sobre Docker, Kubernetes y arquitecturas de microservicios con spring-cloud-netflix.

Introducción

Openshift 3 es un PaaS que utiliza contenedores para la construcción, ejecución y despliegue de aplicaciones. Ha recibido numerosos galardones, tanto en su categoría como PaaS como en otras. Ha sido desarrollado por Red Hat y actualmente existen tres soluciones disponibles según el ámbito en el que nos movamos:

  • Openshift Online: permite crear y ejecutar aplicaciones en el cloud público, cualquiera puede crearse una cuenta gratuita y desplegar su aplicación. Actualmente no tiene disponible la versión 3, que es la que trabaja con contenedores.
  • Openshift Dedicated: te permite disponer de un cluster de Openshift gestionado por Red Hat para que despliegues tus aplicaciones.
  • Openshift Enterprise: admite construir el Paas Openshift sobre la infraestructura privada de tu compañía. Esta es la solución que vamos a analizar.

Openshift Enterprise 3 está construido con tecnologías OpenSource en base a la filosofía de contenedores, utilizando Docker, con Kubernetes como solución de orquestación y gestión y sobre la base de Red Hat Enterprise Linux.

1

 

Entre sus características podemos destacar las siguientes:

  • Portabilidad de las aplicaciones: al estar construido en base a contenedores con Docker, esto permite que nuestra aplicación sea migrada a cualquier sistema que utilice Docker.
  • OpenSource: con todas las posibilidades y ventajas que el software libre nos proporciona.
  • Escalable: permite que las aplicaciones escalen de forma sencilla y automática.
  • Multilenguaje: permite la utilización de diferentes lenguajes, plataformas, bases de datos… permitiendo a los desarrolladores utilizar todas las posibilidades de Docker.
  • Automatización: Openshift automatiza la construcción de tu aplicación, su despliegue, su escalado, la gestión de estado…

Elementos básicos

En esta sección veremos una introducción a los recursos básicos existentes en OSE3, necesario para entender el funcionamiento del PaaS. Usaremos la siguiente imagen correspondiente a la consola para describir dichos recursos además de otra información presente en la misma:

2

 

Así en la imagen podemos distinguir los siguientes elementos:

  • Proyecto (en dorado): es la unidad de agrupación de aplicaciones, permisos… Podemos, por ejemplo, definir un proyecto por entorno.
  • Servicio (en rojo): cada servicio representa a cada una de las aplicaciones que tengamos en nuestro proyecto, el servicio será el punto de entrada a la aplicación y expondrá unos puertos para su comunicación. En este caso el servicio se llama welcome-php.
  • Puertos expuestos (en negro): se trata de los puertos que el servicio expone y con cuales se mapea en nuestro contenedor. En este caso se mapea el puerto 8080 del contenedor al puerto 8080 del servicio. Los puertos expuestos no son como tal un recurso.
  • Ruta (en verde): la ruta es la URL asociada a un servicio para que podamos invocarlo, una ruta puede estar abierta a Internet o ser solo de uso interno. En este caso, la ruta es welcome-php-demo.cloudapps.example.com. Típicamente las rutas por defecto se generan como [nombre del servicio]-[nombre del proyecto].[dominio del PaaS]
  • Despliegue (en morado): representa la configuración de despliegue para esa aplicación, en ella se indica el número de instancias, su configuración, qué imagen de contenedor utilizar, parámetros de escalado… En este caso, la configuración de despliegue se llama welcome-php.
  • Número de despliegue (en azul): cada vez que realicemos un nuevo despliegue este se incrementará. El número de despliegue tampoco es un recurso. No debe confundirse con la versión de nuestra aplicación.
  • Pod (en el círculo azul): cada instancia que deseemos ejecutar de nuestra aplicación se ejecutará en un pod diferente; así, si por ejemplo tenemos dos instancias, tendremos dos instancias de la misma imagen Docker ejecutándose cada una en su propio pod.

Además, en el rectángulo de la derecha podemos ver información relativa al contenedor desplegado en el pod como son la imagen Docker que se está utilizando, el repositorio git a partir del cual se ha generado y los puertos expuestos. El hecho de que se haga referencia a un repositorio git nos demuestra que, en este caso, la imagen se ha generado con S2I (se explica en la sección siguiente).

En este punto debemos explicar más a fondo la configuración de despliegue (que veíamos en morado en la imagen), el que probablemente sea el recurso más importante, deploymentConfig, en el mismo nos podemos encontrar información correspondiente a:

  • Configuración de creación y destrucción de instancias durante despliegue (cuántas podemos tirar y cuántas podemos levantar con la nueva versión simultáneamente).
  • Configuración de los triggers que lanzarán un nuevo despliegue (explicados en la siguiente sección).
  • Configuración del número de réplicas.
  • La imagen Docker que se está utilizando y su versión.
  • Variables de entorno internas al contenedor.
  • Estado del despliegue actual.
  • Resultado de los últimos despliegues.

 

Solución construida

Para poder comprender la solución Openshift construida en este proyecto al que hago referencia debemos introducir adicionalmente una serie de conceptos relativos a los despliegues.

Builds vs Deploys

Para entender el porqué de la solución utilizada es necesario presentar las alternativas presentes a la hora de construir y desplegar aplicaciones en OSE3. En su documentación OSE 3 distingue tres variantes posibles a la hora de realizar un build:

  • Source-to-Image (S2I): es un framework que permite, tomando el código fuente de tu aplicación como entrada, generar una imagen que ejecuta dicho código fuente. Así se creará una imagen Docker por cada versión de tu código fuente que desees ejecutar.
  • Docker build: esta estrategia ejecutará un ‘Docker build’ y esperará a que se genere la imagen Docker en el registry para utilizarla.
  • Estrategia personalizada: esta estrategia consiste en que tú mismo crees una imagen Docker que lo que haga sea precisamente ejecutar el proceso de construcción de tu aplicación en otra imagen, que será la que se despliegue.

Estas diferentes estrategias de construcción lo que nos permiten es generar imágenes Docker para dejar en el registro para posteriormente ejecutarlas. Lo que debemos destacar de estas tres alternativas es que cada vez que queramos desplegar una nueva versión de nuestro código será necesario crear una nueva imagen Docker en el registro, con lo cual si realizamos muchos despliegues puede suponer una sobrecarga para nuestro sistema desde el punto de vista de capacidad de procesamiento y de almacenamiento (supongamos lo que pueden implicar todas esas imágenes en una compañía con cientos de proyectos utilizando el PaaS).

Ahora pasaremos a analizar las posibles formas de lanzar el despliegue de una nueva versión (los triggers). Una vez más, si nos atenemos a la documentación de OSE3, tenemos las siguientes posibilidades:

  • Cambio en la configuración: en el momento en que cambiemos la configuración de despliegue (deploymentConfig) se lanzará un nuevo despliegue de la aplicación con la configuración que acabamos de actualizar.
  • Cambio en la imagen: si definimos que queremos utilizar la última versión de una imagen Docker, en el momento que haya una nueva versión disponible, ésta se desplegará de forma automática.

Así, por ejemplo, una metodología de despliegue completamente válida sería utilizar cualquiera de las tres estrategias de construcción para generar una imagen Docker con nuestro código y lanzar el despliegue por un cambio de versión en dicha imagen. Como ya hemos dicho, la desventaja de dicha solución es la cantidad de imágenes Docker que se generan además de necesitar conocimientos de Docker o de S2I para poder generar dichas imágenes.

En el caso de esta compañía la aproximación que se ha decidido utilizar para el despliegue de microservicios es mucho más sencilla y no requiere conocimientos de Docker. En esta solución, disponemos de una única imagen Docker para desplegar cualquier versión de nuestra aplicación y qué versión se determinará durante el arranque del Docker en base a la configuración del despliegue. Por tanto, utilizaremos despliegue en base a cambio de configuración y cuando cambiemos la versión de nuestra aplicación en la configuración será cuando se lance el nuevo despliegue.

En el caso de este proyecto se trata de una arquitectura de software basada en microservicios, por tanto la imagen Docker estará pensada para ejecutar aplicaciones Java. En la siguiente sección analizaremos dicha imagen Docker y explicaremos su funcionamiento.

Imagen Docker para ejecución de microservicios

Como ya sabemos, para la construcción del Docker se utiliza el fichero Dockerfile que, por explicarlo de una forma muy sencilla, es donde se indica a partir de qué imagen se genera, se encarga de copiar una serie de recursos (scripts, certificados, jars…), se ejecuta una serie de comandos, exponer puertos e indicar qué debe hacer cuando se ejecute la imagen del Docker.

En este caso el Dockerfile no tiene nada de especial, parte de una imagen RHEL, copia los scripts, expone el puerto 8080, e indica el script a ejecutar durante el arranque.

Durante su arranque la imagen utiliza el valor de una variable de entorno, por ejemplo JAR_URL para saber en qué ruta del repositorio Nexus se encuentra el artefacto que debe ejecutar, procede a su descarga y lanza su ejecución. De esta forma tan sencilla una misma imagen Docker nos permite ejecutar diferentes microservicios. Así mismo existen otras variables de entorno para establecer parámetros de ejecución de la aplicación, de la máquina virtual… La imagen Docker espera recibir unos valores para estas variables de entorno y será la configuración de despliegue (deploymentConfig) quien se los proporcione. También podemos definir otras variables de entorno que deseemos que utilice nuestra aplicación.

Así, el procedimiento para desplegar una nueva versión de nuestra aplicación consistirá en  cambiar el deploymentConfig para apuntar a la nueva versión de nuestro artefacto en Nexus, que con la estrategia de despliegue que tenemos (explicado en el apartado anterior) provocará que se vuelva a desplegar, por lo que la imagen Docker descargará nuestro nuevo artefacto de Nexus y lo ejecutará. Esto mismo ocurre si, por ejemplo, cambiamos algún parámetro de la ejecución del mismo, no necesariamente la URL de nuestro artefacto. Cualquier cambio en la configuración, en este caso definida por las variables de entorno que utilicemos, provocará un nuevo despliegue.

En conclusión, de esta forma obtenemos un proceso de despliegue muy sencillo en el que no tenemos que crear imágenes Docker, sino que ya disponemos de una que nos sirve para cualquier microservicio y en el que el despliegue se realiza de forma automática en cuanto cambiamos la configuración. Así, lo único que tendremos que hacer será desplegar la versión del artefacto que queremos ejecutar en Nexus y cambiar el deploymentConfig para que la variable de entorno que se utiliza para descargar el artefacto apunte a la nueva versión en Nexus.

Comandos básicos

En esta sección se pretende enumerar algunos de los comandos básicos para interactuar con Openshift a través de la línea de comandos. Muchas de estas acciones se pueden realizar también a través de la interfaz gráfica. Los comandos son de comprensión sencilla, de todas formas, todos ofrecen el parámetro –help que nos proporcionará la descripción del comando, sus parámetros y el significado de cada uno. Para poder interactuar con OSE3 a través de la consola deberemos descargarnos el cli.

Para ejemplificar los siguientes comandos hemos utilizado una imagen con Openshift Origin, que podréis ver en la sección “Ahora prueba tú” de la segunda parte del post.

Logarnos en Openshift:

3

Crear un nuevo proyecto:

4

Crear una nueva aplicación:

5

Como podemos ver se crea nuestra aplicación a partir de una plantilla eap6-basic-sti con una serie de variables de entorno para poder configurar su funcionamiento. Así mismo, podemos ver cómo se crean otros recursos como servicios, rutas, imageStream…

Comprobar el estado de nuestro proyecto:

6

Vemos que se han creado dos servicios (eap-app y eap-app-ping), cada uno con su deploymentConfig (‘dc’ en la imagen) y su buildConfig (‘bc’ en la imagen). Además, el servicio eap-app está expuesto por la ruta con nombre ‘eap-app-http-route’.

Consultar la información de un recurso:

7

8

 

En este punto cambiaré entre diferentes proyectos para poder mostrar todos los comandos:

Recuperar el listado de pods de nuestro proyecto:

9

Consultar los logs de un pod:

10

Existen una serie de parámetros interesantes del comando logs como –since=5s para recuperar los últimos (5 en ese caso) segundos del log o –follow=true para que nos siga sacando el stream de logs.

Conectarnos de forma remota a un pod:

11

Comprobar la configuración de un pod:

12

Escalar el número de pods:

13

Eliminar un pod:

14

En este caso hemos seteado que queremos dos réplicas, así que en cuanto eliminamos el pod apache-1-ww58o se levantará una nueva instancia para seguir teniendo dos.

Port forward:

15

Redesplegar la última versión de un pod:

16

Este mismo comando nos ofrece la posibilidad de reintentar despliegues así como cancelarlos.

Redesplegar una versión anterior de la app:

17

Aquí vemos cómo se crea el pod apache-3-deploy, que será el encargado de crear los pods apache-3-hce62 y apache-3-3mqb0. Es decir, Openshift utiliza un contenedor para crear y desplegar los contenedores con tu aplicación. El nombre de este contenedor de despliegue siempre será [nombre-aplicación]-[número de despliegue]-deploy.

Más sobre Openshift

Blue-green deployment

En secciones anteriores hemos explicado cómo se realiza el proceso de despliegue, de forma que los pods con la versión antigua empiezan a “tirarse” cuando los de la versión nueva llegan al estado “Running”. Durante su arranque un pod pasará por el estado “Pending” hasta alcanzar el estado “Running”, en el cual se considera que el pod está listo para servir peticiones y, por tanto, se pueden tirar los pods con las versiones anteriores.

Pero la realidad es que nuestro pod puede encontrarse en el estado Running, pero que nuestro servidor de aplicaciones todavía no haya terminado de arrancar. Esto provoca que durante el despliegue haya una fracción de tiempo durante la cual no podamos servir peticiones, que suele ser más o menos lo que tarde nuestra aplicación en arrancar.

Para la mayoría de aplicaciones esto no es un problema, ya que estamos hablando de tiempos inferiores a un minuto y, teniendo en cuenta que en muchos casos los despliegues se realizan a horas intempestivas, los usuarios de la aplicación no tienen porqué verse especialmente perjudicados por estos tiempos.

No obstante, existen otros tipos de aplicaciones, aplicaciones globales que reciben peticiones las 24 horas del día, aplicaciones con miles de peticiones por segundo, por lo que perder un minuto de peticiones resulta una pérdida de servicio significativa. También hay aplicaciones que realizan tareas críticas, como las bancarias, que no pueden tener un downtime. Para estos casos en Openshift existe la posibilidad del blue-green deployment que se explica en este vídeo.

En esencia, el proceso consiste en tener una URL asociada a un servicio, con todos sus pods en el que tendremos la versión vieja. Cuando queramos desplegar una nueva versión creamos un nuevo servicio y el número de pods deseados y desplegamos en ellos la nueva versión comprobando que funcione correctamente. Esta versión no estará disponible para el cliente porque no tiene una URL asociada. En el momento en que deseemos hacer el pase real a producción desasociaremos la URL existente del servicio que tiene la versión vieja y lo asociaremos al servicio con la versión nueva. De esta forma en el momento en que recibamos una petición, nuestra aplicación estará lista para poder resolverla.

Conceptualmente no se trata de nada nuevo, ya que es la misma estrategia que se viene utilizando desde hace mucho y que consiste en registrar y desregistrar en un balanceador los servidores de aplicaciones con las versiones deseadas.

Otra de las grandes ventajas de esta solución es que el proceso de vuelta atrás es realmente sencillo. En caso de detectar un fallo en la nueva versión podemos volver a la anterior simplemente desasociando la URL del servicio actual y asociándola al servicio con la versión anterior. Así podremos depurar la nueva versión y redesplegarla y cuando esté lista, volver a realizar el proceso de asociación de la URL haciéndola disponible al cliente.

Autoescalado

Una de las características que más se echaba de menos en OSE3, comparado con otros PaaS o IaaS, es el tema del autoescalado. Por suerte, éste se está introduciendo en la versión 3.1, aunque todavía se encuentra en fase “Preview”. Para utilizarlo será necesario habilitar las métricas, ya que los criterios de escalado utilizan las mismas. Por ahora la única métrica disponible es el consumo de CPU.
De esta forma, nuestra configuración de escalado consistirá en un número mínimo y máximo de pods que queremos tener disponibles y un consumo ideal de CPU deseado. En función de este consumo de CPU se levantarán o tirarán más instancias. Este proceso no es instantáneo, ya que es necesario un tiempo para recolectar las métricas. Se puede encontrar información adicional sobre el tema en el blog de Openshift y en la documentación.

Hasta aquí hemos conocido los interiores de OSE: su arquitectura, su funcionamiento, los recursos que se utilizan, un ejemplo de contenedor para ejecución de microservicios y los comandos más habituales para interactuar con nuestro proyecto. Ahora que ya lo conocemos y sabemos cómo trabajar con él, veremos un caso real de despliegue de ecosistema de microservicios y la problemática que nos podemos encontrar. Pero eso ya es tema para otro día.

Abraham Rodríguez actualmente desarrolla funciones de ingeniero backend J2EE en Paradigma donde ya ha realizado diversos proyectos enfocados a arquitecturas de microservicios. Especializado en sistemas Cloud, ha trabajado con AWS y Openshift y es Certified Google Cloud Platform Developer. Cuenta con experiencia en diversos sectores como banca, telefonía, puntocom... Y es un gran defensor de las metodologías ágiles y el software libre."

Ver toda la actividad de José Abraham Rodríguez López

Recibe más artículos como este

Recibirás un email por cada nuevo artículo.. Acepto los términos legales

Posts relacionados

Escribe un comentario