Volver
Cómo construir un balanceador de carga en Google Cloud Platform
Categorías
DevOps | Google Cloud Platform | Terraform
Fecha
18-05-2024
El balanceador de carga es una de las piezas claves en la mayoría de aplicaciones web. Bien es cierto que esta pieza fundamental ha adquirido cierta complejidad desde que han surgido las arquitecturas Cloud. En este artículo se analizarán las diferentes partes que componen un balanceador de carga y cómo construirlo en Google Cloud Platform.

¿Qué es un balanceador de carga? Una definición válida podría ser la siguiente, pieza de software que distribuye el tráfico de una aplicación de manera equitativa entre múltiples servidores o entre múltiples instancias de un mismo servidor, optimizando el rendimiento, la disponibilidad y la escalabilidad del sitio web evitando la sobrecarga de un solo servidor y mejorando la tolerancia a fallos.
La parte de entre múltiples servidores o entre múltiples instancias de un mismo servidor es importante, porque en la mayoría de ocasiones, un balanceador de carga también va a poder actuar como un reverse-proxy. Por dar a este término una pequeña definición, un reverse-proxy es una pieza de software que canaliza todas las peticiones de un ecosistema de servidores en un único punto de entrada, ocultando el origen de dichos servidores, devolviendo las respuestas de los servidores como si proviniesen del propio proxy.
Google Cloud Platform
Quizás el punto débil de construir un balanceador de carga en Google Cloud Platform, GCP a partir de ahora, es la complejidad inicial que implica. Esto se debe a que no existe un único objeto a configurar en GCP, sino que se deben crear diferentes recursos de red y conectarlos entre sí. En infraestructuras más clásicas, esto no era tan común, ya que existían piezas únicas como NGINX o PM2, (aunque luego pudieran interconectarse entre sí para formar sistemas más robustos), que desempeñaban la función de balanceador de carga.
Un buen punto de partida para entrar en detalle puede ser empezar a definir todos estos recursos de red que conforman un balanceador de carga en GCP:
- IP Address: dirección IP sobre la cual estará expuesto el balanceador.
- SSL Certificate: certificado SSL utilizado por el proxy para peticiones HTTPS. Puede ser gestionado automáticamente por Google o por otro proveedor.
- Forwarding Rules: es el frontal del balanceador. Debe configurarse con la IP mencionada anteriormente, un puerto (generalmente 443) y un proxy de destino.
- HTTP / HTTP(S) Proxy: pieza intermediaria entre la forwarding rule y el url mapper. En caso de tráfico HTTPS se encarga de resolver el certificado SSL asociado a los dominios correspondientes.
- URL Mapper: responsable de definir las reglas de tráfico del balanceador. Puede recibir múltiples configuraciones; básicamente, cada configuración debe tener tres elementos: el host, la ruta sobre la cual se aplica la regla y el servicio backend al que se apunta. También puede tener reglas de redirección, como por ejemplo para redirigir tráfico HTTP a HTTPS, como se verá más adelante.
- Backend Services: componente cuya función principal es definir cómo se enruta el tráfico a los servicios que están detrás del balanceador de carga.
- Network Endpoint Group: agrupa endpoints específicos, permitiendo el uso de servicios serverless, como Cloud Run, enrutables como backends del balanceador de carga.
En la siguiente imagen podemos observar como se relacionan todos los recursos que conforman un balanceador de carga en torno a las definiciones vistas anteriormente:

