mcpField
E
ecs

ecs

github.com/itsmostafaクラウド・DevOps1.2k149 回インストール2 日前に更新
リポジトリ

ecs について

AWS上でコンテナワークロードをデプロイ、運用するチーム向けのAmazon Elastic Container Service(ECS)リファレンススキルです。FargateとEC2でのクラスター、タスク、サービス管理を、スケーリング、デプロイ、セキュリティ、トラブルシューティングのCLI例とともに説明します。

主な機能

  1. 01クラスター、タスク定義、タスク、サービスの管理
  2. 02FargateまたはEC2でのワークロード実行
  3. 03サービスのスケーリングとデプロイ設定
  4. 04対象ワークロードでのFargate Spot利用
  5. 05タスク、コンテナ、デプロイの問題診断

活用シーン

  1. 01ロードバランサー付きWebサービスのデプロイ
  2. 02単独のバッチまたはキュー処理タスクの実行
  3. 03ワークロード指標に応じたECSサービスのスケーリング
  4. 04起動失敗やデプロイ停滞の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/itsmostafa/aws-agent-skills --skill ecs

インストール後、Claude Code で/ecsを入力して読み込まれたか確認します。

同梱ファイルtask-definitions.md

詳細

AWS ECS

Amazon Elastic Container Service(ECS)は、フルマネージドのコンテナオーケストレーションサービスです。AWS Fargate(サーバーレス)または EC2 インスタンスでコンテナを実行します。

目次

基本概念

クラスター

タスクまたはサービスを論理的にまとめたものです。Fargate タスク、EC2 インスタンス、またはその両方を含めることができます。

タスク定義

アプリケーションの設計図です。コンテナ、リソース、ネットワーク、IAM ロールを定義します。

タスク

タスク定義の実行中のインスタンスです。単独で実行することも、サービスの一部として実行することもできます。

サービス

タスクの希望数を維持します。デプロイ、ロードバランシング、自動スケーリングを処理します。

起動タイプ
タイプ説明用途
Fargateサーバーレスで、タスクごとに料金が発生ほとんどのワークロード
EC2自分で管理するインスタンスGPU、Windows、特定の要件

一般的なパターン

Fargate クラスターを作成する

AWS CLI:

# Create cluster
aws ecs create-cluster --cluster-name my-cluster

# With capacity providers
aws ecs create-cluster \
  --cluster-name my-cluster \
  --capacity-providers FARGATE FARGATE_SPOT \
  --default-capacity-provider-strategy \
    capacityProvider=FARGATE,weight=1 \
    capacityProvider=FARGATE_SPOT,weight=1
タスク定義を登録する
cat > task-definition.json << 'EOF'
{
  "family": "web-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
  "containerDefinitions": [
    {
      "name": "web",
      "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
      "portMappings": [
        {
          "containerPort": 8080,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {"name": "NODE_ENV", "value": "production"}
      ],
      "secrets": [
        {
          "name": "DB_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:db-password"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/web-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs",
          "mode": "non-blocking",
          "max-buffer-size": "25m"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 60
      }
    }
  ]
}
EOF

aws ecs register-task-definition --cli-input-json file://task-definition.json
ロードバランサー付きのサービスを作成する
aws ecs create-service \
  --cluster my-cluster \
  --service-name web-service \
  --task-definition web-app:1 \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={
    subnets=[subnet-12345678,subnet-87654321],
    securityGroups=[sg-12345678],
    assignPublicIp=DISABLED
  }" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/web-tg/1234567890123456,containerName=web,containerPort=8080" \
  --health-check-grace-period-seconds 60 \
  --deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true}"
スタンドアロンタスクを実行する
aws ecs run-task \
  --cluster my-cluster \
  --task-definition my-batch-job:1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={
    subnets=[subnet-12345678],
    securityGroups=[sg-12345678],
    assignPublicIp=ENABLED
  }"
サービスを更新する(新しいイメージをデプロイする)
# Register new task definition with updated image
aws ecs register-task-definition --cli-input-json file://task-definition.json

# Update service to use new version
aws ecs update-service \
  --cluster my-cluster \
  --service web-service \
  --task-definition web-app:2 \
  --force-new-deployment
SQS ベースのスケーリングで Fargate Spot を使用する

バッチ/キューワークロードでは、FARGATE_SPOT を使用するとコストを約 70% 削減できます。通常の FARGATE へのフォールバックを必ず用意してください。

# Create service with Spot + fallback
aws ecs create-service \
  --cluster batch-cluster \
  --service-name queue-processor \
  --task-definition my-processor:1 \
  --desired-count 0 \
  --capacity-provider-strategy \
    capacityProvider=FARGATE_SPOT,weight=4,base=0 \
    capacityProvider=FARGATE,weight=1,base=1 \
  --network-configuration "awsvpcConfiguration={
    subnets=[subnet-12345678],
    securityGroups=[sg-12345678],
    assignPublicIp=DISABLED
  }"

