Prueba de carga distribuida con Kubernetes

Las pruebas de carga son clave para el desarrollo de cualquier infraestructura de backend porque demuestran qué tan bien funciona el sistema cuando se enfrenta a las demandas del mundo real. Un aspecto importante de las pruebas de carga es la simulación adecuada del comportamiento del usuario y del dispositivo para identificar y comprender los posibles cuellos de botella del sistema, mucho antes de implementar aplicaciones en producción.

Sin embargo, la infraestructura de prueba dedicada puede ser costosa y difícil de mantener porque no es necesaria de forma continua. Además, la infraestructura de prueba dedicada suele ser un gasto de capital único con una capacidad fija, lo que hace difícil escalar la prueba de carga más allá de la inversión inicial y puede limitar la experimentación. Esto puede provocar demoras en la productividad para los equipos de desarrollo y desencadenar en aplicaciones que no están probadas de forma adecuada antes de las implementaciones de producción.

Descripción general de la solución

Las pruebas de carga distribuidas mediante la computación en la nube es una opción interesante para una variedad de situaciones de prueba. Las plataformas de Cloud proporcionan un grado alto de elasticidad de infraestructura, lo que hace que sea fácil probar aplicaciones y servicios con un gran número de clientes simulados, cada uno de los cuales genera tráfico con un patrón según los usuarios o dispositivos. Además, el modelo de precios de la computación en la nube se adecua muy bien a la naturaleza elástica de la prueba de carga.

Los contenedores, que ofrecen una alternativa liviana a la ejecución de instancias de máquinas virtuales completas para aplicaciones, son adecuados si quieres escalar clientes simulados con rapidez. Los contenedores son una excelente abstracción para ejecutar clientes de prueba porque son ligeros, fáciles de implementar, están disponibles de inmediato y son adecuados para tareas particulares.

Google Cloud Platform es un entorno excelente para la prueba de carga distribuida con contenedores. La plataforma es compatible con contenedores como entidades de primer nivel a través de Google Kubernetes Engine (GKE), con la tecnología del administrador de clústeres y contenedores de código abierto de Kubernetes. GKE brinda la capacidad de aprovisionar con rapidez la infraestructura del contenedor y herramientas para administrar las aplicaciones y los recursos implementados.

Esta solución demuestra cómo usar GKE para implementar un marco de trabajo de prueba de carga distribuido. El marco de trabajo usa varios contenedores a fin de crear tráfico de pruebas de carga para una API simple basada en REST. A pesar de que esta solución prueba una aplicación web simple, se puede usar el mismo patrón con el fin de crear situaciones de prueba de carga más complejas como aplicaciones de Internet de las cosas (IoT) o de videojuegos. Esta solución analiza la arquitectura general de un marco de trabajo de prueba de carga basada en contenedores. Si quieres obtener un instructivo sobre la configuración un marco de trabajo de muestra, consulta la sección de Instructivos al final de este documento.

Esta solución se centra en el uso de GKE para crear tráfico de pruebas de carga. El sistema a prueba es una aplicación web simple que usa una API de REST. La solución aprovecha un marco de trabajo de prueba de carga existente para modelar las interacciones de la API que se analizan con más detalle en las siguientes secciones. Después de implementar el sistema a prueba, la solución usa Kubernetes Engine para implementar las tareas de prueba de carga distribuida.

Sistema a prueba

En la terminología de pruebas de software, el sistema a prueba es el sistema que tus pruebas están diseñadas para evaluar. En esta solución, el sistema a prueba es una pequeña aplicación web implementada en Google App Engine. La aplicación expone extremos de estilo REST básicos para capturar solicitudes POST de HTTP entrantes (los datos entrantes no se conservan). En una situación real, las aplicaciones web pueden ser complejas y contener una gran variedad de componentes y servicios adicionales, como el almacenamiento en caché, la mensajería y la conservación. Estas complejidades están fuera del alcance de esta solución. Para obtener más información sobre cómo compilar aplicaciones web escalables en Google Cloud Platform, consulta la solución Compila aplicaciones web escalables y resistentes.

El código fuente de la aplicación de muestra está disponible como parte del instructivo al final de este documento.

Ejemplos de cargas de trabajo

La aplicación de ejemplo se basa en el componente de servicio de backend que se encuentra en muchas implementaciones de IoT: los dispositivos primero se registran en el servicio y luego comienzan a hacer informes de métricas o lecturas de sensores, mientras que también se vuelven a registrar en el servicio con regularidad.

