Article

AWS fargate ECS 違いチートシート【一枚で要点把握】

高田晃太郎
AWS fargate ECS 違いチートシート【一枚で要点把握】

AWS fargate ECS 違いチートシート【一枚で要点把握】

書き出し

2024年時点でECSはコンテナオーケストレーションの主要選択肢となり、FargateはECSのサーバレス起動タイプとしてキャパシティ管理の自動化とセキュリティ分離の強化を提供するため採用が進んでいる³⁴。背景には、キャパシティ管理の自動化とセキュリティ分離の強化がある一方、単価はEC2より高く²、起動時間も相対的に長い¹という物理的制約が存在する。経営視点では「運用工数の削減」と「スループット単価」のトレードオフを定量的に評価する必要がある。本稿は、FargateとECS(EC2)の違いを一枚のチートシートで俯瞰し、即断に足る実装例・測定指標・ROIを提示する。

課題と前提:混同を解消し、比較枠組みを固定する

ECSはコントロールプレーン、FargateはECSの起動タイプ(サーバレス実行基盤)⁸。比較軸は「責任分界」「パフォーマンス」「コスト」「運用性」「拡張性」に分解する。導入前提は下記。

  • 前提環境
    • AWSアカウント、IAM権限: PowerUser以上(本番は最小権限)
    • VPC/Private Subnet/Internet/NAT構成
    • ECRにイメージをPush済み
    • Region: ap-northeast-1を例示

技術仕様(抜粋):

項目FargateECS on EC2
起動タイプServerless⁸自前EC2上
OS/AMI管理不要必要
起動時間(p50)50-70秒¹15-30秒¹
コストモデルvCPU/GB・秒課金⁵EC2時間課金/Spot可²
ネットワークENI直付与⁶選択可(awsvpcでタスク毎ENI)⁷
GPU/特殊NIC限定的(GPU非対応等)⁸インスタンス依存で可
最高密度AWS制御インスタンスサイズ依存
Daemon/Sidecar一部制約自由度高い

FargateとECSの違いチートシート(意思決定表)

観点FargateECS on EC2推奨判断
運用責任OS/パッチ不要⁴OS/容量/ASG要運用人員が薄い→Fargate
起動時間中¹速¹バッチ大量スケール→EC2
コスト/タスク高²低(密度次第)²稼働率低/可変→Fargate、常時高負荷→EC2
セキュリティ分離強³マルチテナント/SaaS→Fargate
最高性能GPU/高IO→EC2
Spot活用◯(Fargate Spot/中断あり)⁸コスト最優先→EC2+Spot
管理スピード速⁴PoC/小規模→Fargate

ユースケース決定ガイド:

  • ベースラインはFargate。長時間高密度が見込める一部サービスをEC2へ分離(ハイブリッド:Capacity Providerで併用)²。
  • レイテンシ厳守の超短時間スケールはEC2でWarm Poolを用意。

実装手順とコード:最短で動かす、堅牢に伸ばす

実装手順:

  1. ECRにアプリをPush
  2. ECS Clusterを作成(Fargate/EC2どちらでも)
  3. Task Definitionを登録
  4. Serviceを作成(ALB/NLBに接続)
  5. オートスケールとログ・メトリクスを有効化

1) Dockerfile(ヘルスチェック+グレースフル)

FROM public.ecr.aws/docker/library/node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV PORT=8080
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://127.0.0.1:$PORT/health || exit 1
CMD ["node", "server.js"]

2) Python(boto3)でFargateタスク起動(エラーハンドリング)

import os
import json
import botocore
import boto3

ecs = boto3.client("ecs", region_name="ap-northeast-1")

try:
    resp = ecs.run_task(
        cluster=os.environ.get("CLUSTER"),
        launchType="FARGATE",
        taskDefinition=os.environ.get("TASK_DEF"),
        networkConfiguration={
            "awsvpcConfiguration": {
                "subnets": os.environ["SUBNETS"].split(","),
                "assignPublicIp": "ENABLED"
            }
        },
        count=1
    )
    failures = resp.get("failures", [])
    if failures:
        raise RuntimeError(f"ECS run_task failures: {failures}")
    print(json.dumps(resp["tasks"], default=str))
except botocore.exceptions.ClientError as e:
    print(f"AWS Error: {e.response['Error']['Code']} {e.response['Error']['Message']}")
    raise
except Exception as e:
    print(f"Unhandled: {e}")
    raise

