Azure Load Balancer en Azure Resource Manager

Uno de los elementos claves a la hora de desplegar nuestra solución dentro de máquinas virtuales en Azure es el balanceador de carga. Esto se debe a que para estar cubiertos por el acuerdo de nivel de servicio es necesario tener nuestra aplicación ejecutándose en al menos dos máquinas virtuales dentro de un conjunto de disponibilidad. Aunque ahí de forma directa no se menciona al balanceador, es la pieza clave que hace que nuestros clientes sigan recibiendo servicio si una de nuestras máquinas falla es él.

Trabajando con el modelo de Azure Service Manager, ASM o clásico, cada vez que aprovisionábamos una nueva máquina virtual automáticamente se generaba un nuevo balanceador asociado a la misma. En el caso de Azure Resource Manager, ARM o “el nuevo”, es necesario que de forma activa nosotros creemos el balanceador y hagamos las conexiones necesarias con nuestras máquinas virtuales. Esto se debe a que un balanceador es un recurso más que se encuentra bajo el provedor de recursos de Microsoft.Network. Si queréis más detalles sobre las diferencias entre ASM y ARM podéis encontrarlo en la documentación bajo el artículo “Resource Manager Deployment Model”.

¿Cuál es el problema entonces? A día hoy no existe opción en el portal de gestión de Azure para crear un nuevo balanceador de carga. Esto nos obliga a utilizar PowerShell o las nuevas plantillas de despliegues en JSON para realizarlo. Este artículo entrará en más detalle en conocer cómo crear nuestro balanceador desde dichas plantillas y las opciones de configuración que nos ofrece este tipo de recurso.

La documentación respecto a este tema no es muy extensa, existe el artículo “Azure Resource Manager Support for Load Balancer” que cubre de forma genérica cómo es un LB en el modelo ARM pero sin dar mucho más detalle. En el caso de que quieras generar tu plantilla es necesario ir al repositorio de GitHub y revisar en la colección de plantillas las configuraciones disponibles. Por lo tanto, aprovecharemos este artículo para tener una visión completa de las opciones de configuración.

Lo primero es empezar con las diferentes partes que componen la configuración del balanceador. La imagen que podéis encontrar en la propia documentación es bastante clara.

Esquema balanceador de carga ARM
Esquema balanceador de carga

En ella podemos distinguir los siguientes elementos:

  • Front end IP Pool: la dirección o direcciones IPs en las que el balanceador escuchará para balancear el tráfico a las máquinas que se encuentren por detrás. En un principio, los balanceadores sólo soportaban una única IP; sin embargo, desde los anuncios del último Ignite el mayo pasado ya es posible utilizar más de una.
  • Backend Adress Pool: la colección de direcciones IPs asociadas a las tarjetas de red de las máquinas virtuales a las que queremos balancear el tráfico que recibamos en el balanceador.
  • Reglas de balanceo: la definición de cómo queremos que el tráfico de entrada se balancee entre las máquinas del backend. Una regla define una relación entre un puerto e IP de entrada con un puerto e IPs de salida a las que enviar el tráfico.
  • Reglas de NAT: es posible que en algunas ocasiones no nos interese balancear el tráfico entre un puerto de origen y otro de destino si no que queramos establecer una relación unívoca entre ellos para tener una conexión directa con una máquina específica. Las reglas de NAT nos permiten el realizarlo; suelen emplearse para conexiones por SSH o RDP a equipos concretos.
  • Sondas: el balanceador necesita conocer si una máquina del backend está operativa o no para continuar reenviándole el tráfico. Esto se realiza gracias a las sondas; el balanceador realizará comprobaciones periódicas según lo configuremos para determinar el estado de salud de las máquinas virtuales en todo momento.

Por lo tanto, una vez visto esto, vamos a ver cómo se trasladan los conceptos a una plantilla de JSON para ser desplegada en nuestra suscripción parte por parte.

Definición del recurso

Todo recurso en Azure Resource Manager tiene una estructura de elementos básicos necesarios para poder ser desplegado. Es necesario definir el tipo de recurso, la versión del recurso que queremos emplear, un nombre y el lugar donde va a ser desplegado.

"type": "Microsoft.Network/loadBalancers",
"apiVersion": "2015-05-01-preview",
"name": "frontendLoadBalancer",
"location": "West Europe",
"properties": {
   ...
}

Con eso lo siguiente será completar la sección de propiedades.

Propiedades del balanceador de carga

A partir de este punto tendremos que ir definiendo cada una de las secciones que tiene el balanceador.

Configuración de las IPs de Front End

Nuestro balanceador puede ser configurado como un balanceador externo o interno. La configuración de balanceador externo se realizará cuando las máquinas que se encuentren tras el balanceador reciban el tráfico directamente desde Internet. El uso más común es como frontal para los servidores web. En el lado contrario, la configuración como balanceador interno se realizará cuando queramos exponer una capa interna de nuestra aplicación a otra capa de la misma pero sin exponerla de forma pública en internet.