# Register scalable target (scale to zero when queue empty)
aws application-autoscaling register-scalable-target \
  --service-namespace ecs \
  --resource-id service/batch-cluster/queue-processor \
  --scalable-dimension ecs:service:DesiredCount \
  --min-capacity 0 \
  --max-capacity 20

# Scale-out alarm: messages > 100
aws cloudwatch put-metric-alarm \
  --alarm-name queue-scale-out \
  --metric-name ApproximateNumberOfMessagesVisible \
  --namespace AWS/SQS \
  --dimensions Name=QueueName,Value=my-queue \
  --statistic Average \
  --period 60 \
  --evaluation-periods 1 \
  --threshold 100 \
  --comparison-operator GreaterThanThreshold \
  --alarm-actions <scale-out-policy-arn>

# Scale-in alarm: queue empty for 3 periods (conservative to avoid flapping)
aws cloudwatch put-metric-alarm \
  --alarm-name queue-scale-in \
  --metric-name ApproximateNumberOfMessagesVisible \
  --namespace AWS/SQS \
  --dimensions Name=QueueName,Value=my-queue \
  --statistic Average \
  --period 60 \
  --evaluation-periods 3 \
  --threshold 0 \
  --comparison-operator LessThanOrEqualToThreshold \
  --alarm-actions <scale-in-policy-arn>

タスクあたりのバックログ(AWS 推奨): キューの生の深度を使うと過剰にスケーリングされます。代わりに、メトリクス演算を使って queue depth / RunningTaskCount をターゲット追跡してください。アラームは Application Auto Scaling が管理します。クラスターで Container Insights を有効にする必要があります(ECS/ContainerInsights の RunningTaskCount)。実行中のタスクが 0 件の場合、除数のデータがないため、0 件の状態からはスケーリングできません。そのため、min-capacity が 0 の場合は、上記のアラームパターンを維持してください。

# TargetValue = acceptable latency / avg processing time per message (e.g. 10s / 0.1s = 100)
aws application-autoscaling put-scaling-policy \
  --service-namespace ecs \
  --resource-id service/batch-cluster/queue-processor \
  --scalable-dimension ecs:service:DesiredCount \
  --policy-name sqs-backlog-per-task \
  --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration '{
    "TargetValue": 100,
    "CustomizedMetricSpecification": {
      "Metrics": [
        {"Id": "m1", "ReturnData": false, "MetricStat": {"Stat": "Sum",
          "Metric": {"Namespace": "AWS/SQS", "MetricName": "ApproximateNumberOfMessagesVisible",
            "Dimensions": [{"Name": "QueueName", "Value": "my-queue"}]}}},
        {"Id": "m2", "ReturnData": false, "MetricStat": {"Stat": "Average",
          "Metric": {"Namespace": "ECS/ContainerInsights", "MetricName": "RunningTaskCount",
            "Dimensions": [{"Name": "ClusterName", "Value": "batch-cluster"},
                           {"Name": "ServiceName", "Value": "queue-processor"}]}}},
        {"Id": "e1", "Expression": "m1 / m2", "ReturnData": true}
      ]
    }
  }'

