Implementando una aplicación multitenant en GCP utilizando CloudRun con CloudSql y Secret Manager

Integrando productos autoadministrados de GCP para crear una solución en la nube del tipo Serverless, con soporte multipaís.

Oliver Fierro
GDG Cloud Santiago
11 min readAug 15, 2020

--

En el episodio anterior, estuvimos viendo cómo integrar CloudRun con Secret Manager. Nos servirá de base para entender los siguientes capítulos.

Si no tuviste la oportunidad de revisarlo, lo puedes encontrar aquí:

https://medium.com/gdg-cloud-scl/gcp-cloudrun-en-acción-15c615a1f593

Contexto:

Existe una plataforma de ecommerce que permite a los clientes hacer compras en línea. Para soportar la operación en el backoffice, existe una gran base de datos, la cual almacena información de todos los clientes y sus compras. La empresa detrás de la plataforma de ecommerce ofrece servicios de compra en linea a diferentes países de la región.

Problematica:

  • Al final de cada mes hay miles de registros en la base de datos, lo que hace cada vez más ineficientes las búsquedas de información.
  • Los costos de almacenamiento aumentan día a día y no es posible llevar un control eficiente de cada país.
  • Administrar la información de los clientes de todos los países en una misma base de datos se hace cada vez más complejo y pone en riesgo la operación de todos los países ante fallas en el mecanismo de persistencia.

Requerimiento:

Implementar una aplicación que cada cierto tiempo borre las órdenes de compra confirmadas de los clientes que tienen una antigüedad mayor a x días, para un país en particular (tú podrías implementar un mecanismo de respaldo previo de esas órdenes antes de eliminarlas de la base de datos)

Además se requiere minimizar el riesgo de afectar las ventas y gestión de todos los países ante problemas en la base de datos.

Requisitos Técnicos

Gradle: 6.3+

Java: 1.8 (Springboot)

Google Cloud Platform

  • Una cuenta personal de Google Cloud
  • SDK de Google Cloud
Mi lugar de trabajo, modo HomeOffice

Diseño de la solución

La solución abarca 2 requerimientos:

1.- Separar la información de los clientes por país.

Explicaré previamente un punto muy importante:

  • Según el contexto de la situación actual, debemos manejar información de compras de clientes de diferentes países. En este punto, es relevante conocer el concepto de Tenant. Un tenant es una agrupación de la información que maneja un sistema de acuerdo a ciertas características comunes. Por ejemplo, clientes que pertenecen a un segmento comercial particular, clientes que pertenecen a una empresa específica dentro de un holding, o que comparten un lugar geográfico o país en común. En este caso, nuestra clasificación de clientes será por país.

Para nuestro caso de uso, tendremos clientes de Chile y Argentina. Se propone aislar la información de los cliente por país. Para ello existen 3 variantes:

  • Crear una instancia de cloudsql por cada país
  • Crear una sola instancia de cloudsql donde existan 2 bases de datos, una por cada país
  • Crear una sola instancia de cloudsql, donde exista una sola base de datos, y dentro de ella un schema por cada país

Las 3 opciones tienen sus ventajas y desventajas. En esta oportunidad, utilizaremos la estrategia de crear una instancia de CloudSql por cada país por varias razones, dentro de las cuales destaca la capacidad de tener recursos de base de datos dedicados para cada país, que podemos aprovechar según su demanda particular, y además podemos aplicar políticas de administración de base de datos específicas según cada caso.

2.- Implementar la funcionalidad de borrado de las órdenes de compra antiguas

Para esto, se propone un microservicio que efectuara on demand la limpieza de las ordenes de compra más antiguas de los clientes, con un cierto criterio, las cuales estarán almacenadas en una tabla específica de la base de datos.

  • Esta aplicación se desplegará y ejecutará sobre CloudRun.
  • La información se persistirá en una base de datos relacional, CloudSql
  • Los datos de configuración correspondientes al acceso a la base de datos, se externalizaran con Secret Manager.
  • Para la compilación y despliegue de la solución usaremos CloudBuild

En el siguiente diagrama se muestran los componentes GCP involucrados en la solución y sus interacciones.

Productos GCP

  • CloudRun: Plataforma de cómputo serverless autoadministrada
  • CloudBuild: Herramienta para crear y desplegar la imagen
  • Container Registry: Herramienta para gestionar las imágenes
  • Secret Manager: Herramienta para resguardar nuestros datos sensibles
Camino a Coñaripe, Lago Calafquén, Chile