En el siguiente diagrama, se observa una interacción común de componentes de servicio de backend.

Un diagrama que muestra una interacción común del componente de servicio de backend.

Para modelar esta interacción, puedes usar Locust, una herramienta de prueba de carga distribuida y basada en Python que es capaz de distribuir solicitudes a través de varias rutas de destino. Por ejemplo, puede distribuir solicitudes a las rutas de destino /login y /metrics. Hay muchos paquetes de software de generación de carga disponibles, incluidos JMeter, Gatling y Tsung; cualquiera de ellos podría satisfacer mejor las necesidades de tu proyecto.

La carga de trabajo se basa en la interacción descrita antes y se modela como un conjunto de tareas en Locust. Para aproximarse a los clientes del mundo real, cada tarea de Locust está ponderada. Por ejemplo, el registro se realiza una vez por cada mil solicitudes totales de clientes.

Computación basada en contenedores

Desde una perspectiva arquitectónica, hay dos componentes principales involucrados en la implementación de esta solución de prueba de carga distribuida: la imagen del contenedor de Locust y el mecanismo de administración y organización del contenedor.

La imagen del contenedor de Locust es una imagen de Docker que contiene el software de Locust. El Dockerfile se puede encontrar en el repositorio asociado de GitHub (consulta el instructivo a continuación). Este documento usa una imagen de Python de base y también incluye secuencias de comandos para iniciar el servicio Locust y ejecutar las tareas.

Esta solución usa GKE como el mecanismo de organización y administración de contenedores. GKE, que se basa en el marco de trabajo de código abierto Kubernetes, es el producto de años de experiencia en la ejecución, organización y administración de implementaciones de contenedores en Google. La computación basada en contenedores permite a los desarrolladores enfocarse en sus aplicaciones, en lugar de centrarse en las implementaciones y las integraciones en entornos de hosting. Los contenedores también facilitan la portabilidad de las pruebas de carga para que las aplicaciones en contenedores puedan ejecutarse en varios entornos de nube. GKE y Kubernetes presentan varios conceptos específicos de la organización y administración de contenedores.

Clústeres de contenedores

Un clúster de contenedores es un grupo de instancias de Compute Engine que actúa como la base de toda tu aplicación. La documentación de GKE y Kubernetes se refiere a estas instancias como nodos. Un clúster comprende un único nodo principal y uno o más nodos trabajadores. El principal y los trabajadores se ejecutan en Kubernetes, por lo que los clústeres de contenedores a veces se llaman clústeres de Kubernetes. Para obtener más información sobre los clústeres, consulta la Documentación de Kubernetes Engine.

Pods

Un pod es un grupo de contenedores estrechamente vinculados que se deben implementar juntos. Algunos pods contienen un solo contenedor. Por ejemplo, en esta solución, cada uno de los contenedores de Locust se ejecuta en su propio pod. Sin embargo, a menudo, los pods contienen varios contenedores que trabajan juntos de alguna manera. Por ejemplo, en esta solución, Kubernetes usa un pod con tres contenedores para proporcionar servicios de DNS. En un contenedor, SkyDNS proporciona la funcionalidad de servidor DNS. SkyDNS se basa en un almacén de clave-valor, denominado etcd, que reside en otro contenedor. En el tercer contenedor del pod, kube2sky actúa como un puente entre Kubernetes y SkyDNS.

Controladores de replicación

Un controlador de replicación garantiza que un número específico de “réplicas” de pod esté en ejecución en cualquier momento. Si hay demasiadas, el controlador de replicación finaliza algunos pods. Si hay muy pocas, empieza más. Esta solución tiene tres controladores de replicación: uno garantiza la existencia de un solo pod de servidor DNS; otro mantiene un solo pod principal de Locust; y un tercero mantiene en ejecución a 10 pods trabajadores de Locust.

Servicios

Un pod en particular puede desaparecer por varias razones, incluidas las fallas de nodo o la interrupción intencional del nodo para realizar actualizaciones o mantenimiento. Esto significa que la dirección IP de un pod no proporciona una interfaz confiable para ese pod. Un enfoque más confiable usaría una representación abstracta de esa interfaz que nunca cambia, incluso si el pod subyacente desaparece y se reemplaza por un pod nuevo con una dirección IP diferente. Un servicio GKE proporciona este tipo de interfaz abstracta por medio de la definición de un conjunto lógico de pods y una política para acceder a ellos. En esta solución, hay varios servicios que representan pods o conjuntos de pods. Por ejemplo, hay un servicio para el pod de servidor DNS, otro servicio para el pod principal de Locust y un servicio que representa a los 10 pods trabajadores de Locust.

