Introducción
Aunque las aplicaciones sin servidor se vuelven muy populares hoy en día, EC2 todavía se usa bastante en algunos casos de uso. Y si tenemos muchas instancias de EC2, necesitamos centralizar el monitoreo (monitoring). Para simplificar y centralizar el proceso de monitoring de los servicios de AWS, existe Amazon CloudWatch.
Amazon CloudWatch te permite monitorizar los recursos y las aplicaciones de Amazon Web Services (AWS) en tiempo real. Puedes utilizar CloudWatch para recopilar y hacer un seguimiento de métricas, que son las variables que puedes medir sobre tus recursos y aplicaciones. Una métrica representa una serie de datos ordenados en el tiempo que se publican a CloudWatch. Por ejemplo, el uso de la CPU de una determinada instancia EC2 es una métrica proporcionada por Amazon EC2. Existen miles de tipos de métricas como la carga actual de procesador, la memoria operativa ocupada, el espacio ocupado en el disco, etc. En este tutorial vamos a crear 2 métricas como el espacio ocupado de disco y la memoria operativa disponible.
También te ayudará, entender cómo podemos simplemente habilitar y configurar el servicio de CloudWatch para monitorizar las instancias de EC2 usando Terraform. Además de cómo configurar las alarmas de presupuesto que te avisarán en cuanto sobrepasas el límite de uso establecido.
Otra parte importante de CloudWatch es CloudWatch Logs, que te permite centralizar los logs de todos los sistemas, aplicaciones y servicios de AWS en un único registro. Se crean los logs a partir de los ficheros establecidos como fuentes de datos. En este tutorial vamos a crear los logs usando dos ficheros amazon-cloudwatch-agent.log y /var/log/**.log, pero es totalmente customizable y se pueden usar las expresiones regulares para captar los ficheros que hagan falta.
Creando las instancias de EC2 con Terraform
Amazon EC2 (Elastic Cloud Computing) permite a los usuarios crear máquinas virtuales (servidores) según sus necesidades. Podemos definir los núcleos y memoria de los servidores, tipo de disco, sistema operativo y básicamente, puedes hacer lo que quieras con ellos. Como si fuera su propio ordenador físico, pero en la nube. Se puede, por ejemplo, instalar el software requerido que necesitas y alojar un sitio web.
Aunque se puede crear las instancias de AWS manualmente a través de la interfaz gráfica de la consola AWS, en un entorno real es muchísimo más útil utilizar las herramientas de automatización como Ansible o Terraform, que además nos permiten almacenar la configuración y sus cambios. En este tutorial vamos a ver cómo se puede utilizar Terraform para potenciar las instancias de AWS EC2 con CloudWatch Logs. Para saber que es Terraform y como se puede fácilmente crear una instancia de EC2 te aconsejo ver el tutorial “Primeros pasos con Terraform – crear instancia EC2 en AWS”.
En este tutorial nos centramos en cómo crear una instancia que tenga el servicio CloudWatch activado. Para poder recoger los logs y las métricas de diferentes instancias EC2 hay que instalar un programa CloudWatch Agent en cada instancia. Podemos simplificar el proceso y hacerlo de forma automática utilizando Terraform.
Configuración con Terraform
Para tener todo el código en un lugar específico creamos una nueva carpeta y un fichero main.tf con el contenido así:
//establecemos el provider aws provider "aws" { region = "eu-west-1" } //creamos una variable local que contenga el script bash que creamos aparte locals { userdata = templatefile("userdata.sh", { ssm_cloudwatch_config = aws_ssm_parameter.cw_agent.name }) } //creamos una instancia de ec2 y pasamos como argumentos el script userdata y la clave //SSH (MyKey) resource "aws_instance" "this" { ami = "ami-096f43ef67d75e998" instance_type = "t3.micro" iam_instance_profile = aws_iam_instance_profile.this.name user_data = local.userdata key_name = "MyKey" tags = { Name = "EC2-with-cw-agent" } } //guardamos el fichero de configuración de CloudAgent dentro del parámetro SSM resource "aws_ssm_parameter" "cw_agent" { description = "Cloudwatch agent config to configure custom log" name = "/cloudwatch-agent/config" type = "String" value = file("cw_agent_config.json") } //creamos un grupo de logs que vamos a utilizar en el fichero de configuración de CloudAgent resource "aws_cloudwatch_log_group" "LogGroup_1" { name = "application_logs" // Posibles valores: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653, and 0. // Si eliges 0, los eventos en los logs se guardarán para siempre retention_in_days = 1 // tags - (Opcional) Los tags para distinguir los grupos de logs tags = { Environment = "production" Application = "AWS Tutorial" } } //creamos el log stream para los logs del sistema resource "aws_cloudwatch_log_stream" "syslog" { name = "Syslog" //el grupo de logs al que asignamos el log stream log_group_name = aws_cloudwatch_log_group.LogGroup_1.name } //creamos otro log stream para los logs de CloudWatch Agent resource "aws_cloudwatch_log_stream" "cloudwatchlogs" { name = "Cloudwatch" log_group_name = aws_cloudwatch_log_group.LogGroup_1.name }
Explicando el script de Terraform
Este script de Terraform básicamente hace 3 cosas:
- Crea una variable local para cargar el script user_data usando la función templatefile. Este script se ejecutará cuando se provisione la instancia, dejando la configuración del agente preparada para su ejecución. Dentro del script pasamos como parámetro el nombre del fichero de la configuración de CloudWatch Agent. Como alternativa, podríamos construir una imagen base (AMI) que ya tenga el agente instalado y configurado para la aplicación que vamos a desplegar, ahorrando tiempo en el provisionado de la máquina, muy útil cuando trabajamos con aplicaciones que necesitan escalar muy rápidamente.
- Crea una instancia EC2, configurando el perfil de instancia y user_data de las variables locales.
- Creamos el recurso de parámetro SSM (AWS Security Systems Manager Parameter Store), para cargar el fichero de configuración de Cloudwatch Agent cw_agent_config.json y pasar su nombre al script user_data. El uso del parámetro SSM nos permite guardar el fichero cw_agent_config.json y usar su nombre dentro del script user_data. Los parámetros SSM nos permiten guardar las variables de configuración de una manera segura y sencilla.
El script user_data.sh
Vamos a ver detalladamente el script user_data. Este script se ejecuta al lanzar nuestra instancia EC2 con el siguiente contenido:
#! /bin/bash set -e # Imprime todos los logs de CloudWatch Agent exec > >(tee /var/log/user-data.log|logger -t user-data-extra -s 2>/dev/console) 2>&1 # Actualiza los paquetes instalados yum update -y yum upgrade -y # Instala el agente Cloudwatch wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm rpm -U ./amazon-cloudwatch-agent.rpm # Usa la configuración de Cloudwatch SSM que pasamos con el parámetro SSM /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ -a fetch-config \ -m ec2 \ -c ssm:${ssm_cloudwatch_config} -s
Lo que hace el script bash son unos simples pasos como se muestra a continuación:
- Imprimimos todos los logs de CloudWatch Agent a la ubicación especificada.
- Actualizamos la máquina con el comando yum.
- Instalamos el agente CloudWatch.
- Por último, configuramos el agente de CloudWatch usando el archivo de configuración que pasamos como parámetro ssm_cloudwatch_config.
Configurar las políticas de AWS
Las aplicaciones deben firmar sus peticiones de API con credenciales de AWS. Por lo tanto, necesitamos una estrategia para administrar las credenciales entre diferentes instancias de EC2. Sin embargo, distribuir las credenciales a cada instancia con seguridad plantea muchísimos problemas. Pero AWS tiene un mecanismo muy potente que son los roles de IAM (Identity and Access Management) para resolver este problema. En lugar de crear y distribuir los credenciales de AWS, podemos delegar el permiso para realizar peticiones de API mediante roles de IAM. Por ejemplo, podemos utilizar roles de IAM para conceder permisos a aplicaciones que se ejecutan en nuestras instancias y que necesitan utilizar un bucket en Amazon S3. Para especificar permisos para roles de IAM, creamos una política en formato JSON. Una política de IAM debe conceder o denegar permisos para usar una o varias acciones con los recursos de AWS. También, debe especificar los recursos que se pueden utilizar con la acción: pueden ser todos los recursos o, en algunos casos, recursos específicos. La política también puede incluir condiciones que se aplican al recurso.
Configuración de roles/políticas utilizando Terraform
Para que nuestra instancia EC2 pueda enviar las métricas y los logs al repositorio de CloudWatch tenemos que definir el perfil de instancia AWS para esta instancia. El perfil de instancia EC2 es un contenedor de un rol IAM. Para configurar el agente CloudWatch, usamos el parámetro SSM (Security Session Manager). Así que tenemos que añadir la política AmazonEC2RoleforSSM para poder acceder a él. En la misma carpeta creamos un archivo de Terraform instance_profile.tf con el siguiente contenido:
//creamos dos variables con los nombres de políticas locals { role_policy_arns = [ "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM", "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" ] } //creamos el perfil para la instancia EC2 utilizando el rol que hemos establecido luego resource "aws_iam_instance_profile" "this" { name = "EC2-Profile" role = aws_iam_role.this.name } //añadimos dos políticas para poder conectar con SSM y otra el agente de CloudWatchAgent resource "aws_iam_role_policy_attachment" "this" { count = length(local.role_policy_arns) role = aws_iam_role.this.name policy_arn = element(local.role_policy_arns, count.index) } //creamos una política embebida para nuestro rol resource "aws_iam_role_policy" "this" { name = "EC2-Inline-Policy" role = aws_iam_role.this.id policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Effect" : "Allow", "Action" : [ "ssm:GetParameter" ], "Resource" : "*" } ] } ) } //definimos nuestro rol resource "aws_iam_role" "this" { name = "EC2-Role" path = "/" assume_role_policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Action" : "sts:AssumeRole", "Principal" : { "Service" : "ec2.amazonaws.com" }, "Effect" : "Allow" } ] } ) }
- Creamos un perfil de instancia, el nombre de referencia debe ser el mismo que el del script Terraform anterior.
- Añadimos dos políticas a nuestro rol: AmazonEC2RoleForSSM que permite que EC2 se comunique con el servicio SSM y CloudWatchAgentServerPolicy que permite que EC2 se comunique con el servicio CloudWatch.
- Creamos una política customizada para nuestro rol que permita a EC2 hacer llamadas al servicio SSM (ssm: GetParameter). La razón principal por la que necesitamos este permiso es que queremos que el agente de CloudWatch cargue la configuración desde el parámetro de servicio SSM, y tenemos que incluir este permiso que no está incluido en el rol AmazonEC2RoleForSSM.
- Por último, el script creará la política de roles para ec2.amazonaws.com (EC2).
Configurar AWS CloudWatch
En este paso, crearemos el archivo de configuración del agente de Cloudwatch, la configuración le indicará al agente cómo extraer los logs y las métricas.
Creamos un archivo cw_agent_config.json en la misma carpeta con el siguiente contenido:
{ "agent": { "metrics_collection_interval": 60, "logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log" }, "metrics": { "metrics_collected": { "disk": { "resources": ["/", "/tmp"], "measurement": ["disk_used_percent"], "ignore_file_system_types": ["sysfs", "devtmpfs"] }, "mem": { "measurement": ["mem_available_percent"] } }, "aggregation_dimensions": [["InstanceId", "InstanceType"], ["InstanceId"]] }, "logs": { "logs_collected": { "files": { "collect_list": [ { "file_path": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log", "log_group_name": "amazon-cloudwatch-agent.log", "log_stream_name": "amazon-cloudwatch-agent.log", "timezone": "UTC" }, { "file_path": "/var/log/**.log", "log_group_name": "syslog", "log_stream_name": "syslog", "timezone": "Local" } ] } } } }
El fichero JSON configura el agente CloudWatch para recoger las métricas cada 60 segundos y recopilar la métrica disk_used_percent, la métrica mem_available_percent.
Explicando el fichero de configuración de Cloudwatch Agent
Para que CloudWatch Agent recoja los logs del sistema o cualquier otro fichero, incluidos los ficheros de trazas de tu aplicación, puedes definir el array collect_list dentro de la sección logs y establecer las entradas para cada fichero de logs.
-
- file_path: especifica la ruta del archivo de registro para cargar en CloudWatch Logs. Se aceptan las reglas estándar de comparación glob de Unix, con la adición de ** como un super asterisco. Por ejemplo, especificar /var/log/**.log hace que se recopilen todos los archivos .log dentro de la carpeta /var/log;
- log_group_name: el nombre de grupo donde va a aparecer los logs del fichero;
- log_sream_name: el nombre de log stream al que se asigna los logs del fichero;
- multi_line_start_pattern: especifica el patrón en forma de la expresión regular (regex) para identificar el inicio de un mensaje de log (registro). Un mensaje de registro está formado por una línea que coincide con el patrón y las líneas siguientes que no coinciden con el patrón. Si omites este campo, el modo multilínea se deshabilita y cualquier línea que comience con un carácter que no sea un espacio cierra el mensaje de registro anterior e inicia un nuevo mensaje de registro. Si incluyes este campo puedes usar el formato de fecha establecido en el campo date_time_format de esta manera {date_time_format} para identificar el inicio del mensaje de log .
- date_time_format: el formato de fecha para el mensaje de log. Si omites este campo se utiliza el tiempo actual. Se utiliza el texto y los símbolos especiales que empiezan con %, por ejemplo ‘%d-%b-%Y %H:%M:%S UTC’
Para conocer todas las opciones de configuración de CloudWatch Agent puedes verlas en la documentación oficial de AWS.
Por último, ahora podemos probar nuestra configuración de Terraform ejecutando el siguiente comando dentro de nuestra carpeta:
terraform apply --auto-approve
Y deberías ver una instancia nueva de EC2 en tu cuenta. Deja que EC2 se ejecute durante unos minutos para generar suficiente métrica y vete a la consola de AWS.
El uso de AWS Cloudwatch
Para ver las métricas y los logs de Cloudwatch tienes que ir a la consola de AWS, luego Cloudwatch -> Metrics.
Vemos el espacio customizado donde están nuestras métricas.
Si entras en el espacio CWAgent (nombre por defecto, se puede cambiar) te aparecerán las métricas que puedes ver.
Pinchando cada una, puedes ver la lista de todas las instancias y los valores de métrica para cada una.
Para ver los logs tienes que elegir Logs -> Log groups dentro del panel Cloudwatch.
Se puede ver el grupo application_logs que hemos establecido dentro de nuestro fichero de configuración main.tf. Dentro de este grupo están dos log streams: Syslog y Cloudwatch. Cada log stream representa un fichero con los logs que va actualizándose.
Pinchando cada log stream se puede observar el contenido actualizado de cada fichero.
Así de este modo, podemos recoger los logs que queramos desde diferentes instancias y verlos en un sitio único sin ir a los logs de cada instancia.
Capa gratuita de AWS Cloudwatch
La capa gratuita de AWS te da algunos servicios gratuitamente, pero hay que tener cuidado y no sobrepasar los límites. Puedes ver los precios de Cloudwatch aquí.
Métricas | métricas de monitoreo básico (una métrica que mide un valor cada 5 minutos)
10 métricas de monitoreo detallado (una métrica que mide un valor cada minuto) 1 millón de solicitudes API de Cloudwatch (no aplicable a GetMetricData ni GetMetricWidgetImage) |
Panel | 3 paneles (dashboards) para hasta 50 métricas al mes.
Un panel es una vista que representa diferentes métricas en forma de diferentes widgets (gráficos o simplemente texto). |
Alarmas | 10 métricas de alarma (no se aplica a alarmas de alta resolución)
Una alarma vigila una única métrica de CloudWatch o el resultado de una expresión matemática basada en métricas. La alarma realiza una o varias acciones según el valor de la métrica o expresión con respecto a un umbral durante varios períodos de tiempo. Una alarma de alta resolución es una alarma para un periodo de 10 segundos. La resolución estándar para una alarma son 60 segundos. |
Logs | 5 GB de datos (incorporación, almacenamiento y archivo, y datos escaneados por las consultas de Logs Insights) |
Eventos | Se incluyen todos los eventos excepto los personalizados.
Amazon EventBridge proporciona un flujo casi en tiempo real de eventos del sistema que describen cambios en los recursos de Amazon Web Services (AWS). Mediante las reglas sencillas puedes asignar los eventos y dirigirlos a uno o más flujos. Los eventos de CloudWatch conocen los cambios a medida que se producen y responden a estos cambios enviando mensajes para responder al entorno, activando funciones, realizando cambios y captando información del estado. Todos los eventos del estado que emiten los servicios de AWS son gratuitos. Pero si tu aplicación emite un evento personalizado tienes que pagar 1 USD por 1 millón de eventos (depende de la región). |
Contributor Insights | 1 regla de Contributor Insights por mes
El primer millón de eventos de registro que coinciden con la regla por mes Contributor Insight se usa para analizar los logs y crear reglas que te permite encontrar el mayor contribuyente de cualquier error o evento analizando los logs. Una regla define los campos de logs que vas a usar para determinar el mayor contribuyente como por ejemplo su IpAddress. |
Synthetics | 100 ejecuciones de canary por mes
Amazon CloudWatch Synthetics se usa para crear canaries, los scripts configurables que se ejecutan cada cierto tiempo, para monitorizar los URLs y las APIs. Los canaries siguen las mismas rutas y realizan las mismas acciones que un cliente, lo que permite verificar continuamente experiencia de un cliente incluso cuando no tiene tráfico de clientes en las aplicaciones. Mediante el uso de canaries, puedes descubrir problemas antes de que tus clientes lo hagan. Los canaries son scripts escritos en Node.js o Python. Crean las lambdas en tu cuenta de AWS y funcionan a través de los protocolos HTTP y HTTPS. |
Los presupuestos
Para poder controlar los gastos de AWS es conveniente crear un presupuesto (budget) que nos va avisando sobre el gasto actual a medida que generemos más peticiones, datos etc.
Vayamos a la consola AWS -> Billing -> Budgets y pulsamos el botón “Create budget”.
Nos da la opción de elegir el tipo de presupuesto. Cost budget nos permite controlar los gastos. Usage budget nos permite controlar el uso. Tienes que elegir Usage type – Cloudwatch para ver los tipos de uso que puedes observar. En el caso de Cloudwatch Amazon de momento solo permite controlar algunos parámetros del uso de Cloudwatch como las peticiones o el volumen de los datos procesados, pero no todos. Pero podemos crear el presupuesto con el tipo de uso CW:Requests(Request) o EU-DataProcessingBytes(GB) para tener un aviso temprano en el caso de que sobrepasamos el límite de las peticiones o del tamaño de los logs.
Se puede establecer el presupuesto (Budgeted amount) anual, mensual, trimestral etc. Amazon te da la información sobre el consumo actual para poder estimar el presupuesto necesario de cara al futuro. Como Amazon Cloudwatch te da 1 millón de peticiones de API Cloudwatch puedes dividir 1 millón por 12 meses o establecer un presupuesto anual de 1 millón de peticiones (el servicio de Cloudwatch es gratuito, pero existen ciertos límites). Para los logs puedes crear otro presupuesto con el tipo de uso TimedStorage con el límite de 5 gigas.
Las notificaciones de los presupuestos
Después de establecer el presupuesto, tenemos que poner un umbral de aviso que determine el momento en el que nos avisará Amazon. Tiene sentido poner el umbral al 80% para estar preparado a que el presupuesto se nos haya agotado. Lamentablemente, no existe la opción de que automáticamente pare el servicio que vaya a superar esos límites, por lo que será necesario configurar alertas y realizar la acción manualmente.
El último, pero lo más importante es establecer los canales de comunicación que va a usar Amazon para comunicarte la alerta del presupuesto. Se puede directamente crear una lista de emails o utilizar Amazon SNS que te da muchísimas más opciones como SMS, Lambda, HTTP, SQS etc.
Es aconsejable aparte de los presupuestos de uso crear también los presupuestos de coste para estar más seguro de no sobrepasar los límites del gasto. Creando un presupuesto de 1 euro con un aviso temprano (por ejemplo al 50%) Amazon te avisará si has usado algunos de sus servicios de forma extensiva y has superado los límites que provee la capa gratuita.
Conclusiones
En este artículo hemos usado Terraform para construir una instancia de EC2 con los permisos de Session Manager y Cloudwatch Agent para mostrar las métricas no predeterminadas. Además podemos usar el agente Cloudwatch para enviar nuestros logs desde diferentes instancias a los log groups de Cloudwatch para centralizar todo el proceso de logs. Cuando configures el agente de Cloudwatch, es importante que solo definas las métricas que sean importantes para ti porque no son gratuitas.
Puedes encontrar el código completo en el repositorio de github.
Si quieres seguir aprendiendo sobre el tema, no te pierdas el tutorial Monitorizar aplicaciones en un cluster de Kubernetes.