ECSタスク構成（Django + Nginx on Fargate）：カスタムJSONを用いたタスク定義を体系的に整理してみた

## はじめに

ECS Fargate を利用して、Django アプリと Nginx を組み合わせたリバースプロキシ構成を実装する際、**手動設定では細かいミスが発生しやすい** ことがあります。

:::note warn
しかし、**カスタム JSON を使うことで、設定の一貫性を保ちつつ、効率よくタスク定義を作成できる** ことが分かりました。
:::

普段は Terraform や CloudFormation を使って設定を行っていますが、手動で作成する際は従来の **コンソールからの新しいタスク定義の作成** を行っていました。

本記事では、**カスタム JSON の各項目を解説しながら、タスク定義の仕組みを深掘り** していきます。

## それぞれのソースコードはこちら

**ローカル開発用リポジトリ**

https://github.com/free-honda/reverse-proxy-django-dev

**ローカル開発の備忘録記事**

https://qiita.com/free-honda/items/57bd55b1f70348295a13

**本番 ECS 用リポジトリ**

https://github.com/free-honda/reverse-proxy-django-prd

**本番 ECS 用の備忘録記事**

https://qiita.com/free-honda/items/7e0a5a02dad7c3bdb981

現在、それぞれのリポジトリには README などの説明は記載していませんが、今後追加していく予定です。

## 少し困っていたこと

AWS のマネジメントコンソール上で手動でタスク定義を作成する必要がありましたが、その際に **細かい設定漏れ** などが発生し、少し困っていました。

しかし、**JSON を使って設定する方法** もあるため、今回はその手順をご紹介します。

**こんな感じ👇**

![Screenshot 2025-02-01 at 4.39.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/7ee497ac-7cb4-c8cf-ff70-f808ffb52956.png)

## こんなシナリオのアプリ開発を想定

- **AWS ECS Fargate** を利用し、サーバーレスで Django アプリを運用  
- **Nginx をリバースプロキシ** として使用し、外部からのリクエストを適切に振り分け  
- **アプリのログを AWS CloudWatch Logs に集約** して監視  

**こんな感じ👇**

![Screenshot 2025-02-01 at 5.05.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/d1481a63-25ec-e41e-9d99-b5082ac32ea2.png)


## どのように動作するか？

この ECS タスクは **Django + Nginx のコンテナを Fargate** で動作させる構成になっています。

**リクエストの流れ**

```css
クライアント → Nginx → Django → Nginx → クライアント
　　　　　　　↘ CloudWatch Logs
```

**ポイント**

1. **Nginx（`nginx` コンテナ）** がポート `80` でリクエストを受ける。
2. Nginx がリクエストを **Django（`django` コンテナ）** に転送。
3. **Django** がリクエストを処理し、レスポンスを **Nginx** に返す。
4. **Nginx** が最終レスポンスをクライアントに送る。

**こんな感じ👇**

![Screenshot 2025-02-01 at 5.06.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/a838bfb4-1ae7-ccc4-632c-a43f3c911573.png)


**ログ管理**

- **Django** と **Nginx** のログは共に **CloudWatch Logs** に送信。
  - ロググループ: `/aws/ecs/reverse-proxy-cluster`

## 実際に使った JSON

詳しくは **前回の記事** で紹介していますので、詳細を知りたい方は参考にしてみてください。  

https://qiita.com/free-honda/items/7e0a5a02dad7c3bdb981

この JSON は **ECS Fargate を利用し、Django アプリと Nginx のリバースプロキシ構成** で動作するタスクを定義しています。

```json
{
  "family": "reverse-proxy-task",
  "executionRoleArn": "arn:aws:iam::xxx:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "1024",
  "memory": "2048",
  "containerDefinitions": [
      {
          "name": "django",
          "image": "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/reverse-proxy-django-app",
          "cpu": 512,
          "memory": 1024,
          "portMappings": [
              {
                  "containerPort": 8000,
                  "protocol": "tcp"
              }
          ],
          "essential": true,
          "environment": [
              {
                  "name": "DJANGO_SETTINGS_MODULE",
                  "value": "mysite.settings"
              },
              {
                  "name": "DJANGO_SECRET_KEY",
                  "value": "your-secret-key"
              },
              {
                  "name": "STATIC_ROOT",
                  "value": "/app/staticfiles"
              }
          ],
          "logConfiguration": {
              "logDriver": "awslogs",
              "options": {
                  "awslogs-group": "/aws/ecs/reverse-proxy-cluster",
                  "awslogs-region": "ap-northeast-1",
                  "awslogs-stream-prefix": "ecs"
              }
          }
      },
      {
          "name": "nginx",
          "image": "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/reverse-proxy-django-nginx",
          "cpu": 512,
          "memory": 1024,
          "portMappings": [
              {
                  "containerPort": 80,
                  "protocol": "tcp"
              }
          ],
          "essential": true,
          "logConfiguration": {
              "logDriver": "awslogs",
              "options": {
                  "awslogs-group": "/aws/ecs/reverse-proxy-cluster",
                  "awslogs-region": "ap-northeast-1",
                  "awslogs-stream-prefix": "ecs"
              }
          }
      }
  ]
}
```

