前提条件
- 第1回のVPCネットワーク基盤構築が完了していること
- 第2回のセキュリティとNAT Gateway設定が完了していること
- 第3回のApplication Load Balancer構築が完了していること
- こちらからDockerコンテナでの実行環境が準備できていること
Terraform実行環境の起動
前回と同様に、Dockerコンテナ内で作業を進めます。
# コンテナを起動してbashに入る
docker-compose run --rm terraform
これ以降のコマンドは、すべてこのコンテナ内で実行します。
基礎知識
ECS (Elastic Container Service)とは
ECSは、AWSが提供するフルマネージドなコンテナオーケストレーションサービスです。Dockerコンテナの実行、スケーリング、管理を自動化します。
WEBアプリケーション開発者の視点から見ると
- ローカル開発では
docker run
でコンテナを起動しますが、本番環境では複数のサーバーでコンテナを管理する必要があります - ECSが「どのサーバーでコンテナを動かすか」「コンテナが停止したら再起動する」といった運用を自動化
主な概念
概念 | 説明 | |
---|---|---|
Cluster | コンテナを実行するためのグループ | - |
Task Definition | コンテナの設定書(Dockerfileの実行時版) | CPUメモリ、環境変数、ポート設定 |
Service | Task Definitionを基に、指定した数のタスクを維持 | 「常に2つのタスクを動かす」 |
Task | Task Definitionから作成される実際のコンテナ実行単位 | 動作中のコンテナインスタンス |
Fargate起動タイプ
今回はFargateを使用します。これは、サーバーレスなコンテナ実行環境です。
EC2起動タイプとの違い
項目 | Fargate | EC2 |
---|---|---|
サーバー管理 | 不要(AWS側で管理) | 必要(自分でEC2を管理) |
料金 | 使用したCPU・メモリ分のみ | EC2インスタンス料金 |
スケーリング | 自動 | 手動設定が必要 |
学習コスト | 低い | 高い |
IAMロールの種類
ECSでは2つのIAMロールが必要です。
ECS Execution Role
用途: ECSサービスがタスクを起動・管理するための権限
主な権限
- ECRからDockerイメージをpull
- CloudWatch Logsにログを出力
- Secrets Managerから機密情報を取得
ECS Task Role
用途: アプリケーション(コンテナ内のプログラム)が使用する権限
主な権限
- S3バケットへのアクセス
- RDSへの接続
- 外部APIの呼び出し
権限の分離
ローカル開発では、開発者のAWSクレデンシャルですべてのリソースにアクセスできますが、本番環境では「必要な権限のみ」を付与する原則が重要です。
今回作成するネットワーク構成
リソース | 説明 | 数量 |
---|---|---|
ECS Cluster | コンテナを実行するためのクラスター | 1個 |
ECS Execution Role | ECSサービスがタスクを実行するためのIAMロール | 1個 |
ECS Task Role | アプリケーションが使用するIAMロール | 1個 |
ECR Repository | Dockerイメージを保存するリポジトリ | 1個 |
CloudWatch Log Group | アプリケーションのログを保存 | 1個 |
ネットワークフロー
- ECR: Dockerイメージの保存・取得
- ECS Cluster: Fargateタスクの実行環境
- CloudWatch Logs: アプリケーションログの集約
- ALB: 前回作成したTarget Groupにタスクを登録
Terraformコードの実装
ECSクラスター
Fargateベースのクラスターを作成します。
# terraform/ecs.tf
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = "${var.project_name}-${var.environment}-cluster"
setting {
name = "containerInsights"
value = "enabled"
}
tags = {
Name = "${var.project_name}-${var.environment}-cluster"
}
}
# ECS Cluster Capacity Providers
resource "aws_ecs_cluster_capacity_providers" "main" {
cluster_name = aws_ecs_cluster.main.name
capacity_providers = ["FARGATE", "FARGATE_SPOT"]
default_capacity_provider_strategy {
base = 1
weight = 100
capacity_provider = "FARGATE"
}
}
Container Insightsについて
Container Insightsは、コンテナのメトリクス(CPU、メモリ使用率など)を自動収集する機能です。追加料金が発生しますが、監視には便利です。
IAMロール
ECS Execution RoleとTask Roleを作成します。
# terraform/ecs.tf
# ECS Execution Role
resource "aws_iam_role" "ecs_execution_role" {
name = "${var.project_name}-${var.environment}-ecs-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
tags = {
Name = "${var.project_name}-${var.environment}-ecs-execution-role"
}
}
# ECS Execution Role Policy Attachment
resource "aws_iam_role_policy_attachment" "ecs_execution_role_policy" {
role = aws_iam_role.ecs_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
# ECS Task Role
resource "aws_iam_role" "ecs_task_role" {
name = "${var.project_name}-${var.environment}-ecs-task-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
tags = {
Name = "${var.project_name}-${var.environment}-ecs-task-role"
}
}
# ECS Task Role用のカスタムポリシー(アプリケーション固有の権限)
resource "aws_iam_role_policy" "ecs_task_role_policy" {
name = "${var.project_name}-${var.environment}-ecs-task-policy"
role = aws_iam_role.ecs_task_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "${aws_cloudwatch_log_group.ecs.arn}:*"
}
]
})
}
ECRリポジトリ
Dockerイメージを保存するためのプライベートリポジトリを作成します。
# terraform/ecr.tf
# ECR Repository
resource "aws_ecr_repository" "app" {
name = "${var.project_name}-${var.environment}-app"
image_tag_mutability = "MUTABLE"
force_delete = true
image_scanning_configuration {
scan_on_push = true
}
tags = {
Name = "${var.project_name}-${var.environment}-app-repo"
}
}
# ECR Repository Policy(開発環境用の簡素化されたポリシー)
resource "aws_ecr_repository_policy" "app" {
repository = aws_ecr_repository.app.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowPull"
Effect = "Allow"
Principal = {
AWS = aws_iam_role.ecs_execution_role.arn
}
Action = [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability"
]
}
]
})
}
CloudWatch Logs
アプリケーションのログを保存するLog Groupを作成します。
# terraform/cloudwatch.tf
# CloudWatch Log Group for ECS
resource "aws_cloudwatch_log_group" "ecs" {
name = "/ecs/${var.project_name}-${var.environment}"
retention_in_days = 14 # 開発環境では短期間で削除
# 削除時にログデータも強制削除
skip_destroy = false
tags = {
Name = "${var.project_name}-${var.environment}-ecs-logs"
Environment = var.environment
}
}
ログ保持期間について
本番環境では30日以上に設定することが一般的ですが、開発環境ではコスト削減のため14日に設定しています。
出力の更新
作成したリソースの情報をterraform/outputs.tf
に追加します。
# terraform/outputs.tf(既存の出力に追加)
# ECS関連の出力
output "ecs_cluster_id" {
description = "ECS Cluster ID"
value = aws_ecs_cluster.main.id
}
output "ecs_cluster_arn" {
description = "ECS Cluster ARN"
value = aws_ecs_cluster.main.arn
}
output "ecs_execution_role_arn" {
description = "ECS Execution Role ARN"
value = aws_iam_role.ecs_execution_role.arn
}
output "ecs_task_role_arn" {
description = "ECS Task Role ARN"
value = aws_iam_role.ecs_task_role.arn
}
# ECR関連の出力
output "ecr_repository_url" {
description = "ECR Repository URL"
value = aws_ecr_repository.app.repository_url
}
output "ecr_repository_arn" {
description = "ECR Repository ARN"
value = aws_ecr_repository.app.arn
}
# CloudWatch Logs関連の出力
output "cloudwatch_log_group_name" {
description = "CloudWatch Log Group Name"
value = aws_cloudwatch_log_group.ecs.name
}
output "cloudwatch_log_group_arn" {
description = "CloudWatch Log Group ARN"
value = aws_cloudwatch_log_group.ecs.arn
}
リソースを作成
Dockerコンテナ内で以下のコマンドを実行します。
# terraformディレクトリへ移動
cd terraform
# 実行計画の確認
terraform plan
# リソースの作成
terraform apply
ECSクラスター自体の作成は数秒で完了しますが、IAMロールの反映には1〜2分程度かかる場合があります。
作成結果の確認
# ECS Cluster情報の確認
terraform output ecs_cluster_id
terraform output ecs_cluster_arn
# IAMロール情報の確認
terraform output ecs_execution_role_arn
terraform output ecs_task_role_arn
# ECR Repository URL の確認(重要:次回のDockerイメージpushで使用)
terraform output ecr_repository_url
# CloudWatch Log Group の確認
terraform output cloudwatch_log_group_name
ecr_repository_url
の出力例
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-project-dev-app
AWS Management Consoleで確認
ECS > クラスター
作成されたクラスターを選択し、以下を確認します
カテゴリ | 項目 | 設定値 |
---|---|---|
基本設定 | クラスター名 | {project_name}-{environment}-cluster |
起動タイプ | FARGATE |
|
Container Insights | 有効 |
|
キャパシティプロバイダー | デフォルト | FARGATE |
利用可能 | FARGATE, FARGATE_SPOT |
|
アクティブなサービス | サービス数 | 0 (次回作成予定) |
実行中のタスク | タスク数 | 0 (次回作成予定) |
IAM > ロール
作成された2つのIAMロールを確認します
ECS Execution Role
- ロール名:
{project_name}-{environment}-ecs-execution-role
- 信頼されたエンティティ:
ecs-tasks.amazonaws.com
- アタッチされたポリシー:
AmazonECSTaskExecutionRolePolicy
ECS Task Role
- ロール名:
{project_name}-{environment}-ecs-task-role
- 信頼されたエンティティ:
ecs-tasks.amazonaws.com
- インラインポリシー: CloudWatch Logsへの書き込み権限
ECR > リポジトリ
作成されたECRリポジトリを確認します
カテゴリ | 項目 | 設定値 |
---|---|---|
基本設定 | リポジトリ名 | {project_name}-{environment}-app |
イメージタグの変更可能性 | MUTABLE |
|
イメージ | 保存済みイメージ | 0 (次回pushする) |
CloudWatch > ログ > ロググループ
作成されたLog Groupを確認します
- ロググループ名:
/ecs/{project_name}-{environment}
- 保持期間:
14日
- ログストリーム:
0
(タスク実行時に作成される)
コスト管理
ECSクラスター自体は無料ですが、以下のリソースで料金が発生します。
リソース | 料金発生タイミング | 概算コスト |
---|---|---|
ECS Fargate | タスク実行中のみ | vCPU: $0.04048/時間、メモリ: $0.004445/GB・時間 |
ECR | ストレージ使用量 | $0.10/GB・月 |
CloudWatch Logs | ログ保存量 | $0.50/GB・月 |
Container Insights | メトリクス取得 | 追加料金 |
コスト削減のポイント
Container Insightsを無効にする場合
# terraform/ecs.tf の setting ブロックをコメントアウト
# setting {
# name = "containerInsights"
# value = "enabled"
# }
次のステップ
次回は、RDSデータベースを構築します。今回作成したECS Task RoleにRDSアクセス権限を追加し、アプリケーションからデータベースに接続できる環境を整備します。
- RDS PostgreSQLインスタンスの作成
- DB Subnet Group、Multi-AZ設定
- Secrets Managerでの認証情報管理
- データベース接続確認