Implementación de la solución

Las características multitenant, para acceder a una base de datos específica según el país que se requiera, las otorgará el framework JPA/Hibernate.

Las 2 instancias de la base de datos las configuraremos en CloudSql (engine PostgreSQL 12)

Los datos de conexión a la base de datos los mantendremos en Secret Manager.

Los datasources los configuraremos en base a la información que tenemos almacenada en Secret Manager.

La aplicación la desplegaremos en CloudRun con la ayuda de Cloudbuild.

La solución debe ser capaz de discriminar de acuerdo al tenant informado en el request, a qué base de datos debe ir para eliminar los datos de las órdenes de compra antiguas.

Vamos por este desafio!

Crear la Base de datos

Crearemos 2 instancias de CloudSql, cada una de ellas con su propia base de datos. Cada base de datos representará los Tenant de Chile y Argentina , respectivamente.

Creando la instancia y Base de datos para Chile

Ir a: Menu de GCP -> SQL

Crear instancia:

Elegir PostgreSQL

Crear Base de Datos:

Nota: Si gustas, puedes agregar una password para el usuario postgres que viene por defecto.

Con esta acción hemos generado una base de datos CloudSql. Si vas a la instancia ecommerce-cl vas a ver una sección llamada “Connect to this instance”. Guarda la IP pública, la cual utilizaremos más adelante para la creación de los secretos:

Public IP Address

En este caso:

Public IP address: 34.66.xxx.xxx

Crear un usuario:

Presionar [Create user account]

Ingresar un usuario y password a tu elección. En este caso:

User name: democl

Password: democl2020

Crear Base de datos para la instancia creada (ecommerce-cl)

En este caso crearemos una base de datos dentro de la instancia ecommerce-cl llamada order-management.

Esta instancia y base de datos representará el tenant de los usuarios de Chile.

Habilitar el acceso a la base de datos:

Ir a All instances — Tu instancia (ecommerce-cl) — Menu — Connections

Ir a [+Add Network] y agregar la ip externa 0.0.0.0/0.

Luego presiona [Done — Save]

Haremos el mismo procedimiento de creación de una nueva instancia y base de datos CloudSql para el tenant de los usuarios de Argentina.

Creando la instancia y Base de datos para Argentina

Crear la instancia ecommerce-ar y una base de datos order-management

Crear el usuario:

En este caso

User: demoar

Password: demoar2020

Crear la base de datos:

La configuración final quedaria así:

Guarda las Public IP Address de cada instancia. Las vamos a necesitar cuando se creen los secretos, para configurar el string de conexión a la base de datos.

Habilitar el acceso a la instancia:

Ya tenemos nuestras dos instancias configuradas.

NOTA: En otra oportunidad vamos a ver cómo otorgarle mayor seguridad al acceso a la base de datos, accediendo solo por su IP Privada.

Ahora vamos a ver la configuración de la aplicación.

Configurar Secretos

Para configurar la información sensible de nuestra aplicación, utilizaremos Secret Manager de GCP. Para este fin se deben realizar los siguientes pasos:

Acceder a Secret Manager

Habilitar la API

Crear Mi Primer Secreto

Presionar [CREATE SECRET]

Registrar valores de cada secreto

Debes agregar el nombre del secreto y su valor. Por ejemplo,

Name: CL_DB_USER

Secret Value: democl (el usuario de base de datos que creamos anteriormente)

Label (+ ADD LABEL)

- Key: tenant

- Value: cl

Respite paso a paso el procedimiento anterior para crear todos los secretos necesarios.

Lista de secretos:

Donde:

[IP_ADRESS_CL]: Public IP Adress que le asigno GCP a tu instancia de Chile

[IP_ADRESS_AR]: Public IP Adress que le asigno GCP a tu instancia de Argentina

[PUERTO]: 5432 (por defecto)

[BASEDEDATOS_CL]: order-management

[BASEDEDATOS_AR]: order-management

En resumen, debería quedar así:

Estructura del microservicio

Capa de persistencia

Es la capa que accede directamente a los datos. Utilizaremos el concepto de Repositorio para proveer de diversas operaciones (CRUD) sin tener que implementarlas explícitamente. Para esto, en concreto aplicaremos Java Persistence API (JPA). Esta representado por el package “repositories”.

Capa de servicios

Es una abstracción entre la capa controladora y la capa de persistencia, para mayor flexibilidad. Esta representado por el package “service”.

Capa controladora

Utilizaremos Rest para exponer los servicios. Esta representada por el package “controller”.

  • Configuración: Configuración del microservicio, incluyendo la referencia a los secretos y a la capacidad multitenant. Package “config”.
  • Model: El objeto que representa nuestra entidad que representa una abstracción del modelo de datos. Package “model”.
  • DTO: El objeto de transferencia de la respuesta. Package “dto”

Obtener secretos desde Secret Manager

Vamos a mostrar un truco para poder leer los secretos desde Secret Manger y a su vez hacerlos disponibles para la configuración de la conexión a nuestras bases de datos, todo esto dinámicamente.

Para esto, primero debemos leer los secretos (en el artículo anterior nos enfocamos a este desafío) y asignarlos a un mapa por país (mapa de secretos). Utilizaremos el prefijo del país que le agregamos a cada nombre de secreto, para poder diferenciar cada uno de ellos.

En segundo lugar, pasaremos cada mapa (que contiene la configuración de secretos de cada país) a un mapa global.

Esto seria así:

Conectar a la base de datos CloudSql

La conexión a la base de datos la vamos a manejar dentro de la aplicación para poder tener el control de la característica multitenant. Los aspectos más relevantes son los siguientes:

  • Asignar la configuración de cada país a nuestros datasources respectivos en forma dinámica. Esto sería así:

MultiTenantJpaConfiguration

De esta forma, tendremos al finalizar el proceso un set de datasource identificados cada uno de ellos por su tenant (CL, AR)

Configuración Multitenant en la persistencia

Utilizaremos la estrategia multitenancy del tipo DATABASE de Hibernate.

Implementaremos un interceptor de la petición http para recibir el tenant id que debe venir en el header. Lo dejamos en el contexto.

Implementamos un “Resolver”, capaz de obtener el tenant id del contexto, para que hibernate pueda saber el datasource a utilizar.

Implementaremos una native Query para hacer la limpieza de la tabla “order” de la base de datos.

Eliminaremos los registros más antiguos que 30 días.

Pruebas Locales

Configurar variables de entorno

Previo a la compilación del componente debes configurar las siguientes variables de entorno local:

Cuenta de Servicio GCP:

Por ejemplo:

GCP Project Id:

Por ejemplo:

Ejecutar el microservicio localmente

Abre un terminal y sigue los siguientes pasos

  • Compilar la aplicación
  • Ejecutar la aplicación

Por ejemplo:

Ya puedes probar con POSTMAN tu microservicio en la siguiente url:

http://localhost:8080/TU_PATH

por ejemplo: http://localhost:8080/order

Nota: Recuerda agregar en el Header el KEY “X-TENANT_ID” y en el VALUE el prefijo del país que quieres probar.

Otra alternativa para probar locamente tu microservicio es generar tu imagen docker y probarla como contenedor o en algún cluster local (por ejemplo minikube).

Pruebas locales del microservicio

Ahora vamos por el objetivo final!!

Volcán Mocho-Choshuenco, Panguipulli, Chile

Desplegar el servicio en CloudRun

Ahora vamos a desplegar nuestro microservicio en CloudRun para que veamos la real potencia de esta solución.

Generar y subir la imagen a GCP

Para generar la imagen de tu componente debemos tener el siguiente archivo configurado:

Dockerfile

Luego, debemos ejecutar el siguiente comando:

Por ejemplo:

Desplegar la imagen en CloudRun

Realizaremos el proceso de despliegue de la imagen y creación del servicio en CloudRun con la ayuda de CloudBuild. Debes ejecutar la siguiente linea de comando en tu terminal:

Por ejemplo:

Nuestro servicio desplegado en CloudRun

Probando el servicio desde POSTMAN

Para esto debes utilizar como dominio la URL que se genero para tu servicio CloudRun más el path que expone el microservicio

https://[SERVICENAME]-[CLOUDRUNID].a.run.app/order

Ejemplo: https://order-clean-service-xxxxxxx.a.run.app/order

Hemos terminado!

Código Fuente: https://github.com/oliverfierro77/gcp-cloudrun-multitenant

Espero que hayas disfrutado esta experiencia. Nos vemos, como siempre, en el próximo desafío, donde veremos cómo invocar nuestra solución en forma recurrente (una pista, Schedule).

Desafíate y desafía día a día.

Oliver Fierro

--

--

Oliver Fierro
GDG Cloud Santiago

Cloud Architect & Tech Lead | Speaker & Writter | Co-organizer @GDG Cloud Santiago Chile