Іноді .NET-додатки розбиваються у виробництві, і ніхто не знає, чому, тому що журнали та метрики в порядку. Це досить незручно і робить дебування дуже неприємним. У таких випадках відходи пам'яті можуть спростити дебування і скоротити час вирішення проблем з днів до хвилин. У цій статті пояснюється, як налаштувати відправки для .NET-додатків, розміщених на а потім передати їх команді розробників найзручнішим і безпечним способом. AWS ECS Fargate У цій статті ми будемо створювати ресурси AWS, і я буду посилатися на документацію AWS в конкретних ситуаціях. IAC не буде в нашому фокусі. Проте, якщо ви насолоджуєтеся Terraform так само, як я, ви можете використовувати модулі AWS з відкритим кодом для кожного розділу статті. https://github.com/cloudposse https://github.com/terraform-aws-modules У цій статті ми будемо створювати ресурси AWS, і я буду посилатися на документацію AWS в конкретних ситуаціях. IAC не буде в нашому фокусі. Проте, якщо ви насолоджуєтеся Terraform так само, як я, ви можете використовувати модулі AWS з відкритим кодом для кожного розділу статті. https://github.com/cloudposse https://github.com/terraform-aws-modules https://github.com/cloudposse https://github.com/terraform-aws-modules Архітектура рішення Настав час подивитися на нашу архітектуру.Я почну з припущення, що команда розробників не розглядає можливість витягувати .NET відходи з сховищ, таких як EBS або EFS, через його складність.S3 набагато простіше для розробників отримати будь-який тип файлів, і він ідеально відповідає нашим очікуванням. Крім того, отримання проактивних повідомлень, коли створюється новий .NET-демп буде досить цінним. Наприклад, я буду використовувати Slack, але інші варіанти включають Teams, Mattermost, WhatsApp і так далі. І останнє, але не менш важливе зауваження. Досить складно прив'язати S3 букет природним чином до ECS. З цієї причини ми створимо шар середнього програмного забезпечення, побудований на вершині EFS, DataSync та sidecar ECS контейнер / Lambda функції. EFS буде використовуватися як проміжне файлове сховище для всіх наших завдань ECS, Datasync автоматично передасть дані з EFS на S3, а контейнер sidecar або Lambda очистить старі дані з EFS. Давайте швидко розглянемо схему: 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. Крок за кроком впровадження Створення ECS Fargate task У цьому розділі ми повинні створити завдання ECS Fargate з використанням прикладної програми .NET. передумови Перш ніж приступати до цього, є кілька кроків, які необхідно виконати: 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 Створення кластера Amazon ECS для робочих навантажень Fargate Посібник AWS Цей мінімум Для виконання функції буде достатньо: Trust policy { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } Крім того, мінімум : permissions policy { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" } ] } Створити визначення задачі Після того, як всі передумови готові, настав час створити мінімальне завдання Fargate за допомогою прикладної програми .NET. , і використовуйте це визначення завдання JSON файл: official AWS guide Офіційний путівник 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": [] } Налаштування .NET dumps За замовчуванням програми .NET не генерують жодних відправлень. Щоб налаштувати його, ми повинні встановити наступні змінні середовища: # 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 Ці змінні можна додати безпосередньо до Dockerfile або визначити як змінні середовища в ECS Task Definition json. У нашому прикладі введіть їх у специфікацію завдання ECS. Щоб зробити це, ми додамо їх до Як показано нижче: 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" } ] Як бачите, я використовую кілька місць у COMPlus_DbgMiniDumpName. Dotnet автоматично розширює наступні місць у назві файлу dump: %e - ім'я виконуваного %p - ідентифікатор процесу %t - часовий знак Перегляньте ці два посилання для отримання додаткової інформації про збір та аналіз відправок .NET: Створення .NET Crash Dumps (Microsoft Learn) Debugging .NET Core memory issues (on Linux) with dotnet dump Як бачите, я використовую кілька місць у COMPlus_DbgMiniDumpName. Dotnet автоматично розширює наступні місць у назві файлу dump: %e - ім'я виконуваного %p - ідентифікатор процесу %t - часовий знак Перегляньте ці два посилання для отримання додаткової інформації про збір та аналіз відправок .NET: Створення .NET Crash Dumps (Microsoft Learn) Дебування проблем з пам'яттю .NET Core (на Linux) з відвантаженням дотнет Створення .NET Crash Dumps (Microsoft Learn) Дебування проблем з пам'яттю .NET Core (на Linux) з відвантаженням дотнет Створіть EFS-хранилище і вставте його в ECS Fargate Task Як я згадував на початку цієї статті, прикріплення S3 бактерії до роботи ECS досить складно; замість цього ми будемо використовувати як проміжне сховище для файлів .NET dump, які можна легко монтувати на набір завдань ECS. Amazon EFS (Elastic File System) Щоб створити сховище EFS, виконайте офіційне керівництво AWS: Amazon ECS Tutorial: Using Amazon EFS File Systems Щоб створити сховище EFS, дотримуйтесь офіційного керівництва AWS: Amazon ECS Tutorial: Using Amazon EFS File Systems Amazon ECS Tutorial: Використання файлових систем Amazon EFS Немає нічого особливого, щоб додати до офіційної документації. Просто переконайтеся, що: EFS і ECS Cluster знаходяться в одному VPC EFS можна отримати за допомогою завдань ECS через NFS (порт 2049/tcp). Дозволити вхідний доступ до портів NFS в групі безпеки EFS для цього. Щоб встановити EFS файлову систему в завдання ECS, ми повинні надати необхідні дозволи на Роль IAM (зверніть увагу на місцевласників): 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>" } ] } Як остаточний крок, визначте об'єми EFS та пункти монтажу у визначенні завдань ECS (change fileSystemId). з вашим справжнім ідентифікатором файлової системи після запуску): fs-xxxxxx "volumes": [ { "name": "dotnet-dumps", "efsVolumeConfiguration": { "fileSystemId": "fs-xxxxxx", "rootDirectory": "/" } } ] "mountPoints": [ { "containerPath": "/dumps", "readOnly": false, "sourceVolume": "dotnet-dumps" } ] Налаштування AWS DataSync для передачі файлів EFS на S3 Послуга DataSync є стандартним інструментом AWS для передачі даних між різними типами сховищ. У нашому випадку вона допоможе нам відправляти move.NET з EFS в S3. Щоб досягти нашої мети, ми повинні: 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 Офіційний doc Офіційний doc Створення Slack Alerts на основі AWS Lambda Як було сказано раніше, попередження про відходи new.NET надзвичайно корисні для команди розробників. З точки зору архітектури, попередження можна побудувати по-різному: Проста функція lambda, яка надсилає повідомлення до Slack через API і викликана подіями S3. Повідомлення публікуються на тему SNS за допомогою налаштованих повідомлень про події S3, які потім запускають функцію Lambda для надсилання подій до Slack. Оскільки ми не очікуємо високого навантаження, перший варіант краще для нас.У випадку, якщо ви хочете реалізувати другий варіант використовуйте ці два посилання: Модуль Terraform для розгортання стеку SNS і Lambda Посібник для налаштування подій S3 на SNS Модуль Terraform для розгортання стеку SNS і Lambda Посібник для налаштування подій S3 на SNS Ми використовуємо Python, щоб надсилати повідомлення в Slack. У цій статті ми будемо надсилати лише посилання на файл S3, але в деяких випадках потрібно надсилати весь файл. API Slack змінився деякий час тому, і відправка файлів може бути трохи складною. Якщо ви хочете дізнатися більше, будь ласка, дивіться статтю "Завантаження файлів до Slack з Python". Ми використовуємо Python, щоб надсилати повідомлення в Slack. У цій статті ми будемо надсилати лише посилання на файл S3, але в деяких випадках потрібно надсилати весь файл. API Slack змінився деякий час тому, і надсилання файлів може бути трохи складним. Якщо ви хочете дізнатися більше, будь ласка, дивіться " « Стаття . Завантажити файли в Slack з Python Завантажити файли в Slack з Python Отже, давайте розберемося з попередженням крок за кроком: 1. Create Slack secret Створення AWS Secret Manager Secret З одного поля: Цей ключ повинен містити посилання на ваш Slack webhook (щоб дізнатися більше про перевірку Slack webhook). ) kvendingoldo-dotnet-crash-dump-demo slack_webhook_url Офіційний посібник Офіційний посібник 2. Configure AWS Lambda Ми не будемо глибоко розглядати створення AWS Lambda, але ми підкреслимо деякі ключові моменти. . Офіційний посібник Офіційний посібник Переконайтеся, що роль Lambda IAM має дозвіл на читання з S3: { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::kvendingoldo-dotnet-demo-crash/*" } 2.2: Щоб отримати дані від менеджера AWS Secret, ми повинні вказати змінну середовища в конфігурації AWS Lambda: SECRET_NAME=kvendingoldo-dotnet-demo-crash 2.3: Завантажити код Python в 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: Налаштуйте повідомлення про події S3 для вашого ящика S3. і виберіть » ". Конфігуруйте подію за допомогою наступних опцій: букет -> властивості -> Повідомлення про події Створення повідомлення про події Назва події: kvendingoldo-dotnet-demo-crash Префікс: Демпс Тип події: s3:ObjectCreated:* Мета: <Ім'я функції Lambda> Налаштування EFS Storage Clean-up Ідеально, ланцюжок доставки .NET dumps готовий, але що про старий сміттєзвалище? EFS не дозволяє нам видаляти старі файли за допомогою політики життєвого циклу; ми можемо тільки передати їх на тип сховища рідкісного доступу, що недостатньо, якщо ми не хочемо платити за непотрібний простір. Для вирішення цієї проблеми є два варіанти: Створення ECS sidecar контейнера, який очистить старі EFS файли на етапі ініціалізації Створіть завдання Lambda або ECS, які будуть монтувати EFS, і очистити старі файли CRON. Розглянемо обидва з них. Варіант 1: AWS Lambda Це найкраще рішення, оскільки на нього не впливає життєвий цикл завдань ECS та інші фактори. Щоб реалізувати цю стратегію, потрібно створити функцію Lambda з встановленим сховищем EFS (докладніше про встановлення файлової системи на Lambda з а також наступний код Python: Офіційний doc Офіційний doc 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!") } Як бачите, це простий код, який видаляє файли з встановленого сховища, які старше одного дня. Коли ваша Lambda готова, ми також повинні налаштувати виклик CRON, щоб виконувати функцію періодично. • . Події Cloudwatch Ось так, після всіх цих кроків ваше EFS зберігання буде автоматично очищено за вашим графіком CRON. Варіант 2: контейнер ECS sidecar. Щоб реалізувати цей варіант, ми повинні додати новий контейнер до нашого визначення завдань: { "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 } } Логіка, що стоїть за цим завданням: Ініціалізація нового завдання ECS з двома контейнерами: app і janitor Очистіть застарілі EFS-файли в контейнері janitor і вийдіть.Незважаючи на це, завдання не буде перервано або зупинено через ECS опцію "essential": false. Як бачите, ця техніка досить проста і спирається на команду знайти, яку ви можете налаштувати. Наприклад, вона видаляє файли, які старші 10080 хвилин (7 днів). Звичайно, ця стратегія менш бажана, ніж перша при справі з довготривалими завданнями ECS, але вона може бути більш зручною для короткотривалих завдань ECS або прототипування. Час тестування У цьому розділі ми не будемо глибоко занурюватися в розробку додатків .NET. Для цілей тестування ви можете змінити які ми використовували спочатку. Серія Aspnetapp Серія Aspnetapp Найпростіший спосіб викликати аварію .NET Цей метод зазвичай використовується для моделювання . Environment.FailFast() жорсткий крах Давайте симулювати аварію: Додати Environment.FailFast («kvendingoldo-dotnet-demo-crash .NET example crash»); лінія до файлу dotnet-docker/samples/aspnetapp/aspnetapp/Program.cs. Створіть новий зображення докера і перетворіть завдання ECS. ECS Task буде припинено, але спочатку буде генеруватися .NET-відбиток, який буде доступний на S3 через кілька секунд. На останньому етапі, ви отримаєте повідомлення на вашому Slack, як це: 📦 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 Можливі поліпшення Перед тим, як обкласти статтю, я хотів би надати деякі зауваження про можливі зміни: Буде добре генерувати попередньо підписані URL-адреси для об'єктів S3 Налаштуйте політики життєвого циклу для бактерії S3, щоб автоматично видаляти старі відходи з бактерії Використовуйте SNS для надсилання повідомлень про нові об'єкти S3 до декількох цілей Висновок У виробничих середовищах важлива швидка видимість у випадку збоїв.Автоматизація доставки відходів скорочує MTTR (Mean Time To Resolution) і покращує реакцію на інциденти. Як бачите, впровадження цієї процедури не так складно, як ви могли б очікувати.Так, ми використовували багато послуг AWS для виконання цих завдань, але коли ми дивимося глибше, вони всі важливі. Я сподіваюся, що ця стаття допомогла вам побудувати особистий ланцюжок доставки сміття і зробила вашу команду розробників щасливішими. Будь ласка, будь ласка, змініть запропонований підхід, і будь ласка, зв'яжіться зі мною в будь-який час, якщо у вас є будь-які питання. Щасливе кодування!