処理中の作業をスケールインから保護する: ワーカーは処理中に ProtectionEnabled=true を設定し(コンテナエージェントのエンドポイント $ECS_AGENT_URI/task-protection/v1/state、または aws ecs update-task-protection --cluster <c> --tasks <id> --protection-enabled --expires-in-minutes 60)、完了時に解除します。タスクロールには ecs:GetTaskProtection と ecs:UpdateTaskProtection が必要です。サービスのタスクのみが対象です。

Fargate Spot の中断への対応: Spot タスクには、終了の 2 分前に SIGTERM が送信されます。アプリケーションでこれを捕捉し、正常にシャットダウンしてください。SQS コンシューマーの場合、処理中のメッセージに対して ChangeMessageVisibility を呼び出し、タイムアウトではなくキューに戻るようにします。

自動スケーリング
# Register scalable target
aws application-autoscaling register-scalable-target \
  --service-namespace ecs \
  --resource-id service/my-cluster/web-service \
  --scalable-dimension ecs:service:DesiredCount \
  --min-capacity 2 \
  --max-capacity 10

# Target tracking policy
aws application-autoscaling put-scaling-policy \
  --service-namespace ecs \
  --resource-id service/my-cluster/web-service \
  --scalable-dimension ecs:service:DesiredCount \
  --policy-name cpu-target-tracking \
  --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration '{
    "TargetValue": 70.0,
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "ECSServiceAverageCPUUtilization"
    },
    "ScaleOutCooldown": 60,
    "ScaleInCooldown": 120
  }'

20 秒間隔のメトリクスでスケーリングを高速化する: 高解像度のサービスメトリクスを有効にし、PredefinedMetricType として ECSServiceAverageCPUUtilizationHighResolution または ECSServiceAverageMemoryUtilizationHighResolution を使用します。既存のサービスでは、--monitoring の変更によってデプロイが開始されるため、高解像度のポリシーはデプロイ完了後に作成してください。CODE_DEPLOY/EXTERNAL のデプロイコントローラーではサポートされません。追加の CloudWatch 料金が発生します。

aws ecs update-service \
  --cluster my-cluster \
  --service web-service \
  --monitoring "metricConfigurations=[{metricNames=[CPUUtilization,MemoryUtilization],resolutionSeconds=20}]"

CLI リファレンス

クラスター管理
コマンド説明
aws ecs create-clusterクラスターを作成
aws ecs describe-clustersクラスターの詳細を取得
aws ecs list-clustersクラスターの一覧を表示
aws ecs delete-clusterクラスターを削除
タスク定義
コマンド説明
aws ecs register-task-definitionタスク定義を作成
aws ecs describe-task-definitionタスク定義を取得
aws ecs list-task-definitionsタスク定義の一覧を表示
aws ecs deregister-task-definitionバージョンの登録を解除
サービス
コマンド説明
aws ecs create-serviceサービスを作成
aws ecs update-serviceサービスを更新
aws ecs describe-servicesサービスの詳細を取得
aws ecs delete-serviceサービスを削除
タスク
コマンド説明
aws ecs run-taskスタンドアロンタスクを実行します
aws ecs stop-task実行中のタスクを停止します
aws ecs describe-tasksタスクの詳細を取得します
aws ecs list-tasksタスクを一覧表示します

ベストプラクティス

セキュリティ
  • AWS API へのアクセスにはタスクロールを使用します(アクセスキーは使用しません)
  • ECR/Secrets へのアクセスには実行ロールを使用します
  • シークレットは Secrets Manager または Parameter Store に保存します
  • NAT ゲートウェイを備えたプライベートサブネットを使用します
  • API 監査のために CloudTrail を有効にします
  • IAM でタスクサイズに上限を設定します — ecs:task-cpu/ecs:task-memory 条件キーは、RegisterTaskDefinition、CreateService、UpdateService だけでなく、RunTask と StartTask にも適用されます
パフォーマンス
  • CPU/メモリを適切なサイズにします — 監視して調整します
  • 耐障害性のあるワークロードには Fargate Spot を使用します(70% のコスト削減)
  • 監視のためにコンテナインサイトを有効にします
  • 内部通信にはサービスディスカバリーを使用します