En el caso del balanceador externo partiremos de la siguiente configuración. En este caso sólo se está empleando una única IP pública, si quisiéramos añadir una segunda sería necesario añadir un nuevo elemento al array de frontendIPConfigurations a continuación de la existente con una coma y replicando el bloque.

 "frontendIPConfigurations": [
          {
            "name": "IPExterna1",
            "properties": {
              "publicIPAddress": {
                "id": "[resourceId('Microsoft.Network/publicIPAddresses', '$nombreIpExterna1$')]"
              }
            }
          }
  ]

A la hora de asociar la IP esto se hace a través de una referencia a un recurso del tipo IP que habremos creado previamente en nuestro grupo de recursos. Será necesario que reemplacemos $nombreIpExterna1$ por el nombre de nuestra IP. Por ejemplo, si hubiéramos creado una IP de la siguiente manera tendríamos que cambiar $nombreIpExterna1$ por IPExterna1

    {
      "type": "Microsoft.Network/publicIPAddresses",
      "apiVersion": "2015-06-15",
      "name": "IPExterna1",
      "location": "West Europe",
      "properties": {
        "publicIPAllocationMethod": "Dynamic", //La otra opción es estática
        "dnsSettings": {
          "domainNameLabel": "IPExterna1"
        }
      }
    }

Si lo desplegamos veremos los recursos en nuestro grupo dentro del portal. Dado que no existe nada que esté haciendo uso del balanceador o de las IPs, la plataforma no asigna la IP en un primer lugar hasta que no haya una máquina escuchando.

Balanceador externo creado
Balanceador externo creado

En el caso de que quisiéramos crear un balanceador interno la configuración de esta sección se vería modificada. Tendremos la opción de escoger en este caso si la IP que va a tener es estática o dinámica dentro de la subred en la que el balanceador será desplegado.

En el caso de que sea estática:

"frontendIPConfigurations": [
          {
            "name": "IPInterna1",
            "properties": {
              "privateIPAllocationMethod": "Static",
              "privateIPAddress": "10.0.0.4", 
              "subnet": {
                "id": "[concat(resourceId('Microsoft.Network/virtualNetworks, '$nombreRedVirtual$'), '/subnets/', '$nombreSubred$')]"
              }
            }
          }
        ]

En este escenario es necesario haber creado previamente una red virtual en Azure con las diferentes subredes en las que se va a repartir los servicios de nuestra aplicación. Tras ello, debermos reemplazar $nombreRedVirtual$ y $nombreSubred$ por los valores correctos. También es necesario que la IP pertenezca al rango de dicha subred y sea una IP válida para ser asignada teniendo en cuenta las IPs que se reserva la plataforma de cada red virtual que creamos.

