Desplegando Liferay 7.3 CE en Kubernetes 

Check english version here

Kubernetes es un orquestador de contenedores en cloud, de código libre, con el cual podemos administrar aplicaciones en contenedores, automatizar despliegues y ajustar su escalado entre muchas más funciones.
 

Liferay Cloud Hosting, Liferay Installer, Docker Container and VMEn este blog trabajaremos principalmente con la API de k8s mediante interfaz de línea de comandos usando kubectl para manejar los objetos: namespaces, services, ingress, volumes, pods y deployments.
Si no estás familiarizado con k8s, es recomendable leer los conceptos básicos sobre ello en su web oficial y este documento sobre la Arquitectura en Cluster de Google Kubernetes Engine (GKE), donde se explica qué es un cluster, cluster principal y nodo.

Seguidamente se describe cómo desplegar Liferay 7.3 CE en conjunto a un stack de servicios en Kubernetes, Nginx como ingress, MySQL 5.7 como base de datos y ElasticSearch como indexador de búsqueda.

Además mostraremos cómo poder manejar k8s desde el dashboard de una manera gráfica.

Requisitos

La ejecución de este Blog se ha realizado sobre macOS Catalina 10.15.3, requiriendo tener instalado:

  1. Docker desktop (concretamente se ha realizado sobre v2.2.0.3 y Engine v19.03.5

  2. Kubernetes (concretamente se ha realizado sobre v1.15.5) habilitado una vez instalada la versión de Docker. Desde la interfaz gráfica de Docker, podremos ejecutar una configuración básica en Kubernetes de forma ágil activando su configuración.

     

  3. Comprobamos que tenemos docker + Kubernetes up & running desde el Docker Dashboard y comprobamos que tenemos los recursos de CPU y Memoria suficientes como para desplegar el stack de servicios que pretendemos desplegar. Concretamente, para el desarrollo de este blog se ha necesitado re-asignar a Docker un total de 12 CPUs y 16GB de memoria.

     

  4. Instalar kubectl

  5. Instalar minikube 

(La instalación de Minikube es opcional. Se recomienda para agilizar la configuración de k8s. Si no se usa Minikube será necesario configurar nginx-ingress-controller)

 

¡ Comenzamos !

Empezaremos configurando Minikube con los recursos que asignamos a docker, 12 CPU y 16GB de memoria. Para ello abriremos un terminal y ejecutaremos:

minikube config set cpus 12

minikube config set memory 16384

Ahora realizaremos un delete y seguidamente un start, tal y como nos indica:

minikube delete

minikube start

Observamos que la VM se arranca con los recursos deseados

 

Una vez tengamos arrancado Minikube, comenzaremos creando el namespace donde alojaremos todos los recursos dentro de nuestro cluster durante todo el blog. Por defecto, si no se indica un namespace, todos los recursos creados irán al namespace “default”.

Abrimos un terminal y ejecutaremos:


kubectl create namespace liferay-prod


donde liferay-prod es el nombre del namespace que queremos crear y que usaremos durante todo el blog.
La posibilidad de crear distintos namespaces dentro del cluster nos permitirá controlar el despliegue de pods dentro de un namespace deseado evitando posibles colisiones entre ellos, haciéndolos convivir en un entorno de manera controlada.

Ahora podemos comenzar a desplegar nuestros recursos dentro de nuestro namespace en nuestro cluster en Kubernetes. Comenzaremos desplegando una imagen de Liferay 7.3 CE de la manera más rápida y sencilla, pero para ello desplegaremos con anterioridad una serie de dependencias que tendrá nuestro Liferay:

  • Base de datos → MySQL 5.7

  • Indexador de búsqueda → ElasticSearch

  • … cualquier otro servicio que deseemos.

 

  1. Desplegando MySQL: Para ello usaremos el siguiente manifiesto YAML, que primero creará el servicio (con el cual podrá comunicarse la BD), seguidamente el volumen persistente que requerirá la BD (donde se persistirá la información almacenada en la BD) y finalmente el deployment (plantilla que seguirá cada pod para su despliegue).

    Guardaremos el manifiesto en el fichero database-deployment.yaml y ejecutaremos en un terminal:

    kubectl apply -f database-deployment.yaml -n=liferay-prod

    Este comando aplicará todo el manifiesto en el namespace liferay-prod

    Podremos comprobar la correcta aplicación de nuestros recursos haciendo uso de los siguientes comandos:
    kubectl get deployments database -n=liferay-prod


    kubectl get services database -n=liferay-prod

    kubectl get pvc database-data -n=liferay-prod

     

    Para consultar información en más detalle usaremos el atributo describe en lugar de get, seguido del tipo de recurso del cual queramos obtener más información:

    kubectl describe deployment database -n=liferay-prod

    Tras la aplicación del manifiesto, tendremos nuestro primer pod corriendo en Kubernetes, ya que el comando apply sobre un tipo deployment , por defecto, arranca el número de replicas indicado en la plantilla. Podremos consultar el estado del pod con el siguiente comando, el cual filtra el listado de pods por la etiqueta app=database dentro del namespace liferay-prod:

    kubectl get pods -l app=database -n=liferay-prod
     


    Con el siguiente comando podremos listar todos los pods que tenemos corriendo en nuestro namespace:

    kubectl get pods -n=liferay-prod
     

     

  2. Desplegando ElasticSearch: Para ello usaremos el siguiente manifiesto YAML, que primero creará el servicio (con el cual podrá comunicarse ElasticSearch), seguidamente el volumen persistente que requerirá el indexador (donde se persistirá la información almacenada en por él) y finalmente el deployment (plantilla que seguirá cada pod para su despliegue).

    Usaremos la imagen pública de Elasticsearch de Liferay DXP Cloud, ya que se encuentra configurada con los módulos necesarios para Liferay 7.3 CE. También se puede partir de una imagen de ES limpia desde su registry en Docker Hub, pero requerirá añadir los módulos: analysis-icu, analysis-kuromoji, analysis-smartcn y analysis-stempel.


    Guardaremos el manifiesto en el fichero search-deployment.yaml y ejecutaremos en un terminal:

    kubectl apply -f search-deployment.yaml -n=liferay-prod

    Este comando aplicará todo el manifiesto en el namespace liferay-prod:
     


    Al igual que hemos realizado anteriormente con MySQL, podremos comprobar la aplicación correcta de service, deployment y volumen con los comandos get y describe.
     

     

  3. Desplegando Liferay 7.3 CE: Para ello usaremos el siguiente manifiesto YAML, que primero creará el servicio (con el cual podrá comunicarse Liferay), seguidamente el volumen persistente que requerirá Liferay (donde se persistirá la información almacenada de la parte documental), otro volumen persistente que utilizaremos para realizar configuración en el despliegue y finalmente el deployment (plantilla que seguirá cada pod para su despliegue).

    Guardaremos el manifiesto en el fichero liferay-deployment.yaml y ejecutaremos en un terminal:

    kubectl apply -f liferay-deployment.yaml -n=liferay-prod

    Este comando aplicará todo el manifiesto en el namespace liferay-prod:
     


    Al igual que hemos realizado anteriormente con MySQL, podremos comprobar la aplicación correcta de service, deployment y volúmenes con los comandos get y describe.
     



    Tras aplicar el manifiesto, tendremos un pod de nuestro deployment liferay corriendo, pero al ser una imagen por defecto de Liferay 7.3 CE, ésta estará ejecutando como base de datos Hypersonic y estará usando ElasticSearch embebido como motor de búsqueda.

    Ahora procedemos a configurar Liferay para apuntar a nuestros servicios database y search:

    a) Guardaremos los siguientes ficheros en un directorio files en nuestra máquina para copiarlos seguidamente al volumen persistente de Liferay-data:
    ~/files/portal-ext.properties ​​​: Configuramos la conexión a la BD definida anteriormente desplegada y configuramos Liferay para trabajar en cluster. Utilizaremos la configuración de cluster en multicast, pero si queremos trabajar con cluster por unicast debemos añadir el fichero de configuración unicast (usando DNS_PING o JDBC_PING) al liferay_home y modificar las propiedades cluster.link.channel.properties.control y cluster.link.channel.properties.transport.0.
    ~/files/com.liferay.portal.search.elasticsearch6.configuration.ElasticsearchConfiguration.config:
    Donde transportAddresses apunta al nombre del servicio definido para nuestros deployments de search y el puerto donde escuchará ElasticSearch, el cual hemos dejado abierto en el manifiesto del mismo.
    search.liferay-prod.svc.cluster.local podría reemplazarse por search, ya que ambos servicios(liferay y search) se encuentran en el mismo namespace.

    b) Aprovecharemos el pod arrancado para copiar dentro del volumen persistente los ficheros de configuración:

    kubectl get pods -l app=liferay -n=liferay-prod
     


    kubectl cp ./files liferay-6f9554fdf9-dglnd:/mnt/liferay -n=liferay-prod
    (la ejecución de este comando no devolverá ninguna salida)
    Puedes comprobar que están dentro del directorio, accediendo por SSH ejecutando el siguiente comando:
    kubectl exec -it liferay-6f9554fdf9-dglnd /bin/bash
     

    Por defecto, una imagen docker de Liferay, busca el directorio files dentro del directorio especificado en la variable de entorno LIFERAY_MOUNT_DIR cuyo valor es /mnt/liferay y copiará el contenido a liferay_home. Si necesitas ampliar más información respecto a variables de entorno o cualquier configuración de la imagen, puedes revisar la documentación de Docker Hub.

    Ahora procedemos a eliminar el pod existente para arrancar la configuración cargada en el volumen:
    kubectl delete pods liferay-6f9554fdf9-dglnd -n=liferay-prod

    Esta acción realizará que se re-despliegue otro pod automáticamente y cuando éste esté activo eliminará el deseado.

    Obtenemos el último pod desplegado:

    kubectl get pods -l app=liferay -n=liferay-prod



    Accedemos a los logs de Liferay para comprobar el correcto despliegue** de Liferay y su conexión con las dependencias de base de datos y search engine:

    **(este primer despliegue tardará un poco más de lo normal debido a que crea todas las tablas dentro de nuestro esquema de base de datos)

    kubectl logs liferay-6f9554fdf9-nvmsj -n=liferay-prod --tail 1000

    ¡ En este momento ya tendremos 1 nodo de Liferay 7.3 CE UP & Running !

    Ahora podemos escalar Liferay 7.3 CE. Para ello podemos modificar el manifiesto del fichero liferay-deployment.yaml modificando el número de réplicas que establecimos por defecto a 1, o bien ejecutar el comando “kubectl scale” sobre el deployment que queremos escalar:

    kubectl scale deployment.v1.apps/liferay -n=liferay-prod --replicas=2



    Esta ejecución nos levantará 1 pod más de Liferay 7.3 CE:

    Podremos comprobar que los pods se comunican entre sí accediendo a los logs del pod con más tiempo de vida:

    kubectl logs liferay-6f9554fdf9-67bbt -n=liferay-prod --tail 1000



    Podremos realizar un scale down de la misma manera que hemos realizado el scale up, bien modificando el manifiesto en el fichero yaml y realizando un kubectl apply -f o bien mediante el comando kubectl scale:

    kubectl scale deployment.v1.apps/liferay -n=liferay-prod --replicas=1


    Como podemos ver, en la imagen superior se está terminando la ejecución del pod liferay-6f9554fdf9-ltpbl . Ahora podemos comprobar que el resto de pods vivos han visto el apagado de este pod, accediendo nuevamente a sus logs:





    4. Desplegando Nginx Ingress:
    Antes de desplegar Nginx Ingress, habilitaremos en Minikube el recurso Ingress, con el fin de que nos asigne una IP dentro del nodo y podamos resolver el dns contra la ip del Ingress:

    minikube addons enable ingress

    Una vez habilitado, usaremos el siguiente manifiesto yaml, el cual creará un recurso de tipo Ingress mediante el que haremos uso de una cookie a modo de sticky session entre cliente y pod liferay, con el fin de poder mantener la sesión. De esta manera, delegaremos en el cluster de contenedores liferay en k8s el balanceo de la carga.
    Usaremos como host apuntando a nuestro cluster liferay, un dominio que tendremos que introducir en nuestro fichero hosts.

    Guardaremos el manifiesto en el fichero nginx-ingress.yaml y ejecutaremos en un terminal:

    kubectl apply -f nginx-ingress.yaml -n=liferay-prod

    Este comando aplicará todo el manifiesto en el namespace liferay-prod:



    Ahora comprobaremos que IP se le ha asignado a nuestro Ingress:

    kubectl get ingress -n=liferay-prod



    **El tiempo de asignación de la IP puede oscilar entre 1 y 2 minutos.

     

    Necesitaremos modificar el fichero hosts de nuestra máquina para apuntar a la IP que se le ha asignado al Ingress. Para ello, en un terminal, ejecutaremos:

    sudo nano /etc/hosts

    Y añadiremos al fichero la entrada:

    192.168.64.7 liferay.kubernetes.com



    Una vez guardado el fichero hosts, podremos abrir nuestro navegador preferido y acceder a http://liferay.kubernetes.com

 

 