En el siguiente diagrama se muestran los contenidos del nodo principal y los nodos trabajadores.

Un diagrama que muestra los contenidos del nodo principal y los nodos trabajadores.

Implementa el sistema a prueba

La solución usa App Engine para ejecutar el sistema a prueba. Con el fin de implementar el sistema a prueba, necesitas una cuenta activa de Google Cloud Platform de modo que puedas instalar y ejecutar el SDK de Cloud. Con el SDK listo, puedes implementar la aplicación web de muestra con un solo comando. El código fuente de la aplicación web está disponible como parte del instructivo al final de este documento.

Implementa tareas de la prueba de carga

A fin de implementar tareas de la prueba de carga, primero implementa el principal de la prueba de carga y luego implementa un grupo de diez trabajadores de pruebas de carga. Con esta cantidad de trabajadores de pruebas de carga, puedes crear una cantidad sustancial de tráfico con fines de prueba. Sin embargo, ten en cuenta que generar cantidades excesivas de tráfico a sistemas externos puede parecerse a un ataque de denegación del servicio. Asegúrate de revisar las Condiciones del Servicio de Google Cloud Platform y la Política de Uso Aceptable de Google Cloud Platform.

El principal de la prueba de carga

El primer componente de la implementación es el principal de Locust, que es el punto de entrada para ejecutar las tareas de prueba de carga descritas arriba. El principal de Locust se implementa como un controlador de replicación con una única réplica porque necesitamos solo un principal. Un controlador de replicación es útil incluso cuando se implementa un solo pod, ya que garantiza una alta disponibilidad.

La configuración del controlador de replicación especifica varios elementos, incluidos el nombre del controlador (locust-master), las etiquetas para organización (name: locust, role: master) y los puertos que el contenedor debe exponer (8089 para la interfaz web, 5557 y 5558 para comunicarse con los trabajadores). Esta información se usa más tarde para configurar el controlador de trabajadores de Locust. El siguiente fragmento contiene la configuración de los puertos:

...
ports:
  - name: locust-master-web
    containerPort: 8089
    protocol: TCP
  - name: locust-master-port-1
    containerPort: 5557
    protocol: TCP
  - name: locust-master-port-2
    containerPort: 5558
    protocol: TCP

A continuación, implementamos un servicio para garantizar que los puertos expuestos sean accesibles a otros pods a través del hostname:port dentro del clúster y que se pueda hacer referencia a través de un nombre de puerto descriptivo. El servicio permite a los trabajadores de Locust descubrir con facilidad el principal y comunicarse de manera confiable con él, incluso si el principal falla y el controlador de replicación lo reemplaza con un pod nuevo. El servicio principal de Locust también incluye una directiva para crear una regla de reenvío externa al nivel de clúster, que proporciona la capacidad al tráfico externo de acceder a los recursos del clúster. Ten en cuenta que aún debes crear reglas de firewall para proporcionar acceso completo a las instancias de destino.

Después de implementar el principal de Locust, puedes acceder a la interfaz web con la dirección IP pública de la regla de reenvío externo. Después de implementar los trabajadores de Locust, puedes iniciar la simulación y ver estadísticas adicionales a través de la interfaz web de Locust.

Los trabajadores de la prueba de carga

El siguiente componente de la implementación incluye a los trabajadores de Locust, que ejecutan las tareas de prueba de carga descritas antes. Los trabajadores de Locust se implementan mediante un único controlador de replicación que crea diez pods. Los pods se extienden a través del clúster de Kubernetes. Cada pod usa variables de entorno para controlar información importante de la configuración, como el nombre de host del sistema a prueba y el nombre de host del principal de Locust. La configuración del controlador de replicación del trabajador se puede encontrar en el instructivo a continuación. La configuración contiene el nombre del controlador, locust-worker, las etiquetas para la organización, name: locust, role: worker, y las variables de entorno descritas antes. El siguiente fragmento contiene la configuración del nombre, las etiquetas y el número de réplicas:

kind: ReplicationController
  apiVersion: v1
  metadata:
    name: locust-worker
    labels:
      name: locust
      role: worker
  spec:
    replicas: 10
    selector:
      name: locust
      role: worker
...

