「ECSとFargateの違いは?ALBとの連携方法や、マルチAZ設計のポイントが整理できていない」
こうした悩みを持つLinuxエンジニアは少なくありません。コンテナ技術そのものはDockerで学べても、AWSで"止まらない本番構成"を組み立てるには、ECS固有の設計ルールを体系的に把握する必要があります。
この記事では、AWS ECS・Fargateを使ったコンテナ本番環境の設計手順を、タスク定義・ALBとの連携・マルチAZ分散配置・Auto Scaling・IAMロール設計まで順を追って解説します。セキュリティグループ設計・デプロイ失敗時の自動ロールバック設定・ゼロダウンタイムデプロイ・VPCエンドポイントによるコスト最適化に加え、タスク定義のバージョン管理と本番で頻出するエラーの対処法も網羅しています。動作確認済みのAWS CLIコマンドと設定例を交えながら進めるので、設計の全体像を掴みながら読み進めてください。
動作確認環境: Amazon Linux 2023 / AWS CLI 2.x
この記事のポイント
・FargateはサーバーレスでEC2管理が不要。本番用途では最初の選択肢になる
・タスク定義でCPU・メモリ・コンテナ定義・ログ設定を一元管理する
・ECSサービス+ALBでマルチAZに分散配置してヘルスチェックを自動化する
・maximumPercent=200/minimumHealthyPercent=100でゼロダウンタイムデプロイを実現する
・Auto ScalingはCPU使用率70%をトリガーにするターゲットトラッキングが基本
・実行ロール(起動権限)とタスクロール(アプリ権限)を分離して最小権限を実現する
・デプロイ失敗時はcircuit breakerで自動ロールバックしサービス停止を防ぐ
・タスク定義JSONをファイル管理してGitで変更履歴を残すと構成ドリフトを防げる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
ECSとFargateとは?起動タイプの違いと選び方
ECS(Elastic Container Service)はAWSのコンテナオーケストレーションサービスです。DockerコンテナをAWS上で管理・実行するための基盤として機能します。
ECSには起動タイプが2つあります。
・EC2起動タイプ: コンテナを実行するEC2インスタンスを自分で管理する
・Fargate起動タイプ: コンテナの実行基盤をAWSがすべて管理するサーバーレス
ほとんどの新規構築ではFargateを選ぶのが正解です。EC2起動タイプは、コンテナホストのOSパッチ管理・ECSエージェントの更新・スケール時のインスタンス管理が必要になります。FargateであればこれらをすべてAWSに任せられます。
EC2起動タイプが適しているのは、GPUを使う機械学習コンテナや、ARM64など特定のアーキテクチャが必要な場合に限られます。
2つの起動タイプの管理責任の違いを整理しておきます。
| 管理項目 | EC2起動タイプ | Fargate起動タイプ |
|---|---|---|
| コンテナホストのOSパッチ | 自分で管理 | AWSが担当 |
| ECSエージェントの更新 | 自分で管理 | AWSが担当 |
| インスタンスのスケーリング | Auto Scaling Groupを自分で設計 | タスク単位で自動管理 |
| コンテナ定義・起動 | ECSが管理 | ECSが管理 |
| アプリケーションコード | 自分で管理 | 自分で管理 |
ECSの主要コンポーネントの整理
ECSを設計する前に、主要コンポーネントの役割を整理しておきます。・クラスター: ECSリソースの論理的なグループ。Fargateタスクが実行される空間
・タスク定義: コンテナの設定テンプレート(CPU・メモリ・イメージ・環境変数・ログ設定)
・タスク: タスク定義を元に実際に起動したコンテナのインスタンス
・サービス: 指定した数のタスクを常時稼働させ、ALBと連携する管理エンティティ
「タスク定義=設計図」「タスク=インスタンス」「サービス=台数管理と外部公開」というイメージで捉えると整理しやすいです。
クラスターとContainer Insightsの設定
ECSクラスターを作成する際に、Container Insightsを有効化することを強く推奨します。Container Insightsを有効にすると、CPU・メモリ使用率・タスク数・ネットワーク転送量などのメトリクスがCloudWatch Metricsに自動収集されます。本番障害の原因調査にほぼ必ず使うことになります。# ECSクラスターを作成する(Container Insights有効化) aws ecs create-cluster --cluster-name myapp-cluster --settings "name=containerInsights,value=enabled" --region ap-northeast-1 # 既存クラスターにContainer Insightsを後から有効化する aws ecs update-cluster-settings --cluster myapp-cluster --settings "name=containerInsights,value=enabled" --region ap-northeast-1
aws_ecs_cluster_capacity_providers リソースで両者を明示的に設定できます。タスク定義の設計ポイント
タスク定義はECS設計の核心部分です。以下の5点を押さえておけば、現場レベルの設計ができます。1. CPUとメモリの割り当て基準
Fargateでは、タスク全体のCPUとメモリを事前に決める必要があります。使えるサイズの組み合わせは固定されています。代表的なパターンは次のとおりです。
| タスクCPU(vCPU) | 選択できるメモリ(GB) | 用途の目安 |
|---|---|---|
| 0.25 vCPU | 0.5 ~ 2 GB | 軽量なAPIやバッチ処理 |
| 0.5 vCPU | 1 ~ 4 GB | 標準的なWebアプリ(小規模) |
| 1 vCPU | 2 ~ 8 GB | 中規模Webアプリ・マイクロサービス |
| 2 vCPU | 4 ~ 16 GB | 高トラフィックAPI・画像処理 |
| 4 vCPU | 8 ~ 30 GB | 大規模バッチ・データ処理 |
2. コンテナ定義と環境変数・シークレットの設計
タスク定義のコンテナ定義で重要なのは、環境変数とシークレットの扱い方です。・一般的な設定値(非機密): environmentに直接指定
・パスワード・APIキー(機密情報): AWS Secrets ManagerまたはParameter StoreをsecretsセクションでARN参照する
以下はAWS CLIでタスク定義を登録するJSON例です(抜粋)。
# タスク定義のJSON(コンテナ定義部分の抜粋) { "family": "myapp-task", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "512", "memory": "1024", "containerDefinitions": [ { "name": "myapp", "image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest", "portMappings": [ { "containerPort": 8080, "protocol": "tcp" } ], "environment": [ {"name": "APP_ENV", "value": "production"}, {"name": "LOG_LEVEL", "value": "info"} ], "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": "ecs" } } } ] }
コンテナ側には環境変数として渡されるため、アプリケーションのコードを変更する必要がないこと。そしてタスク定義のJSONにパスワードが平文で残らないことです。
3. awslogsでCloudWatch Logsにログを転送する
Fargateではコンテナの標準出力・標準エラー出力を、awslogsドライバー経由でCloudWatch Logsに転送します。タスクが終了してもログが残るため、障害調査に不可欠な設定です。
ロググループは事前に作成しておく必要があります。
# CloudWatch Logsのロググループを作成する aws logs create-log-group --log-group-name /ecs/myapp --region ap-northeast-1 # ロググループの保持期間を30日に設定する(必ず設定すること) aws logs put-retention-policy --log-group-name /ecs/myapp --retention-in-days 30
コンテナ定義の
awslogs-stream-prefix も必ず設定してください。複数のFargateタスクが同時に動いている状態でこの設定が抜けると、各タスクのCloudWatchログストリーム名が衝突し、どのタスクのログか判別できなくなります。障害発生時のログ調査で大幅な時間ロスにつながるため、設定漏れがないかタスク定義を作成したときに確認する習慣をつけてください。4. タスク定義でコンテナのヘルスチェックを設定する
ALBのヘルスチェックとは別に、タスク定義レベルでコンテナのヘルスチェックを設定できます。コンテナ自体の死活監視をECSが行うことで、ALBよりも早い段階で異常なタスクを検出して入れ替えられます。
# タスク定義のコンテナヘルスチェック設定(JSON抜粋) { "healthCheck": { "command": [ "CMD-SHELL", "curl -f http://localhost:8080/health || exit 1" ], "interval": 30, "timeout": 5, "retries": 3, "startPeriod": 60 } } # startPeriodは起動直後のヘルスチェック猶予時間(秒) # アプリの起動に時間がかかる場合は大きめに設定する # 例: Javaアプリで起動に45秒かかるなら startPeriod を 60~90 に設定する
5. タスク定義JSONをファイルで管理してバージョン管理に乗せる
コンソール上でタスク定義の変更を手動で積み重ねていくと、ステージングと本番の設定が少しずつずれていく「構成ドリフト」が発生します。「本番だけなぜか動かない」という問題の多くは、このずれが原因です。コンソール変更はGitに残らないため、「誰が何をいつ変えたか」の追跡にCloudTrailを掘る必要が生じます。対策として、タスク定義のJSONをファイルとして切り出してGitで管理する方法を推奨します。
# 既存のタスク定義をJSONファイルとして書き出す(初回) aws ecs describe-task-definition --task-definition myapp-task --query 'taskDefinition' --output json > task-definition.json # JSONファイルを編集した後、新しいリビジョンとして登録する aws ecs register-task-definition --cli-input-json file://task-definition.json # Gitで変更を管理する git add task-definition.json git commit -m "Update myapp-task: increase memory from 1024 to 2048"
TerraformでECSをIaC管理する場合は、本番のECSサービスリソースに
lifecycle { prevent_destroy = true }を設定しておくと、terraform destroyによる誤削除を防止できます。terraform plan -refresh-onlyを定期実行することで、コンソールでの手動変更(構成ドリフト)もコードレベルで即座に検出でき、JSONファイルとGitだけで管理するよりも早い段階でずれに気づけます。ECSサービスとALBの連携設定
ECSサービスはタスクの台数を管理し、ALBと組み合わせることで外部からのHTTPリクエストを各Fargateタスクに分散します。1. セキュリティグループの設計方針
ALBとECSタスクのセキュリティグループは、それぞれ役割を分けて設計します。正しい構成の原則は「FargateタスクのインバウンドはALBのセキュリティグループIDのみを許可する」ことです。0.0.0.0/0(全インターネット)を開けてしまうと、タスクに直接アクセスできてしまいます。# ALB用セキュリティグループ(インターネットからの80/443を受け入れる) aws ec2 create-security-group --group-name myapp-alb-sg --description "Security group for ALB" --vpc-id vpc-0123456789abcdef0 # ALB SGにインターネットからのHTTPS(443)を許可する aws ec2 authorize-security-group-ingress --group-id sg-alb-xxxxxxxx --protocol tcp --port 443 --cidr 0.0.0.0/0 # ECSタスク用セキュリティグループ(ALBのSGからのみコンテナポートを受け入れる) aws ec2 create-security-group --group-name myapp-ecs-sg --description "Security group for ECS Fargate tasks" --vpc-id vpc-0123456789abcdef0 # ECSタスクSGへの許可:ソースをALBのSGに限定する(0.0.0.0/0は絶対に使わない) aws ec2 authorize-security-group-ingress --group-id sg-ecs-xxxxxxxx --protocol tcp --port 8080 --source-group sg-alb-xxxxxxxx
2. ターゲットグループの作成
ALBとECSサービスを接続するために、まずターゲットグループを作成します。Fargateの場合はターゲットタイプを「ip」に設定することが必要です(EC2タイプではなく)。
# ターゲットグループを作成する(FargateはIPターゲット) aws elbv2 create-target-group --name myapp-tg --protocol HTTP --port 8080 --vpc-id vpc-0123456789abcdef0 --target-type ip --health-check-path /health --health-check-interval-seconds 30 --healthy-threshold-count 2 --unhealthy-threshold-count 3 # 出力例 { "TargetGroups": [ { "TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/myapp-tg/abc123def456", "TargetGroupName": "myapp-tg", "Protocol": "HTTP", "Port": 8080, "HealthCheckPath": "/health", ... } ] }
HTTP または HTTPS を指定してください。TCP を指定するとALBとの連携でエラーになります(後述のトラブルシュート参照)。ヘルスチェックパス(/health)はアプリケーション側で必ず実装してください。200 OKを返すだけの軽量なエンドポイントで十分です。3. ECSサービスでロードバランサーを設定する
ECSサービスを作成する際にALBのターゲットグループを紐付けます。# ECSサービスを作成する(ALBと連携・マルチAZ・デプロイcircuit breaker有効) aws ecs create-service --cluster myapp-cluster --service-name myapp-service --task-definition myapp-task:1 --desired-count 2 --launch-type FARGATE --network-configuration "awsvpcConfiguration={ subnets=[subnet-0a1b2c3d4e,subnet-0f1e2d3c4b], securityGroups=[sg-ecs-xxxxxxxx], assignPublicIp=DISABLED }" --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/myapp-tg/abc123,containerName=myapp,containerPort=8080" --deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true},maximumPercent=200,minimumHealthyPercent=100"
・assignPublicIp=DISABLED: Fargateタスクはプライベートサブネットに配置し、インターネットからの直接アクセスを遮断します。外部公開はALBをパブリックサブネットに置いて行うのが本番設計の基本パターンです
・maximumPercent=200/minimumHealthyPercent=100: ゼロダウンタイムデプロイの核心設定です。「旧タスクを落とす前に新タスクを起動して切り替える」ブルーグリーン風のデプロイが実現します。たとえばdesired-count=2の場合、更新時に最大4タスク(200%)まで起動を許可し、常に2タスク(100%)の稼働を保証するため、ユーザーリクエストを止めずにデプロイできます
・deploymentCircuitBreaker: デプロイ中にタスクが連続して起動失敗した場合、自動的に以前のタスク定義にロールバックします。IAM権限不足やヘルスチェック失敗でデプロイが詰まった際に、サービス全体が止まる事態を防げます
・リソースの作成順序: ECSサービスはALBとリスナーが完全に作成されてから作成してください。ALBリスナーが存在しない状態でECSサービスを作成すると、ロードバランサーへの登録が失敗します。CLIで手動作成する場合は、
aws elbv2 describe-listeners でリスナーの存在を確認してからECSサービスのコマンドを実行してください。TerraformでIaC管理する場合は、ECSサービスリソースに depends_on = [aws_lb_listener] を明示することで、この順序をコードで確実に保証できますsubnetsには2つの異なるAZのサブネットを指定することで、自動的にマルチAZ分散配置されます。
サービス作成後はタスクが正常に起動しているかを確認してください。RunningカウントとDesiredカウントが一致し、Pendingが0になっている状態が正常です。
# ECSサービスの起動状態を確認する aws ecs describe-services --cluster myapp-cluster --services myapp-service --query 'services[0].{Status:status,Running:runningCount,Desired:desiredCount,Pending:pendingCount}' # 正常な出力例 { "Status": "ACTIVE", "Running": 2, "Desired": 2, "Pending": 0 }
4. マルチAZのサブネット設計パターン
本番設計でFargateタスクとALBを配置するサブネットの基本構成は次のとおりです。| 用途 | サブネット種別 | 配置するリソース |
|---|---|---|
| 10.0.1.0/24(ap-northeast-1a) | パブリック | ALB、NATゲートウェイ |
| 10.0.2.0/24(ap-northeast-1c) | パブリック | ALB、NATゲートウェイ |
| 10.0.11.0/24(ap-northeast-1a) | プライベート | Fargateタスク |
| 10.0.12.0/24(ap-northeast-1c) | プライベート | Fargateタスク |
5. タスク配置戦略でAZ均等分散を明示する
ECSサービスはデフォルトでAZ均等分散(SPREAD)のタスク配置戦略を取りますが、明示的に設定することが推奨されます。# タスク配置戦略を確認する aws ecs describe-services --cluster myapp-cluster --services myapp-service --query 'services[0].placementStrategy' # AZ均等分散の配置戦略を設定する(update-service) aws ecs update-service --cluster myapp-cluster --service myapp-service --placement-strategy "[{"type":"spread","field":"attribute:ecs.availability-zone"}]"
AWSのインフラ設計をLinuxエンジニアとして体系的に学びたい方は、
AWSをLinuxエンジニアが学ぶためのロードマップもあわせてご覧ください。
Auto ScalingでFargateを自動スケールさせる
ECSサービスのAuto Scalingを設定することで、トラフィックの増減に応じてFargateタスクの台数を自動調整できます。1. Application Auto Scalingの設定
ECSのAuto ScalingはApplication Auto Scalingを通じて設定します。# ECSサービスをAuto Scalingのターゲットとして登録する aws application-autoscaling register-scalable-target --service-namespace ecs --scalable-dimension ecs:service:DesiredCount --resource-id service/myapp-cluster/myapp-service --min-capacity 2 --max-capacity 10
aws_appautoscaling_target リソースに相当します。2. スケールアウト・スケールインのポリシー設定
CPU使用率をトリガーにするターゲットトラッキングポリシーが、最もシンプルで安定した設定です。# CPU使用率70%を目標にするターゲットトラッキングポリシーを設定する aws application-autoscaling put-scaling-policy --service-namespace ecs --scalable-dimension ecs:service:DesiredCount --resource-id service/myapp-cluster/myapp-service --policy-name myapp-cpu-scaling --policy-type TargetTrackingScaling --target-tracking-scaling-policy-configuration '{ "TargetValue": 70.0, "PredefinedMetricSpecification": { "PredefinedMetricType": "ECSServiceAverageCPUUtilization" }, "ScaleOutCooldown": 60, "ScaleInCooldown": 300 }' # メモリ使用率80%でもスケールするポリシーを追加する(CPU・メモリの両方を監視) aws application-autoscaling put-scaling-policy --service-namespace ecs --scalable-dimension ecs:service:DesiredCount --resource-id service/myapp-cluster/myapp-service --policy-name myapp-memory-scaling --policy-type TargetTrackingScaling --target-tracking-scaling-policy-configuration '{ "TargetValue": 80.0, "PredefinedMetricSpecification": { "PredefinedMetricType": "ECSServiceAverageMemoryUtilization" }, "ScaleOutCooldown": 60, "ScaleInCooldown": 300 }'
CPUとメモリの両方にポリシーを設定しておくと、CPU負荷よりも先にメモリが逼迫するタイプのアプリ(JVMヒープを多く使うJavaアプリなど)でもスケールアウトが機能します。現場では「スケールアウトは素早く、スケールインは慎重に」が鉄則です。
IAMロール設計|タスクロールと実行ロールの違い
FargateのIAM設計で最も混乱しやすいのが、タスクロールと実行ロールの違いです。・実行ロール(Task Execution Role): ECSがタスクを起動・管理するために必要な権限。ECRからイメージをpullする、SecretsManagerからシークレットを取得する、CloudWatch Logsにログを書き込む権限が含まれる。タスクの「起動作業」を行うECSエージェントが使うロール。
・タスクロール(Task Role): コンテナの中で動くアプリケーションが他のAWSサービスを呼び出すための権限。S3にファイルをアップロードする、DynamoDBを参照する等の権限をここで設定する。
混同しやすいですが、実行ロールはタスクを「起動する」ための権限、タスクロールはコンテナが「実際に使う」権限と覚えてください。
# 実行ロールに必要な最低限のポリシー(AWSマネージドポリシー) # AmazonECSTaskExecutionRolePolicy に以下が含まれる # - ecr:GetAuthorizationToken # - ecr:BatchGetImage # - logs:CreateLogStream # - logs:PutLogEvents # SecretsManagerを使う場合は追加で以下が必要 # - secretsmanager:GetSecretValue # - kms:Decrypt(KMSで暗号化している場合) # 実行ロールの信頼ポリシー(ecs-tasks.amazonaws.com が引き受ける) { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "ecs-tasks.amazonaws.com"}, "Action": "sts:AssumeRole" }] } # タスクロールの作成例(S3とDynamoDBを使うアプリケーション用) aws iam create-role --role-name myapp-task-role --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "ecs-tasks.amazonaws.com"}, "Action": "sts:AssumeRole" }] }' # タスクロールに最小権限ポリシーをアタッチする # 例: S3バケットへのPUTのみ許可(GetやDeleteは付与しない) aws iam put-role-policy --role-name myapp-task-role --policy-name myapp-s3-put --policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::myapp-uploads/*" }] }'
実行ロールとタスクロールの両方に、信頼ポリシーで ecs-tasks.amazonaws.com を Principal に指定することが必要です。ec2.amazonaws.com を誤って指定するとロールのアタッチ自体は通っても権限が機能しないため注意してください。
本番でよくあるトラブルシュート
「ResourceInitializationError: unable to pull secrets or registry auth」が出る
ECRからコンテナイメージをpullできない状態です。Fargateタスクが起動する前段階で失敗しているため、CloudWatch Logsにもエラーが残りません。原因はほぼ2択です。・Task Execution RoleにAmazonECSTaskExecutionRolePolicyが付与されていない
・プライベートサブネット内のFargateタスクからECRへの経路がない(NATゲートウェイまたはVPCエンドポイントが未設定)
VPCエンドポイントでNATゲートウェイを省いてECRに接続する場合は、以下の3つのエンドポイントが最低限必要です。
# ECRイメージpullに必要なVPCエンドポイントを確認する aws ec2 describe-vpc-endpoints --filters "Name=vpc-id,Values=vpc-0123456789abcdef0" --query 'VpcEndpoints[*].{service:ServiceName,state:State}' # ECRのイメージpullに必要な3つのエンドポイント # - com.amazonaws.ap-northeast-1.ecr.dkr → Dockerイメージの取得 # - com.amazonaws.ap-northeast-1.ecr.api → ECR API呼び出し # - com.amazonaws.ap-northeast-1.s3 → ECRレイヤーの取得(S3バケット経由) # CloudWatch Logsへの書き込みに必要 # - com.amazonaws.ap-northeast-1.logs # SecretsManagerを使う場合も必要 # - com.amazonaws.ap-northeast-1.secretsmanager
タスクが起動直後にSTOPPEDになる
ECSサービスでタスクが起動してもすぐにSTOPPEDになる場合、原因の大半は以下の3つです。・ECRへのアクセス権限不足(実行ロールの権限漏れ)
・コンテナの起動コマンドが即時終了している(プロセスがフォアグラウンドで起動していない)
・SecretsManagerからシークレットが取得できない
停止理由はコンソールまたはCLIで確認できます。
# 停止したタスクの停止理由を確認する aws ecs describe-tasks --cluster myapp-cluster --tasks arn:aws:ecs:ap-northeast-1:123456789012:task/myapp-cluster/a1b2c3d4 --query 'tasks[0].{status:lastStatus,stopCode:stopCode,reason:stoppedReason}' # 出力例(実行ロールの権限不足の場合) { "status": "STOPPED", "stopCode": "TaskFailedToStart", "reason": "CannotPullContainerError: pull image manifest has been retried 5 time(s): ..." } # CloudWatch Logsでコンテナのエラーログを確認する aws logs get-log-events --log-group-name /ecs/myapp --log-stream-name ecs/myapp/a1b2c3d4 --limit 50
ALBのヘルスチェックが通らない
ECSサービスとALBを連携させた際に、タスクが起動してもすぐにDRAININGになる場合は、ALBのヘルスチェックが失敗しています。よくある原因と確認コマンドは次のとおりです。
・セキュリティグループでALBからコンテナポートへのアクセスが許可されていない
・ヘルスチェックパスが間違っている(/health が404を返している)
・コンテナのアプリが起動完了前にヘルスチェックが来てタイムアウトしている
# ターゲットグループのヘルスチェック状態を確認する aws elbv2 describe-target-health --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/myapp-tg/abc123 --query 'TargetHealthDescriptions[*].{id:Target.Id,port:Target.Port,state:TargetHealth.State,reason:TargetHealth.Reason}' # 出力例(unhealthyの場合) [ { "id": "10.0.1.45", "port": 8080, "state": "unhealthy", "reason": "Target.ResponseCodeMismatch" } ]
セキュリティグループの設定では、ALBのセキュリティグループをソースとして、コンテナポートへのインバウンドを許可する設定が正しい構成です。0.0.0.0/0を許可する設定は本番環境では避けてください。
Fargateタスクから外部への通信が失敗する
Fargateタスクをプライベートサブネットに配置した場合、ECRへのイメージpullや外部APIへのHTTPアクセスが失敗することがあります。よくある原因は次の2つです。
・NATゲートウェイが設定されていない(またはルートテーブルがNATを向いていない)
・VPCエンドポイントが未設定でNATゲートウェイも存在しない
# プライベートサブネットのルートテーブルを確認する # 0.0.0.0/0 がNATゲートウェイ(nat-xxxxxxxxx)に向いているか確認 aws ec2 describe-route-tables --filters "Name=association.subnet-id,Values=subnet-0a1b2c3d4e" --query 'RouteTables[0].Routes[*].{dest:DestinationCidrBlock,gw:GatewayId,nat:NatGatewayId}' # 出力例(正常な場合) [ {"dest": "10.0.0.0/16", "gw": "local", "nat": null}, {"dest": "0.0.0.0/0", "gw": null, "nat": "nat-0abc12345def67890"} ] # VPCエンドポイント経由でECRにアクセスする場合は以下のエンドポイントを作成する # - com.amazonaws.ap-northeast-1.ecr.dkr (イメージpull) # - com.amazonaws.ap-northeast-1.ecr.api (API呼び出し) # - com.amazonaws.ap-northeast-1.s3 (ECRレイヤーの取得) # - com.amazonaws.ap-northeast-1.logs (CloudWatch Logsへの書き込み) # - com.amazonaws.ap-northeast-1.secretsmanager (シークレット取得) aws ec2 describe-vpc-endpoints --filters "Name=vpc-id,Values=vpc-0123456789abcdef0" --query 'VpcEndpoints[*].{service:ServiceName,state:State}'
「ECS service failed to stabilize」でDesired Countに達しない
サービスを作成・更新してもタスクがDesired Countに達しない状態です。AWSコンソールの「サービスイベント」タブにエラーメッセージが出るため、まずそこを確認してください。CLIで確認する場合は次のコマンドを使います。# ECSサービスのイベントを確認する(直近の10件) aws ecs describe-services --cluster myapp-cluster --services myapp-service --query 'services[0].events[:10].{createdAt:createdAt,message:message}' # 出力例(ヘルスチェック失敗でタスクが入れ替わり続けている場合) [ { "createdAt": "2024-01-15T10:23:45+09:00", "message": "(service myapp-service) (task a1b2c3d4) failed container health checks." }, { "createdAt": "2024-01-15T10:23:20+09:00", "message": "(service myapp-service) registered 1 targets in (target-group arn:...)" } ]
・ヘルスチェックパスの設定ミス:/healthで200が返るかcurlで事前に確認する。アプリ側に/healthエンドポイントが実装されていないケースが多い
・セキュリティグループの許可漏れ:ALBのSGからECSタスクのSGへのインバウンドが開いているか確認する。ポート番号の指定ミスにも注意
・ECRイメージ取得の失敗:実行ロール(Task Execution Role)にAmazonEC2ContainerRegistryReadOnlyまたはAmazonECSTaskExecutionRolePolicyがアタッチされているか確認する
「InvalidParameterException」でターゲットグループのプロトコルが合わない
ターゲットグループ作成時にプロトコルをTCP にするとALBとの連携でエラーになります。NLB(Network Load Balancer)向けの設定をALBに適用してしまった場合に発生します。ALBのターゲットグループには必ず HTTP または HTTPS を指定してください。# ターゲットグループのプロトコルを確認する aws elbv2 describe-target-groups --names myapp-tg --query 'TargetGroups[0].{Protocol:Protocol,TargetType:TargetType,Port:Port}' # 出力例(間違ってTCPになっている場合) { "Protocol": "TCP", "TargetType": "ip", "Port": 8080 } # 修正方法: ターゲットグループを削除して再作成する(protocolはHTTPを指定) aws elbv2 delete-target-group --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/myapp-tg/abc123 aws elbv2 create-target-group --name myapp-tg --protocol HTTP --port 8080 --vpc-id vpc-0123456789abcdef0 --target-type ip --health-check-path /health
本記事のまとめ
AWS ECS・Fargateを使ったコンテナ本番環境の設計手順をまとめます。| 設計項目 | ポイント |
|---|---|
| 起動タイプの選定 | 新規構築はFargate一択。EC2管理が不要でコスト最適化しやすい |
| クラスター設定 | Container Insightsを必ず有効化。メトリクスが障害調査の基礎になる |
| タスク定義のCPU・メモリ | 小さく始めてCloudWatchで監視しながら調整する |
| シークレット管理 | 機密情報はSecretsManagerのARN参照。環境変数への平文埋め込みは禁止 |
| ログ設定 | awslogsドライバーでCloudWatch Logsへ転送。保持期間とawslogs-stream-prefixを必ず設定する |
| コンテナヘルスチェック | タスク定義でhealthCheckを設定しstartPeriodを適切に設定する |
| タスク定義のバージョン管理 | JSONファイルをGitで管理して構成ドリフトを防ぐ。IaCツール導入の起点にもなる |
| セキュリティグループ設計 | ECSタスクのインバウンドはALBのSG IDに限定。0.0.0.0/0は使わない |
| サブネット設計 | Fargateはプライベートサブネット、ALBはパブリックサブネットに配置する |
| マルチAZ分散 | サブネットを複数AZに配置しdesired-countを2以上にする |
| ゼロダウンタイムデプロイ | maximumPercent=200/minimumHealthyPercent=100で旧タスク停止前に新タスクを起動する |
| デプロイ自動ロールバック | deploymentCircuitBreaker有効化。デプロイ失敗時のサービス停止を防ぐ |
| Auto Scaling | CPU・メモリ使用率でターゲットトラッキング。min=2、スケールインは慎重に |
| IAMロール | 実行ロール(起動時の権限)とタスクロール(アプリの権限)を分離する |
| アウトバウンド設計 | プライベートサブネットにはNATゲートウェイまたはVPCエンドポイントが必要 |
ECS・Fargateの設計はVPC設計と密接に連動しています。
プライベートサブネット・NATゲートウェイ・セキュリティグループの設計と組み合わせることで、より堅牢な本番環境が完成します。
AWSのインフラ設計をLinuxエンジニアとして体系的に学びたい方は、
AWSをLinuxエンジニアが学ぶためのロードマップもご覧ください。
マルチAZを含む冗長設計の実践については、
AWSの冗長設計を体系的に学ぶ上級ガイドでさらに詳しく解説しています。
AWSのコンテナ設計を「実務の型」として身につけませんか?
ECSやFargateの設定方法は調べれば分かります。でも「なぜその構成を選ぶのか」を説明できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます