【AWS】夜間に予期せず停止したECSタスク停止理由の確認方法

## はじめに

ECS を利用していて、既存タスクが予期せず停止したが理由を知りたい。
停止したタスク情報を GUI から確認しようとしたけど、タスク情報が見れなくなった。
上記のような経験はありませんか？

原因はタスク停止イベント発生後に 1 時間以上経過したため、
確認できなくなったことかもしれません。
本記事は 上記問題の解消点を記載した内容となります。

## 結論

タスクの状態変更イベントを含む ECS のライフサイクルイベントを、
CloudWatch Logs に記録する設定の有効化をすることで解決

## 直面した問題

基本的に ECS タスクは停止時にタスクの停止理由を確認することで、停止原因を確認できます。

■手動停止した際の停止理由をGUIから確認
※コンソール上より停止したタスクを選択するこで、上部に表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1764568/b0745390-90c9-e5a8-2a5f-8c07b49f896b.png)

■手動停止した際の停止理由をCLIから確認(stoppedReasonに記載)
```bash
aws ecs describe-tasks --cluster <タスクが属するクラスタ名> --tasks <停止したタスクARN>
```
```json:出力結果
{
    "tasks": [
        {
            "cpu": "512",
            "desiredStatus": "STOPPED",
            "executionStoppedAt": "2023-12-11T14:34:12.289000+00:00",
            "healthStatus": "UNKNOWN",
            "lastStatus": "STOPPED",
            "launchType": "FARGATE",
            "memory": "1024",
            "overrides": {
                "containerOverrides": [
                    {
                        "name": "web"
                    }
                ],
                "inferenceAcceleratorOverrides": []
            },
            "platformVersion": "1.4.0",
            "platformFamily": "Linux",
            "stopCode": "UserInitiated",
            "stoppedAt": "2023-12-11T14:34:38.259000+00:00",
            "stoppedReason": "Task stopped by user" #ここから停止理由の確認が可能
            ~
            }
        }
    ],
    "failures": []
}
```

ただ、本内容はタスクが停止から 1 時間以上経過した場合閲覧不可となってしまう模様です。
このままでは、夜間に意図せず停止したタスクの停止理由が追えなくなってしまいます。

> ■AWS 公式ページより[(DescribeTasks)](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DescribeTasks.html)
> Currently, stopped tasks appear in the returned results for at least one hour.

## 解決方法

タスク停止情報を CloudWatachLogs に出力することで解決

### 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1764568/076f446a-b053-2e44-9fbe-1fa3478c9e91.png)

### 設定してみた

#### 1.出力先となる CloudWatchLogs の作成
停止理由の出力先となるCloudWatchLogsの作成
例として、「/aws/events/ecs/ECSStatus」という名前のロググループを作成

```bash
aws logs create-log-group --log-group-name /aws/events/ecs/ECSStatus
```

#### 2.CloudWatchLogs に対して EventBritge からログイベント配置のための権限付与

CloudWatchLogs にはアクセス許可を制御するためのポリシー(リソースポリシー)が存在しており、
EventBritge からログイベントを配置するためには、ポリシー設定が必要となる。

ポリシー設定コマンド

```bash
aws --region ap-northeast-1 logs put-resource-policy --policy-name 'EventBridgeCloudWatchLogs' --policy-document '{
    "Statement": [
        {
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Effect": "Allow",
            "Principal": {
                "Service": ["events.amazonaws.com", "delivery.logs.amazonaws.com"]
            },
            "Resource": "arn:aws:logs:ap-northeast-1:xxx:log-group:/aws/events/ecs/ECSStatus/*:*",
            "Sid": "TrustEventBridgeToStoreECSLifecycleLogEvents"
        }
    ],
    "Version": "2012-10-17"
}'

```

| コマンド                                     | 内容                                                                                                                                                                            |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| aws --region region logs put-resource-policy | AWS CLI で CloudWatch Logs サービスに対してリソースポリシーを作成または更新するためのコマンド                                                                                   |
| --policy-name 'EventBridgeCloudWatchLogs'    | 作成するポリシーの名前を設定                                                                                                                                                    |
| --policy-document                            | リソースポリシーの JSON ドキュメントを指定                                                                                                                                      |
| Action                                       | logs:CreateLogStream（ログストリームの作成）と logs:PutLogEvents（ログイベントの追加）を許可                                                                                    |
| Principal                                    | 本ステートメント内のアクション実行を許可される AWS サービスの指定<br/>events.amazonaws.com（AWS EventBridge）と delivery.logs.amazonaws.com（CloudWatch Logs のデリバリー機能） |
| Resource                                     | このポリシーが適用されるリソースを指定                                                                                                                                          |

次のコマンドを使用して、ポリシーの設定確認

```bash
aws logs describe-resource-policies --region ap-northeast-1 --output json
```

#### 3.EventBritge の設定

AWS のサービスからのイベントは、ほぼリアルタイムに EventBridge に提供されており、
ECS はコンテナイベントである「インスタンス状態変更」「タスク状態変更」「サービスアクション」
などのイベント Amazon EventBridge に自動的に送信しています。
※API イベントを CloudTrail が常に拾っているようなイメージ
参考先：[Amazon ECS イベントおよび EventBridge](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/cloudwatch_event_stream.html)

1. EventBritge にイベントパターンを記述し特定のログを検知するように設定
   特定のクラスターに属する ECS のタスク状態が「STOPPED」に変更された際に発生するイベントを検知

