Problemas de Saturación en un Cluster de ElastiCache: Monitoreo y Alerta con AWS CloudWatch

Problemas de Saturación en un Cluster de ElastiCache: Monitoreo y Alerta con AWS CloudWatch

Cuando un cliente experimenta problemas de saturación en su cluster de Amazon ElastiCache, es crucial implementar una solución para monitorear y alertar sobre la condición de la cola de Redis. En este artículo, exploraremos cómo crear un script en Python que se conecta a Redis, obtiene métricas de una cola, y las envía a AWS CloudWatch. Además, veremos cómo configurar los permisos necesarios para ejecutar este script en un entorno AWS.

El Problema de Saturación

ElastiCache es un servicio de caché en memoria gestionado por AWS que se utiliza para mejorar el rendimiento de las aplicaciones al reducir la latencia de las consultas de datos. Sin embargo, cuando la carga en el cluster es alta, las colas de Redis pueden saturarse, afectando el rendimiento general. Para abordar este problema, necesitamos una manera de monitorear el estado de la cola y generar alertas.

El Script de Monitoreo en Python

A continuación, presentamos un script de Python que realiza las siguientes tareas:

  1. Conexión a Redis con TLS

  2. Obtención del número de tareas pendientes en la cola celery

  3. Obtención de los últimos 10 elementos de la cola

  4. Envío de métricas a CloudWatch

  5. Registro de logs en CloudWatch Logs

import redis
import boto3
import time
from datetime import datetime

def main():
    # Configuración de la conexión Redis
    redis_host = 'clustercfg.privado.bsuboi.use1.cache.amazonaws.com'
    redis_port = 6379

    # Conectar a Redis con TLS
    r = redis.Redis(
        host=redis_host,
        port=redis_port,
        ssl=True,
        ssl_cert_reqs=None
    )

    # Obtener el número de tareas pendientes en la cola 'celery'
    pending_tasks = r.llen('celery')

    # Obtener las últimas 10 tareas de la cola 'celery'
    last_10_tasks = r.lrange('celery', -10, -1)

    # Configuración de CloudWatch Logs
    log_group_name = '/aws/lambda/your-log-group'

    # Crear cliente de CloudWatch Logs
    logs_client = boto3.client('logs')

    # Generar un nombre único para el stream de logs basado en la fecha y hora actual
    timestamp_str = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
    log_stream_name = f'log-stream-{timestamp_str}'

    # Verificar si el log stream existe; si no, crear uno nuevo
    logs_client.create_log_stream(logGroupName=log_group_name, logStreamName=log_stream_name)

    # Configuración de CloudWatch Metrics
    cloudwatch = boto3.client('cloudwatch')

    # Enviar la métrica a CloudWatch
    cloudwatch.put_metric_data(
        Namespace='CeleryTasks',
        MetricData=[
            {
                'MetricName': 'PendingTasks',
                'Dimensions': [
                    {
                        'Name': 'QueueName',
                        'Value': 'celery'
                    },
                ],
                'Unit': 'Count',
                'Value': pending_tasks
            },
        ]
    )

    # Mensaje para enviar a CloudWatch Logs
    if pending_tasks < 3:
        log_message = 'The server is OK'
    else:
        log_message = 'PROBLEM ON THE SERVER'

    # Mensajes adicionales para enviar a CloudWatch Logs
    additional_messages = [
        f'Pending tasks in the celery queue: {pending_tasks}',
        f'Message logged in CloudWatch Logs: {log_message}'
    ]

    # Añadir las últimas 10 tareas a los mensajes
    last_10_tasks_message = 'Last 10 tasks in the queue:\n' + '\n'.join(task.decode('utf-8') for task in last_10_tasks)
    additional_messages.append(last_10_tasks_message)

    # Enviar los mensajes a CloudWatch Logs
    timestamp = int(time.time() * 1000)  # Convertir el tiempo a milisegundos
    log_events = [
        {
            'timestamp': timestamp,
            'message': message
        }
        for message in additional_messages
    ]

    logs_client.put_log_events(
        logGroupName=log_group_name,
        logStreamName=log_stream_name,
        logEvents=log_events
    )

    # Imprimir en la salida estándar
    print(f'Pending tasks in the celery queue: {pending_tasks}')
    print(f'Message logged in CloudWatch Logs: {log_message}')
    print(f'Last 10 tasks in the queue:\n{last_10_tasks_message}')

if __name__ == "__main__":
    main()

Configuración de Permisos en IAM

Para que este script funcione correctamente, el usuario de IAM debe tener permisos adecuados para acceder a CloudWatch y CloudWatch Logs. A continuación se muestra una política de IAM que puedes adjuntar al usuario:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "cloudwatch:PutMetricData"
            ],
            "Resource": "*"
        }
    ]
}

Pasos para configurar el usuario IAM:

  1. Inicia sesión en la Consola de Gestión de AWS.

  2. Navega a la sección de IAM (Identity and Access Management).

  3. Selecciona "Usuarios" y elige el usuario que utilizarás.

  4. En la pestaña "Permisos", haz clic en "Agregar permisos".

  5. Selecciona "Adjuntar políticas existentes directamente".

  6. Busca y selecciona la política personalizada (o crea una nueva si es necesario).

  7. Revisa los permisos y haz clic en "Agregar permisos".

Configuración de AWS CLI

Asegúrate de que AWS CLI esté configurado con las credenciales del usuario IAM:

aws configure

Esto te pedirá que ingreses tu AWS Access Key ID, AWS Secret Access Key, la región predeterminada y el formato de salida. Completa estos campos con las credenciales del usuario IAM que has configurado.

Este script proporciona una solución efectiva para monitorear la saturación en un cluster de ElastiCache Redis y alertar sobre posibles problemas mediante CloudWatch. La configuración adecuada de permisos en IAM y de AWS CLI es esencial para asegurar que el script funcione correctamente y tenga acceso a los recursos necesarios.

Implementar este tipo de monitoreo puede ayudar a mantener el rendimiento óptimo de tus aplicaciones y a prevenir problemas antes de que afecten a los usuarios finales.