Parfois, les applications .NET échouent dans la production, et personne ne sait pourquoi, parce que les journaux et les métriques sont OK. C'est assez gênant et rend le dépannage très désagréable. Dans de tels cas, les déchets de mémoire pourraient simplifier le dépannage et réduire le temps de dépannage de jours à minutes. Cet article explique comment configurer les déchets pour les applications .NET déployées sur et ensuite les transmettre à l'équipe de développement de la manière la plus pratique et la plus sûre. AWS ECS Fargate Dans cet article, nous allons créer des ressources AWS, et je vais me référer à la documentation AWS dans des situations particulières. L'IAC ne sera pas dans notre focus. Néanmoins, si vous aimez Terraform autant que moi, vous pouvez utiliser des modules AWS open-source pour chaque section de l'article. De mon côté, je peux vous recommander de jeter un coup d'œil à deux projets de modules AWS Terraform: https://github.com/cloudposse https://github.com/terraform-aws-modules Dans cet article, nous allons créer des ressources AWS, et je vais me référer à la documentation AWS dans des situations particulières. L'IAC ne sera pas dans notre focus. Néanmoins, si vous aimez Terraform autant que moi, vous pouvez utiliser des modules AWS open-source pour chaque section de l'article. De mon côté, je peux vous recommander de jeter un coup d'œil à deux projets de modules AWS Terraform: https://github.com/cloudposse https://github.com/terraform-aws-modules https://github.com/cloudposse https://github.com/terraform-aws-modules Architecture des solutions Il est temps de jeter un coup d'œil à notre architecture. Je vais commencer par supposer que l'équipe de développement n'est pas envisageant de retirer les déchets .NET du stockage comme EBS ou EFS en raison de sa complexité. S3 est beaucoup plus simple pour les développeurs d'obtenir n'importe quel type de fichier, et il correspond parfaitement à nos attentes. En dehors de cela, recevoir des notifications proactives lorsque un nouveau .NET dump est généré serait assez précieux. Par exemple, j'utiliserai Slack, mais d'autres options incluent Teams, Mattermost, WhatsApp, etc. Pour envoyer le message de notification, nous utiliserons des déclencheurs Lambda et S3. Pour cette raison, nous allons créer une couche de middleware construite sur la fonction EFS, DataSync et sidecar ECS conteneur / Lambda. EFS sera utilisé comme un stockage de fichiers intermédiaire pour toutes nos tâches ECS, Datasync transférera automatiquement les données de EFS à S3, et un conteneur sidecar ou Lambda nettoyera les anciennes données de EFS. Revenons rapidement au diagramme : 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. Implémentation étape par étape Create ECS Fargate task Dans cette section, nous devons créer une tâche ECS Fargate à l'aide d'une application .NET exemplaire. Pré-requis Avant de procéder, il y a quelques étapes qui doivent être effectuées : 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 Créer un cluster Amazon ECS pour les charges de travail Fargate Le guide AWS Ce minimum Pour l'exécution du rôle sera suffisant: Trust policy { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } De même que le minimum : permissions policy { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" } ] } Définir la tâche Une fois que toutes les conditions préalables sont prêtes, il est temps de créer une tâche minimale Fargate avec une application .NET exemplaire. , et utilisez cette définition de tâche fichier json: Guide officiel AWS Guide officiel 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": [] } Configuration de .NET dumps Par défaut, les applications .NET ne génèrent pas de déchets. Pour la configurer, nous devons définir les variables environnementales suivantes : # 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 Ces variables peuvent être ajoutées directement à Dockerfile ou définies comme variables environnementales dans le json de définition des tâches ECS. Dans notre exemple, nous les injecterons dans la spécification de la tâche ECS. Pour ce faire, nous les ajouterons à la Comme indiqué ci-dessous : 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" } ] Comme vous pouvez le voir, j'utilise quelques placeholders dans COMPlus_DbgMiniDumpName. Dotnet étend automatiquement les placeholders suivants dans le nom de fichier de dump: %e - nom exécutable %p - ID du processus %t - timestamp Consultez ces deux liens pour plus d’informations sur la collecte et l’analyse des déchets d’accident .NET : Collect .NET Crash Dumps (Microsoft Apprendre) Débogage des problèmes de mémoire .NET Core (sur Linux) avec le dump de dotnet Comme vous pouvez le voir, j'utilise quelques placeholders dans COMPlus_DbgMiniDumpName. Dotnet étend automatiquement les placeholders suivants dans le nom de fichier de dump: %e - nom exécutable %p - ID du processus %t - timestamp Consultez ces deux liens pour plus d’informations sur la collecte et l’analyse des déchets d’accident .NET : Collect .NET Crash Dumps (Microsoft Apprendre) Débogage des problèmes de mémoire .NET Core (sur Linux) avec le dump de dotnet Collect .NET Crash Dumps (Microsoft Apprendre) Débogage des problèmes de mémoire .NET Core (sur Linux) avec le dump de dotnet Créer un stockage EFS et le monter sur la tâche ECS Fargate Comme je l'ai mentionné au début de cet article, attacher un bouchon S3 à un travail ECS est assez difficile; au lieu de cela, nous allons utiliser comme stockage intermédiaire pour les fichiers .NET dump, qui peuvent être facilement montés sur un ensemble de tâches ECS. Amazon EFS (Elastic File System) Pour créer un stockage EFS, suivez le guide officiel AWS: Amazon ECS Tutorial: Utilisation des systèmes de fichiers Amazon EFS Pour créer un stockage EFS, suivez le guide officiel AWS : Amazon ECS Tutorial: Utilisation des systèmes de fichiers Amazon EFS Amazon ECS Tutorial: Utilisation des systèmes de fichiers Amazon EFS Il n'y a rien de spécial à ajouter à la documentation officielle. EFS et ECS Cluster sont dans le même VPC EFS peut être accédé par les tâches ECS via NFS (port 2049/tcp). Permettre l'accès entrant aux ports NFS dans le groupe de sécurité EFS pour ce faire. Pour monter le système de fichiers EFS dans la tâche ECS, nous devons accorder les autorisations nécessaires à la Rôle IAM (attention accordée aux gardiens de place): 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>" } ] } Comme dernière étape, définissez les volumes EFS et les points de montage dans votre définition de tâche ECS (change fileSystemId) avec votre véritable id système de fichiers après le bootstrapping): fs-xxxxxx "volumes": [ { "name": "dotnet-dumps", "efsVolumeConfiguration": { "fileSystemId": "fs-xxxxxx", "rootDirectory": "/" } } ] "mountPoints": [ { "containerPath": "/dumps", "readOnly": false, "sourceVolume": "dotnet-dumps" } ] Configurer AWS DataSync pour transférer des fichiers EFS vers S3 Le service DataSync est un outil AWS standard pour le transfert de données entre différents types de stockage. Dans notre cas, il nous aidera à déposer move.NET de EFS à S3. Pour atteindre notre objectif, nous devons : 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 Ce doc officiel Ce doc officiel Créer des alertes Slack basées sur AWS Lambda Comme mentionné précédemment, les alertes sur les déchets new.NET sont extrêmement utiles pour l’équipe de développement. From the architecture viewpoint, alerts can be built in a different of ways: Une fonction lambda simple qui envoie des messages à Slack via l'API et déclenchée par des événements S3. Les messages sont publiés sur un sujet SNS en utilisant des notifications d'événement S3 configurées, qui déclenchent ensuite une fonction Lambda pour envoyer les événements à Slack. Puisque nous ne nous attendons pas à une charge élevée, la première option est meilleure pour nous. Dans ce cas, si vous voulez mettre en œuvre la deuxième option, utilisez ces deux liens: Module Terraform pour déployer la pile SNS et Lambda Un guide pour configurer les événements S3 en SNS Module Terraform pour déployer la pile SNS et Lambda Un guide pour configurer les événements S3 en SNS Nous utilisons Python pour envoyer des messages dans Slack. Dans cet article, nous n'enverrons qu'un lien vers le fichier S3, mais dans certains cas, il est nécessaire d'envoyer l'intégralité du fichier. L'API Slack a changé il y a quelque temps, et l'envoi de fichiers peut être un peu compliqué. Nous utilisons Python pour envoyer des messages dans Slack. Dans cet article, nous n'enverrons qu'un lien vers le fichier S3, mais dans certains cas, il est nécessaire d'envoyer l'intégralité du fichier. L'API Slack a changé il y a quelque temps, et l'envoi de fichier peut être un peu compliqué. « Article . Uploading files to Slack with Python Uploading files to Slack with Python Ok, construisons l'alerte étape par étape: 1. Create Slack secret Créer AWS Secret Manager secret Avec un champ : Cette clé doit contenir un lien vers votre webhook Slack (pour en savoir plus sur la vérification du webhook Slack) ) de kvendingoldo-dotnet-crash-dump-demo slack_webhook_url Le guide officiel Le guide officiel 2. Configure AWS Lambda Nous n'entrerons pas en profondeur sur la création d'AWS Lambda, mais nous mettrons en évidence certains points clés. . Le guide officiel Le guide officiel Assurez-vous que le rôle Lambda IAM a des autorisations de lecture à partir de S3: { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::kvendingoldo-dotnet-demo-crash/*" } 2.2 : Pour obtenir des données du gestionnaire AWS Secret, nous devons spécifier la variable environnementale dans la configuration AWS Lambda : SECRET_NAME=kvendingoldo-dotnet-demo-crash 2.3 : télécharger le code Python sur 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 : Configurez les notifications d'événement S3 pour votre boîte S3. « Sélectionner » Configurez l’événement en utilisant les options suivantes : bucket -> propriétés -> Notifications d’événement Créer une notification d’événement Nom de l'événement: kvendingoldo-dotnet-demo-crash Étiquette : dumps Type d’événement : s3:ObjectCreated:* Objectif : <Nom de votre fonction Lambda> Configurer le nettoyage du stockage EFS Parfait, la chaîne de livraison des déchets .NET est prête, mais qu’en est-il du vieux déchet? EFS ne nous permet pas de supprimer de vieux fichiers en utilisant des politiques de cycle de vie; nous ne pouvons les transférer que vers le type de stockage d’Access peu fréquent, ce qui n’est pas suffisant si nous ne voulons pas payer pour l’espace inutile. Pour résoudre ce problème, il existe deux options : Créer un conteneur ECS sidecar qui nettoiera les anciens fichiers EFS lors de la phase d'initialisation Créer une tâche Lambda ou ECS qui montera EFS, et nettoyer les anciens fichiers par CRON. Vérifions les deux. Option 1 : AWS Lambda C'est la meilleure solution car elle n'est pas affectée par le cycle de vie des tâches ECS et d'autres facteurs. Pour mettre en œuvre cette stratégie, vous devez créer une fonction Lambda avec un stockage EFS monté (en savoir plus sur le montage d'un système de fichiers sur Lambda à partir de et le code Python suivant : Le doc officiel Le doc officiel 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!") } Comme vous pouvez le voir, il s’agit d’un code simple qui supprime les fichiers du stockage monté qui sont plus anciens qu’un jour. Lorsque votre Lambda est prêt, nous devons également configurer le déclencheur CRON pour exécuter la fonction périodiquement. _ le . Règles de l'événement Cloudwatch Après toutes ces étapes, votre stockage EFS sera automatiquement nettoyé par votre horaire CRON. Option 2 : conteneur ECS sidecar. To implement this option we have to to add new container to our task definition: { "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 logique derrière cette tâche : Initialisez une nouvelle tâche ECS avec deux conteneurs: app et janitor Nettoyez les fichiers EFS obsolètes dans le conteneur janitor et sortez. indépendamment, la tâche ne sera pas interrompue ou arrêtée en raison de l'option ECS "essentiel": faux. Comme vous pouvez le voir, cette technique est assez simple et repose sur la commande trouver, que vous pouvez personnaliser. Dans l'exemple, il supprime les fichiers qui sont plus anciens que 10080 minutes (7 jours). Bien sûr, cette stratégie est moins souhaitable que la première lorsqu'il s'agit de tâches ECS de longue durée, mais il peut être plus pratique pour les tâches ECS de courte durée ou le prototypage. Temps de test Dans cette section, nous ne ferons pas une plongée profonde dans la construction d'applications .NET. À des fins de test, vous pouvez modifier la que nous avons utilisé au début. Échantillon Aspnetapp Échantillon Aspnetapp Le moyen le plus simple de provoquer une panne de .NET est de Cette méthode est couramment utilisée pour simuler . Environment.FailFast() Les crashes dures Simulons le crash : Ajouter Environment.FailFast("kvendingoldo-dotnet-demo-crash .NET exemple crash"); ligne vers le fichier dotnet-docker/samples/aspnetapp/aspnetapp/Program.cs. Créez une nouvelle image de docker et ré-créez la tâche ECS. ECS Task se terminera, mais générera d'abord un déversement de crash .NET, qui sera disponible sur S3 dans quelques secondes. Dans la phase finale, vous recevrez un message sur votre Slack comme ceci: 📦 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 Possible improvements Avant d’écrire l’article, je voudrais faire quelques remarques sur les changements possibles : Il serait une bonne idée de générer des URL pré-signées pour les objets S3 Définissez des politiques de cycle de vie pour le coffre S3 pour supprimer automatiquement les vieux déchets du coffre Utilisez SNS pour envoyer des notifications sur les nouveaux objets S3 à plusieurs destinations Conclusion Dans les environnements de production, la visibilité rapide des défauts est essentielle.L'automatisation de la livraison des déchets réduit le MTTR (Mean Time To Resolution) et améliore la réponse aux incidents. Oui, nous avons utilisé de nombreux services AWS pour accomplir ces tâches, mais quand nous regardons plus en profondeur, ils sont tous importants. J'espère que cet article vous a aidé à construire une chaîne de livraison de déchets personnelle et à rendre votre équipe de développement plus heureuse. Sentez-vous libre de modifier l'approche proposée, et s'il vous plaît me contacter à tout moment si vous avez des questions. Bonne journée de codage !