ECS on GPU EC2：ECSクラスターで動かすCUDAコンテナ基盤自動構築

# はじめに

## 本記事の目的
本記事では、Terraformを用いてAWS上にGPU搭載のEC2インスタンスを使ったECSクラスターを自動構築し、CUDA対応コンテナを簡単に動作させる基盤を作る方法を解説します。

特に、深層学習やGPGPUによる高負荷計算処理など、GPUリソースを必要とするユースケースにおいて、インフラの構築・運用を効率化することを目的としています。

「GPU付きEC2を起動して、CUDAが動くDockerコンテナをECSで動かしたいけど、設定が多くて大変そう...」という方に向けて、構築の自動化と再現性の確保を主眼に置いています。

作成されたレポジトリーはこちらです。


## 使用技術スタック

| 技術                 | 用途                                                                 |
|----------------------|----------------------------------------------------------------------|
| AWS ECS              | GPUコンテナの実行・管理基盤（EC2ベースのクラスター）              |
| AWS EC2              | GPU搭載のg4dnインスタンス（CUDA処理の実行）                        |
| Terraform            | インフラ構成のコード化と自動構築                                    |
| Docker               | CUDA対応コンテナの作成・実行                                        |
| NVIDIA ドライバ + CUDA | GPUによる高速処理を可能にする                                        |
| PyTorch              | 深層学習フレームワーク（動作確認用に使用）                          |

## 処理の流れ（起動～実行まで）
### 1. Terraformスクリプトの実行
bin/terraform_apply により、以下のリソースを自動作成：
+ VPC、サブネット、セキュリティグループ
+ ECSクラスター（EC2起動タイプ）
+ g4dn.xlargeのGPU搭載EC2インスタンス（NVIDIAドライバ付きAMI）
+ ECSタスク定義とサービス（CUDAコンテナ）

### 2. ECS上でコンテナ起動
GPU対応のDockerイメージ（PyTorch + CUDA）を使って、ECSタスクが起動。GPUが認識されていることをログで確認可能

### 3. CUDA・PyTorchの動作確認
起動したコンテナで torch.cuda.is_available() を実行し、CUDAが有効なことを検証

### 4. 停止・破棄
環境を不要にした場合は bin/terraform_destroy によりすべてのリソースを削除


# ECSの基本概念とFargateとの違い

Amazon ECS は、AWS が提供するフルマネージドなコンテナオーケストレーションサービスです。Docker コンテナの起動・停止・スケジューリング・スケーリングなどを自動で管理できます。
ECS には主に以下の2つの実行タイプがあります：

| 実行タイプ | 概要                                                             | 適したユースケース                                       |
|------------|------------------------------------------------------------------|----------------------------------------------------------|
| EC2        | ユーザーがEC2インスタンスを用意し、その上でコンテナを実行       | GPU・特殊ドライバが必要なケース、細かなチューニング     |
| Fargate    | サーバーレスで、AWSがコンテナのインフラを自動管理               | 小規模アプリ、スケーラビリティ重視、手軽に試したいとき |

### 本構成でEC2を選ぶ理由
Fargate は便利ですが、現時点（2025年4月）では GPUサポートがない ため、CUDAコンテナなどGPUを必要とするワークロードでは EC2 実行タイプ一択 となります。

このため、本記事では EC2ベースの ECS クラスターを構築し、GPUリソースを持つコンテナを直接実行します。

## コンテナの構成とCUDA対応：実装方法
本セクションでは、PyTorch + CUDA で GPU 処理を行うコンテナの作成方法について、具体的な Dockerfile と Python スクリプトを交えて解説します。
このコンテナは、ECS 上で起動した際に GPU が正しく認識されているかどうかを検証するためのものです。

### Dockerfile：PyTorch + CUDA対応イメージ
以下は、CUDA 11.8 + cuDNN 8 対応の PyTorch ベースイメージを使った Dockerfile です。
```Dockerfile
# ベースイメージ（CUDA11.8 + cuDNN8 対応 PyTorch）
FROM pytorch/pytorch:2.1.0-cuda11.8-cudnn8-runtime

# pip のアップグレード（依存関係のエラー回避）
RUN pip install --upgrade pip

# アプリケーションコードをコンテナ内にコピー
COPY main.py /main.py

# コンテナ起動時に main.py を実行
CMD ["python", "/main.py"]
```

### main.py：GPUが使えるか確認するPythonコード
以下は、PyTorch で GPU（CUDA）が正しく動作しているかを確認するための Python スクリプトです。
```python
import torch

print("CUDA Available:", torch.cuda.is_available())
print("Device Name:", torch.cuda.get_device_name(0))

x = torch.tensor([1.0, 2.0, 3.0]).cuda()
y = torch.tensor([9.0, 8.0, 7.0]).cuda()
z = x + y

print("Result:", z)
```

### CUDA対応ECS AMIの取得
ECS GPU 対応インスタンスを立ち上げるには、Amazon が提供する ECS 対応 GPU AMIを使用します。
```terraform
data "aws_ami" "app" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-ecs-gpu-hvm-*-x86_64-ebs"]
  }

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "root-device-type"
    values = ["ebs"]
  }
}
```