```bash
aws events put-rule --name {イベント名} --description {ルールの説明} --state "ENABLED" --event-pattern {検知するイベントパターン}
```

```bash
aws events put-rule --name ECSStatus --description ECSStatusCheck --state "ENABLED" --event-pattern "{\"detail-type\":[\"ECS Task State Change\"],\"source\":[\"aws.ecs\"],\"detail\":{\"desiredStatus\":[\"STOPPED\"],\"clusterArn\":[\"arn:aws:ecs:ap-northeast-1:<アカウントID>:cluster/kensyo\"],\"lastStatus\":[\"STOPPED\"]}}"
※シェルスクリプトでは、ダブルクォート内にあるダブルクォートをエスケープする必要がある。
```

■[参考]上記検知するイベントパターンの展開(json 形式)
※不特定多数のコンテナから検知しないために対象クラスタも限定

```json
{
  "detail-type": [
    "ECS Task State Change"
  ],
  "source": [
    "aws.ecs"
  ],
  "detail": {
    "desiredStatus": [
      "STOPPED"
    ],
    "clusterArn": [
        "arn:aws:ecs:ap-northeast-1:<AWSアカウント>:cluster/kensyo" 
    ],
    "lastStatus": [
      "STOPPED"
    ]
  }
}
```

2.  イベントがイベントパターンと一致したとき CloudwatchLogs に転送するように設定

```bash
aws events put-targets --rule "ECSStatus" --targets "Id"="1","Arn"="arn:aws:logs:ap-northeast-1:xxxx:log-group:/aws/events/ecs/ECSStatus"
```

#### 4.コンテナを停止し CloudWatchLogs から ECS 停止情報を確認
「/aws/events/ecs/ECSStatus」にログストリームが生成されていることを確認

■/aws/events/ecs/ECSStatus
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1764568/57a79ee7-e40a-e0c3-a51a-b1602e4d9bd0.png)

内容を確認すると、「stopCode(停止理由)」が含まれるログが記録されていることが確認できた。
つまり、コンテナの停止理由をCloudWatchLogsから確認可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1764568/40f4efd8-5eea-6309-0896-975cdbea81e5.png)


以上でコンテナの終了理由を Cloudwathch から確認することができました。

### その他補足
ECS には Container Insights という、
タスク(コンテナ)単位でメトリクスを確認できるようになる機能も存在している。

主にタスクごとの CPU やメモリ利用率などが確認したい場合はこちらの機能の利用を見当すべきである。
　 ECS にて取得可能なメトリクス：[Amazon ECS Container Insights metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-metrics-ECS.html)

#### 1.対象コンテナの Container Insights の有効化

★CLI の場合(既存コンテナの設定変更)

```bash
aws ecs update-cluster-settings --cluster {ECSクラスタ名} --settings name=containerInsights,value=enabled
```

★GUI の場合
「対象 ECS クラスタを選択」→「クラスターを更新」→「モニタリング」→「Container Insights の使用：有効」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1764568/9377b51c-39fe-cb60-673c-5bee961b21ed.png)

#### 2.Container Insights 出力先 CloudWatchLogs の確認

Container Insights 有効化後にタスクを起動すれば、
「/aws/ecs/containerinsights/<クラスタ名>/performance」のプレフィクスを持つ CloudwatchLogs ロググループが自動生成される。

ただし、デフォルトで取得可能なメトリクスにはコンテナの停止理由が含まれていないため、
本ロググループから停止理由の確認は不可能。

★CloudwatchLogs の SS★
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1764568/71dcf951-4711-8cc0-4e46-8c7fa693e51c.png)

```json:ContainerInsightログ抜粋
{
    "Version": "0",
    "Type": "Container",
    "ContainerName": "web",
    "TaskId": "21498f6bdb0443a99aefd0886ac30b16",
    "TaskDefinitionFamily": "kensyo",
    "TaskDefinitionRevision": "1",
    "ServiceName": "kensyo-service",
    "ClusterName": "kensyo",
    "Image": "httpd:2.4",
    "ContainerKnownStatus": "PENDING",
    "Timestamp": 1702306020000,
    "CpuUtilized": 0.03978291432062785,
    "CpuReserved": 0,
    "MemoryUtilized": 6,
    "StorageReadBytes": 0,
    "StorageWriteBytes": 0,
    "NetworkRxBytes": 0,
    "NetworkRxDropped": 0,
    "NetworkRxErrors": 0,
    "NetworkRxPackets": 45689,
    "NetworkTxBytes": 0,
    "NetworkTxDropped": 0,
    "NetworkTxErrors": 0,
    "NetworkTxPackets": 939
}

```
## 設定の注意点

・Container Insights 料金が適用される
・CloudwatchLogs に出力するため Logs の料金が適用される

## 最後に

ログが見えなくなるって本当に怖い。。

## 参考元

■Container Insights 内で Amazon ECS ライフサイクル イベントを確認する
https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/container-insights-ECS-lifecycle-events.html

■ECS タスクの停止理由を CloudWatch Logs に保管する方法を教えてください
https://dev.classmethod.jp/articles/tsnote-ecs-how-can-i-store-the-reason-for-stopping-ecs-tasks-in-cloudwatch-logs/#toc-1

■amazon-ecs-stopped-tasks-cwlogs
https://github.com/aws-samples/amazon-ecs-stopped-tasks-cwlogs

■Amazon ECS イベント
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_cwe_events.html