Conclusión

Hemos conseguido desplegar Liferay, base de datos, nginx ingress y motor de búsqueda en k8s, configurando Liferay para que sea fácilmente escalable en cuestión de un par de minutos gracias a k8s como orquestador de contenedores y una serie de imágenes docker provistas para configurar con mucha agilidad.
Hemos podido comenzar a trabajar con los comandos de k8s a través de kubectl y hemos aprendido a configurar un manifiesto yaml para generar despliegues en k8s.

 



 

Haciendo uso del dashboard de Minikube

En k8s podremos usar un dashboard con el que podremos realizar todas las acciones de una forma gráfica. Como en este how-to nos hemos apoyado en Minikube para la configuración de nuestro nodo, usaremos el dashboard que nos facilita Minikube. Para ello abriremos un terminal y ejecutaremos

minikube dashboard



Nos abrirá una ventana de navegación en nuestro browser por defecto.

Una vez dentro, podremos visualizar nuestro cluster, nodo y namespaces (y dentro de éste, sus recursos) desde nuestro navegador y realizar las acciones/modificaciones que deseemos:




 

Blogs

Una duda Marcial, 

he realizado algunas pruebas y el ingress como cookie de afinidad es una generada por el propio ingress. ¿Se podría utilizar JSESSIONID o podría entrar en conflicto a la hora de mantener la afinidad?

Un saludo y muchisimas gracias de nuevo