はじめに:AKS から ECS/EKS へ、想定外の選択肢の多さ
個人の趣味開発で Azure から AWS に移行することになり、AKS(Azure Kubernetes Service)と ACI(Azure Container Instances)で動いていたコンテナアプリケーションを AWS に移植することになりました。「Kubernetes なら同じ」「コンテナは移植性が高い」という楽観的な見立てで始めた結果、本番環境で次々と予期しない問題が発生。Azure の「シンプルな選択肢」と AWS の「多様なコンテナサービス」の根本的な違いを痛感しました。
最初のローカル環境でのテストは順調でした。Docker イメージも ECR にプッシュできたし、ECS でコンテナも起動しています。
# これは普通に動く
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker tag my-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
# ECS タスク起動
aws ecs run-task --cluster my-cluster --task-definition my-app:1 --launch-type FARGATE
しかし実際に障害対応訓練で複雑なデプロイやスケーリングのトラブルシューティングを始めた途端、問題が続出しました。
問題1:ECS と EKS のどちらを選ぶべきか分からない
Azure では「AKS(Kubernetes が必要)」か「ACI(単純なコンテナ実行)」の 2 択でシンプルでしたが、AWS は ECS、EKS、Fargate、App Runner と選択肢が多すぎて、初期設計で迷走しました。
問題2:ECS タスク定義が「設定と実態が乖離」して原因不明のエラー
AKS では Deployment YAML を apply すれば常に宣言状態が保たれるのに、ECS は「タスク定義のリビジョン管理」が別物で、「どのリビジョンが実際に動いているか」が分からなくなりました。
問題3:Fargate の「ネットワークモード awsvpc」で IP 枯渇
AKS では Pod の IP が内部ネットワークで自動割り当てされるのに、Fargate では各タスクに ENI(Elastic Network Interface)が必要で、サブネットの IP が枯渇してタスク起動失敗。Azure では経験したことのない問題でした。
問題4:EKS のログが CloudWatch に流れない(デフォルトでは何も設定されていない)
AKS では Azure Monitor との統合が標準で有効なのに、EKS は「Fluent Bit を自分でデプロイ」しないと何もログが見えず、障害時に完全に手詰まりになりました。
問題5:ECS サービスのローリングアップデートが「古いタスクが止まらない」
AKS の Rolling Update は自動的に古い Pod を削除してくれるのに、ECS では「minimum healthy percent」と「maximum percent」の設定を間違えると、古いタスクが延々と残り続けることに気づくまで 1 時間かかりました。
問題6:コンテナイメージのスキャンが ECR では「手動有効化」
Azure Container Registry では脆弱性スキャンが標準で有効なのに、ECR は「イメージスキャン設定」を明示的に ON にしないと何もチェックされず、本番環境に脆弱性のあるイメージがデプロイされていました。
これら全ての問題と向き合う中で、AWS のコンテナサービスは「柔軟だが、設定の明示性が高い」という設計思想だと理解しました(気がします)。
Azure Container Services との設計の違い:シンプル vs 柔軟性の世界観
Azure Container Services と AWS コンテナサービスは、どちらも「コンテナ実行基盤」ですが、アーキテクチャと統合度が大きく異なります。
| 項目 | Azure (AKS / ACI) | AWS (ECS / EKS / Fargate) | 影響(実経験) |
|---|---|---|---|
| 選択肢の数 | 2 つ(AKS / ACI) | 4 つ以上(ECS / EKS / Fargate / App Runner) | AWS は「どれを選ぶか」の判断が必要 |
| Kubernetes 互換 | AKS のみ | EKS のみ | ECS は独自 API(Kubernetes YAML が使えない) |
| タスク/Pod 定義 | YAML(宣言的) | ECS はタスク定義(JSON)、EKS は YAML | ECS は「リビジョン管理」が別概念 |
| ネットワークモード | Pod IP は自動割り当て | ECS Fargate は ENI(IP アドレス消費) | サブネットの IP 枯渇が発生しやすい |
| ログ統合 | Azure Monitor 標準統合 | ECS は CloudWatch、EKS は手動設定(Fluent Bit) | EKS はログが「見えない」デフォルト状態 |
| ローリングアップデート | 自動的に古い Pod 削除 | ECS は minimum/maximum percent 設定が必要 | 設定ミスで古いタスクが残り続ける |
| イメージスキャン | ACR で標準有効 | ECR で手動有効化 | 脆弱性チェック漏れが発生しやすい |
| サービスメッシュ | AKS + Istio/Linkerd(手動) | App Mesh(別サービス) | AWS は統合されていない(別途設定) |
| 料金 | ノード課金 + コントロールプレーン無料(AKS) | ECS 無料 / EKS コントロールプレーン課金 + ノード課金 | EKS は AKS より高コスト |
この表を見ると、Azure は「シンプルで統合された選択肢」、AWS は「柔軟だが明示的設定が必要」です。障害切り分けでは「どのサービスのどの設定が問題か」を理解する必要があります。
実際の障害シナリオ:Azure ユーザーがハマるポイント
シナリオA:ECS タスクが起動しない(ENI 不足によるサブネット IP 枯渇)
背景
- ECS Fargate でマイクロサービスを 50 タスク起動しようとした
- AKS では問題なく動いていたが、AWS では「起動に失敗する」
問題検知
# ECS サービスのイベントを確認
aws ecs describe-services --cluster my-cluster --services my-service --query 'services[0].events[0:5]'
# → "service my-service was unable to place a task because no container instance met all of its requirements"
# タスクの詳細を確認
aws ecs describe-tasks --cluster my-cluster --tasks $(aws ecs list-tasks --cluster my-cluster --service my-service --query 'taskArns[0]' --output text)
# → stoppedReason: "ResourceInitializationError: unable to pull secrets or registry auth"
最初の仮説(外れ)
# IAM ロールの権限不足?
aws iam get-role --role-name ecsTaskExecutionRole --query 'Role.AssumeRolePolicyDocument'
# → 権限は正しい(AmazonECSTaskExecutionRolePolicy が付与されている)
# ECR のイメージが存在するか確認
aws ecr describe-images --repository-name my-app --image-ids imageTag=latest
# → イメージは存在する
サブネットの確認
# ECS タスクが起動しているサブネットを確認
aws ec2 describe-subnets --subnet-ids subnet-abc123 --query 'Subnets[0].[AvailableIpAddressCount,CidrBlock]'
# → [ 0, "10.0.1.0/24" ] ← 利用可能な IP が 0 個!
# サブネットの CIDR: 10.0.1.0/24 = 256 個の IP アドレス
# AWS 予約: 5 個(.0, .1, .2, .3, .255)
# 実質: 251 個利用可能
# しかし Fargate は「1 タスク = 1 ENI = 1 IP アドレス」消費
# 50 タスク × 2 サービス = 100 タスク = 100 IP 消費
# さらに ALB、RDS、Lambda の ENI も同じサブネットを使っていた → IP 枯渇
真因発覚
# AKS では Pod IP は内部ネットワーク(10.244.0.0/16 など)で自動割り当て
# Fargate は各タスクに ENI(VPC の IP アドレス)を割り当てるため、サブネット IP が枯渇
# 解決策1: より大きな CIDR のサブネットを作成(/20 や /19)
# 解決策2: 複数サブネットを使用してタスクを分散
短期対処
# 新しいサブネットを作成(より大きな CIDR)
aws ec2 create-subnet \
--vpc-id vpc-xyz789 \
--cidr-block 10.0.16.0/20 \
--availability-zone us-east-1a
# → /20 = 4096 個の IP アドレス
# ECS サービスの更新(新しいサブネットを使用)
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--network-configuration "awsvpcConfiguration={subnets=[subnet-new123],securityGroups=[sg-abc123]}"
検証
# タスクが正常に起動するか確認
aws ecs list-tasks --cluster my-cluster --service my-service --desired-status RUNNING
# → 50 タスクが正常に起動
# サブネットの利用状況を確認
aws ec2 describe-subnets --subnet-ids subnet-new123 --query 'Subnets[0].AvailableIpAddressCount'
# → 4046(まだ余裕がある)
学び
- AKS では Pod IP は内部ネットワークで自動割り当てされ、VPC の IP を消費しない
- Fargate では各タスクに ENI が必要で、サブネットの IP アドレスを消費する
- 大規模デプロイ前にサブネット CIDR のサイジングが必須(/24 では不足しやすい)
シナリオB:ECS タスク定義のリビジョン管理で「古い設定が動いている」(実例:16:00 ~ 16:40)
背景
- 環境変数を更新してデプロイしたはずが、アプリケーションが古い設定で動いている
- AKS では
kubectl applyで常に最新 YAML が反映されるのに…
問題検知
# アプリケーションログで確認
aws logs tail /ecs/my-app --since 5m
# → "DATABASE_URL=old-db.example.com" ← 古い設定が使われている!
# タスク定義を更新したはずなのに…
タスク定義の確認
# 最新のタスク定義を確認
aws ecs describe-task-definition --task-definition my-app --query 'taskDefinition.revision'
# → 5(リビジョン 5 が最新)
# タスク定義の環境変数を確認
aws ecs describe-task-definition --task-definition my-app:5 \
--query 'taskDefinition.containerDefinitions[0].environment'
# → [ { "name": "DATABASE_URL", "value": "new-db.example.com" } ] ← 正しい!
実行中のタスクを確認
# 実際に動いているタスクのタスク定義リビジョンを確認
aws ecs list-tasks --cluster my-cluster --service my-service --query 'taskArns[0]' --output text
# → arn:aws:ecs:us-east-1:123456789012:task/my-cluster/abc123
aws ecs describe-tasks --cluster my-cluster --tasks abc123 \
--query 'tasks[0].taskDefinitionArn'
# → "arn:aws:ecs:us-east-1:123456789012:task-definition/my-app:3" ← リビジョン 3 が動いている!
真因発覚
# ECS サービスが使用しているタスク定義リビジョンを確認
aws ecs describe-services --cluster my-cluster --services my-service \
--query 'services[0].taskDefinition'
# → "arn:aws:ecs:us-east-1:123456789012:task-definition/my-app:3"
# 原因判明:
# - タスク定義のリビジョン 5 は「登録」されているが、サービスは「リビジョン 3」を使い続けている
# - AKS では Deployment を apply すると自動的に新しい Pod が起動するが、
# ECS では「サービスの更新」を明示的に実行しないと新しいリビジョンが使われない
短期対処
# ECS サービスを更新して最新リビジョンを使用
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--task-definition my-app:5 \
--force-new-deployment
# または最新リビジョンを自動で使う
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--task-definition my-app \
--force-new-deployment
検証
# 新しいタスクが起動しているか確認
aws ecs list-tasks --cluster my-cluster --service my-service --desired-status RUNNING
# タスクのタスク定義リビジョンを確認
aws ecs describe-tasks --cluster my-cluster --tasks $(aws ecs list-tasks --cluster my-cluster --service my-service --query 'taskArns[0]' --output text) \
--query 'tasks[0].taskDefinitionArn'
# → "arn:aws:ecs:us-east-1:123456789012:task-definition/my-app:5" ← リビジョン 5 が動いている!
# ログで確認
aws logs tail /ecs/my-app --since 2m
# → "DATABASE_URL=new-db.example.com" ← 正しい設定が反映された
学び
- AKS では Deployment を
kubectl applyすると自動的に新しい ReplicaSet が作成され、古い Pod が削除される - ECS では「タスク定義の登録」と「サービスの更新」が別ステップ
- CI/CD パイプラインでは「タスク定義登録 → サービス更新」を自動化する必要がある
シナリオC:EKS のログが CloudWatch に流れない(実例:13:30 ~ 14:20)
背景
- EKS クラスターを作成し、アプリケーションを デプロイ
- AKS では Azure Monitor にログが自動的に流れるのに、EKS では何も見えない
問題検知
# CloudWatch Logs で EKS のログを探す
aws logs describe-log-groups --log-group-name-prefix /aws/eks
# → [] ← Log Group が存在しない!
# AKS では Container Insights が標準で有効だったのに…
EKS コントロールプレーンログの確認
# EKS クラスターのログ設定を確認
aws eks describe-cluster --name my-cluster --query 'cluster.logging'
# → {
# "clusterLogging": [
# {
# "types": ["api", "audit", "authenticator", "controllerManager", "scheduler"],
# "enabled": false ← 無効!
# }
# ]
# }
# コントロールプレーンログを有効化
aws eks update-cluster-config \
--name my-cluster \
--logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}'
アプリケーションログの確認
# kubectl でログを確認(これは動く)
kubectl logs -n default my-app-pod-abc123
# → ログは出力されているが、CloudWatch には流れていない
# AKS では Azure Monitor エージェントが自動的にログを収集するが、
# EKS では「Fluent Bit または Fluentd を自分でデプロイ」する必要がある
Fluent Bit のデプロイ
# AWS が提供する Fluent Bit DaemonSet をデプロイ
kubectl apply -f https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml
# Fluent Bit が起動しているか確認
kubectl get pods -n amazon-cloudwatch
# IAM ロールの確認(Fluent Bit が CloudWatch Logs に書き込む権限)
# IRSA (IAM Roles for Service Accounts) の設定が必要
IRSA の設定
# OIDC プロバイダーを作成(まだ作成していない場合)
eksctl utils associate-iam-oidc-provider --cluster my-cluster --approve
# Fluent Bit 用の IAM ロールを作成
eksctl create iamserviceaccount \
--name fluent-bit \
--namespace amazon-cloudwatch \
--cluster my-cluster \
--attach-policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
--approve \
--override-existing-serviceaccounts
# Fluent Bit Pod を再起動
kubectl rollout restart daemonset/fluent-bit -n amazon-cloudwatch
検証
# CloudWatch Logs で Log Group を確認
aws logs describe-log-groups --log-group-name-prefix /aws/containerinsights/my-cluster
# → Log Group が作成された!
# ログが流れているか確認
aws logs tail /aws/containerinsights/my-cluster/application --since 5m
# → アプリケーションログが表示される
学び
- AKS では Azure Monitor Container Insights が標準で統合されている
- EKS では「コントロールプレーンログ」と「アプリケーションログ」を別々に設定する必要がある
- アプリケーションログには Fluent Bit/Fluentd を手動デプロイ + IRSA 設定が必須
シナリオD:ECS ローリングアップデートで「古いタスクが止まらない」(実例:15:30 ~ 16:10)
背景
- 新しいバージョンをデプロイしたが、古いタスクと新しいタスクが混在している
- AKS の Rolling Update では自動的に古い Pod が削除されるのに…
問題検知
# ECS サービスのタスク一覧を確認
aws ecs list-tasks --cluster my-cluster --service my-service --query 'taskArns' --output text
# → 10 個のタスクが動いている(desired count = 5 なのに?)
# タスクの詳細を確認
for task in $(aws ecs list-tasks --cluster my-cluster --service my-service --query 'taskArns[]' --output text); do
aws ecs describe-tasks --cluster my-cluster --tasks $task --query 'tasks[0].[taskArn,taskDefinitionArn,lastStatus]'
done
# → 5 個はリビジョン 3、5 個はリビジョン 4(新旧混在)
ECS サービスの設定を確認
# デプロイ設定を確認
aws ecs describe-services --cluster my-cluster --services my-service \
--query 'services[0].deploymentConfiguration'
# → {
# "deploymentCircuitBreaker": { "enable": false, "rollback": false },
# "maximumPercent": 200,
# "minimumHealthyPercent": 100
# }
# 原因判明:
# - maximumPercent: 200 → desired count 5 の 200% = 10 タスクまで起動可能
# - minimumHealthyPercent: 100 → desired count 5 の 100% = 5 タスクは常に健全である必要
# つまり:
# 1. 新しいタスク 5 個を起動(合計 10 個)
# 2. 古いタスクを停止するはずが、「ヘルスチェックが失敗」しているため停止されない
ヘルスチェックの確認
# ターゲットグループのヘルスチェック状態を確認(ALB を使用している場合)
aws elbv2 describe-target-health --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abc123
# → 新しいタスク 5 個が "unhealthy"(ヘルスチェック失敗)
# ヘルスチェック設定を確認
aws elbv2 describe-target-groups --target-group-arns arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abc123 \
--query 'TargetGroups[0].[HealthCheckPath,HealthCheckIntervalSeconds,HealthyThresholdCount]'
# → [ "/health", 30, 3 ]
# アプリケーションの /health エンドポイントを確認
curl http://新しいタスクのIP:8080/health
# → 404 Not Found ← 新しいバージョンでは /health エンドポイントが削除されていた!
短期対処
# ヘルスチェックパスを修正(新しいバージョンでは / がヘルスチェック)
aws elbv2 modify-target-group \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abc123 \
--health-check-path /
# または古いリビジョンにロールバック
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--task-definition my-app:3 \
--force-new-deployment
検証
# ターゲットの健全性を再確認
aws elbv2 describe-target-health --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abc123
# → 新しいタスク 5 個が "healthy"
# 古いタスクが停止されたか確認
aws ecs list-tasks --cluster my-cluster --service my-service --query 'taskArns | length(@)'
# → 5(desired count 通りに動いている)
学び
- AKS の Rolling Update は自動的に古い Pod を削除する(readinessProbe が成功した時点で)
- ECS では「ヘルスチェックが成功」しないと古いタスクが停止されない
- デプロイ前に「ヘルスチェック設定」と「アプリケーションのヘルスエンドポイント」の整合性を確認する必要がある
AWS コンテナサービスの優先順位:障害発生時の実戦順序
最初の 3 分:どこで何が起きているか把握
# 1. ECS/EKS のサービス/デプロイメント状態を確認
# ECS の場合
aws ecs describe-services --cluster $CLUSTER_NAME --services $SERVICE_NAME \
--query 'services[0].[runningCount,desiredCount,deployments]'
# EKS の場合
kubectl get deployments -n $NAMESPACE
kubectl get pods -n $NAMESPACE
# 2. タスク/Pod のステータスを確認
# ECS
aws ecs list-tasks --cluster $CLUSTER_NAME --service $SERVICE_NAME --desired-status STOPPED --query 'taskArns[0:5]'
aws ecs describe-tasks --cluster $CLUSTER_NAME --tasks $TASK_ARN --query 'tasks[0].stoppedReason'
# EKS
kubectl describe pod $POD_NAME -n $NAMESPACE
# 3. ログを確認
# ECS
aws logs tail /ecs/$SERVICE_NAME --since 10m --filter-pattern "ERROR"
# EKS
kubectl logs -n $NAMESPACE $POD_NAME --tail=100
次の 10 分:原因カテゴリ別切り分け
1) サブネット IP 枯渇 → ECS Fargate で頻発
Fargate は 1 タスク = 1 ENI = 1 IP 消費。
# サブネットの利用可能 IP を確認
aws ec2 describe-subnets --subnet-ids $SUBNET_ID \
--query 'Subnets[0].[AvailableIpAddressCount,CidrBlock]'
# → AvailableIpAddressCount が 0 または少数なら枯渇
# ENI の数を確認
aws ec2 describe-network-interfaces \
--filters "Name=subnet-id,Values=$SUBNET_ID" \
--query 'NetworkInterfaces | length(@)'
対応案
- より大きな CIDR のサブネットを作成(/20 や /19)
- 複数サブネットに分散
- 不要な ENI を削除
2) ECS タスク定義のリビジョン不一致 → 設定と実態の乖離
タスク定義を更新したのに古いリビジョンが動いている。
# サービスが使用しているタスク定義リビジョンを確認
aws ecs describe-services --cluster $CLUSTER_NAME --services $SERVICE_NAME \
--query 'services[0].taskDefinition'
# 実行中のタスクのリビジョンを確認
aws ecs describe-tasks --cluster $CLUSTER_NAME --tasks $TASK_ARN \
--query 'tasks[0].taskDefinitionArn'
# 最新のタスク定義リビジョンを確認
aws ecs describe-task-definition --task-definition $TASK_DEFINITION_FAMILY \
--query 'taskDefinition.revision'
対応案
aws ecs update-service --task-definition $TASK_DEFINITION:latest --force-new-deployment- CI/CD パイプラインで自動化
3) EKS ログが見えない → Fluent Bit 未設定
EKS はデフォルトでログが CloudWatch に流れない。
# CloudWatch Logs で Log Group を確認
aws logs describe-log-groups --log-group-name-prefix /aws/containerinsights/$CLUSTER_NAME
# → Log Group が存在しない場合は Fluent Bit 未設定
# Fluent Bit がデプロイされているか確認
kubectl get pods -n amazon-cloudwatch
# → Pod が存在しない場合はデプロイが必要
対応案
- Fluent Bit DaemonSet をデプロイ
- IRSA(IAM Roles for Service Accounts)を設定
- EKS コントロールプレーンログも有効化(
aws eks update-cluster-config --logging)
4) ECS ローリングアップデートで古いタスクが残る → ヘルスチェック失敗
新しいタスクのヘルスチェックが失敗すると、古いタスクが停止されない。
# ターゲットグループのヘルスチェック状態を確認
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN
# ヘルスチェック設定を確認
aws elbv2 describe-target-groups --target-group-arns $TARGET_GROUP_ARN \
--query 'TargetGroups[0].[HealthCheckPath,HealthCheckIntervalSeconds,HealthyThresholdCount]'
対応案
- ヘルスチェックパスとアプリケーションのエンドポイントを一致させる
-
minimumHealthyPercentとmaximumPercentを見直す - デプロイサーキットブレーカーを有効化(自動ロールバック)
5) ECR イメージが pull できない → IAM 権限不足
タスク実行ロールに ECR の権限がない。
# タスク実行ロールを確認
aws ecs describe-task-definition --task-definition $TASK_DEFINITION \
--query 'taskDefinition.executionRoleArn'
# ロールのポリシーを確認
aws iam list-attached-role-policies --role-name ecsTaskExecutionRole
# AmazonECSTaskExecutionRolePolicy が必要
# または以下の権限:
# - ecr:GetAuthorizationToken
# - ecr:BatchCheckLayerAvailability
# - ecr:GetDownloadUrlForLayer
# - ecr:BatchGetImage
対応案
- タスク実行ロールに
AmazonECSTaskExecutionRolePolicyをアタッチ - CloudWatch Logs への書き込み権限も必要(
logs:CreateLogGroup,logs:CreateLogStream,logs:PutLogEvents)
よくある落とし穴と対策
| 落とし穴 | 理由 | AWS での対策 |
|---|---|---|
| サブネット IP 枯渇 | Fargate は 1 タスク = 1 ENI = 1 IP 消費 | /20 以上の CIDR を使用、複数サブネット分散 |
| ECS タスク定義のリビジョン不一致 | タスク定義登録とサービス更新が別ステップ | CI/CD で update-service を自動化 |
| EKS ログが見えない | Fluent Bit が未設定 | Fluent Bit DaemonSet + IRSA を設定 |
| ローリングアップデートで古いタスクが残る | ヘルスチェック失敗で古いタスク停止されない | ヘルスチェック設定とアプリのエンドポイントを一致 |
| ECR イメージが pull できない | タスク実行ロールに ECR 権限がない | AmazonECSTaskExecutionRolePolicy をアタッチ |
| ECS と EKS のどちらを選ぶか分からない | 選択肢が多い | Kubernetes YAML 資産があれば EKS、独自 API で良ければ ECS |
| イメージスキャンが動いていない | ECR で手動有効化が必要 | リポジトリ作成時に --image-scanning-configuration scanOnPush=true
|
移行実装時のチェックリスト
□ サブネット CIDR:Fargate 使用時は /20 以上を推奨(IP 枯渇対策)
□ ECS タスク定義:リビジョン管理を Git でバージョン管理
□ ECS サービス:minimumHealthyPercent と maximumPercent を適切に設定
□ ECS デプロイ:deploymentCircuitBreaker を有効化(自動ロールバック)
□ ECR:イメージスキャンを有効化(scanOnPush=true)
□ EKS ログ:Fluent Bit DaemonSet + IRSA を設定
□ EKS コントロールプレーンログ:有効化(api, audit, authenticator など)
□ ヘルスチェック:ALB/NLB のヘルスチェックパスとアプリのエンドポイントを一致
□ IAM ロール:タスク実行ロールに AmazonECSTaskExecutionRolePolicy をアタッチ
□ CloudWatch Logs:Log Group の保存期間を設定(コスト最適化)
さいごに
AWS のコンテナサービスは Azure のような「シンプルな選択肢」ではなく、「多様なサービスから適切なものを選び、明示的に設定する」アプローチです。ECS、EKS、Fargate それぞれに特性があり、適材適所での使い分けが求められます。
Azure では「AKS を使えば大体 OK」という快適さがありましたが、AWS では「どのサービスをどう組み合わせるか」を理解する必要があります。しかし、柔軟性が高い分、細かい制御やコスト最適化は AWS の方が有利な場面もあります。
本記事の優先度順に切り分ければ、落ち着いて障害対応できるのかなー、と思いました。
補足:AWS マネジメントコンソールでの基本操作
CLI に慣れていない場合、AWS マネジメントコンソール(ポータル)での操作が便利です。以下は障害対応で頻繁に使う画面の場所と確認方法です。
ECS の基本操作
1. クラスターの作成
1. AWS マネジメントコンソールで「ECS」を検索
2. 左メニューから「Clusters」を選択
3. 「Create cluster」をクリック
4. Cluster name を入力(例:production-cluster)
5. Infrastructure で「AWS Fargate (serverless)」を選択
- EC2 インスタンス管理不要
6. Monitoring で「Use Container Insights」をチェック(推奨)
7. 「Create」をクリック
2. タスク定義の作成
1. ECS の左メニューから「Task definitions」を選択
2. 「Create new task definition」をクリック
3. Task definition family を入力(例:my-app)
4. Infrastructure requirements:
- Launch type: AWS Fargate
- OS: Linux/X86_64
- CPU: 0.5 vCPU(タスクサイズ)
- Memory: 1 GB
5. Container - 1 の設定:
- Name: my-app-container
- Image URI: 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
- Port mappings: 8080 (Container port)
6. Environment variables(オプション):
- Key: DATABASE_URL, Value: xxx
7. Logging:
- Log driver: awslogs
- Log group: /ecs/my-app(自動作成)
8. 「Create」をクリック
3. サービスの作成(継続実行)
1. クラスターの詳細画面を開く
2. 「Services」タブで「Create」をクリック
3. Deployment configuration:
- Task definition: 先ほど作成したもの
- Service name: my-app-service
- Desired tasks: 5(起動するタスク数)
4. Networking:
- VPC: 既存の VPC を選択
- Subnets: パブリックサブネット(または NAT 経由のプライベート)
- Security group: HTTP/HTTPS を許可
5. Load balancing(オプション):
- Load balancer type: Application Load Balancer
- 既存の ALB を選択または新規作成
- Target group: 新規作成
- Health check path: /health
6. Service auto scaling(オプション):
- Minimum tasks: 5
- Maximum tasks: 20
- Target value: CPU 70%(CPU 使用率 70% で自動スケール)
7. 「Create」をクリック
4. タスクの状態確認
1. クラスター詳細画面 → 「Services」タブ
2. サービス名をクリック
3. 「Tasks」タブで実行中のタスク一覧を確認
4. タスクをクリックして詳細を確認:
- Last status: RUNNING / STOPPED
- Desired status: RUNNING
- Stopped reason(停止時): エラー原因が表示
5. 「Logs」タブでコンテナログを確認
- CloudWatch Logs へのリンク
5. タスクの停止理由確認(障害時)
1. クラスター詳細画面 → 「Services」タブ
2. サービス名をクリック
3. 「Events」タブで最近のイベントを確認
例:
- "service my-app-service was unable to place a task"
- "ResourceInitializationError: unable to pull secrets"
4. 「Tasks」タブで「Stopped」をフィルター
5. 停止したタスクをクリック
6. 「Stopped reason」を確認
例:
- "Essential container in task exited"
- "Task failed ELB health checks"
EKS の基本操作
1. クラスターの作成
1. AWS マネジメントコンソールで「EKS」を検索
2. 「Add cluster」→「Create」をクリック
3. Cluster configuration:
- Name: production-eks
- Kubernetes version: 1.28(最新推奨)
4. Cluster service role:
- 「Create new role」で自動作成(初回のみ)
5. 「Next」をクリック
6. Networking:
- VPC: 既存の VPC を選択
- Subnets: 複数の AZ にまたがるサブネットを選択
- Security groups: デフォルトまたは新規作成
7. Cluster endpoint access: Public and private(推奨)
8. 「Next」→「Next」→「Create」をクリック
→ 作成に 10~15 分かかる
2. ノードグループの追加
1. クラスター詳細画面 → 「Compute」タブ
2. 「Add node group」をクリック
3. Node group configuration:
- Name: production-nodes
- Node IAM role: 新規作成または既存を選択
4. 「Next」をクリック
5. Node group compute configuration:
- AMI type: Amazon Linux 2(推奨)
- Instance types: t3.medium(開発), m5.large(本番)
- Disk size: 20 GB
6. Node group scaling configuration:
- Desired size: 3
- Minimum size: 3
- Maximum size: 10
7. 「Next」→「Next」→「Create」をクリック
3. kubectl の設定
1. AWS CloudShell を開く(コンソール右上のアイコン)
2. 以下のコマンドを実行:
aws eks update-kubeconfig --name production-eks --region us-east-1
3. kubectl が使えることを確認:
kubectl get nodes
→ ノード一覧が表示される
4. ログの確認(Fluent Bit 設定後)
1. CloudWatch の「Log groups」を開く
2. /aws/containerinsights/production-eks/application を選択
3. 最新の Log stream を開く
4. アプリケーションログが表示される
5. エラーを検索(Ctrl+F で「ERROR」)
5. Pod の状態確認(CloudShell 経由)
1. AWS CloudShell を開く
2. kubectl でリソースを確認:
# Pod 一覧
kubectl get pods -n default
# Pod の詳細(エラー時)
kubectl describe pod <pod-name> -n default
# Pod のログ
kubectl logs <pod-name> -n default
# リソース使用状況
kubectl top pods -n default
ECR の基本操作
1. リポジトリの作成
1. AWS マネジメントコンソールで「ECR」を検索
2. 「Repositories」を選択
3. 「Create repository」をクリック
4. Repository name を入力(例:my-app)
5. Image scan settings:
- 「Scan on push」をチェック(脆弱性スキャン)
6. Encryption settings: AWS managed key(デフォルト)
7. 「Create repository」をクリック
2. イメージのプッシュ
1. リポジトリ詳細画面を開く
2. 右上の「View push commands」をクリック
3. 表示されたコマンドをローカルで実行:
# ログイン
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
# イメージのビルド
docker build -t my-app .
# タグ付け
docker tag my-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
# プッシュ
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
3. 脆弱性スキャン結果の確認
1. リポジトリの詳細画面を開く
2. 「Images」タブでイメージ一覧を確認
3. Scan status 列を確認:
- COMPLETE: スキャン完了
- FAILED: スキャン失敗
4. イメージをクリック → 「Vulnerabilities」タブ
5. 検出された脆弱性が重大度別に表示:
- CRITICAL: 緊急対応が必要
- HIGH: 優先度高
- MEDIUM / LOW: 計画的に対応
サブネット IP 枯渇の確認
1. VPC サブネットの利用状況
1. AWS マネジメントコンソールで「VPC」を検索
2. 左メニューから「Subnets」を選択
3. ECS タスクが起動しているサブネットをクリック
4. 「Details」タブで以下を確認:
- IPv4 CIDR: 10.0.1.0/24(256 個のアドレス)
- Available IPv4 addresses: 0 ← IP 枯渇!
5. IP 枯渇の場合は以下の対応:
- より大きな CIDR のサブネットを新規作成(/20 推奨)
- 複数サブネットに分散
2. 新しいサブネットの作成
1. VPC の「Subnets」画面で「Create subnet」をクリック
2. VPC ID: 既存の VPC を選択
3. Subnet settings:
- Subnet name: ecs-subnet-large
- Availability Zone: us-east-1a
- IPv4 CIDR block: 10.0.16.0/20(4096 個のアドレス)
4. 「Create subnet」をクリック
5. ECS サービスの設定で新しいサブネットを使用
画面操作のチートシート
| 確認したいこと | 開く画面 | 見るべき項目 |
|---|---|---|
| ECS タスクの停止理由 | ECS → Clusters → Services → Tasks | Stopped reason |
| ECS サービスのイベント | ECS → Clusters → Services → Events | 最近のエラーメッセージ |
| ECS タスクのログ | ECS → Task 詳細 → Logs タブ | CloudWatch Logs へのリンク |
| ECR イメージの脆弱性 | ECR → Repositories → Images | Vulnerabilities タブ |
| サブネット IP 枯渇 | VPC → Subnets → 対象サブネット | Available IPv4 addresses |
| EKS Pod の状態 | CloudShell で kubectl get pods | STATUS 列(Running/Error) |
| EKS ログ | CloudWatch → /aws/containerinsights/xxx | 最新の Log stream |
| ALB ヘルスチェック | EC2 → Target Groups → Targets タブ | Health status |
よく使う kubectl コマンド(EKS)
1. リソースの確認
# Pod 一覧
kubectl get pods -n default
# Deployment 一覧
kubectl get deployments -n default
# Service 一覧
kubectl get services -n default
# すべてのリソース
kubectl get all -n default
2. 障害調査
# Pod の詳細(エラー原因を確認)
kubectl describe pod <pod-name> -n default
# Pod のログ
kubectl logs <pod-name> -n default
# 前回のコンテナのログ(再起動後)
kubectl logs <pod-name> -n default --previous
# リソース使用状況
kubectl top pods -n default
kubectl top nodes
3. デバッグ
# Pod 内でコマンド実行
kubectl exec -it <pod-name> -n default -- /bin/bash
# ポートフォワード(ローカルからアクセス)
kubectl port-forward <pod-name> 8080:8080 -n default
# イベント確認
kubectl get events -n default --sort-by='.lastTimestamp'
注釈:本記事で出現する主要な専門用語
ECS (Amazon Elastic Container Service)
- AWS の独自コンテナオーケストレーションサービス。Kubernetes ではなく、AWS 独自の API とタスク定義を使用。AKS のような Kubernetes YAML は使えない。
EKS (Amazon Elastic Kubernetes Service)
- AWS のマネージド Kubernetes サービス。AKS に相当。標準の Kubernetes API が使えるが、ログ収集などは手動設定が必要。
Fargate
- サーバーレスなコンテナ実行環境。EC2 インスタンスの管理不要だが、1 タスク = 1 ENI = 1 IP 消費という特性がある。ACI(Azure Container Instances)に近い。
タスク定義 (Task Definition)
- ECS でコンテナの設定を定義する JSON ファイル。Docker イメージ、CPU/メモリ、環境変数などを記述。Kubernetes の Deployment YAML に相当するが、リビジョン管理が別概念。
ENI (Elastic Network Interface)
- VPC 内の仮想ネットワークインターフェース。Fargate では各タスクに 1 つの ENI が割り当てられ、VPC の IP アドレスを消費する。AKS の Pod IP とは異なり、サブネットの IP を直接使用。
リビジョン (Revision)
- ECS タスク定義のバージョン番号。タスク定義を更新するたびに新しいリビジョンが作成される。サービスは特定のリビジョンを指定して実行する。
minimumHealthyPercent / maximumPercent
- ECS のデプロイ設定。minimumHealthyPercent は「デプロイ中も維持する必要がある最小タスク数の割合」、maximumPercent は「デプロイ中に起動できる最大タスク数の割合」。
Fluent Bit
- 軽量なログ転送エージェント。EKS ではアプリケーションログを CloudWatch Logs に送信するために DaemonSet としてデプロイする必要がある。AKS では Azure Monitor エージェントが標準で統合されている。
IRSA (IAM Roles for Service Accounts)
- EKS で Kubernetes Service Account に IAM ロールを紐付ける仕組み。Fluent Bit などが CloudWatch Logs に書き込むために必要。
ECR (Amazon Elastic Container Registry)
- AWS のコンテナイメージレジストリ。Azure Container Registry (ACR) に相当。イメージスキャンは手動で有効化する必要がある。
AKS (Azure Kubernetes Service)
- Azure のマネージド Kubernetes サービス。コントロールプレーンは無料で、Azure Monitor との統合が標準。EKS に相当。
ACI (Azure Container Instances)
- Azure のサーバーレスコンテナ実行サービス。Fargate に近いが、IP アドレス消費の問題は少ない。
Azure Monitor Container Insights
- AKS のログとメトリクスを自動的に収集する Azure Monitor の機能。標準で有効化されており、追加設定不要。EKS では Fluent Bit を手動デプロイする必要がある。
デプロイサーキットブレーカー (Deployment Circuit Breaker)
- ECS の自動ロールバック機能。新しいタスクのヘルスチェックが失敗し続けると、自動的に前のリビジョンにロールバックする。
ヘルスチェック (Health Check)
- ALB/NLB がターゲット(タスク/Pod)の健全性を確認する仕組み。ヘルスチェックが失敗すると、ロードバランサーはトラフィックを送信しない。
ローリングアップデート (Rolling Update)
- 新しいバージョンを段階的にデプロイする方式。AKS では自動的に古い Pod を削除するが、ECS では設定に依存。
サブネット CIDR
- VPC のサブネットに割り当てられる IP アドレス範囲。/24 は 256 個、/20 は 4096 個の IP アドレスを提供。Fargate では IP 消費が多いため、大きな CIDR が推奨される。