0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS コンテナサービス:ACA/AKSユーザーが陥る罠と趣味開発での問題切り分けメモ

Posted at

はじめに: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]'

対応案

  • ヘルスチェックパスとアプリケーションのエンドポイントを一致させる
  • minimumHealthyPercentmaximumPercent を見直す
  • デプロイサーキットブレーカーを有効化(自動ロールバック)

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 が推奨される。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?