はじめに
AWS Fargateのタスクの停止理由を確認する方法が分からず詰まったため、今回記事を書きました。
ECSのコンソール上では、タスクが停止してから1時間以内であれば、以下のように停止理由であるエラー内容を確認できます。
ただし、上記のエラーは、1時間経過すると、以下のように見られなくなります。
過去にさかのぼって確認したい場合、CloudWatch logsなどに保存する必要があります。
今回は、ECSタスクの停止理由をさかのぼって確認するために、タスクが停止になった時のイベント情報をCloudWatch logsに保存する方法について、解説します。
構成図
構成図は、下記です。
汎用性がありますので、CloudFormationで、イベントルールとロググループを作成します。
流れとしては、ECSタスクが停止した時に、EventBridgeがトリガーとなり、ECSタスクの停止ログをCloudWatch Logsに保存します。
また、CloudWatch Logs Insightsでログデータを検索して分析をしてみます。
CloudFormationでスタックを作成
こちらのテンプレート通りに、スタックを作成します。
CloudFormationテンプレート (クリックすると展開します)
ecs-stopped-tasks-cwlogs.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Deploys the resources needed to store events of Amazon ECS stopped tasks in CloudWatch Logs
Parameters:
CWLogGroupName:
Type: String
Description: The CloudWatch log group name to store the events
Default: /aws/events/ECSStoppedTasksEvent
CWLogGroupRetention:
Type: Number
Description: The number of days to retain the events in the log group
AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]
Default: 30
Resources:
EventRule:
Type: AWS::Events::Rule
Properties:
Name: ECSStoppedTasksEvent
Description: Triggered when an Amazon ECS Task is stopped
EventPattern:
source:
- aws.ecs
detail-type:
- ECS Task State Change
detail:
desiredStatus:
- STOPPED
lastStatus:
- STOPPED
State: ENABLED
Targets:
- Arn: !GetAtt LogGroup.Arn
Id: ECSStoppedTasks
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Ref CWLogGroupName
RetentionInDays: !Ref CWLogGroupRetention
LogEventsPolicy:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: LogEventsPolicy
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LogEventsPolicy",
"Effect": "Allow",
"Principal": {
"Service": [
"delivery.logs.amazonaws.com",
"events.amazonaws.com"
]
},
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "${LogGroup.Arn}"
}
]
}
スタックを作成し、CREATE_COMPLETEになったことを確認します。
リソースの確認
スタックの作成により、リソースは2つ作成されますので、確認しておきます。
CloudWatch ロググループ
/aws/events/ECSStoppedTasksEvent
というロググループ名で作成されています。
EventBridgeのルール
ルール名ECSStoppedTasksEvent
で作成されています。
イベントパターンは、ECSがSTOPPED
のステータスであり、ターゲットは、CloudWatch ロググループの/aws/events/ECSStoppedTasksEvent
です。
イベントパターンを特定のクラスターにする
現状のEventBridgeのイベントパターンですと、すべてのクラスターの停止したタスク理由が保存されます。
{
"source": ["aws.ecs"],
"detail-type": ["ECS Task State Change"],
"detail": {
"desiredStatus": ["STOPPED"],
"lastStatus": ["STOPPED"]
}
}
クラスターを以下の通りに指定してあげることで、特定のクラスターのタスクのみをログに残すこともできます。
{
"source": ["aws.ecs"],
"detail-type": ["ECS Task State Change"],
"detail": {
"clusterArn": ["arn:aws:ecs:ap-northeast-1:[アカウントID]:cluster/[クラスター名]"],
"desiredStatus": ["STOPPED"],
"lastStatus": ["STOPPED"]
}
}
ちなみに、特定のクラスターの特定のタスク定義に絞ることも可能です。
{
"source": ["aws.ecs"],
"detail-type": ["ECS Task State Change"],
"detail": {
"taskDefinitionArn": ["arn:aws:ecs:ap-northeast-1:[アカウントID]:task-definition/[タスク定義名]:[バージョン数]"],
"clusterArn": ["arn:aws:ecs:ap-northeast-1:[アカウントID]:cluster/[クラスター名]"],
"desiredStatus": ["STOPPED"],
"lastStatus": ["STOPPED"]
}
}
リソースの絞り方は、後述するCloudWatch Logsに出力される「ログの1例」を参考にしましょう。
ただし、今回は、すべてのクラスターでタスクの停止をログに残すようにします。
タスク停止してみる
ECSのタスクの起動を失敗させるため、プライベートサブネット内でパブリック IPを付与させる設定でタスクを起動し、タスクを停止させました。
/aws/events/ECSStoppedTasksEvent
を見てみると、json形式でログが出力されていました。
~省略~
"stoppedReason": "ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve ecr registry auth: service call has been retried 3 time(s): RequestError: send request failed caused by: Post \"https://api.ecr.ap-northeast-1.amazonaws.com/\": dial tcp 3.112.64.212:443: i/o timeout. Please check your task network configuration."
~省略~
「ログの1例」です。 (クリックすると展開します)
{
"version": "0",
"id": "c4b21fc6-c933-7f28-00cd-4f7162c5b304",
"detail-type": "ECS Task State Change",
"source": "aws.ecs",
"account": "[アカウントID]",
"time": "2023-04-14T18:07:00Z",
"region": "ap-northeast-1",
"resources": [
"arn:aws:ecs:ap-northeast-1:[アカウントID]:task/nginx/dcf8140fcd23495bb7c8bb3919fd485a"
],
"detail": {
"attachments": [
{
"id": "67f94f8d-b24a-4066-bcb6-e47df4bf2e93",
"type": "elb",
"status": "DELETED",
"details": []
},
{
"id": "e2b1dc3a-ade7-4188-92ff-d36fe3ebf504",
"type": "eni",
"status": "DELETED",
"details": [
{
"name": "subnetId",
"value": "subnet-0bbe65b1222f92210"
},
{
"name": "networkInterfaceId",
"value": "eni-08caee15559291301"
},
{
"name": "macAddress",
"value": "0a:bc:9f:87:01:5f"
},
{
"name": "privateDnsName",
"value": "ip-10-0-1-72.ap-northeast-1.compute.internal"
},
{
"name": "privateIPv4Address",
"value": "10.0.1.72"
}
]
}
],
"attributes": [
{
"name": "ecs.cpu-architecture",
"value": "x86_64"
}
],
"availabilityZone": "ap-northeast-1c",
"capacityProviderName": "FARGATE_SPOT",
"clusterArn": "arn:aws:ecs:ap-northeast-1:[アカウントID]:cluster/nginx",
"connectivity": "CONNECTED",
"connectivityAt": "2023-04-14T17:29:26.067Z",
"containers": [
{
"containerArn": "arn:aws:ecs:ap-northeast-1:[アカウントID]:container/nginx/dcf8140fcd23495bb7c8bb3919fd485a/3ee885e1-3001-4e2f-bb31-d843bdfa2cff",
"lastStatus": "STOPPED",
"name": "nginx",
"image": "[アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:dev",
"imageDigest": "sha256:fd0f22e46bd78637783815f3ea5f98ff3d09de764789eaccf1ca412d97743563",
"runtimeId": "dcf8140fcd23495bb7c8bb3919fd485a-2531612879",
"taskArn": "arn:aws:ecs:ap-northeast-1:[アカウントID]:task/nginx/dcf8140fcd23495bb7c8bb3919fd485a",
"networkInterfaces": [
{
"attachmentId": "e2b1dc3a-ade7-4188-92ff-d36fe3ebf504",
"privateIpv4Address": "10.0.1.72"
}
],
"cpu": "0"
}
],
"cpu": "256",
"createdAt": "2023-04-14T17:29:22.086Z",
"desiredStatus": "STOPPED",
"enableExecuteCommand": false,
"ephemeralStorage": {
"sizeInGiB": 20
},
"group": "service:nginx",
"launchType": "FARGATE",
"lastStatus": "STOPPED",
"memory": "512",
"overrides": {
"containerOverrides": [
{
"name": "nginx"
}
]
},
"platformVersion": "1.4.0",
"pullStartedAt": "2023-04-14T17:29:53.482Z",
"pullStoppedAt": "2023-04-14T17:29:58.077Z",
"startedAt": "2023-04-14T17:30:13.12Z",
"startedBy": "ecs-svc/4374651048025955904",
"stoppingAt": "2023-04-14T18:00:53.753Z",
"stoppedAt": "2023-04-14T18:07:00.681Z",
"stoppedReason": "stoppedReason": "ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve ecr registry auth: service call has been retried 3 time(s): RequestError: send request failed caused by: Post \"https://api.ecr.ap-northeast-1.amazonaws.com/\": dial tcp 3.112.64.212:443: i/o timeout. Please check your task network configuration.",
"stopCode": "SpotInterruption",
"taskArn": "arn:aws:ecs:ap-northeast-1:[アカウントID]:task/nginx/dcf8140fcd23495bb7c8bb3919fd485a",
"taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:[アカウントID]:task-definition/nginx:1",
"updatedAt": "2023-04-14T18:07:00.681Z",
"version": 8
}
}
これで、いつでもタスクの停止理由を確認することができます。
CloudWatch Logs Insightsを利用し、分析する
CloudWatch Logs Insights を使用すると、ログデータを簡単に検索して分析できます。
今回は、停止したタスクのうち、確認したいタスクの停止理由を検索するクエリをご紹介します。
クエリは、2つ実行します。
- 停止したタスクのタスクIDを検索する
- クエリ:
fields detail.taskArn | filter detail.clusterArn like "Cluster_Name" and detail.group like "Service_Name"
- クエリ:
- 停止したタスクのうち、確認したいタスクのタスクIDから、タスク理由を表示する
- クエリ:
fields detail.stoppedReason, detail.containers.0.name, detail.containers.0.exitCode | filter detail.taskArn like "Task_ID"
- クエリ:
詳しいCloudWatch Logs Insights のクエリ構文は、こちらのドキュメントを一読ください
実際の操作画面を表示しながら、クエリを実行します。
私の場合、クラスター名は、nginx
、サービス名は、nginx-service
です。
- [Logs Insightsで表示]をクリックします。
- ロググループと時間を設定後、1つめのクエリを実行しますと、タスクのIDが表示されます。今回、タスクIDの
c92d1197454846d092efca65a4429599
のタスク停止理由を見てみます。 - 2つめのクエリを入力し実行します。
- 停止理由は、
Task stopped by user
、つまりユーザーがタスクを停止したと分かりました exitCode
は、こちらが参考になります。今回は、0
とあるため、ユーザーによって停止と分かります。
- 停止理由は、
他のタスクIDを実行すると、detail.containers.0.exitCode
がないものもありますね。
他の停止理由やエラー内容は、公式ドキュメントで解説されているため、ご参考ください。