2026年9月26日
2026年9月26日
AWS ECSでコンテナをデプロイする方法:ECRへのプッシュからFargateのサービス作成・更新まで
Amazon ECS でコンテナを動かすには、イメージを ECR にプッシュし、タスク定義で「どのイメージをどのリソースで動かすか」を決め、サービスで「何個を常時動かし、どのロードバランサーにつなぐか」を指定する、という3段階の作業が必要です。この記事では Fargate を使う前提で、AWS CLI によるデプロイの手順、ローリング更新とロールバックの設定、起動しないときの調べ方を説明します。
ECSを構成する要素
最初に用語を整理しておくと、設定の意味が追いやすくなります。
- クラスタ: タスクを動かす論理的な単位。Fargate なら、サーバーの管理は不要です
- タスク定義: コンテナイメージ、CPU・メモリ、ポート、環境変数、ログの出力先などを記述した設計図。変更するたびに新しいリビジョン(
myapp:1、myapp:2…)が作られます - タスク: タスク定義から起動された実行中のコンテナの集まり
- サービス: 指定した数のタスクを常に動かし続け、停止したら作り直す仕組み。ロードバランサーとの連携やデプロイの方式もここで決めます
実行基盤には、サーバー管理が不要な Fargate と、自分で EC2 インスタンスを管理する EC2 起動タイプがあります。小〜中規模で運用の手間を減らしたいなら Fargate、GPU や特殊なインスタンスが必要な場合や細かくコストを調整したい場合は EC2 が候補になります。
事前に用意するもの
- VPC とサブネット(タスクはプライベートサブネットに置き、ALB をパブリックサブネットに置く構成が一般的)
- Application Load Balancer とターゲットグループ(Fargate のタスクは IP アドレスで登録されるため、ターゲットタイプは
ip) - 2種類の IAM ロール
- タスク実行ロール: ECS がイメージを ECR から取得し、ログを CloudWatch Logs に書くためのロール。AWS 管理ポリシー
AmazonECSTaskExecutionRolePolicyを付けるのが基本 - タスクロール: コンテナ内のアプリが S3 などの AWS サービスを使うためのロール。アプリが必要とする権限だけを付ける
- タスク実行ロール: ECS がイメージを ECR から取得し、ログを CloudWatch Logs に書くためのロール。AWS 管理ポリシー
この2つのロールを混同するのは典型的なつまずきです。「イメージが取得できない」ならタスク実行ロール、「アプリから S3 に書き込めない」ならタスクロールを確認します。
手順1:イメージをビルドしてECRにプッシュする
リポジトリを作成し、Docker を ECR にログインさせてからプッシュします。アカウント ID とリージョンは自分の環境に置き換えてください。
aws ecr create-repository --repository-name myapp \
--image-scanning-configuration scanOnPush=true
aws ecr get-login-password --region ap-northeast-1 \
| docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com
TAG=$(git rev-parse --short HEAD)
docker build --platform linux/amd64 -t myapp:$TAG .
docker tag myapp:$TAG 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:$TAG
docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:$TAG
ポイントは2つあります。
- **タグに
latestを使わない**。Git のコミットハッシュなどの一意なタグにすると、どのタスクがどのコードで動いているかが明確になり、前のタグに戻すだけでロールバックできます。 - CPU アーキテクチャを合わせる。Apple シリコンの Mac でビルドすると、既定では ARM64 のイメージになります。タスク定義が X86_64 のままだと起動に失敗するため、
--platformで明示するか、タスク定義側を ARM64 にします。
手順2:タスク定義を登録する
タスク定義を JSON で書き、登録します。
{
"family": "myapp",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"runtimePlatform": {
"operatingSystemFamily": "LINUX",
"cpuArchitecture": "X86_64"
},
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/myappTaskRole",
"containerDefinitions": [
{
"name": "app",
"image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:abc1234",
"essential": true,
"portMappings": [{ "containerPort": 8080, "protocol": "tcp" }],
"environment": [{ "name": "APP_ENV", "value": "production" }],
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:myapp/db-password"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "app"
}
}
}
]
}
aws logs create-log-group --log-group-name /ecs/myapp
aws ecs register-task-definition --cli-input-json file://taskdef.json
パスワードなどの秘密情報は environment に直書きせず、secrets で Secrets Manager や SSM パラメータストアから渡します。この場合、タスク実行ロールに該当シークレットを読む権限(必要に応じて KMS の復号権限も)を付ける必要があります。Fargate の CPU とメモリは組み合わせが決まっているので、使える値は ECS のドキュメントで確認してください(2026年10月現在)。
手順3:サービスを作成する
ALB のターゲットグループに接続したサービスを作ります。
aws ecs create-service \
--cluster myapp-cluster \
--service-name myapp \
--task-definition myapp:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-0aaa1111,subnet-0bbb2222],securityGroups=[sg-0ccc3333],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/myapp-tg/0123456789abcdef,containerName=app,containerPort=8080" \
--health-check-grace-period-seconds 60 \
--deployment-configuration "minimumHealthyPercent=100,maximumPercent=200,deploymentCircuitBreaker={enable=true,rollback=true}"
aws ecs wait services-stable --cluster myapp-cluster --services myapp
設定の意図を補足します。
- タスクを2つ以上にし、サブネットを異なるアベイラビリティーゾーンにまたがらせると、片方の障害やデプロイ中も処理を続けられます。
- minimumHealthyPercent を 100、maximumPercent を 200 にすると、新しいタスクが正常になってから古いタスクを止める順序になり、デプロイ中に処理能力が落ちません。その分、一時的にタスク数が増えます。
- デプロイサーキットブレーカーを有効にすると、新しいタスクが起動に失敗し続けた場合にデプロイを失敗とみなし、
rollback=trueなら直前の正常なリビジョンに戻します。 health-check-grace-period-secondsは、起動直後のタスクを ALB のヘルスチェック失敗で止めないための猶予です。起動に時間がかかるアプリでは長めにします。
セキュリティグループは、タスク側で「ALB のセキュリティグループからの 8080 番のみ許可」とするのが基本です。
手順4:新しいバージョンをデプロイする
コードを更新したら、新しいタグでイメージをプッシュし、タスク定義の image を書き換えて新しいリビジョンを登録し、サービスを更新します。
aws ecs register-task-definition --cli-input-json file://taskdef.json
aws ecs update-service --cluster myapp-cluster --service myapp \
--task-definition myapp:2
aws ecs wait services-stable --cluster myapp-cluster --services myapp
問題があれば、--task-definition myapp:1 を指定して update-service を実行すれば前のリビジョンに戻せます。同じタグのまま中身だけ差し替えた場合は --force-new-deployment で再デプロイできますが、どのイメージで動いているかが曖昧になるため、タグを変える運用をおすすめします。
デプロイ方式には、ここで説明したローリング更新のほか、ブルー/グリーンデプロイなどの選択肢があります。提供される方式や設定方法は更新されることがあるため、採用する際は最新の ECS ドキュメントを確認してください。
タスクが起動しないときの調べ方
まず停止したタスクの理由を確認します。
aws ecs list-tasks --cluster myapp-cluster --service-name myapp --desired-status STOPPED
aws ecs describe-tasks --cluster myapp-cluster --tasks <タスクARN> \
--query 'tasks[].[stoppedReason,containers[].reason]'
よくある原因を、起こりやすい順に挙げます。
- イメージを取得できない(CannotPullContainerError)。プライベートサブネットに置いたタスクが ECR に到達できない場合です。NAT ゲートウェイを用意するか、ECR(api と dkr)、S3(ゲートウェイ型)、CloudWatch Logs の VPC エンドポイントを作成します。タスク実行ロールの権限不足でも同じ症状になります。
- ALB のヘルスチェックに失敗して停止を繰り返す。ターゲットグループのヘルスチェックのパスとポートがアプリと合っているか、アプリが 0.0.0.0 で待ち受けているかを確認します。CloudWatch Logs のアプリのログも併せて見ます。
- アプリがすぐに異常終了する。終了コードとログを確認します。環境変数やシークレットの名前の誤り、アーキテクチャの不一致がよくある原因です。
- メモリ不足で停止する(OutOfMemory)。タスク定義のメモリを増やすか、アプリのメモリ使用量を見直します。
WordPressをECSで動かす場合の注意
WordPress を ECS で動かす場合、コンテナは作り直されるたびに中身が初期状態に戻ります。アップロードされたメディアや管理画面から追加したプラグインは消えるため、wp-content/uploads は Amazon EFS をマウントするか S3 へ保存するプラグインを使い、データベースは RDS に置きます。プラグインやテーマはイメージに含めて、コードとして管理する運用が向いています。
まとめ
- ECS は「ECR のイメージ」「タスク定義」「サービス」の3段階でデプロイする
- タスク実行ロール(イメージ取得・ログ)とタスクロール(アプリの権限)を区別する
- イメージのタグは一意にし、CPU アーキテクチャをタスク定義と合わせる
- サーキットブレーカーとロールバックを有効にし、更新は新しいリビジョンの登録と update-service で行う
- 起動しないときは stoppedReason を確認し、ネットワーク到達性・ヘルスチェック・ログの順に調べる。本番の前にステージング環境で一連の流れを試す