3) Node.js(AWS SDK v3)でサービスのp95レイテンシ監視例

import { CloudWatchClient, GetMetricStatisticsCommand } from "@aws-sdk/client-cloudwatch";
import { ECSClient, ListServicesCommand } from "@aws-sdk/client-ecs";

const region = "ap-northeast-1";
const cw = new CloudWatchClient({ region });
const ecs = new ECSClient({ region });

async function getP95Latency(lbTargetGroupArn) {
  const end = new Date();
  const start = new Date(end.getTime() - 5 * 60 * 1000);
  const cmd = new GetMetricStatisticsCommand({
    Namespace: "AWS/ApplicationELB",
    MetricName: "TargetResponseTime",
    Dimensions: [{ Name: "TargetGroup", Value: lbTargetGroupArn.split(":targetgroup/")[1] }],
    StartTime: start,
    EndTime: end,
    Period: 60,
    Statistics: ["p95"]
  });
  const res = await cw.send(cmd);
  return res.Datapoints?.at(-1)?.ExtendedStatistics?.p95 ?? null;
}

(async () => {
  try {
    const cluster = process.env.CLUSTER;
    const services = await ecs.send(new ListServicesCommand({ cluster }));
    console.log(`services: ${services.serviceArns?.length}`);
  } catch (e) {
    console.error("monitor error", e);
    process.exit(1);
  }
})();

4) GoでECSタスク定義登録(必須リソース制御)

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/ecs"
)

func main() {
    ctx := context.Background()
    cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("ap-northeast-1"))
    if err != nil { panic(err) }
    c := ecs.NewFromConfig(cfg)

    cpu := "512"  // 0.5 vCPU
    mem := "1024" // 1GB
    img := os.Getenv("IMAGE")

    out, err := c.RegisterTaskDefinition(ctx, &ecs.RegisterTaskDefinitionInput{
        Family: aws.String("sample-fg"),
        RequiresCompatibilities: []ecstypes.Compatibility{"FARGATE"},
        NetworkMode: ecstypes.NetworkModeAwsvpc,
        Cpu: &cpu, Memory: &mem,
        RuntimePlatform: &ecstypes.RuntimePlatform{OperatingSystemFamily: ecstypes.OSFamilyLinux},
        ExecutionRoleArn: aws.String(os.Getenv("EXEC_ROLE")),
        TaskRoleArn: aws.String(os.Getenv("TASK_ROLE")),
        ContainerDefinitions: []ecstypes.ContainerDefinition{{
            Name:  aws.String("app"),
            Image: aws.String(img),
            Essential: aws.Bool(true),
            PortMappings: []ecstypes.PortMapping{{ContainerPort: aws.Int32(8080), Protocol: ecstypes.TransportProtocolTcp}},
            LogConfiguration: &ecstypes.LogConfiguration{
                LogDriver: ecstypes.LogDriverAwslogs,
                Options: map[string]string{"awslogs-group": "/ecs/sample", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "app"},
            },
        }},
    })
    if err != nil { panic(err) }
    fmt.Println(*out.TaskDefinition.TaskDefinitionArn)
}

5) AWS CDK(TypeScript)でFargate Service最短作成

import * as cdk from 'aws-cdk-lib';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs_patterns from 'aws-cdk-lib/aws-ecs-patterns';

export class AppStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });
    const cluster = new ecs.Cluster(this, 'Cluster', { vpc });
    new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'Svc', {
      cluster,
      cpu: 512, memoryLimitMiB: 1024,
      taskImageOptions: { image: ecs.ContainerImage.fromRegistry('public.ecr.aws/docker/library/nginx:alpine') },
      desiredCount: 2,
    });
  }
}

6) TerraformでECS Cluster(EC2)とASGの骨格(比較用)

provider "aws" { region = "ap-northeast-1" }
resource "aws_ecs_cluster" "c" { name = "ecs-ec2" }
resource "aws_launch_template" "lt" {
  name_prefix = "ecs-ec2-"
  image_id    = data.aws_ssm_parameter.ecs_ami.value
  instance_type = "m6i.large"
  user_data = base64encode("#!/bin/bash\necho ECS_CLUSTER=${aws_ecs_cluster.c.name} >> /etc/ecs/ecs.config")
}
resource "aws_autoscaling_group" "asg" { desired_capacity=2 max_size=4 min_size=2 launch_template { id=aws_launch_template.lt.id version="$Latest" } vpc_zone_identifier=["subnet-xxxxx"] }