### Launch Template の作成

ECS クラスタに参加する EC2（GPU搭載）のインスタンスを定義します。

```terraform
resource "aws_launch_template" "ecs_gpu_launch_template" {
  name_prefix   = "ecs-gpu-"
  image_id      = data.aws_ami.app.id
  instance_type = "g4dn.xlarge"
  key_name      = aws_key_pair.keypair.key_name

  iam_instance_profile {
    name = var.instance_profile_name
  }

  network_interfaces {
    associate_public_ip_address = true
    security_groups             = [var.sg_ecs_id]
    subnet_id                   = var.public_subnet_1a_id
  }

  user_data = base64encode(<<-EOF
#!/bin/bash
set -euo pipefail

# ECS クラスター名と GPU サポート設定
cat <<EOT > /etc/ecs/ecs.config
ECS_CLUSTER=chatbot-cluster
ECS_ENABLE_GPU_SUPPORT=true
EOT

# ECS 再起動スクリプト
cat <<'EOT' > /usr/local/bin/start-ecs.sh
#!/bin/bash
systemctl stop ecs || true
systemctl enable ecs
systemctl start ecs
EOT

chmod +x /usr/local/bin/start-ecs.sh

# cloud-init 完了時に ECS を起動
cat <<'EOT' > /etc/cloud/cloud.final.d/99-start-ecs.cfg
#cloud-config
runcmd:
  - [ /usr/local/bin/start-ecs.sh ]
EOT
EOF
  )

  instance_market_options {
    market_type = "spot"

    spot_options {
      instance_interruption_behavior = "terminate"
      spot_instance_type             = "one-time"
    }
  }

  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "gpu-ecs-instance"
    }
  }
}
```

### Auto Scaling Group の設定

ECS の GPU インスタンスを ASG（Auto Scaling Group）で管理します。

```terraform
resource "aws_autoscaling_group" "ecs_gpu_asg" {
  name                      = "ecs-gpu-asg"
  desired_capacity          = 1
  max_size                  = 1
  min_size                  = 1
  vpc_zone_identifier       = [var.public_subnet_1a_id]
  health_check_type         = "EC2"
  health_check_grace_period = 300

  launch_template {
    id      = aws_launch_template.ecs_gpu_launch_template.id
    version = "$Latest"
  }

  tag {
    key                 = "Name"
    value               = "gpu-ecs-instance"
    propagate_at_launch = true
  }

  lifecycle {
    create_before_destroy = true
  }
}
```

### EC2 キーペアの管理

SSHログインやトラブル時のデバッグ用にキーペアもTerraformで定義します。
```terraform
resource "aws_key_pair" "keypair" {
  key_name   = "${var.app_name}-keypair"
  public_key = file("./modules/ec2/src/keypair.pub")

  tags = {
    Name = "${var.app_name}-keypair"
  }
}
```

### CloudWatch ロググループの作成

ECS コンテナのログは CloudWatch Logs に出力させるのが定番です。

```terraform
resource "aws_cloudwatch_log_group" "ecs_log" {
  name              = "/ecs/pytorch-gpu"
  retention_in_days = 7
}
```

### ECS クラスターの作成
ECS クラスターを作成し、Container Insights を有効化することで CloudWatch でリソースの可視化が可能になります。

```terraform
resource "aws_ecs_cluster" "gpu_cluster" {
  name = "${var.app_name}-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }

  tags = {
    Name = "${var.app_name}-cluster"
  }
}
```

### ECSタスク定義（GPUリソース付き）
CUDA コンテナを ECS 上で動かすには、GPUリソース要求を明示的に設定する必要があります。

```terraform
resource "aws_ecs_task_definition" "pytorch_gpu_task" {
  family                   = "pytorch-gpu-task"
  network_mode             = "bridge"
  requires_compatibilities = ["EC2"]
  execution_role_arn       = var.iam_role_ecs_role_arn
  task_role_arn            = var.iam_role_ecs_role_arn

  container_definitions = jsonencode([
    {
      name      = "pytorch-gpu-container"
      image     = "${var.ai_repository_url}:latest"
      essential = true
      cpu       = 4096,
      memory    = 15000,

      resourceRequirements = [
        {
          type  = "GPU"
          value = "1"
        }
      ],
      logConfiguration = {
        logDriver = "awslogs",
        options = {
          awslogs-group         = "/ecs/pytorch-gpu",
          awslogs-region        = "ap-northeast-1",
          awslogs-stream-prefix = "ecs"
        }
      }
    }
  ])
}
```

### ECSサービスの作成
1つのタスクを常に動作させるシンプルなサービス構成です。

```terraform
resource "aws_ecs_service" "pytorch_gpu_service" {
  name            = "pytorch-gpu-service"
  cluster         = aws_ecs_cluster.gpu_cluster.id
  task_definition = aws_ecs_task_definition.pytorch_gpu_task.arn
  desired_count   = 1
  launch_type     = "EC2"

  deployment_controller {
    type = "ECS"
  }
}
```

## 実行結果

クラウドコンソール上で、以下の画面が出たら成功

![aws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/9b5814eb-590a-4c66-9365-cddbdfe96a6a.png)