A veces las aplicaciones .NET caen en la producción, y nadie sabe por qué, porque los registros y las métricas están bien. Es bastante molesto y hace que el desorden sea muy desagradable. En tales casos, los desperdicios de memoria podrían simplificar el desorden y reducir el tiempo de resolución de problemas de días a minutos. Este artículo explica cómo configurar los suministros para las aplicaciones .NET implementadas en y luego transmitiéndolos al equipo de desarrollo de la manera más conveniente y segura. AWS ECS Fargate En este artículo, crearemos recursos de AWS, y me referiré a la documentación de AWS en situaciones particulares. IAC no estará en nuestro foco. Sin embargo, si disfrutas de Terraform tanto como yo, puedes usar módulos de código abierto de AWS para cada sección del artículo. https://github.com/cloudposse https://github.com/terraform-aws-modules En este artículo, crearemos recursos de AWS, y me referiré a la documentación de AWS en situaciones particulares. IAC no estará en nuestro foco. Sin embargo, si disfrutas de Terraform tanto como yo, puedes usar módulos de código abierto de AWS para cada sección del artículo. https://github.com/cloudposse https://github.com/terraform-aws-modules https://github.com/cloudposse https://github.com/terraform-aws-modules Arquitectura de Soluciones Es hora de echar un vistazo a nuestra arquitectura. Comenzaré presumiendo que el equipo de desarrolladores no está considerando sacar los desechos .NET de almacenamiento como EBS o EFS debido a su complejidad. S3 es mucho más sencillo para los desarrolladores obtener cualquier tipo de archivo, y se ajusta perfectamente a nuestras expectativas. Además de eso, recibir notificaciones proactivas cuando se genera un nuevo .NET dump sería bastante valioso. Por ejemplo, usaré Slack, pero otras opciones incluyen Teams, Mattermost, WhatsApp, y así sucesivamente. Es bastante complicado anexar un bucket S3 nativamente a ECS. Por esa razón, crearemos una capa de middleware construida sobre la función EFS, DataSync y sidecar ECS container / Lambda. EFS se utilizará como un almacenamiento de archivos intermedio para todas nuestras tareas ECS, Datasync transferirá datos de EFS a S3 automáticamente, y un contenedor sidecar o Lambda limpiará los datos antiguos de EFS. Veamos rápidamente el diagrama: AWS Lambda deletes old EFS files by the schedule configured in EventBridge. Alternatively, during ECS Task bootstrap phase, sidecar container removes outdated dumps from EFS and quits. janitor During .NET application crash, a new dump is created at EFS filesystem, and only after that the process is terminated. DataSync moves data to S3 after a new file is uploaded to EFS. When an S3 hook detects a newly uploaded file, AWS Lambda is triggered. AWS Lambda uses IAM to obtain the necessary secrets from AWS Secret Manager. AWS Lambda sends a message to Slack via API. Implementación paso a paso Creación de tareas ECS Fargate En esta sección necesitamos crear una Tareas ECS Fargate usando una aplicación .NET de muestra. Precondiciones Antes de proceder, hay algunos pasos que hay que completar: Setup ECS cluster via AWS Console, or Terraform. An official AWS guide: Creating an Amazon ECS cluster for Fargate workloads Create an IAM execution role for ECS task. To do it, you can follow . In the scope of this article I will use name for IAM execution role. this AWS guide kvendingoldo-dotnet-crash-dump-demo Crear un clúster de Amazon ECS para cargas de trabajo Fargate Guía de AWS Este mínimo Para la ejecución de la función será suficiente: Trust policy { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } Además, el mínimo : permissions policy { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" } ] } Definición de Tareas Una vez que todos los prerequisitos estén listos, es hora de crear una tarea mínima de Fargate con una aplicación .NET de muestra. , y use esta definición de tareas .json archivo: Guía oficial de AWS Guía oficial de AWS { "containerDefinitions": [ { "cpu": 0, "essential": true, "image": "mcr.microsoft.com/dotnet/samples:aspnetapp", "mountPoints": [], "name": "app", "portMappings": [ { "containerPort": 8000, "hostPort": 8000, "protocol": "tcp" } ], "systemControls": [], "volumesFrom": [] } ], "cpu": "256", "executionRoleArn": "kvendingoldo-dotnet-crash-dump-demo", "family": "kvendingoldo-dotnet-crash-dump-demo", "memory": "512", "networkMode": "awsvpc", "placementConstraints": [], "requiresCompatibilities": ["FARGATE"], "volumes": [], "tags": [] } Configuración de .NET dumps Por defecto, las aplicaciones .NET no generan ningún desperdicio. Para configurarlo, debemos establecer las siguientes variables ambientales: # Forces the runtime to generate a stack dump on unhandled exceptions. COMPlus_StackDumpOnUnhandledException=1 # Enable mini dump generation on crash COMPlus_DbgEnableMiniDump=1 # Choose dump type: # 1 = Mini, # 2 = Full (use carefully) # 4 = Triage (includes stack, threads, and some heap info — a good balance for debugging). COMPlus_DbgMiniDumpType=2 # Target path for dump file (EFS is mounted here) COMPlus_DbgMiniDumpName=/dumps/dump-%e-%p-%t.dmp Estas variables se pueden agregar directamente al Dockerfile o definir como variables de entorno en el json de definición de tareas de ECS. En nuestro ejemplo, vamos a inyectarlos en la especificación de tareas de ECS. Para lograr esto, los agregaremos a la Como se muestra abajo: containerDefinitions[0].environment "environment": [ { "name": "COMPlus_StackDumpOnUnhandledException", "value": "1" }, { "name": "COMPlus_DbgMiniDumpType", "value": "4" }, { "name": "COMPlus_DbgEnableMiniDump", "value": "1" }, { "name": "COMPlus_DbgMiniDumpName", "value": "/dumps/%t-kvendingoldo-dotnet-demo-crash.dmp" } ] Como puede ver, uso algunos poseedores de lugar en COMPlus_DbgMiniDumpName. Dotnet expande automáticamente los siguientes poseedores de lugar en el nombre del archivo de desechos: %e - Nombre ejecutable %p - ID de proceso %t - timestamp Consulte estos dos enlaces para obtener más información sobre la recopilación y el análisis de los desperdicios de .NET: Colectar .NET Crash Dumps (Microsoft Learn) Debugging problemas de memoria .NET Core (en Linux) con descarga de dotnet Como puede ver, uso algunos poseedores de lugar en COMPlus_DbgMiniDumpName. Dotnet expande automáticamente los siguientes poseedores de lugar en el nombre del archivo de desechos: %e - Nombre ejecutable %p - ID de proceso %t - timestamp Consulte estos dos enlaces para obtener más información sobre la recopilación y el análisis de los desperdicios de .NET: Colectar .NET Crash Dumps (Microsoft Learn) Debugging problemas de memoria .NET Core (en Linux) con descarga de dotnet Colectar .NET Crash Dumps (Microsoft Learn) Debugging problemas de memoria .NET Core (en Linux) con descarga de dotnet Crear almacenamiento EFS y montarlo en la Tarea ECS Fargate Como mencioné al comienzo de este artículo, unir un bucket S3 a un trabajo ECS es bastante difícil; en cambio, usaremos como almacenamiento intermedio para los archivos de descarga .NET, que se pueden montar fácilmente en un conjunto de tareas ECS. Amazon EFS (Elastic File System) Para crear almacenamiento EFS, siga la guía oficial de AWS: Amazon ECS Tutorial: Uso de sistemas de archivos Amazon EFS Para crear almacenamiento EFS, siga la guía oficial de AWS: Amazon ECS Tutorial: Using Amazon EFS File Systems Amazon ECS Tutorial: Uso de los sistemas de archivos Amazon EFS No hay nada especial que añadir a la documentación oficial. simplemente asegúrese de que: EFS y ECS Cluster están en el mismo VPC EFS se puede acceder por tareas ECS a través de NFS (port 2049/tcp). Permitir el acceso de entrada a las puertas NFS en el grupo de seguridad de EFS para hacerlo. Para montar el sistema de archivos EFS en la tarea ECS debemos conceder los permisos necesarios a la Función IAM (prestar atención a los poseedores de lugares): kvendingoldo-dotnet-crash-dump-demo { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowEFSAccess", "Effect": "Allow", "Action": [ "elasticfilesystem:ClientMount", "elasticfilesystem:ClientWrite", "elasticfilesystem:ClientRootAccess" ], "Resource": "arn:aws:elasticfilesystem:<region>:<account-id>:file-system/<filesystem-id>" } ] } Como último paso, defina los volúmenes EFS y los puntos de montaje en su definición de tareas ECS (change fileSystemId). con su verdadero ID de sistema de archivos después del bootstrapping): fs-xxxxxx "volumes": [ { "name": "dotnet-dumps", "efsVolumeConfiguration": { "fileSystemId": "fs-xxxxxx", "rootDirectory": "/" } } ] "mountPoints": [ { "containerPath": "/dumps", "readOnly": false, "sourceVolume": "dotnet-dumps" } ] Configurar AWS DataSync para transferir archivos EFS a S3 El servicio DataSync es una herramienta estándar de AWS para la transferencia de datos entre diferentes tipos de almacenamiento. En nuestro caso, nos ayudará a mover.NET desde EFS a S3. Para alcanzar nuestro objetivo, debemos: Create an S3 bucket to store our.NET dumps. Further in this article I’ll use S3 bucket name kvendingoldo-dotnet-demo-crash Use to create a bucket. this official doc Create DataSync Use to create DataSync. this official doc Some service parameters I'll be using: Source: EFS Destination: S3 bucket (e.g., ) s3://kvendingoldo-dotnet-demo-crash/ Include path filters like /dumps/* Schedule sync every minute El Doc Oficial El Doc Oficial Crea alertas de Slack basadas en AWS Lambda Como se dijo anteriormente, las alertas sobre los desperdicios de new.NET son extremadamente útiles para el equipo de desarrollo. Desde el punto de vista de la arquitectura, las alertas se pueden construir de una manera diferente: Una simple función lambda que envía mensajes a Slack a través de la API y desencadenados por eventos de S3. Los mensajes se publican en un tema SNS utilizando notificaciones de eventos S3 configuradas, que luego desencadenan una función Lambda para enviar los eventos a Slack. Dado que no esperamos una carga alta, la primera opción es mejor para nosotros.En caso, si desea implementar la segunda opción use estos dos enlaces: Módulo Terraform para el despliegue de SNS y Lambda Stack Guía para configurar eventos S3 en SNS Módulo Terraform para el despliegue de SNS y Lambda Stack Guía para configurar eventos S3 en SNS Usamos Python para enviar mensajes a Slack. En este artículo solo enviaremos un enlace al archivo S3, pero en algunos casos es necesario enviar todo el archivo. Slack API ha cambiado hace algún tiempo, y el envío de archivos puede ser un poco complicado. Si desea saber más, por favor vea el artículo "Upload files to Slack with Python". Usamos Python para enviar mensajes a Slack. En este artículo solo enviaremos un enlace al archivo S3, pero en algunos casos se requiere enviar todo el archivo. Slack API ha cambiado hace algún tiempo, y el envío de archivos puede ser un poco complicado. Si desea saber más, por favor vea el " “El artículo. Cargar archivos a Slack con Python Cargar archivos a Slack con Python Bueno, vamos a construir la alerta paso a paso: 1. Create Slack secret Creación de AWS Secret Manager Secret Con un campo: Esta clave debe contener un enlace a su webhook Slack (para obtener más información sobre la verificación de webhook Slack). ) de kvendingoldo-dotnet-crash-dump-demo slack_webhook_url the official guide La Guía Oficial 2. Configure AWS Lambda No vamos a profundizar en la creación de AWS Lambda, pero destacaremos algunos puntos clave.Para obtener más información fundamental sobre la configuración de AWS Lambda, consulte . La Guía Oficial La Guía Oficial Asegúrese de que la función Lambda IAM tenga permiso para leer desde S3: { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::kvendingoldo-dotnet-demo-crash/*" } 2.2: Para obtener datos del gerente de AWS Secret, debemos especificar la variable de entorno en la configuración de AWS Lambda: SECRET_NAME=kvendingoldo-dotnet-demo-crash Subir el código de Python a Lambda import json import urllib3 import os import boto3 def get_secret(secret_name): client = boto3.client("secretsmanager") try: response = client.get_secret_value(SecretId=secret_name) if "SecretString" in response: secret = response["SecretString"] try: return json.loads(secret) except json.JSONDecodeError: return secret else: return response["SecretBinary"] except Exception as e: print(f"Error retrieving secret: {e}") return None def lambda_handler(event, context): print("Event received:", json.dumps(event)) secret_name = os.environ.get('SECRET_NAME', '') if secret_name == "": return { 'statusCode': 500, 'body': json.dumps("SECRET_NAME env variable is empty") } secret = get_secret(secret_name) slack_webhook_url = secret["slack_webhook_url"] for record in event['Records']: bucket_name = record['s3']['bucket']['name'] file_name = record['s3']['object']['key'] region = record['awsRegion'] if ".aws" in file_name: print(f"Skipping internal file: {file_name}") continue message = ( f":package: *New .NET dump is uploaded!*\n\n" f":cloud: Bucket: `{bucket_name}`\n" f":floppy_disk: File: `{file_name}`\n" f":link: Link: https://{bucket_name}.s3.{region}.amazonaws.com/{file_name}" ) http = urllib3.PoolManager() slack_resp = http.request( "POST", slack_webhook_url, body=json.dumps({ "text": message }), headers={ "Content-Type": "application/json" } ) if slack_resp.status != 200: raise Exception( f"Slack webhook request failed with status {slack_resp.status}: {slack_resp.data.decode('utf-8')}") return { "statusCode": 200, "body": json.dumps("Message has been sent successfully!") } 2.4: Configure las Notificaciones de Evento S3 para su bucket S3. y seleccionar » Configure el evento utilizando las siguientes opciones: bucket -> Propiedades -> Notificaciones de Eventos Crear una notificación de evento Nombre del evento: kvendingoldo-dotnet-demo-crash Categoría: DUMPS Tipo de evento: s3:ObjetoCreado:* Objetivo: <Nombre de la función Lambda> Configurar la limpieza de almacenamiento EFS Perfecto, la cadena de entrega de .NET dump está lista, pero ¿qué pasa con el viejo dump? EFS no nos permite borrar archivos viejos usando políticas de ciclo de vida; solo podemos transferirlos al tipo de almacenamiento de acceso infrecuente que no es suficiente si no queremos pagar por el espacio innecesario. Para resolver este problema, hay dos opciones: Crear un contenedor ECS sidecar que limpiará los antiguos archivos EFS en la fase de inicialización Cree una tarea Lambda o ECS que montará EFS y limpiará los archivos antiguos por CRON. Vamos a comprobar los dos. Opción 1: AWS Lambda Esta es la mejor solución porque no está afectada por el ciclo de vida de las tareas ECS y otros factores. Para implementar esta estrategia, necesita crear una función Lambda con un almacenamiento EFS montado (leer más sobre montar un sistema de archivos en Lambda desde y el siguiente código de Python: the official doc El Doc Oficial import os import time import json def lambda_handler(event, context): # Note: you can only mount the filesystem to the /mnt/ directory. directory = '/mnt/dumps' # File pattern to match pattern = 'crash.dmp' # Time in minutes (by default 1d) minutes_old = 1440 # Convert minutes to seconds age_seconds = minutes_old * 60 # Current time now = time.time() for root, dirs, files in os.walk(directory): for file in files: if pattern in file: file_path = os.path.join(root, file) file_mtime = os.path.getmtime(file_path) if now - file_mtime > age_seconds: print(f"Found a file that older than {minutes_old} minutes: {file_path}") try: os.remove(file_path) except Exception as e: print(f"Failed to delete {file_path}: {e}") return { "statusCode": 200, "body": json.dumps("EFS clean-up completed successfully!") } Como puede ver, este es un código simple que elimina los archivos del almacenamiento montado que son antiguos de más de un día. Cuando su Lambda está listo también necesitamos configurar el desencadenante CRON para ejecutar la función periódicamente. y . Reglas de eventos de Cloudwatch Eso es todo, después de todos estos pasos, su almacenamiento EFS se limpiará automáticamente por su calendario CRON. Opción 2: Contenedor ECS sidecar. Para implementar esta opción tenemos que añadir un nuevo contenedor a nuestra definición de tareas: { "essential": false, "name": "janitor", "image": "public.ecr.aws/amazonlinux/amazonlinux:2", "command": [ "bash", "-lc", "find /dumps -name '*crash.dmp*' -type f -mmin +10080 -print -delete" ], "mountPoints": [ { "containerPath": "/dumps", "readOnly": false, "sourceVolume": "dotnet-dumps" } ], "linuxParameters": { "initProcessEnabled": true } } La lógica detrás de esta tarea: Inicializa una nueva tarea ECS con dos contenedores: app y janitor Limpiar archivos EFS desactualizados en el contenedor de janitor y salir. independientemente, la tarea no será interrumpida o detenida debido a la opción ECS "esencial": falso. Como puede ver, esta técnica es bastante sencilla y depende del comando encontrar, que puede personalizar. En el ejemplo, elimina los archivos que son mayores de 10080 minutos (7 días). Por supuesto, esta estrategia es menos deseable que la primera cuando se trata de tareas ECS de larga duración, pero puede ser más conveniente para tareas ECS de corta duración o prototipos. Tiempo de prueba En esta sección, no vamos a hacer una profundización en la construcción de aplicaciones .NET. Para fines de prueba, puede modificar la que usamos al principio. Símbolo Aspnetapp Símbolo Aspnetapp La forma más sencilla de causar un accidente de .NET es Este método se utiliza comúnmente para simular . Environment.FailFast() Cracks duros Simulemos el accidente: Añadir Environment.FailFast("kvendingoldo-dotnet-demo-crash .NET ejemplo de accidente"); línea al archivo dotnet-docker/samples/aspnetapp/aspnetapp/Program.cs. Construye una nueva imagen de docker y vuelva a crear la tarea ECS. ECS Task terminará, pero primero generará un dump de colisión de .NET, que estará disponible en S3 en unos segundos. En la fase final, recibirá un mensaje en su Slack como este: 📦 New .NET dump is uploaded! ☁️ Bucket: kvendingoldo-dotnet-demo-crash 💾 File: 1739104252-kvendingoldo-dotnet-demo-crash.dmp 🔗 Link: https://kvendingoldo-dotnet-demo-crash.s3.us-east-2.amazonaws.com/1739104252-kvendingoldo-dotnet-demo-crash.dmp Posibles mejoras Antes de empaquetar el artículo, me gustaría dar algunos comentarios sobre los posibles cambios: Sería una buena idea generar URLs pre-signados para objetos S3 Define políticas de ciclo de vida para el bucket de S3 para eliminar automáticamente los desechos viejos del bucket Usar SNS para enviar notificaciones sobre nuevos objetos S3 a múltiples destinos Conclusión En entornos de producción, la visibilidad rápida en fallos es crucial.La entrega automática de desechos reduce el tiempo medio de resolución (MTTR) y mejora la respuesta a los incidentes. Como puede ver, implementar este procedimiento no es tan difícil como podría esperar. Sí, usamos muchos servicios de AWS para realizar estas tareas, pero cuando miramos más profundamente, todos son importantes. Espero que este artículo le haya ayudado a construir una cadena de entrega de desechos personal y ha hecho que su equipo de desarrollo sea más feliz. Siéntate libre de modificar el enfoque propuesto, y por favor contacta conmigo en cualquier momento si tienes alguna pregunta. ¡Feliz Codificación!