信頼性
  • 複数の AZ にまたがってデプロイします
  • ヘルスチェックを適切に設定します
  • 適切な登録解除の遅延を設定します
  • デプロイにはサーキットブレーカーを使用します
aws ecs update-service \
  --cluster my-cluster \
  --service web-service \
  --deployment-configuration '{
    "deploymentCircuitBreaker": {
      "enable": true,
      "rollback": true,
      "resetOnHealthyTask": true,
      "thresholdConfiguration": {"type": "BOUNDED_PERCENT", "value": 50}
    }
  }'
  • サーキットブレーカーのしきい値を調整します: デフォルトの BOUNDED_PERCENT/50 は、希望する数の 50% で、失敗数は 3~200 に制限されます。UNBOUNDED_PERCENT はこの制限を解除します(大規模サービス向け)。COUNT は value を固定の失敗数として使用します(例:開発環境で素早くロールバックする場合は低い値)。resetOnHealthyTask: false にすると、失敗を連続ではなく累積してカウントします。
  • 早期成功条件(ローリングデプロイのみ):新しいリビジョンで希望するタスクのうち healthyPercent の割合が正常になると、デプロイを成功としてマークします。残りのタスクは通常のサービススケーリングで起動します。healthyPercent は minimumHealthyPercent 以上 100 以下である必要があります。レプリカサービスの minimumHealthyPercent はデフォルトで 100 なので、より低い healthyPercent を使用する場合は明示的に設定してください。早期完了後は、サーキットブレーカーとアラームによるロールバックは適用されなくなります。sourceServiceRevisionCleanup が BLOCKING の場合は成功前に古いタスクをドレインし、DEFERRED の場合は先に成功を宣言してから古いタスクを非同期でドレインします(長時間の接続、スケールイン保護)。
aws ecs update-service \
  --cluster my-cluster \
  --service web-service \
  --deployment-configuration '{
    "strategy": "ROLLING",
    "minimumHealthyPercent": 75,
    "earlySuccessCriteria": {"enable": true, "healthyPercent": 90, "sourceServiceRevisionCleanup": "BLOCKING"}
  }'
  • Service Connect のゾーン対応ルーティングはデフォルトで有効です(同じ AZ のエンドポイントを優先し、AZ 間のコストを削減します)。既存のサービスで有効にするには、一度再デプロイする必要があります。
  • EC2 起動タイプ:Amazon Linux 2023 ECS 最適化 AMI に移行します。 AL2 ECS 最適化 AMI は 2026 年 6 月 30 日にサポート終了となりました(新しい AMI は提供されず、エージェントのバージョンも固定されています)。
  • Fargate: プラットフォームバージョン 1.3.0 は 2026 年 6 月 15 日に非推奨となりました。LATEST または 1.4.0 を使用してください。
コスト最適化
  • バッチワークロードには Fargate Spot を使用します
  • タスクリソースのサイズを適切に設定します
  • 不要なときはゼロにスケールします
  • Fargate/Spot を混在させるにはキャパシティープロバイダーを使用します

トラブルシューティング

タスクが起動しない

確認項目:

# View stopped tasks
aws ecs describe-tasks \
  --cluster my-cluster \
  --tasks $(aws ecs list-tasks --cluster my-cluster --desired-status STOPPED --query 'taskArns[0]' --output text)

よくある原因:

  • イメージが見つからない(ECR の権限)
  • シークレットへのアクセスが拒否されている
  • ネットワーク設定(サブネット、セキュリティグループ)
  • リソース制限を超過している
コンテナが再起動を繰り返す

デバッグ:

# Check CloudWatch logs
aws logs get-log-events \
  --log-group-name /ecs/web-app \
  --log-stream-name "ecs/web/abc123"

# Check task details
aws ecs describe-tasks \
  --cluster my-cluster \
  --tasks task-arn \
  --query 'tasks[0].containers[0].{reason:reason,exitCode:exitCode}'

原因:

  • ヘルスチェックの失敗
  • アプリケーションのクラッシュ
  • メモリ不足
ECS Exec によるライブデバッグ