En el caso de que sea dinámica, la configuración es similar. Únicamente cambiaremos la forma en la que la reserva de IP debe de realizarse y eliminaremos la propiedad privateIPAddress

      "properties": {
        "frontendIPConfigurations": [
          {<pre lang="json">
            "name": "IPExterna1",
            "properties": {
              "privateIPAllocationMethod": "Dynamic",
              "subnet": {
                "id": "[concat(resourceId ('Microsoft.Network/virtualNetworks, '$nombreRedVirtual$'), '/subnets/', '$nombreSubred$')]"
              }
            }
          }
        ]

Configuración de las IPs de Backend

Una vez que ya hemos configurado la IP que tendrá nuestro balanceador para recibir el tráfico será necesario configurar a qué máquinas se va a redirigir. Sin embargo, la realidad no es así. A la hora de configurar el balanceador no especificamos qué máquinas son a las que vamos a dirigir el tráfico, únicamente definidos una o varias pool con un nombre. Será en el proceso de crear las tarjetas de red de las máquinas virtuales cuando les indiquemos que se suscriban a ese pool del balanceador para recibir el tráfico.

Desde el punto de vista de la plantilla únicamente será necesario definirlo de la siguiente manera:

"backendAddressPools": [
          {
            "name": "ConjuntoBackend1"
          }
        ]

Configuración de las reglas de balanceo

Una vez definido dónde queremos recibir el tráfico y hacia dónde lo queremos enviar será necesario que configuremos las reglas para que se balancee. Podemos crear tantas reglas como queramos hasta un máximo de 150 por balanceador. La configuración básica de una regla es la siguiente:

"loadBalancingRules": [
  {
    "name": "HTTP-80",
    "properties": {
      "protocol": "TCP",
      "frontendPort": 80,
      "frontendIPConfiguration": {
        "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'frontendLoadBalancer'), '/frontendIpConfigurations/IPExterna1')]"
      },
      "backendPort": 80,
      "backendAddressPool": {
        "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'frontendLoadBalancer'), '/backendAddressPools/ConjuntoBackend1')]"
      }
    }
  }
]

En ella definimos el nombre de la regla, el protocolo aplicable (TCP o UDP), el puerto público donde escuchar, el puerto interno donde reenviar el tráfico y los identificadores de la IP de frontend y el pool de backend. Una vez desplegada veremos en el portal algo similar a lo siguiente.

Regla de balanceo
Regla de balanceo

Aunque es posible crear una regla del balanceador sin una sonda asociada a través de la plantilla, es recomendable que añadamos una para que nuestras máquinas estén correctamente monitorizadas en el puerto y protocolo correcto. Además de ello, hay otros parámetros que podemos configurar. Una definición más completa sería la siguiente.

"loadBalancingRules": [
  {
    "name": "HTTP-80",
    "properties": {
      "protocol": "TCP",
      "frontendPort": 80,
      "frontendIPConfiguration": {
        "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'frontendLoadBalancer'), '/frontendIpConfigurations/IPExterna1')]"
      },
      "backendPort": 80,
      "backendAddressPool": {
        "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'frontendLoadBalancer'), '/backendAddressPools/ConjuntoBackend1')]"
      },
      "loadDistribution": "Default",
      "idleTimeoutInMinutes": 5,
      "enableFloatingIP": false,
      "probe": {
        "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'frontendLoadBalancer'), '/probes/HTTPprobe')]"
      }
    }
  }
]

En ella vemos que podemos definir el tipo de distribución de carga que realiza el balanceador. Existen tres tipos de balanceo disponibles:

  • Default: el algoritmo emplea cinco parámetros para realizar el hash: IP de origen, puerto de origen, IP de destino, puerto de destino y protocolo
  • SourceIp: el algoritmo emplea dos parámetros para realizar el hash: IP de origen, IP de destino
  • SourceIpProtocol: el algoritmo emplea tres parámetros para realizar el hash: IP de origen, IP de destino y protocolo

También podemos configurar de forma personalizada el máximo tiempo que el balanceador mantendrá las conexiones en estado IDLE; el valor puede estar entre 4 y 30 minutos. Activar o desactivar el uso de IP flotante, útil en entornos de failover donde la IP se asigna a otro servidor en caso de que el primario falle como sucede con SQL Server Always On. Y finalmente referenciar a una sonda, veremos con más detalle más adelante cómo se crean.

Configuración de las reglas de NAT

Las reglas de NAT son muy parecidas a las de balanceo lo único que estas están enfocadas a exponer un puerto concreto de una máquina. Al igual que ellas, no se define en este momento qué máquina es la que tiene esa regla específica de NAT si no que a la hora de crear la tarjeta de red de la máquina virtual especificaremos que emplee dicha regla de NAT. La configuración es la siguiente.

"inboundNatRules": [
  {
    "name": "SSH-22",
    "properties": {
      "protocol": "TCP",
      "frontendPort": 22,
      "backendPort": 22,
      "frontendIPConfiguration": {
        "id": "[concat(resourceId('Microsoft.Network/loadBalancers', 'frontendLoadBalancer'), '/frontendIpConfigurations/IPExterna1')]"
      }
    }
  }
]

En ella definimos el nombre de la regla, el protocolo, los puertos del frontend y del backend, y la referencia a qué IP pública va asociada nuestra regla.

Configuración de las sondas

La última sección será configurar una sonda para que monitorice nuestros extremos y determine si las máquinas responden de forma correcta o no. Existen dos tipos de sondas: TCP y HTTP.

Las primeras sirven para realizar una validación a nivel del protocolo de red de que la máquina está respondiendo. El balanceador intenta establecer una conexión TCP realizando el handshake, si tiene éxito considera que la máquina responde correctamente. Las segundas están enfocadas a realizar no solo la comprobación TCP si no a intentar establecer una conexión a nivel de aplicación con el protocolo HTTP; si el servidor devuelve el código 200 considerará que la máquina funciona correctamente.

Para crear una sonda del tipo TCP tendremos que definir lo siguiente. El tiempo mínimo en segundos entre consultas es de 5 y el número mínimo de intentos antes de descartar una máquina del balanceador es 2.

"probes": [
  {
    "name": "HTTP-80-probe",
    "properties": {
      "port": 80,
      "protocol": "TCP",
      "intervalInSeconds": 5,
      "numberOfProbes": 2
    }
  }
]

En el caso de que la sonda sea del tipo HTTP tendremos que modificar el tipo de protocolo y añadirle la propiedad de requestPath que indicará la ruta a la que tiene que realizar la consulta HTTP.

"probes": [
  {
    "name": "HTTP-80-probe",
    "properties": {
      "port": 80,
      "protocol": "HTTP",
      "requestPath": "/",
      "intervalInSeconds": 5,
      "numberOfProbes": 2
    }
  }
]

Para terminar

En cada uno de los puntos anteriores tenéis el paso a paso de cómo configurar cada una de las secciones; sin embargo, esto no implica que sea necesario incluir toda y cada una de ellas. En la propia documentación de la API podéis encontrar cuáles son obligatorias y cuáles no.

Espero que os haya resultado útil ;)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.