「Dockerで開発しているが、本番環境のコンテナ運用をどうすればいいかわからない」「Kubernetesは難しすぎる」――そんな悩みを持つ開発者・IT担当者に最適なのが、AWS ECS/Fargateです。
この記事では、Dockerコンテナを本番環境にデプロイするまでの具体的な手順を解説します。ECSの基本概念からタスク定義の設計、Fargateによるサーバーレスなコンテナ運用まで、実務で使える知識を体系的にお届けします。
ECS/Fargateとは?コンテナ運用の選択肢を整理する
AWS ECS(Elastic Container Service)は、Dockerコンテナを大規模に実行・管理するためのコンテナオーケストレーションサービスです。そしてFargateは、そのECSのコンピューティングエンジンの一つで、サーバーの管理なしにコンテナを実行できます。
EC2起動タイプとFargate起動タイプの比較
ECSには2つの起動タイプがあり、用途に応じて選択します。
| 項目 | EC2起動タイプ | Fargate起動タイプ |
|---|---|---|
| サーバー管理 | EC2インスタンスの管理が必要 | 不要(サーバーレス) |
| スケーリング | インスタンスの追加・削除が必要 | タスク数の変更だけで完了 |
| コスト | インスタンス単位の課金 | vCPU・メモリの使用量課金 |
| カスタマイズ性 | OS・カーネルレベルの設定が可能 | 限定的 |
| 推奨ケース | GPU利用、大量のコンテナ実行 | 一般的なWebアプリ、API |
中小企業や少人数チームには、運用負荷の低いFargate起動タイプを強く推奨します。サーバーのパッチ適用やキャパシティプランニングから解放され、アプリケーションの開発に集中できます。
Dockerの基本については、Dockerとは?中小企業向けガイドも参考にしてください。
ECSの主要コンポーネント
ECSを理解するために、主要なコンポーネントを整理しましょう。
- クラスター:タスクやサービスをグルーピングする論理的な単位
- タスク定義:コンテナの設計図。イメージ、CPU、メモリ、環境変数などを定義
- タスク:タスク定義に基づいて実行されるコンテナのインスタンス
- サービス:タスクの実行数を維持し、ロードバランサーと連携する仕組み
事前準備|ECRにDockerイメージをプッシュする
ECSでコンテナを実行するには、まずDockerイメージをAWS ECR(Elastic Container Registry)にプッシュする必要があります。
ECRリポジトリの作成
# ECRリポジトリを作成
aws ecr create-repository \
--repository-name mycompany/web-app \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES256 \
--region ap-northeast-1
scanOnPush=trueを設定すると、イメージをプッシュするたびに脆弱性スキャンが自動実行されます。本番環境で使用するイメージには必ず有効にしましょう。
Dockerイメージのビルドとプッシュ
# ECRにログイン
aws ecr get-login-password --region ap-northeast-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com
# イメージをビルド(マルチステージビルド推奨)
docker build -t mycompany/web-app:latest .
# タグ付け
docker tag mycompany/web-app:latest \
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mycompany/web-app:latest
docker tag mycompany/web-app:latest \
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mycompany/web-app:$(git rev-parse --short HEAD)
# プッシュ
docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mycompany/web-app:latest
docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mycompany/web-app:$(git rev-parse --short HEAD)
タグにはlatestだけでなく、Gitのコミットハッシュやバージョン番号も付与しましょう。デプロイの追跡やロールバックが容易になります。
本番向けDockerfileのベストプラクティス
# マルチステージビルドの例(Node.jsアプリケーション)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
本番用Dockerfileのポイントは以下の通りです。
- マルチステージビルド:ビルドツールを含まない軽量なイメージを作成
- 非rootユーザー:セキュリティのためrootで実行しない
- HEALTHCHECK:コンテナの正常性を自動チェック
- alpineベース:イメージサイズの最小化
タスク定義の設計|実務で押さえるべきポイント
タスク定義は、ECSの核となる設定です。実務で重要なポイントを押さえた設計例を紹介します。
実践的なタスク定義の例
{
"family": "web-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "web-app",
"image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mycompany/web-app:latest",
"essential": true,
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NODE_ENV",
"value": "production"
}
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/prod/database-url"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/web-app",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
タスク定義で注意すべきポイント
1. executionRoleArnとtaskRoleArnの違い
| ロール | 用途 | 必要な権限の例 |
|---|---|---|
| executionRoleArn | ECSエージェントがタスクを起動するために使用 | ECRからのイメージ取得、CloudWatch Logsへの書き込み |
| taskRoleArn | コンテナ内のアプリケーションが使用 | S3アクセス、DynamoDBアクセスなど |
IAMロールの設計については、AWS IAMのベストプラクティスを参照してください。
2. シークレットの管理
環境変数にパスワードやAPIキーを直接書くのは絶対に避けてください。AWS Systems Manager Parameter StoreやSecrets Managerを使い、secretsフィールドで参照します。
3. CPU・メモリの適切なサイジング
Fargateで指定できるCPU・メモリの組み合わせは制限があります。代表的な組み合わせは以下の通りです。
| CPU(vCPU) | メモリ(MB) | 用途目安 |
|---|---|---|
| 256(0.25) | 512〜2048 | 軽量API、バッチ処理 |
| 512(0.5) | 1024〜4096 | 一般的なWebアプリ |
| 1024(1) | 2048〜8192 | 中規模のWebアプリ |
| 2048(2) | 4096〜16384 | 高負荷なアプリケーション |
| 4096(4) | 8192〜30720 | データ処理、機械学習推論 |
ECSサービスの作成とロードバランサー連携
タスク定義ができたら、次はサービスを作成してALB(Application Load Balancer)と連携させます。
ALBターゲットグループの作成
# ターゲットグループの作成(IPタイプ:Fargate必須)
aws elbv2 create-target-group \
--name web-app-tg \
--protocol HTTP \
--port 3000 \
--vpc-id vpc-0123456789abcdef0 \
--target-type ip \
--health-check-path /health \
--health-check-interval-seconds 30 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3
ECSサービスの作成
aws ecs create-service \
--cluster mycompany-prod \
--service-name web-app-service \
--task-definition web-app:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration '{
"awsvpcConfiguration": {
"subnets": ["subnet-0123456789abcdef0", "subnet-0123456789abcdef1"],
"securityGroups": ["sg-0123456789abcdef0"],
"assignPublicIp": "DISABLED"
}
}' \
--load-balancers '[
{
"targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/web-app-tg/1234567890123456",
"containerName": "web-app",
"containerPort": 3000
}
]' \
--deployment-configuration '{
"maximumPercent": 200,
"minimumHealthyPercent": 100,
"deploymentCircuitBreaker": {
"enable": true,
"rollback": true
}
}'
重要な設定ポイントを解説します。
- desired-count: 2:最低2つのタスクを異なるAZに配置して可用性を確保
- assignPublicIp: DISABLED:プライベートサブネットに配置し、ALB経由でのみアクセス可能にする
- deploymentCircuitBreaker:デプロイ失敗時に自動でロールバック
- minimumHealthyPercent: 100:デプロイ中もサービスが停止しないローリングアップデート
Auto Scalingの設定
トラフィックの変動に対応するため、ECSサービスにAuto Scalingを設定します。
ターゲット追跡スケーリングポリシー
# スケーラブルターゲットの登録
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--scalable-dimension ecs:service:DesiredCount \
--resource-id service/mycompany-prod/web-app-service \
--min-capacity 2 \
--max-capacity 10
# CPU使用率に基づくスケーリングポリシー
aws application-autoscaling put-scaling-policy \
--service-namespace ecs \
--scalable-dimension ecs:service:DesiredCount \
--resource-id service/mycompany-prod/web-app-service \
--policy-name cpu-scaling \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration '{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
},
"ScaleInCooldown": 300,
"ScaleOutCooldown": 60
}'
この設定では、CPU使用率が70%を超えるとタスクが追加され、下回ると削減されます。スケールアウトは60秒、スケールインは300秒のクールダウンを設定して、頻繁なスケーリングを防いでいます。
デプロイ戦略とCI/CD連携
本番環境では、安全なデプロイ戦略が不可欠です。
ローリングアップデート
ECSのデフォルトのデプロイ方式です。新しいタスクを起動してから古いタスクを停止することで、ダウンタイムゼロのデプロイを実現します。
# 新しいイメージでタスク定義を更新してデプロイ
aws ecs update-service \
--cluster mycompany-prod \
--service web-app-service \
--task-definition web-app:2 \
--force-new-deployment
Blue/Greenデプロイ
より安全なデプロイには、AWS CodeDeployと連携したBlue/Greenデプロイを使用します。新しいバージョンに段階的にトラフィックを移行し、問題があれば即座にロールバックできます。
# Blue/Greenデプロイの設定は、サービス作成時にdeploymentControllerを指定
{
"deploymentController": {
"type": "CODE_DEPLOY"
}
}
デプロイの自動化スクリプト
CI/CDパイプラインからECSにデプロイするスクリプトの例です。
#!/bin/bash
set -e
# 変数の設定
CLUSTER="mycompany-prod"
SERVICE="web-app-service"
FAMILY="web-app"
IMAGE_URI="123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/mycompany/web-app"
TAG=$(git rev-parse --short HEAD)
# 1. イメージのビルドとプッシュ
docker build -t ${IMAGE_URI}:${TAG} .
docker push ${IMAGE_URI}:${TAG}
# 2. 新しいタスク定義を登録
TASK_DEF=$(aws ecs describe-task-definition --task-definition ${FAMILY} --query taskDefinition)
NEW_TASK_DEF=$(echo $TASK_DEF | jq --arg IMAGE "${IMAGE_URI}:${TAG}" \
'.containerDefinitions[0].image = $IMAGE | del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)')
NEW_REVISION=$(aws ecs register-task-definition --cli-input-json "$NEW_TASK_DEF" --query 'taskDefinition.revision' --output text)
# 3. サービスを更新
aws ecs update-service \
--cluster ${CLUSTER} \
--service ${SERVICE} \
--task-definition ${FAMILY}:${NEW_REVISION}
# 4. デプロイ完了を待機
aws ecs wait services-stable --cluster ${CLUSTER} --services ${SERVICE}
echo "Deploy completed: ${FAMILY}:${NEW_REVISION}"
トラブルシューティングとモニタリング
ECS/Fargateの運用で遭遇しやすい問題と対処法を紹介します。
よくあるエラーと対処法
| エラー | 原因 | 対処法 |
|---|---|---|
| CannotPullContainerError | ECRからイメージを取得できない | executionRoleのECR権限、VPCエンドポイントを確認 |
| ResourceNotFoundException | 指定したリソースが存在しない | タスク定義のARN、サブネットIDを確認 |
| Task stopped: Essential container exited | コンテナがクラッシュ | CloudWatch Logsでアプリケーションログを確認 |
| Service is unable to place a task | リソース不足 | サブネットのIPアドレス枯渇、セキュリティグループを確認 |
CloudWatch Logsでのログ確認
# タスクのログを確認
aws logs get-log-events \
--log-group-name /ecs/web-app \
--log-stream-name "ecs/web-app/タスクID" \
--limit 50
# 停止したタスクの理由を確認
aws ecs describe-tasks \
--cluster mycompany-prod \
--tasks タスクARN \
--query 'tasks[0].{stoppedReason:stoppedReason,stopCode:stopCode}'
AWS Lambdaの活用事例と組み合わせれば、ECSタスクの異常検知をLambdaで処理し、Slackに通知するような仕組みも構築できます。
まとめ|ECS/Fargateで運用負荷を最小限にコンテナを本番運用する
AWS ECS/Fargateを使えば、サーバー管理の負担なくDockerコンテナを本番環境で運用できます。本記事のポイントを振り返りましょう。
- Fargateを選択:サーバー管理不要で、アプリケーション開発に集中できる
- ECRでイメージ管理:脆弱性スキャンを有効にし、Gitハッシュでタグ管理する
- タスク定義の設計:シークレット管理、ヘルスチェック、ログ設定を適切に行う
- サービスとALBの連携:複数AZに分散配置して可用性を確保する
- Auto Scaling:CPU使用率に基づく自動スケーリングで負荷に対応する
- 安全なデプロイ:ローリングアップデートとサーキットブレーカーで安全にデプロイする
コンテナ運用の基盤ができたら、RDSでのデータベース構築やCloudFrontによるCDN配信と組み合わせて、本格的なWebアプリケーション基盤を構築していきましょう。インフラ全体をTerraformでコード管理することも忘れずに。
関連記事
AWS CloudFrontでサイト高速化|CDN設定からキャッシュ戦略まで実践解説
AWS CloudWatchで監視・アラート設定|運用担当者のための実践ガイド
AWS CodePipelineでCI/CD構築|コード変更から本番デプロイまでの自動化
AWS Cost Explorerでコスト可視化|ムダを見つけて月額費用を削減する実践術
AWS IAMのベストプラクティス|最小権限の原則を実務で実装する方法
AWS RDSの実務ガイド|データベース構築・バックアップ・パフォーマンスチューニング
AWS S3の実務活用ガイド|バケット設計・アクセス制御・コスト最適化の実践
AWS VPCのネットワーク設計入門|サブネット・セキュリティグループの実践構成