SSH を使わずに、実行中のコンテナに直接接続します。サービスで enableExecuteCommand: true を設定し、コンテナイメージに SSM エージェントを含める必要があります(ほとんどのベースイメージに含まれています)。

# Enable on existing service
aws ecs update-service \
  --cluster my-cluster \
  --service web-service \
  --enable-execute-command

# Get a shell in a running task
TASK_ARN=$(aws ecs list-tasks --cluster my-cluster --service-name web-service \
  --query 'taskArns[0]' --output text)

aws ecs execute-command \
  --cluster my-cluster \
  --task $TASK_ARN \
  --container web \
  --interactive \
  --command "/bin/sh"

要件: タスクロールには、ssmmessages:CreateControlChannel、ssmmessages:CreateDataChannel、ssmmessages:OpenControlChannel、ssmmessages:OpenDataChannel の権限が必要です。

サービスのデプロイが停止する
# Check deployment status
aws ecs describe-services \
  --cluster my-cluster \
  --services web-service \
  --query 'services[0].deployments'

# Check events
aws ecs describe-services \
  --cluster my-cluster \
  --services web-service \
  --query 'services[0].events[:5]'

原因:

  • 新しいタスクのヘルスチェックに失敗している
  • キャパシティーが不足している
  • ターゲットグループのヘルスチェックに失敗している
アクションログ(デプロイ中に ECS が行った処理)

クラスターごとにオプトインします。サービスのデプロイ(状態遷移、ロールバック、ライフサイクルフック)およびマネージドデーモンのライフサイクル中に ECS が行う処理を、タイムスタンプ付きで記録します。logLevel の INFO/WARN/ERROR とステータスの理由が含まれます。停止タスクの 1 時間の保持期間を過ぎても、障害に関するメタデータを保持します。CloudWatch のベンデッドログとして課金されます。

aws logs put-delivery-source \
  --name my-ecs-action-logs \
  --resource-arn arn:aws:ecs:us-east-1:123456789012:cluster/my-cluster \
  --log-type EcsActionLogs

aws logs put-delivery-destination \
  --name my-ecs-logs-destination \
  --output-format json \
  --delivery-destination-configuration '{"destinationResourceArn": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/vendedlogs/ecs/action-logs/my-cluster"}'

aws logs create-delivery \
  --delivery-source-name my-ecs-action-logs \
  --delivery-destination-arn arn:aws:logs:us-east-1:123456789012:delivery-destination:my-ecs-logs-destination

必要な権限: logs:PutDeliverySource、logs:PutDeliveryDestination、logs:CreateDelivery、logs:GetDelivery、ecs:AllowVendedLogDeliveryForResource。ロググループのリソースポリシーで、delivery.logs.amazonaws.com に logs:CreateLogStream/logs:PutLogEvents を許可する必要があります。サービスまたはデーモンの ARN ごとに 1 つのログストリームが作成されます。

EC2 ホストを AL2023 に移行した後の OOM キル

AL2023 は cgroup v2 を使用します。タスクレベルの memory だけを設定している場合、コンテナからメモリ制限を認識できないため、JVM などのランタイムはホストメモリを基にヒープサイズを決定します。コンテナレベルの memory をタスクメモリと同じ値に設定するか、/etc/ecs/ecs.config で ECS_PROPAGATE_TASK_MEMORY_LIMIT_CGROUPV2=true を設定してください(エージェント 1.104.0 以降)。cgroup v2 では報告されるメモリにページキャッシュも含まれるため、使用率は AL2 より高く表示されます。

ECR からイメージを取得できない

実行ロールに次の権限があることを確認してください:

{
  "Effect": "Allow",
  "Action": [
    "ecr:GetAuthorizationToken",
    "ecr:BatchCheckLayerAvailability",
    "ecr:GetDownloadUrlForLayer",
    "ecr:BatchGetImage"
  ],
  "Resource": "*"
}

次の項目も確認してください:

  • ECR の VPC エンドポイント(プライベートサブネットの場合)
  • NAT ゲートウェイ(プライベートサブネットの場合)
  • セキュリティグループで HTTPS のアウトバウンド通信が許可されていること

参考資料

コメント