### 1. タスク定義全体の概要

```json
{
  "family": "reverse-proxy-task",
  "executionRoleArn": "arn:aws:iam::xxx:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "1024",
  "memory": "2048",
  "containerDefinitions": [...]
}
```

**ECS タスク定義のポイント**

- **family**  
  - タスク定義の名前（例: `reverse-proxy-task`）。  
  - 更新時に新バージョンが作成される。

- **executionRoleArn**  
  - ECS タスクの実行ロール（IAM ロール）。  
  - 例えば、ECR（Amazon Elastic Container Registry）からイメージを取得する権限を持つ。

- **networkMode: `"awsvpc"`**  
  - AWS VPC ネットワークモードを使用。  
  - 各コンテナに独立した ENI（Elastic Network Interface）が割り当てられる。  
  - セキュリティグループやサブネット設定が必要。

- **requiresCompatibilities: `["FARGATE"]`**  
  - Fargate を使用する設定（EC2 ではなく、サーバーレスで動作）。

- **cpu / memory**  
  - タスク全体のリソース割り当て（CPU 1024 / メモリ 2048MB）。  
  - 2つのコンテナ（Django + Nginx）がこのリソースを分け合う。

### 2. containerDefinitions（コンテナ設定）
ECS タスク内で動作する 2つのコンテナ（Django アプリ & Nginx）を定義しています。

#### (1) Django コンテナ

```json
{
  "name": "django",
  "image": "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/reverse-proxy-django-app",
  "cpu": 512,
  "memory": 1024,
  "portMappings": [
      {
          "containerPort": 8000,
          "protocol": "tcp"
      }
  ],
  "essential": true,
  "environment": [
      {
          "name": "DJANGO_SETTINGS_MODULE",
          "value": "mysite.settings"
      },
      {
          "name": "DJANGO_SECRET_KEY",
          "value": "your-secret-key"
      },
      {
          "name": "STATIC_ROOT",
          "value": "/app/staticfiles"
      }
  ],
  "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
          "awslogs-group": "/aws/ecs/reverse-proxy-cluster",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
      }
  }
}
```

**Django コンテナのポイント**

- **image**  
  - ECR に保存された Django アプリの Docker イメージを使用。

- **cpu: 512 / memory: 1024**  
  - タスク全体（1024/2048）の半分をこのコンテナが利用。

- **portMappings**  
  - Django アプリはコンテナ内のポート `8000` でリクエストを受け付ける。

- **environment（環境変数）**  
  - `DJANGO_SETTINGS_MODULE`: Django の設定ファイルを指定（`mysite.settings`）。  
  - `DJANGO_SECRET_KEY`: Django のシークレットキー（実際は AWS Secrets Manager に置くべき）。  
  - `STATIC_ROOT`: 静的ファイルのディレクトリを `/app/staticfiles` に指定。

- **logConfiguration**  
  - ログドライバーに `awslogs` を指定。  
  - `/aws/ecs/reverse-proxy-cluster` にログを保存。

#### (2) Nginx コンテナ

```json
{
  "name": "nginx",
  "image": "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/reverse-proxy-django-nginx",
  "cpu": 512,
  "memory": 1024,
  "portMappings": [
      {
          "containerPort": 80,
          "protocol": "tcp"
      }
  ],
  "essential": true,
  "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
          "awslogs-group": "/aws/ecs/reverse-proxy-cluster",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
      }
  }
}
```

**Nginx コンテナのポイント**

- **image**  
  - ECR に保存された Nginx の Docker イメージを使用。

- **cpu: 512 / memory: 1024**  
  - Django と同じく、タスク全体（1024/2048）の半分を使用。

- **portMappings**  
  - コンテナのポート `80`（HTTP）でリクエストを受け付ける。  
  - 外部からのリクエストはまず Nginx が受け取る。

- **logConfiguration**  
  - Django と同じ CloudWatch Logs にログを送信（`/aws/ecs/reverse-proxy-cluster`）。

## まとめ
リバースプロキシの部分ではかなり苦戦しましたが、最終的に無事やり遂げることができてほっとしています。

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

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