スタートアップのCTOとして、あるいは初期アーキテクチャを設計するTech Leadとして、「AWS ECS(Elastic Container Service)とEKS(Elastic Kubernetes Service)、どちらを選ぶべきか?」という判断に直面したことはありませんか?
私は経済産業大臣賞を受賞した木材流通SaaSの開発において、この意思決定をチーム2名、3ヶ月でのリリース必須という制約の中で行いました。結論としてECS on Fargateを採用し、現在まで安定運用を続けています。
この記事では、ECS vs EKS選定における7つの評価軸と、実際のコスト試算、Terraformコード例を公開します。単なる「どっちが良い?」論争ではなく、あなたのビジネス制約の中で最適な選択をするための意思決定フレームワークを提供します。
前提:なぜこの比較が重要なのか
スタートアップが直面する現実
- リソース制約: エンジニア1〜3名、予算月額10〜50万円
- 時間制約: 3〜6ヶ月でPMF(Product Market Fit)検証必須
- 技術的負債リスク: 初期選定ミスが2年後に致命傷になる可能性
よくある失敗パターン
- Kubernetes信仰による過剰投資: 「将来のスケールを見据えてEKS」→学習コストで開発停滞
- ECS軽視による機会損失: 「KubernetesがデファクトだからEKS一択」→シンプルな要件に過剰
- コスト試算の欠如: 「とりあえずECS」→予想外の課金でスケール時に破綻
7つの評価軸による定量比較
以下の表は、私が実際に使用した評価フレームワークです。各軸を★1〜5で評価し、あなたのプロジェクトの優先順位に応じて重み付けしてください。
| 評価軸 | ECS on Fargate | EKS | 重要度(例) |
|---|---|---|---|
| ①学習コスト | ★★★★★(低い) | ★★☆☆☆(高い) | ★★★★★ |
| ②運用コスト | ★★★★☆(低い) | ★★☆☆☆(高い) | ★★★★★ |
| ③スケーラビリティ | ★★★★☆(十分) | ★★★★★(最高) | ★★★☆☆ |
| ④チームスキル依存 | ★★★★☆(低い) | ★★☆☆☆(高い) | ★★★★★ |
| ⑤エコシステム | ★★★☆☆(AWS特化) | ★★★★★(K8s全般) | ★★☆☆☆ |
| ⑥ベンダーロックイン | ★★☆☆☆(高い) | ★★★★☆(低い) | ★☆☆☆☆ |
| ⑦将来性・拡張性 | ★★★☆☆(中程度) | ★★★★★(高い) | ★★★☆☆ |
私のプロジェクトでの優先順位
最重要: ①学習コスト、②運用コスト、④チームスキル依存 理由: チーム2名、3ヶ月リリース、Kubernetes未経験
結果: ECS on Fargateスコア 4.2/5.0 vs EKSスコア 2.8/5.0
評価軸の詳細解説
①学習コスト:「3ヶ月で本番運用できるか?」
ECS on Fargate: ★★★★★
- 学習時間: 1週間でタスク定義・サービス・クラスター概念を理解可能
- 必須知識: Docker、AWS基礎(VPC/IAM/CloudWatch)のみ
- 罠が少ない: シンプルなAPI、ドキュメント充実
EKS: ★★☆☆☆
- 学習時間: 最低1ヶ月(Pod/Service/Ingress/Deployment/ConfigMap/Secret...)
- 必須知識: Docker + Kubernetes概念 + kubectl + Helm + ネットワーク深い理解
- 罠が多い: RBAC、ネットワークポリシー、ストレージクラス等の複雑性
実体験:私のチームでの学習曲線
ECS習得タイムライン:
Day 1-2: Dockerコンテナ化
Day 3-4: Terraformでタスク定義作成
Day 5-7: ALB統合、デプロイ自動化完了
EKS(別プロジェクトで経験):
Week 1-2: Kubernetes基礎学習(Udemy/公式ドキュメント)
Week 3-4: EKS構築、ネットワーク設定で苦戦
Week 5-6: Ingress/Cert-Manager設定、デバッグ地獄
Week 7-8: ようやく本番投入...
②運用コスト:「月額費用の現実」
具体的なコスト試算(月間10万PV、APIサーバー想定)
ECS on Fargate構成:
- Fargate Task (0.5 vCPU, 1GB RAM) x 2台 24時間稼働
→ $35/月
- ALB (Application Load Balancer)
→ $23/月 + データ転送料 $10/月
- CloudWatch Logs (5GB/月)
→ $2.5/月
- RDS (db.t3.small)
→ $30/月
合計: 約 $100/月(約14,000円)
EKS構成:
- EKSコントロールプレーン
→ $73/月(固定費)
- EC2ワーカーノード (t3.medium x 2台)
→ $60/月
- ALB
→ $23/月 + データ転送料 $10/月
- CloudWatch Logs
→ $2.5/月
- RDS (db.t3.small)
→ $30/月
合計: 約 $200/月(約28,000円)
コスト差: 月額約2倍(EKSが高い)
隠れコスト:人件費
- ECS: 運用・トラブルシューティング 月5時間
- EKS: 運用・トラブルシューティング 月20時間 + 学習コスト
エンジニア単価を月額80万円と仮定:
- ECS人件費: 80万円 ÷ 160時間 × 5時間 = 2.5万円/月
- EKS人件費: 80万円 ÷ 160時間 × 20時間 = 10万円/月
真のコスト差: ECS 16.5万円/月 vs EKS 38万円/月 = 約2.3倍
③スケーラビリティ:「どこまで成長できるか?」
ECS on Fargate: ★★★★☆
- 対応可能規模: 月間1,000万PV程度まで十分
- オートスケーリング: Application Auto Scalingで容易
- 制約: 超大規模(数千Pod)では非効率
EKS: ★★★★★
- 対応可能規模: 数千〜数万Pod、Google/Netflix級
- 高度な制御: HPA/VPA/Cluster Autoscaler
- 柔軟性: カスタムコントローラー、Operator
現実的な判断基準
ECS on Fargateで十分なケース:
- 月間PV: 〜1,000万
- マイクロサービス数: 〜20個
- 同時接続数: 〜10,000
EKSが必要になるケース:
- 月間PV: 5,000万以上
- マイクロサービス数: 50個以上
- 複雑なサービスメッシュ(Istio等)必須
- マルチクラウド戦略
私の経験: 経済産業大臣賞受賞SaaSは月間50万PV程度で、ECSで全く問題なし。
④チームスキル依存:「誰がメンテできるか?」
ECS on Fargate: ★★★★☆
- 必要なスキルレベル: AWS初級〜中級
- 採用のしやすさ: Docker経験者なら1週間でキャッチアップ可能
- 属人化リスク: 低い(ドキュメント豊富)
EKS: ★★☆☆☆
- 必要なスキルレベル: Kubernetes中級以上(CKA相当)
- 採用のしやすさ: K8s経験者は市場に少なく高額
- 属人化リスク: 高い(設定が複雑で引き継ぎ困難)
スタートアップの現実
シナリオ: コア開発メンバーが退職した場合
- ECS: 新メンバーが1週間でキャッチアップ → リスク低
- EKS: 新メンバーが1ヶ月でもキャッチアップ困難 → リスク高
⑤エコシステム:「周辺ツールの充実度」
ECS: ★★★☆☆
- 強み: AWS統合(CloudWatch/X-Ray/Secrets Manager完璧)
- 弱み: Kubernetes標準ツール(Helm/Kustomize)使用不可
EKS: ★★★★★
- 強み: K8sエコシステム全て(Prometheus/Grafana/ArgoCD/Istio)
- 弱み: AWS統合は一部手動設定必要
⑥ベンダーロックイン:「将来の移行コスト」
ECS: ★★☆☆☆(ロックイン高い)
- AWS外への移行は実質的に全再構築
EKS: ★★★★☆(ロックイン低い)
- GKE/AKSへの移行は比較的容易(YAMLの互換性高い)
現実的な判断
問い: 「5年以内にAWSから移行する可能性は?」
- 可能性低い(<10%): ロックインリスク無視してOK → ECS選択肢
- 可能性高い(>30%): マルチクラウド前提 → EKS
私の場合、AWS移行可能性は5%以下と判断し、ECS採用。
⑦将来性・拡張性:「2年後も使い続けられるか?」
ECS: ★★★☆☆
- AWS Copilot、App Runnerなど新機能追加あり
- サーバーレス方向への進化
EKS: ★★★★★
- Kubernetesがデファクトスタンダード
- CNCFエコシステムの継続的進化
実装例:Terraform コードでの比較
ECS on Fargate構成(シンプル)
# VPC、セキュリティグループ等は省略
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = "my-startup-cluster"
}
# Task Definition
resource "aws_ecs_task_definition" "app" {
family = "my-app"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "512" # 0.5 vCPU
memory = "1024" # 1GB
execution_role_arn = aws_iam_role.ecs_execution_role.arn
task_role_arn = aws_iam_role.ecs_task_role.arn
container_definitions = jsonencode([{
name = "app"
image = "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:latest"
portMappings = [{
containerPort = 8080
protocol = "tcp"
}]
environment = [
{ name = "ENV", value = "production" }
]
secrets = [
{
name = "DATABASE_URL"
valueFrom = aws_secretsmanager_secret.db_url.arn
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/my-app"
"awslogs-region" = "ap-northeast-1"
"awslogs-stream-prefix" = "ecs"
}
}
}])
}
# ECS Service
resource "aws_ecs_service" "app" {
name = "my-app-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.ecs_tasks.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = "app"
container_port = 8080
}
depends_on = [aws_lb_listener.app]
}
# Auto Scaling
resource "aws_appautoscaling_target" "ecs" {
max_capacity = 10
min_capacity = 2
resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.app.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
resource "aws_appautoscaling_policy" "ecs_cpu" {
name = "cpu-autoscaling"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs.resource_id
scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs.service_namespace
target_tracking_scaling_policy_configuration {
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
target_value = 70.0
}
}
コード量: 約80行 理解難易度: ★★☆☆☆
EKS構成(複雑)
# EKS Cluster
resource "aws_eks_cluster" "main" {
name = "my-startup-cluster"
role_arn = aws_iam_role.eks_cluster.arn
version = "1.28"
vpc_config {
subnet_ids = concat(var.private_subnet_ids, var.public_subnet_ids)
endpoint_private_access = true
endpoint_public_access = true
}
depends_on = [
aws_iam_role_policy_attachment.eks_cluster_policy,
aws_iam_role_policy_attachment.eks_vpc_resource_controller,
]
}
# Node Group
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "main-node-group"
node_role_arn = aws_iam_role.eks_nodes.arn
subnet_ids = var.private_subnet_ids
scaling_config {
desired_size = 2
max_size = 10
min_size = 2
}
instance_types = ["t3.medium"]
depends_on = [
aws_iam_role_policy_attachment.eks_worker_node_policy,
aws_iam_role_policy_attachment.eks_cni_policy,
aws_iam_role_policy_attachment.eks_container_registry_policy,
]
}
# IAM Roles(省略:ECS以上に複雑)
# ...
# OIDC Provider(EKSで必須)
data "tls_certificate" "eks" {
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
resource "aws_iam_openid_connect_provider" "eks" {
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [data.tls_certificate.eks.certificates[0].sha1_fingerprint]
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
# AWS Load Balancer Controller(Helmで別途インストール必要)
# Ingress設定(別途K8s YAMLで管理)
# ...
その後、Kubernetesマニフェスト(Deployment/Service/Ingress)を別途作成:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:latest
ports:
- containerPort: 8080
env:
- name: ENV
value: production
# ...以下50行以上の設定
---
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
# ...以下複雑な設定
コード量: Terraform 150行 + Kubernetes YAML 100行以上 理解難易度: ★★★★★
移行シナリオ:「いつEKSに移行すべきか?」
ECS → EKS 移行が必要になるシグナル
- マイクロサービス数が30個以上: ECSの管理が煩雑化
- 複雑なサービスメッシュ必要: Istio/Linkerd等のK8sエコシステム活用
- マルチクラウド戦略: GCP/Azure展開も視野
- カスタムオーケストレーション: Operator/CRD開発が必要
移行コストの現実
- 期間: 2〜6ヶ月(規模による)
- コスト: エンジニア2名 × 3ヶ月 = 約500万円
- リスク: ダウンタイム、設定ミス、パフォーマンス劣化
私の推奨戦略
フェーズ1(0〜2年目): ECS on Fargateで高速成長 フェーズ2(2〜3年目): スケール状況を評価、必要ならEKS移行検討 フェーズ3(3年目以降): 事業成長に応じて最適化
意思決定チェックリスト
ECS on Fargateを選ぶべき条件(3つ以上該当)
- チームのK8s経験者が0〜1名
- 3〜6ヶ月以内のリリース必須
- マイクロサービス数が20個以下
- 月間PV 1,000万以下
- AWS移行可能性が低い(<10%)
- 運用コストを最小化したい
- シンプルさを重視
EKSを選ぶべき条件(3つ以上該当)
- チームのK8s経験者が2名以上
- 時間的余裕がある(6ヶ月以上)
- マイクロサービス数が30個以上
- 月間PV 5,000万以上を想定
- マルチクラウド戦略あり
- K8sエコシステム活用必須(Istio等)
- 将来的な拡張性を最優先
実プロダクトでの結果:経済産業大臣賞受賞SaaSの事例
採用構成
- 選択: ECS on Fargate
- 理由: チーム2名、3ヶ月リリース、K8s未経験
- 規模: マイクロサービス5個、月間50万PV
成果
- 開発期間: 3ヶ月でリリース成功
- 運用コスト: 月額12万円(インフラ全体)
- 可用性: 99.9%以上(年間ダウンタイム < 9時間)
- スケール対応: オートスケーリングで急激なトラフィック増にも自動対応
2年後の評価
- ECS選択は正解: 現在もECSで十分、EKS移行不要
- 学習コスト削減: K8s学習時間を機能開発に投資できた
- 運用負荷: 週5時間程度の運用で安定稼働
まとめ:意思決定の本質
重要な3つの原則
- 「今」のビジネス制約を最優先: 「将来のスケール」は幻想かもしれない
- 学習コスト・運用コストは真のコスト: インフラ費用だけで判断しない
- 移行は可能: ECS → EKSは実行可能、後で判断しても遅くない
私の推奨
スタートアップ(シード〜シリーズA): ECS on Fargateを強く推奨
- 理由: 高速なリリース、低い運用負荷、十分なスケーラビリティ
中堅企業(複雑な要件): 個別評価
- K8s経験者がいる → EKS検討
- いない → ECSから開始
エンタープライズ: EKS
- 理由: 高度な制御、マルチクラウド、エコシステム活用
次のステップ
この記事で提供した7つの評価軸を使い、あなたのプロジェクトに最適な選択をしてください。
もし技術選定やアーキテクチャ設計でお悩みなら、お気軽にご相談ください。経済産業大臣賞受賞プロダクトの開発で培った実践知を、あなたのプロジェクトに活かします。
関連記事: