AWS上級 #1 ECS と Fargate — コンテナのデプロイ
AWS 基礎 7 編 でアカウント / IAM / セキュリティ / CloudWatch の土台を据え、AWS 中級 7 編 で EC2 / VPC / S3 / RDS / Route 53 / ALB / CloudFront に慣れたなら、いよいよ コンテナ へ一段上がります。
AWS 上級 7 編は EC2 1 台に直接載せる方式 を抜け出し、コンテナ / サーバーレス / メッセージ / シークレット / ワークフローのような 運用規模で出会う道具箱 を整理します。
- #1 ECS と Fargate — コンテナのデプロイ ← 今回
- #2 ECR — イメージレジストリ
- #3 Lambda 基礎
- #4 API Gateway + Lambda
- #5 EventBridge / SQS / SNS
- #6 Secrets Manager / Parameter Store
- #7 Step Functions 入門
今回はその出発点 — ECS と Fargate。Docker で作ったイメージを AWS でどう動かすかの標準パターンを掴みます。
EC2 1 台に直接載せる方式の限界 #
中級 #2 EC2 運用 の流れ — EC2 インスタンスを作って SSH で入り、nginx / docker / コードを直接インストールして systemd で起動 — は シンプルな場合は十分 です。ただし規模が大きくなると渇きが来ます。
| 渇き | EC2 直接運用 |
|---|---|
| 同じ環境の再現 | OS パッチ、依存ドリフトで毎回違う |
| スケールアウト | AMI 作り → ASG → デプロイ — 分単位 |
| 無停止デプロイ | 複雑なシェルスクリプト / 別ツール |
| ロールバック | スナップショット → 起動 → トラフィック移動 |
| ヘルスチェック / 自動復旧 | systemd では限界 |
この渇きをコンテナが一気に解くのがモダンインフラの流れ。AWS でその入口が ECS です。
ECS が担うこと #
Amazon ECS (Elastic Container Service) は AWS のマネージドコンテナオーケストレーター。Docker イメージを受け取って どのマシンで、何個立ち上げ、トラフィックをどう送るか を決めておけば ECS が運用してくれます。
ECS vs EKS — 一行比較 #
| ECS | EKS | |
|---|---|---|
| 正体 | AWS 自体のオーケストレーター | AWS が管理する Kubernetes |
| 学習曲線 | 浅い (AWS の中によく溶け込む) | 急 (k8s 自体の学習が必要) |
| 他クラウド移植性 | 低い (AWS 専用) | 高い (k8s 標準) |
| エコシステム | AWS ツール + 一部コミュニティ | k8s 全エコシステム (Helm、ArgoCD 等) |
| 運用負担 | 低い | 高い (Control Plane コスト + 運用知識) |
| 適した場合 | 小〜中規模、AWS 依存 OK | 大規模、マルチクラウド、k8s 標準が必要 |
初めてコンテナ運用を始めるなら ECS から。EKS は 中級 #1 EC2/VPC のような土台 + k8s 自体の学習が終わった後で。
ECS のもう一人の友人として App Runner もあります。ECS よりさらにシンプル (イメージ → URL を一発)。ただしオプションが狭く、運用の領域を ECS / Fargate が占めるのが現在の標準です。
ECS の 4 つの構成要素 #
ECS を理解するには 4 つの構成要素 だけ覚えれば十分です。
┌──────────────────────────────────────┐
│ Cluster — まとまりの単位 │
│ ┌────────────────────────────────┐ │
│ │ Service — 常に N 個維持 │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Task #1 │ │ Task #2 │ │ │
│ │ │ (コンテナ) │ │ (コンテナ) │ │ │
│ │ └────────────┘ └────────────┘ │ │
│ │ ↑ Task Definition (設計図) │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘1) Task Definition — コンテナの設計図 #
JSON 1 枚。何をどう立ち上げるか が全部入っています。
- どのイメージ (
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1) - CPU / メモリ (
512/1024 MB) - 環境変数 / Secrets
- ポートマッピング
- ログドライバ (通常 CloudWatch Logs)
- IAM ロール (Task Role + Execution Role — 後で詳しく)
- ヘルスチェック
{
"family": "myapp",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/myapp-task-role",
"containerDefinitions": [
{
"name": "web",
"image": "123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1",
"essential": true,
"portMappings": [{ "containerPort": 8000, "protocol": "tcp" }],
"environment": [
{ "name": "ENV", "value": "production" }
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:myapp/db-AbCdEf"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "web"
}
}
}
]
}Task Definition は revision (myapp:7 のような番号) で累積されます。新しいイメージをデプロイするには新しい revision を作って Service がそれを参照するように変える形。
2) Task — 実行中のインスタンス #
Task Definition を実際に立ち上げた 1 つのコンテナ (またはコンテナの束) です。EC2 のインスタンスに相当。
- 1 Task = 1 つの Task Definition revision の実行
- Task の中に コンテナが複数個 あることも (サイドカーパターン — メインアプリ + ログ収集等)
- Task は自分の ENI (ネットワークインターフェース) + IP を持つ (
awsvpcモード)
3) Service — N 個を常に維持 #
「Task を 1 回立ち上げよ」だけだとそれが死んだら終わり。Service はその上で次を担います:
- 「この Task Definition の Task を 常に N 個 維持せよ」
- 死んだら自動再起動
- ALB / NLB と接続してトラフィックを受ける (中級 #6)
- デプロイ戦略 (rolling、blue/green)
- Auto Scaling (CPU / メモリ / リクエスト数ベース)
運用ワークロード (Web サーバー、API 等) はほとんど Service で立ち上げます。単発のバッチ作業だけ Service なしで Task を直接実行 (RunTask)。
4) Cluster — まとまり #
Service / Task が住む論理的なまとまり。通常は 環境単位 で分離:
prod-clusterstaging-clusterdev-cluster
Cluster は 無料 です (Cluster 自体に費用はない)。中で動く Task のリソースが費用。だから環境別に自由に分けて OK。
Launch Type — EC2 vs Fargate #
ECS が Task を実際にどこに立ち上げるかを決める方式です。2 つのモード があります。
EC2 Launch Type #
私が EC2 インスタンスの束 (ASG) を運用し、ECS はその上にコンテナをスケジューリング。
ECS Service
│ (スケジュール)
▼
EC2 #1 EC2 #2 EC2 #3 ← 私が運用 (ASG、AMI、パッチ、セキュリティ)
▲ ▲ ▲
コンテナ コンテナ コンテナ長所:
- インスタンスコスト = EC2 価格 (長期割引 / Reserved / Spot)
- GPU / 大メモリ / 特殊インスタンスが自由
短所:
- EC2 自体を運用しなければならない — AMI 最新化、OS セキュリティパッチ、ECS エージェント更新
- インスタンスの詰め込み (binpacking) を意識する必要
- 空のインスタンスが立ったままだとその時間分が無駄
Fargate Launch Type #
EC2 が見えません。Task の CPU / メモリだけを宣言 すれば AWS がそこに勝手にコンテナを立ち上げます。
ECS Service
│ (スケジュール)
▼
[AWS 管理領域 — 見えない]
│
▼
コンテナ (Task)長所:
- EC2 運用 0 — OS パッチ、ASG、AMI すべて AWS がやる
- Task 単位の課金 (分単位、vCPU + メモリ)
- 空のインスタンスの無駄なし
短所:
- 単価が EC2 より高い (管理コスト含む)
- GPU / 特殊インスタンス / 一部ネットワークオプション不可
- コンテナ当たり vCPU 0.25〜16、メモリ 0.5〜120GB の上限
どちらを選ぶか #
| 場合 | 推奨 |
|---|---|
| 小〜中トラフィック | Fargate — 運用負担 0 |
| コストが極めて大きい場合 | EC2 + Reserved / Spot |
| GPU / 特殊ワークロード | EC2 |
| 変動トラフィック / バッチ | Fargate Spot (最大 70% 割引) |
| k8s に慣れているが ECS 限定 | EC2 + 自由 |
このシリーズと 実践 6 編 は全て Fargate ベース で進めます。運用負担を大きく減らせて学習曲線が緩やかなため。
2 つの IAM ロール — Execution Role vs Task Role #
ECS 運用で最もよく混乱するところです。
Execution Role #
ECS エージェントが Task を立ち上げるのに必要な 権限。Task が始まる直前に AWS が使用。
- ECR からイメージを pull
- CloudWatch Logs グループ / ストリームを作成
- Secrets Manager / Parameter Store から secret を取得 (Task 起動時点で注入)
基本的にアカウントに ecsTaskExecutionRole 1 つで十分 (AWS マネージドポリシー AmazonECSTaskExecutionRolePolicy を付与)。
Task Role #
コンテナの中のコード が AWS API を呼ぶときに使う権限。ランタイムで使用。
- コードの中で
boto3.client("s3").get_object(...)→ S3 アクセス - コードの中で
dynamodb.get_item(...)→ DynamoDB アクセス
各アプリごとに 最小権限 の Task Role を別途作るのが原則。基礎 #6 セキュリティ基本 の最小権限パターン。
Execution Role → ECS が使用 (イメージ pull、ログ作成、secret 注入)
Task Role → 私のコードが使用 (S3、DynamoDB、SQS 呼び出し等)これら 2 つを混同して 1 つのロールに詰め込むとセキュリティ事故につながります。
最初のデプロイ — Hello, ECS #
完全なフローを 1 度追ってみます。すでに Docker イメージがあるとして。
1) ECR にイメージを push #
#2 ECR で詳しく扱いますが、先にフロー:
# 認証
aws ecr get-login-password --region ap-northeast-2 \
| docker login --username AWS --password-stdin \
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com
# ビルド + タグ + push
docker build -t myapp .
docker tag myapp:latest \
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1
docker push \
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v12) Cluster を作る #
aws ecs create-cluster --cluster-name prod-clusterコンソールで 1 クリックでも。改めて言いますが無料。
3) Task Definition の登録 #
上の JSON をファイル (task-definition.json) として保存し:
aws ecs register-task-definition \
--cli-input-json file://task-definition.json成功すれば myapp:1 の revision が作られます。
4) Service の作成 (ALB と一緒に) #
ALB の Target Group (中級 #6) を事前に作っておいた状態で:
aws ecs create-service \
--cluster prod-cluster \
--service-name myapp \
--task-definition myapp:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-aaa,subnet-bbb],securityGroups=[sg-xxx],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=web,containerPort=8000"この行を実行する瞬間に ECS が:
- Fargate の上にコンテナを 2 個立ち上げ
- 各コンテナの ENI を Target Group に登録
- ALB がヘルスチェック通過後トラフィックをルーティング
ALB の DNS (または Route 53 (中級 #5) のドメイン) に接続すれば完了。
5) 新バージョンのデプロイ #
# 新イメージ push (myapp:v2)
docker tag myapp:v2 ...; docker push ...
# Task Definition の新 revision (イメージタグだけ変えて再登録)
aws ecs register-task-definition --cli-input-json file://task-definition-v2.json
# → myapp:2
# Service が新 revision を使うよう更新
aws ecs update-service \
--cluster prod-cluster \
--service myapp \
--task-definition myapp:2ECS が rolling update で勝手に — 新 Task 2 個を立ち上げてヘルスチェック通過したら旧 Task 2 個を終了。サービス停止なし。
Service のデプロイオプション #
デフォルトは rolling update ですが他に 2 つ。
Rolling Update (デフォルト) #
minimumHealthyPercent (デフォルト 100) と maximumPercent (デフォルト 200) の 2 つのつまみで調整。
minHealthy=100, maxPercent=200→ desired=2 のとき一瞬最大 4 個まで (新 2 + 旧 2)、旧を終了。無停止。minHealthy=50, maxPercent=100→ 旧 1 個終了 → 新 1 個 → 旧 1 個終了 → 新 1 個。コスト節約。
Blue / Green (CodeDeploy 連携) #
新環境 (green) を丸ごと作って ALB の listener を一瞬で切り替える方式。ロールバックが即可能。
External (Spinnaker / 自前コントローラ) #
ECS に「どうデプロイするか」を外部ツールに委任。大きな組織でのみ。
Auto Scaling — トラフィックに合わせて増やす #
Service の上に Application Auto Scaling を載せて desired count を自動調整。
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/prod-cluster/myapp \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 2 --max-capacity 10
aws application-autoscaling put-scaling-policy \
--service-namespace ecs \
--resource-id service/prod-cluster/myapp \
--scalable-dimension ecs:service:DesiredCount \
--policy-name cpu60 \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration file://cpu-60.jsoncpu-60.json の中に PredefinedMetricSpecification: ECSServiceAverageCPUUtilization、TargetValue: 60.0。
スケールトリガーの基本候補:
- ECS Service の平均 CPU
- ECS Service の平均メモリ
- ALB の RequestCountPerTarget (リクエスト数ベース)
Service Connect — サービス間通信 #
複数のマイクロサービスが ECS の上で互いに呼び合う方式です。2 つのオプション:
1) ALB / NLB 経由 #
各サービスの前に ALB。サービス A → https://service-b.internal/ (Route 53 private hosted zone) → ALB → Service B。
長所: 標準 HTTP、外部と一貫。短所: ALB コスト、1 ホップ追加。
2) Service Connect (ECS 自体) #
ECS がコンテナの隣に proxy サイドカー (Envoy ベース) を自動で挟み込み mesh のように動作。DNS が Cluster の中で自動登録 (web.myapp.local)。
{
"serviceConnectConfiguration": {
"enabled": true,
"namespace": "myapp",
"services": [
{
"portName": "web",
"discoveryName": "web",
"clientAliases": [{ "port": 8000, "dnsName": "web" }]
}
]
}
}小さなシステムでは ALB 1 つで十分。マイクロサービスが複数になったら Service Connect 検討。
コスト — どこから出るか #
Fargate ベース:
時間当たり = (vCPU 時間) × $0.0506
+ (メモリ GB 時間) × $0.0055
+ (Data Transfer)
例: 0.5 vCPU + 1GB Fargate 1 個を 1 ヶ月 (730h)
= 0.5 × 0.0506 × 730 + 1 × 0.0055 × 730
= $18.5 + $4.0
= $22.5 / 月 (ソウルリージョン基準おおよそ)加えて:
- ALB: 時間当たり + LCU 単位
- NAT Gateway (private subnet からインターネットに出るとき): 時間当たり + GB
- CloudWatch Logs: ingest GB + storage GB
NAT Gateway が意外と大きい。1 ヶ月 $30 水準 — 小さなサービスでは Fargate 自体より NAT コストの方が大きい場合も。
コスト節約オプション #
- Fargate Spot: 変動 / バッチワークロードに 70% 割引。突然終了することがあり stateless なワークロードでのみ
- Compute Savings Plans: 1〜3 年契約で最大 50% 割引
- Right-sizing: CloudWatch Container Insights で実使用量を確認後 vCPU / メモリを減らす — 最も効果が出る項目
よく出会う落とし穴 #
1) Task が死んでは再起動を繰り返す #
Service が自動再起動してくれるので表面的には「動いているように見えます」が、実は コンテナが起動直後に終了 しています。原因:
- ヘルスチェック失敗 (アプリが遅く起動して ALB が unhealthy 判定)
- コンテナの中でエラーで即時 exit
- メモリ不足 (OOM killed)
CloudWatch Logs (基礎 #7) で stopped reason を確認:
aws ecs describe-tasks --cluster prod-cluster \
--tasks <task-id> --query 'tasks[0].stoppedReason'2) イメージ pull 権限不足 #
Task 起動直後に “CannotPullContainerError” → 99% は Execution Role の ECR 権限不足。AWS マネージドの AmazonECSTaskExecutionRolePolicy を付けたか確認。
3) Secret の注入ができない #
Task Definition の secrets が空で入る → Execution Role が Secrets Manager / Parameter Store の ARN に secretsmanager:GetSecretValue / ssm:GetParameter 権限がありません。詳細は #6。
4) ALB Target が unhealthy #
デプロイは出来たのに ALB のヘルスチェックが失敗。よく見る原因:
- ヘルスチェック path がアプリにない (
/healthエンドポイント忘れた) - Security Group が ALB → Task のトラフィックを塞ぐ
- アプリが 0.0.0.0 でなく 127.0.0.1 にバインド (コンテナの外から接続不可)
5) Task Definition の revision が暴走 #
v1 → v2 → … → v847 のように際限なく溜まる。直接整理しないとコンソールが重くなります。運用ポリシーで 30 日以上未使用 revision を自動整理 または IaC が整理するように。
6) NAT Gateway コストの爆発 #
private subnet の Task が外部 API を頻繁に呼び出す → NAT Gateway の Data Processing 料金が EC2 料金を超える。代替:
- VPC Endpoint (S3、ECR、Secrets Manager 等よく使うサービスに) — トラフィックが NAT を経由しない
- 外部 API 呼び出しが多ければ同じ AZ の NAT から同じ AZ の Task → AZ 間トラフィックコスト回避
まとめ #
今回つかんだもの:
- EC2 直接運用の限界 — 環境再現、スケール、無停止デプロイ、ロールバック、ヘルスチェックがコンテナで自然に解ける
- ECS の役割 — AWS のマネージドコンテナオーケストレーター。EKS は k8s 標準が必要なとき
- 4 つの構成要素 — Cluster (まとまり) / Service (N 個維持) / Task (実行中のコンテナ) / Task Definition (設計図)
- Launch Type — EC2 (直接運用、コスト最適化) vs Fargate (運用 0、単価高い)。シリーズは Fargate ベース
- 2 つの IAM ロール — Execution Role (ECS が Task を立ち上げる権限) vs Task Role (コードが AWS API を呼ぶ権限)。絶対に混同しないこと
- 最初のデプロイの流れ — ECR push → Cluster → Task Definition → Service (ALB 接続)
- デプロイ — rolling (デフォルト) / blue-green (CodeDeploy) / external
- Auto Scaling — Application Auto Scaling で CPU / メモリ / リクエスト数ベース
- Service Connect — Service 間通信を ALB なしで mesh に
- コスト — vCPU + メモリ + ALB + NAT。NAT が意外と大きい。Spot、Savings Plans、Right-sizing
- 落とし穴 — Task 無限再起動 (ヘルスチェック / OOM)、イメージ pull 権限、Secret 権限、ALB unhealthy、revision 暴走、NAT コスト
次回 — ECR #
ECS が立ち上げるイメージがどこから来るかという話です。Amazon ECR (Elastic Container Registry) に次回詳しく入ります。
#2 ECR — イメージレジストリ では private repo の作り方、認証、push / pull、イメージスキャン、ライフサイクルポリシー、マルチアーキテクチャイメージまで — ECS のパートナーを一気に整理します。