時には .NET アプリケーションが生産に失敗し、なぜか、ログやメトリクスがOKなので、誰も知らない。 この記事では、デプロイされた .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 解決策アーキテクチャ そろそろ私たちのアーキテクチャを調べてみる時です。私は、開発チームが、その複雑さのために、EBSやEFSのようなストレージから .NET ゴミを引っ張ることを検討していないと仮定します。 それ以外に、新しい .NET ダンプが生成されたときにプロアクティブな通知を受け取ることは非常に有用です. たとえば、私は Slack を使用しますが、他のオプションには Teams、Mattermost、WhatsApp などがあります。 ECS に S3 バケットをネイティブに接続することはかなり複雑です. そのため、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. Step-by-step 実施 ECS タスクの作成 このセクションでは、サンプル .NET アプリケーションを使用して ECS Fargate タスクを作成する必要があります。 前提条件 進む前に、いくつかのステップが完了する必要があります: 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 Fargate ワークロード用の Amazon ECS クラスタの作成 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": "*" } ] } タスク定義の作成 すべての前提条件が完了したら、サンプル .NET アプリで最小限の Fargate タスクを作成する時です。 , このタスク定義 json ファイルを使用する: AWS公式ガイド 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 ダンプの設定 デフォルトでは、.NET アプリケーションはゴミを生成しません. To configure it, we must set the following environment variables: # 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 タスク定義 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 - プロセス ID %t - タイムスタンプ この 2 つのリンクを参照して、 .NET クラッシュ ダンプの収集と分析についての詳細を参照してください。 Collect .NET Crash Dumps (Microsoft Learn) デバッグ .NET Core メモリの問題 (Linux) with dotnet dump ご覧の通り、私はCOMPlus_DbgMiniDumpName でいくつかの場所保持者を使用しています. Dotnet は自動的に dump ファイル名の次の場所保持者を拡張します: %e - 実行名 %p - プロセス ID %t - タイムスタンプ この 2 つのリンクを参照して、 .NET クラッシュ ダンプの収集と分析についての詳細を参照してください。 Collect .NET Crash Dumps (Microsoft Learn) デバッグ .NET Core メモリの問題 (Linux) with dotnet dump Collect .NET Crash Dumps (Microsoft Learn) デバッグ .NET Core メモリの問題 (Linux) with dotnet dump EFS ストレージを作成し、ECS Fargate タスクにマウントします。 この記事の初めに述べたように、ECSの仕事にS3バケットを付けることはかなり困難です。 .NET ドンプ ファイルの中間ストレージとして、ECS タスクのセットに簡単にマウントできます。 Amazon EFS (Elastic File System) EFS ストレージを作成するには、公式の AWS ガイドに従ってください: Amazon ECS チュートリアル: Amazon EFS File Systems を使用する EFS ストレージを作成するには、公式の AWS ガイドに従ってください: Amazon ECS チュートリアル: Amazon EFS File Systems を使用する Amazon ECS チュートリアル: Amazon EFS ファイルシステムの使用 公式文書に追加する特別なものは何もありません. Just make sure that: EFS と ECS クラスタは同じ VPC にあります。 EFS は NFS (ポート 2049/tcp) 経由で ECS タスクによってアクセスできます。 EFS ファイルシステムを ECS タスクにマウントするには、必要な許可を与えなければなりません。 IAM role ( pay attention to placeholders ) : (場所保有者に注意を払う) 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) でマウント ポイントを設定します。 ブートストラップ後の実際のファイルシステムIDで): 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 ツールです。 私たちの目標を達成するためには、私たちは: 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 this official doc AWS Lambda ベースの Slack Alerts 作成 前述したように、 new.NET ダンプに関する警告は、開発チームにとって非常に役に立ちます。 アーキテクチャの観点から、アラートはさまざまな方法で構築できます: API 経由で Slack にメッセージを送信し、S3 イベントによって引き起こされるシンプルな lambda 関数。 メッセージは、設定された S3 イベント通知を使用して SNS トピックに投稿され、その後 Lambda 関数を起動してイベントを Slack に送信します。 私たちは高負荷を期待しないので、最初のオプションは私たちにとってより良い場合、あなたが第二のオプションを実装したい場合は、これらの2つのリンクを使用してください: Terraform モジュール SNS および Lambda スタックの展開 S3 イベントを SNS に設定するためのガイド Terraform モジュール SNS および Lambda スタックの展開 S3 イベントを SNS に設定するためのガイド Python を使用して Slack にメッセージを送信します。この記事では、S3 ファイルへのリンクのみを送信しますが、一部の場合にはファイル全体を送信する必要があります。 Slack API はしばらく前から変更されており、ファイルの送信は少し複雑です。 この記事では、Slack にメッセージを送信するために Python を使用します。この記事では、S3 ファイルへのリンクのみを送信しますが、一部のケースでは、ファイル全体を送信する必要があります。Slack API はしばらく前から変更されており、ファイルの送信は少し複雑です。 「記事。 ファイルを Python で Slack にアップロード ファイルを Python で Slack にアップロード さて、ステップごとに警告を構築しましょう: 1. Create Slack secret AWS Secret Manager Secret を作成する 1 フィールド: このキーには、Slack Webhook へのリンクが含まれる必要があります(Slack Webhook チェックについて詳しく知るには、Slack Webhook チェックを参照してください)。 ( ) kvendingoldo-dotnet-crash-dump-demo slack_webhook_url オフィシャルガイド オフィシャルガイド 2. Configure AWS Lambda AWS Lambda の作成について詳しく説明するつもりはありませんが、いくつかの重要な点を挙げます。AWS Lambda のセットアップに関するより基本的な情報については、AWS Lambda を参照してください。 . オフィシャルガイド オフィシャルガイド Lambda IAM ロールが S3 から読み取る権限を持っていることを確認してください。 { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::kvendingoldo-dotnet-demo-crash/*" } 2.2: AWS Secret Manager からデータを取得するには、AWS Lambda 構成で環境変数を指定する必要があります。 SECRET_NAME=kvendingoldo-dotnet-demo-crash 2.3:Lambda に Python コードをアップロード 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 イベント通知を設定します。 「 SELECT」 ". 次のオプションを使用してイベントを設定します。 bucket -> properties -> イベント通知 イベント通知の作成 イベント名: kvendingoldo-dotnet-demo-crash タグ : DUMPS イベントの種類: s3:ObjectCreated:* ターゲット: <Your Lambda function Name> EFS ストレージ クリーンアップ 完璧に、 .NET ゴミの配信チェーンは準備ができていますが、古いゴミについてどうでしょうか? EFS は、ライフサイクル ポリシーを使用して古いファイルを削除することを許可しません。 この問題を解決するには、2つの選択肢があります: 初期化段階で古い EFS ファイルをクリアする ECS sidecar コンテナを作成する EFS をマウントする Lambda または ECS タスクを作成し、古いファイルを CRON によってクリーニングします。 両方チェックしてみよう。 オプション1:AWS Lambda これは、ECS タスクのライフサイクルやその他の要因に影響されていないため、最適なソリューションです。この戦略を実装するには、マウントされた EFS ストレージを備えた Lambda 関数を作成する必要があります(Lambda にファイルシステムをマウントする方法をもっと知る) )および次のPythonコード: 公式ドック 公式ドック 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!") } ご覧のとおり、これは単純なコードで、1 日を超える古いマウントストレージからファイルを削除します。 Lambda が準備が整ったとき、CRON トリガーを定期的に実行するように設定する必要があります。 ーーー Cloudwatch イベントルール それは、これらのすべてのステップの後、あなたの EFS ストレージは、あなたの CRON スケジュールによって自動的にクリアされます。 選択肢2:ECSサイドカーコンテナ。 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 } } この課題の背後にある論理: 2 つのコンテナで新しい ECS タスクを初期化する: app と janitor janitor コンテナ内の時代遅れの EFS ファイルをクリーンアップし、出力します. Regardless, the task will not be interrupted or stopped due to ECS option "essential": false. あなたが見ることができるように、このテクニックは非常にシンプルで、あなたがカスタマイズできる検索コマンドに依存しています. 例えば、それは10080分(7日)以上古いファイルを削除します。 もちろん、この戦略は、長期的なECSタスクに対処するときに最初よりも望ましくありませんが、短期的なECSタスクやプロトタイプのためのより便利かもしれません。 テスト時間 このセクションでは、.NET アプリケーション ビルドに深く潜入することはありません。 最初に使っていたもの。 sample aspnetapp sample aspnetapp .NET のクラッシュを起こす最も簡単な方法は、 この方法は、シミュレーションに一般的に使用されます。 . Environment.FailFast() ハードクラッシュ 衝突をシミュレートしよう: Environment.FailFast(「kvendingoldo-dotnet-demo-crash .NET example crash」)を追加し、 dotnet-docker/samples/aspnetapp/aspnetapp/Program.cs ファイルにリンクします。 新しいドッカーイメージを作成し、ECS タスクを再作成します。 ECS タスクは終了しますが、最初に .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 可能な改善 記事をまとめる前に、潜在的な変更についていくつかのコメントを提供したい: S3 オブジェクト向けに事前署名された URL を生成することが良いでしょう。 S3 バケットのライフサイクルポリシーを設定して、古いゴミをバケットから自動的に削除します。 SNS を使用して、新しい S3 オブジェクトに関する通知を複数の目的地に送信する 結論 生産環境では、故障の迅速な可視性が不可欠です。自動化されたダンプ配送は、MTTR(Mean Time To Resolution)を短縮し、事故対応を改善します。 この手順を実装することは、予想するほど難しいものではありませんが、これらのタスクを実行するために多くの AWS サービスを使用しましたが、より深く見ると、それらはすべて重要です。 この記事はあなたが個人的なゴミ配達チェーンを構築し、開発チームをより幸せにしてくれたことを願っています。 提案されたアプローチを変更する自由に感じて下さい、あなたは何らかの質問がある場合はいつでも私に連絡してください。 ハッピーコード!