個人的備忘録：TerraformでECSタスク定義（aws_ecs_task_definition）を設定し、一部を解説してみた

## はじめに
ECSタスクをAWS Fargateで実行する際、Datadogを活用した監視やCloudWatch Logsを使ったログ管理が重要になります。

本記事では、Terraformを用いてECSタスク定義を構築する手順を解説します。

## 書こうと思ったきっかけ
ECSタスクを運用する中で、コンテナの監視やログ管理を適切に設定しないと、トラブル時の原因特定が困難になると実感しました。

DatadogやCloudWatch Logsを組み合わせることで、リアルタイムの監視や効率的なデバッグが可能になるため、その方法を整理してみました。

## 実際に解説してみた

このTerraformの設定は、**AWS Fargate上で動作するECSタスク** を定義しています。

タスクには、Datadog監視用のエージェントとアプリケーションコンテナが含まれており、**CloudWatch Logsでログを管理** する構成になっています。

:::note info
***それぞれのパートについて詳しく解説します。***
:::

### 1. ECSタスクの基本設定

```hcl
resource "aws_ecs_task_definition" "main" {
  family                   = "Datadog"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"
  memory                   = "512"
  execution_role_arn       = aws_iam_role.ecs_task_execution_role.arn
```

- **何をしているか？**

  - **タスク名 (`family`) を `"Datadog"` に設定**  
  - **ネットワークモード (`network_mode`) を `"awsvpc"` に指定**（各コンテナに独自の ENI が付与される）  
  - **Fargate を使用 (`requires_compatibilities`)**  
  - **CPU とメモリを指定 (`cpu`, `memory`)**  
    - `cpu = 256`（0.25 vCPU）、`memory = 512 MB`（リソースが少なめの軽量コンテナ向け）  
  - **実行ロール (`execution_role_arn`) を指定し、タスクが AWS のサービスへアクセスできるようにする**

### 2. ボリュームの設定

```hcl
  volume {
    name = "cws-instrumentation-volume"
  }
```

- **何をしているか？**

  - **`cws-instrumentation-volume` という名前のボリュームを作成**  
  - **コンテナ間で共有するデータの保存用**  

### 3. Datadog用の初期設定コンテナ (`cws-instrumentation-init`)

```hcl
{
  name       = "cws-instrumentation-init"
  image      = "datadog/cws-instrumentation:latest"
  essential  = false
  user       = "0"
  command    = [
    "/cws-instrumentation",
    "setup",
    "--cws-volume-mount",
    "/cws-instrumentation-volume"
  ]
  mountPoints = [
    {
      sourceVolume  = "cws-instrumentation-volume"
      containerPath = "/cws-instrumentation-volume"
      readOnly      = false
    }
  ]
```

- **何をしているか？**

  - **Datadog のセキュリティ監視用の初期設定コンテナ**  
  - **`command` により `/cws-instrumentation setup` を実行し、ボリュームに設定情報を書き込む**  
  - **ボリュームを `/cws-instrumentation-volume` にマウント**  

### 4. Datadogエージェント (`datadog-agent`)

```hcl
{
  name      = "datadog-agent"
  image     = "datadog/agent:latest"
  essential = true
  environment = [
    {
      name  = "DD_API_KEY"
      value = "xxx"
    },
    {
      name  = "DD_SITE"
      value = "ap1.datadoghq.com"
    },
    {
      name  = "ECS_FARGATE"
      value = "true"
    },
    # メトリクス収集（CPU・メモリ）を有効化
    {
      name  = "DD_CONTAINER_METRICS_ENABLED"
      value = "true"
    },
```

- **何をしているか？**

  - **Datadog の監視エージェントを起動**  
  - **環境変数で Datadog の API キー (`DD_API_KEY`) や監視サイト (`DD_SITE`) を指定**  
  - **CPU/メモリメトリクス (`DD_CONTAINER_METRICS_ENABLED=true`) を有効化**  
  - **不要な機能（APM, Process Monitoring, Security Agent など）を無効化**  
    - `DD_PROCESS_AGENT_ENABLED=false`  
    - `DD_APM_ENABLED=false`  
    - `DD_SECURITY_AGENT_ENABLED=false`  
    - `DD_LOGS_ENABLED=false`  

### 5. アプリケーションコンテナ (`my-app-repo`)

```hcl
{
  name  = "my-app-repo"
  image = "881490128743.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo"
  entryPoint = ["/usr/src/app/app"] # 実行ファイルの絶対パスを指定
```

- **何をしているか？**

  - **アプリケーションコンテナ**  
  - **ECR (Amazon Elastic Container Registry) に格納された `my-app-repo` の Docker イメージを使用**  
  - **エントリーポイント (`entryPoint`) を `/usr/src/app/app` に指定**  

**依存関係の設定**

```hcl
  dependsOn = [
    {
      containerName = "datadog-agent"
      condition     = "HEALTHY"
    },
    {
      containerName = "cws-instrumentation-init"
      condition     = "SUCCESS"
    }
  ]
```

- **Datadog エージェント (`datadog-agent`) がヘルスチェック (`HEALTHY`) を通過し、**  
- **`cws-instrumentation-init` のセットアップが成功 (`SUCCESS`) してから起動。**  

**ポート設定**

```hcl
  portMappings = [
    {
      containerPort = 1323
      hostPort      = 1323
      protocol      = "tcp"
    }
  ]
```

- **アプリケーションの通信を `1323` ポートで受け付ける** 

### 6. CloudWatch Logs の設定

```hcl
logConfiguration = {
  logDriver = "awslogs"
  options = {
    awslogs-group         = "/ecs/datadog"
    awslogs-region        = "ap-northeast-1"
    awslogs-stream-prefix = "my-app-repo"
  }
}
```