Nota: es importante añadir que se debe modificar el registro de la zona DNS del dominio correspondiente para que apunte a la IP del balanceador de carga.
Implementándolo con Terraform
De nada sirve la teoría sin la práctica. Por lo que a continuación, se detalla paso a paso cómo construir un balanceador de carga en Terraform. El ejemplo que se propone es un ejemplo real, concretamente el de la arquitectura de este blog, formado por un frontend y un headless CMS.
Como se ha podido observar en la imagen anterior, el ejemplo propuesta hace uso del servicio Cloud Run. Cada uno de estos servicios Cloud Run ya dispone de un balanceador de carga, debido a que Cloud Run se encarga de gestionar el tráfico entre los contenedores necesarios para dar servicio al tráfico de nuestro servicio. Aún así vamos a construir un balanceador de carga para "poner" por delante de todos los servicios de nuestra aplicación, actuando como reverse proxy.
resource "google_compute_global_address" "site-public-load-balancer-ip" {
name = "site-public-load-balancer-ip"
}El siguiente paso es generar un certificado SSL para poder recibir tráfico HTTPS.
resource "google_compute_managed_ssl_certificate" "site-cert" {
name = "site-cert"
managed {
domains = ["site.com", "www.site.com"]
}
}Una vez hecho esto, es necesario que nuestros servicios de Cloud Run sean enrutables. Esto es debido a que Cloud Run es un servicio serverless que va generando contenedores bajo demanda para cada uno de nuestros servicios. Para ello es necesario crear un network endpoint group para cada uno de los servicios.
resource "google_compute_region_network_endpoint_group" "site-frontend-neg" {
name = "site-frontend-neg"
network_endpoint_type = "SERVERLESS"
region = var.region
cloud_run {
service = google_cloud_run_service.site-frontend.name
}
}
resource "google_compute_region_network_endpoint_group" "site-cms-neg" {
name = "site-cms-neg"
network_endpoint_type = "SERVERLESS"
region = var.region
cloud_run {
service = google_cloud_run_service.site-cms.name
}
}Se puede observar que cada NEG apunta a su respectivo servicio de Cloud Run, como se puede apreciar en el diagrama expuesto previamente.
Ahora es necesario definir los backends del balanceador de carga, cada uno de ellos apuntará a su NEG correspondiente.
resource "google_compute_backend_service" "site-frontend-backend-service" {
name = "site-frontend-backend-service"
protocol = "HTTP"
port_name = "http"
timeout_sec = 30
backend {
group = google_compute_region_network_endpoint_group.site-frontend-neg.id
}
}
resource "google_compute_backend_service" "site-cms-backend-service" {
name = "site-cms-backend-service"
protocol = "HTTP"
port_name = "http"
timeout_sec = 30
backend {
group = google_compute_region_network_endpoint_group.site-cms-neg.id
}
}Con esto ya es posible construir las piezas fundamentales de nuestro balanceador de carga, que determinan el comportamiento del balanceador desde que entra tráfico por la IP del balanceador hasta el enrutado final.
Lo primero es enrutar el tráfico HTTPS que entra por la IP del balanceador. Para ello es necesario crear una forwarding rule, que habilita el tráfico en el puerto 443 y lo redirige a un proxy.
resource "google_compute_global_forwarding_rule" "site-public-load-balancer-fw" {
name = "site-public-load-balancer-fw"
target = google_compute_target_https_proxy.site-public-load-balancer-proxy.id
port_range = "443"
ip_address = google_compute_global_address.site-public-load-balancer-ip.address
}Una vez hecho esto, es necesario definir el proxy, encargado de cargar los certificados SSL y de asociar un conjunto de reglas de enrutado.
resource "google_compute_target_https_proxy" "site-public-load-balancer-proxy" {
name = "site-public-load-balancer-proxy"
url_map = google_compute_url_map.site-public-load-balancer-mapper.id
ssl_certificates = [
google_compute_managed_ssl_certificate.site-cert.id
]
}Por último, las reglas de enrutado, quizás la pieza más importante, donde se define que rutas van a que servicios backends, definidos previamente. Como se puede observar, en este caso hay un conjunto de rutas cuyo tráfico se enruta hacia el CMS y el resto de tráfico se enruta por defecto al frontend.
resource "google_compute_url_map" "site-public-load-balancer-mapper" {
name = "site-public-load-balancer-mapper"
default_service = google_compute_backend_service.site-frontend-backend-service.id
host_rule {
hosts = ["site.com"]
path_matcher = "site-api-matcher"
}
path_matcher {
name = "site-api-matcher"
default_service = google_compute_backend_service.site-frontend-backend-service.id
path_rule {
paths = [
"/admin", "/admin/", "/admin/*",
"/api", "/api/", "/api/*",
"/i18n", "/i18n/", "/i18n/*",
"/content-manager", "/content-manager/", "/content-manager/*",
"/content-type-builder", "/content-type-builder/", "/content-type-builder/*",
"/upload", "/upload/", "/upload/*",
"/users-permissions", "/users-permissions/", "/users-permissions/*",
]
service = google_compute_backend_service.site-cms-backend-service.id
}
}
}¿Qué ocurre con el tráfico HTTP? Bueno, sencillo, se debe redirigir el tráfico que venga por el puerto 80 para que sea procesado por el puerto 443. Para ello es necesario hacer uso de los tres últimos componentes que se han mencionado: un forwarding rule, un proxy y un url mapper. En este caso el mapper será el encargado de redirigir dicho tráfico.
resource "google_compute_global_forwarding_rule" "site-public-load-balancer-http-fw" {
name = "site-public-load-balancer-http-fw"
target = google_compute_target_http_proxy.site-public-load-balancer-http-proxy.id
port_range = "80"
ip_address = google_compute_global_address.site-public-load-balancer-ip.address
}
resource "google_compute_target_http_proxy" "site-public-load-balancer-http-proxy" {
name = "site-public-load-balancer-http-proxy"
url_map = google_compute_url_map.site-public-load-balancer-http-mapper.id
}
resource "google_compute_url_map" "site-public-load-balancer-http-mapper" {
name = "site-public-load-balancer-http-mapper"
default_url_redirect {
https_redirect = true
redirect_response_code = "MOVED_PERMANENTLY_DEFAULT"
strip_query = false
}
}Con esto, el balanceador de carga queda completamente operativo. A lo largo de este artículo, hemos construido paso a paso un balanceador de carga mediante un ejemplo práctico, aplicable a un caso concreto con dos servicios de Cloud Run: uno para el frontend y otro para un CMS. Sin embargo, los pasos descritos son escalables a otras arquitecturas de aplicaciones similares.
Este artículo ha sido escrito por un no-devops, por lo que perdón de antemano por posibles imprecisiones. Enjoy!