Para los trabajadores de Locust, no es necesario implementar ningún servicio adicional porque los propios pods trabajadores no necesitan ser compatibles con ninguna comunicación de entrada: se conectan de modo directo al pod principal de Locust.

En el siguiente diagrama, se muestra la relación entre el principal y los trabajadores de Locust.

Diagrama que muestra los pods de Kubernetes con el nodo principal y los nodos trabajadores de Locust.

Después de que el controlador de replicación implementa los trabajadores de Locust, puedes volver a la interfaz web principal de Locust y ver que la cantidad de secundarios corresponde a la cantidad de trabajadores implementados.

Ejecuta tareas de pruebas de carga

Comienza la prueba de carga

La interfaz web principal de Locust te permite ejecutar las tareas de prueba de carga en el sistema a prueba, como se muestra en la siguiente imagen:

Una captura de pantalla de la interfaz web inicial de Locust.

Para comenzar, especifica el número total de usuarios a simular y la velocidad a la que debe generarse cada usuario. A continuación, haz clic en Start swarming (Comenzar a generar) para comenzar la simulación. A medida que avanza el tiempo y se generan los usuarios, verás que las estadísticas comienzan a agregarse para las métricas de simulación, como la cantidad de solicitudes y solicitudes por segundo, como se muestra en la siguiente imagen:

Una captura de pantalla de la interfaz web de Locust mientras la generación está en marcha.

Si quieres detener la simulación, haz clic en Stop (Detener) y la prueba terminará. Los resultados completos se pueden descargar en una hoja de cálculo.

Escala clientes

Aumentar la cantidad de usuarios simulados requerirá de un aumento en el número de pods trabajadores de Locust. Como se especifica en el controlador de trabajadores de Locust, el controlador de replicación implementa 10 pods trabajadores de Locust. Para aumentar la cantidad de pods que implementa el controlador de replicación, Kubernetes ofrece la capacidad de cambiar el tamaño de los controladores sin tener que volver a implementarlos. Por ejemplo, puedes cambiar el número de pods trabajadores con la herramienta de línea de comandos de kubectl. El siguiente comando escala el grupo de pods trabajadores de Locust a 20:

$ kubectl scale --replicas=20 replicationcontrollers locust-worker

Después de generar el comando de escala, espera unos minutos para que todos los pods se implementen y comiencen. Una vez que todos los pods comenzaron, vuelve a la interfaz web principal de Locust y reinicia las pruebas de carga.

Recursos y costos

En esta solución se usan cuatro nodos de GKE, cada uno de ellos respaldado por una instancia estándar de VM de Compute Engine del tipo n1-standard-1. Puedes usar la calculadora de precios de GCP para obtener una estimación del costo mensual de ejecutar este clúster de contenedores. Como se mencionó antes, puedes personalizar el tamaño del clúster de contenedores para adaptarlo a tus necesidades. La calculadora de precios te permite personalizar las características del clúster para obtener una estimación de lo que costaría escalar hacia arriba o hacia abajo.

Pasos siguientes

Ya viste cómo usar GKE con el fin de crear un marco de trabajo de prueba de carga para una aplicación web simple. GKE te permite especificar el número de nodos de contenedores que actúan como la base para tu marco de trabajo de pruebas de carga. GKE también te permite organizar los trabajadores de pruebas de carga en pods y declarar cuántos pods deseas que GKE mantenga en ejecución.

Puedes usar este mismo patrón a fin de crear marcos de trabajo de prueba de carga para muchas situaciones y aplicaciones diferentes. Por ejemplo, puedes usar este patrón con el fin de crear marcos de trabajo de prueba de carga para sistemas de mensajería, sistemas de administración de transmisión de datos y sistemas de bases de datos. Puedes crear nuevas tareas de Locust o incluso cambiar a un marco de trabajo de prueba de carga diferente.

Otra forma de ampliar el marco de trabajo presentado en esta solución es personalizar las métricas que se recopilan. Por ejemplo, es posible que desees medir las solicitudes por segundo, supervisar la latencia de la respuesta a medida que aumenta la carga o verificar las tasas de error de respuesta y los tipos de errores. Existen varias opciones de supervisión disponibles, incluido Stackdriver Monitoring.

Instructivo

El contenido completo del instructivo, que incluye las instrucciones y el código fuente, está disponibles en GitHub, en https://github.com/GoogleCloudPlatform/distributed-load-testing-using-kubernetes.

¿Te sirvió esta página? Envíanos tu opinión:

Enviar comentarios sobre…