- **何をしているか？**  

  - **CloudWatch Logs (`awslogs`) にアプリケーションのログを送信**  
  - **ロググループ `/ecs/datadog` を使用**  
  - **AWS リージョン `ap-northeast-1` に設定**  
  - **ログストリームのプレフィックスを `"my-app-repo"` にする**  

## ここまでの設定周りを表にしてみた

| 設定項目 | 説明 |
|----------|------------------------------------------|
| **ECS タスク定義** | Fargate で動作する ECS タスクを定義 |
| **ネットワークモード (`awsvpc`)** | 各コンテナに独立した ENI を割り当てる |
| **CPU / メモリ設定** | `256 vCPU / 512MB` |
| **Datadog 初期設定 (`cws-instrumentation-init`)** | Datadog のセキュリティ設定を準備 |
| **Datadog エージェント (`datadog-agent`)** | モニタリング用コンテナを起動 |
| **アプリケーション (`my-app-repo`)** | ECR から取得したアプリケーションコンテナ |
| **依存関係 (`dependsOn`)** | Datadog エージェントが正常に起動してからアプリを起動 |
| **ポート設定 (`portMappings`)** | `1323` ポートで通信 |
| **CloudWatch Logs 設定 (`logConfiguration`)** | ログを `/ecs/datadog` に送信 |

## 実際の完成したコードについて

```hcl
# ECS タスク定義（Fargate で動作するコンテナの定義）
resource "aws_ecs_task_definition" "main" {
  family                   = "Datadog"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"
  memory                   = "512"
  execution_role_arn       = aws_iam_role.ecs_task_execution_role.arn

  volume {
    name = "cws-instrumentation-volume"
  }

  container_definitions = jsonencode([
    {
      name       = "cws-instrumentation-init"
      image      = "datadog/cws-instrumentation:latest"
      essential  = false
      user       = "0"
      command    = [
        "/cws-instrumentation",
        "setup",
        "--cws-volume-mount",
        "/cws-instrumentation-volume"
      ]
      mountPoints = [
        {
          sourceVolume  = "cws-instrumentation-volume"
          containerPath = "/cws-instrumentation-volume"
          readOnly      = false
        }
      ]
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "/ecs/datadog"
          awslogs-region        = "ap-northeast-1"
          awslogs-stream-prefix = "cws-instrumentation-init"
        }
      }
    },
    {
      name      = "datadog-agent"
      image     = "datadog/agent:latest"
      essential = true
      environment = [
        {
          name  = "DD_API_KEY"
          value = "xxx"
        },
        {
          name  = "DD_SITE"
          value = "ap1.datadoghq.com"
        },
        {
          name  = "ECS_FARGATE"
          value = "true"
        },
        # メトリクス収集（CPU・メモリ）を有効化
        {
          name  = "DD_CONTAINER_METRICS_ENABLED"
          value = "true"
        },
        # Process Monitoring を完全に無効化
        {
          name  = "DD_PROCESS_AGENT_ENABLED"
          value = "false"
        },
        {
          name  = "DD_PROCESS_CONFIG_ENABLED"
          value = "false"
        },
        {
          name  = "DD_ORCHESTRATOR_EXPLORER_ENABLED"
          value = "false"
        },
        {
          name  = "DD_SYSTEM_PROBE_ENABLED"
          value = "false"
        },
        # APM, ログ, セキュリティ機能も無効化
        {
          name  = "DD_LOGS_ENABLED"
          value = "false"
        },
        {
          name  = "DD_APM_ENABLED"
          value = "false"
        },
        {
          name  = "DD_TRACE_ENABLED"
          value = "false"
        },
        {
          name  = "DD_SECURITY_AGENT_ENABLED"
          value = "false"
        },
        {
          name  = "DD_RUNTIME_SECURITY_CONFIG_ENABLED"
          value = "false"
        },
        {
          name  = "DD_RUNTIME_SECURITY_CONFIG_EBPFLESS_ENABLED"
          value = "false"
        }
      ]
      healthCheck = {
        command     = ["CMD-SHELL", "/probe.sh"]
        interval    = 30
        timeout     = 5
        retries     = 2
        startPeriod = 60
      }
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "/ecs/datadog"
          awslogs-region        = "ap-northeast-1"
          awslogs-stream-prefix = "datadog-agent"
        }
      }
    },
    {
      name  = "my-app-repo"
      image = "881490128743.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo"
      entryPoint = ["/usr/src/app/app"] # 実行ファイルの絶対パスを指定
      mountPoints = [
        {
          sourceVolume  = "cws-instrumentation-volume"
          containerPath = "/cws-instrumentation-volume"
          readOnly      = true
        }
      ]
      linuxParameters = {
        capabilities = {
          add = ["SYS_PTRACE"]
        }
      }
      dependsOn = [
        {
          containerName = "datadog-agent"
          condition     = "HEALTHY"
        },
        {
          containerName = "cws-instrumentation-init"
          condition     = "SUCCESS"
        }
      ]
      portMappings = [
        {
          containerPort = 1323
          hostPort      = 1323
          protocol      = "tcp"
        }
      ]
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "/ecs/datadog"
          awslogs-region        = "ap-northeast-1"
          awslogs-stream-prefix = "my-app-repo"
        }
      }
    }
  ])
}
```

## まとめ
この記事は、自分用の備忘録としてまとめたものです。その点をご理解いただけると幸いです。

:::note info
***もし、この記事の内容が少しでも参考になれば嬉しく思います。***
:::

今後も同様の内容を継続して投稿していきますので、温かく見守っていただけるとありがたいです。