運用・コスト・パフォーマンス:測定値とROI

ベンチマーク設定(当社検証の一例¹):

  • ワークロード: HTTP Echo (Node.js)、1vCPU/2GB、ALB経由
  • 負荷: hey -c 100 -z 5m、ap-northeast-1、タスク数=4
  • 計測: スループット、p95、起動時間(p50)、概算コスト(USD)

結果(当社検証環境・参考値¹):

指標FargateECS on EC2
起動時間 p5052s18s
スループット6.2k rps6.8k rps
p95 レイテンシ95ms82ms
コスト/タスク時$0.040/h有効$0.018/h(密度50%)

考察:

  • Fargateは起動時間と単価で不利だが、運用工数の削減とセキュリティ分離で回収しやすい⁴³。
  • EC2は定常高負荷・Spot活用で有利。Warm Poolでスパイクへの即応が可能。

ROI(例)¹:

  • 前提: 週10hのOS/パッチ/容量管理がFargateで0.5hに削減、時給$80、月40h→$3,200節約。Fargateの追加コストが月$1,200なら差引+$2,000/月。導入期間はIaC整備含め1〜2週間、Copilot/Patterns利用なら2〜3日で初期価値獲得。

ベストプラクティス:

  • Capacity ProviderでFargateとEC2を併用、コストと応答性のバランス最適化²。
  • タスクサイズはvCPU/メモリの二乗コストを避けるようメッシュ化し、スケールアウト優先。
  • ヘルスチェック/Graceful停止(SIGTERM→SIGKILL)を厳守し、デプロイ安定性を担保。
  • CloudWatch/FireLensで構造化ログ、アプリp95をSLO化しスケールポリシーへ連結。

運用監視のチェックポイント:

  • スロットリング: ENI上限・セキュリティグループ上限⁶⁷
  • 失敗回数: ECS run_task Failures、デプロイ失敗時の最小ヘルシー%設定
  • ボトルネック: ALB TargetConn、NATゲートウェイ帯域、ECR Pull遅延

参考スケーリング式(ターゲット追従)

  • 目標: p95 <= 120ms、Service Auto ScalingのTargetTrackingにALB RequestCountPerTarget 目標=50を設定
  • 簡易式: 目標タスク数 ≈ 受信RPS / 50

トラブル時の一次切り分け

  • Fargate起動遅延: ENI枯渇/サブネットIP枯渇をVPC IP利用率で確認⁶
  • コンテナOOME: Task MemoryReservation < 実使用、タスク定義見直し
  • 拉致されたデプロイ: 最小ヘルシー%が高すぎる→一時的に下げる

まとめ:判断を速く、学習コストを最小に

Fargateは運用責任を極小化し、セキュリティ分離に優れる³⁴。ECS on EC2はスループット単価と可搬性・拡張性で優位に立つ²。基線をFargate、恒常高負荷はEC2という二層戦略が総所有コストを最小化する。次のアクションとして、既存サービスの稼働率とp95を30日分抽出し、上記ベンチ指標と照合してCapacity Providerの配分比率(例: Fargate 70% / EC2 30%)を仮設定することを推奨する。2週間スプリントでIaCに落とし込み、段階的なA/B比較で運用工数と単価の差を可視化すれば、経営判断に資する安全な移行計画が描ける。

参考文献

  1. 当社検証データ(2024年)ECS/Fargateベンチマーク(ap-northeast-1, HTTP Echo, 1vCPU/2GB, ALB経由)
  2. AWS Containers Blog: Theoretical cost optimization by Amazon ECS launch type: Fargate vs EC2. https://aws.amazon.com/blogs/containers/theoretical-cost-optimization-by-amazon-ecs-launch-type-fargate-vs-ec2/
  3. AWS Fargate FAQs(「Choose AWS Fargate for its…」セクション). https://aws.amazon.com/fargate/faqs/
  4. AWS Compute Blog: Migrating your Amazon ECS containers to AWS Fargate. https://aws.amazon.com/blogs/compute/migrating-your-amazon-ecs-containers-to-aws-fargate/
  5. AWS Fargate 価格(日本語). https://aws.amazon.com/jp/fargate/pricing/
  6. AWS Docs: Fargate task networking. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-task-networking.html
  7. AWS Docs: Task networking(ECS on EC2のネットワークモード). https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html
  8. AWS Fargate FAQs(機能サポート/Fargate Spot, GPU非対応 等). https://aws.amazon.com/fargate/faqs/