콘텐츠로 이동

ECS vs EC2

분류: Layer 3 - AWS 인프라 & 보안

EC2는 AWS에서 빌려 쓰는 가상 서버이고, ECS는 Docker 컨테이너를 자동으로 배포/관리해주는 컨테이너 오케스트레이션 서비스이다.

“우리 서비스가 어디서 돌아가는지”를 이해하는 핵심 질문이다. EC2 위에서 직접 서버를 관리하는 것과, ECS로 컨테이너를 관리하는 것은 운영 방식이 근본적으로 다르다.

2.5 ECS 등장 배경 — 수동 Docker 배포의 한계

섹션 제목: “2.5 ECS 등장 배경 — 수동 Docker 배포의 한계”

ECS는 re:Invent 2014에서 프리뷰로 발표돼 2015년 4월 9일 GA 됐다. 그 이전에 Docker를 프로덕션에 띄우는 표준 절차는 “EC2 인스턴스를 띄우고 SSH로 접속해 docker run을 직접 호출”이었고, 컨테이너 수가 늘어날수록 다음 한계가 드러났다.

  • 노드 장애 시 수동 복구: EC2가 죽으면 누군가 알람을 받고 docker run을 다시 친다. desired state를 사람이 머릿속에 들고 있는 구조라 인스턴스 30대를 넘기면 사실상 운영 불가
  • 클러스터 관리·서비스 디스커버리를 각 팀이 자체 구현: AWS 회고에 따르면 ECS 이전 고객들은 “their own cluster management solutions, deal with configuration management, and manage their containers and associated metadata”를 직접 운영해야 했다. etcd/Consul/HAProxy 같은 컴포넌트를 손수 조립한다는 뜻 (AWS - Celebrating 10 Years of Amazon ECS)
  • bin packing 비효율: 어떤 EC2에 어떤 컨테이너를 띄울지 사람이 결정하므로 평균 CPU 활용률이 10~20%대에 머무는 사례가 흔했음

ECS가 푼 메커니즘: 위 한계는 본문 5.5절에서 다루는 Desired State Reconciliation으로 일괄 해소된다. 운영자가 “Task 3개 유지”라고 선언만 하면 ECS Scheduler가 actual state를 주기적으로 검사해 desired state와 일치시킨다 — docker run을 명령형으로 부르는 mental model에서 “원하는 상태를 선언하면 시스템이 맞춘다”는 선언형으로 패러다임이 이동한 지점이다. 같은 시기에 Kubernetes(2014.6 오픈소스화)와 Docker Swarm(2015.11)도 같은 패턴을 채택했고, ECS는 이 모델을 AWS 매니지드 서비스로 이식한 것 (SFEIR - Kubernetes vs Docker Swarm vs ECS vs Nomad).

ECS 초기 모델에는 “오케스트레이션은 해주지만 EC2 클러스터 자체는 여전히 직접 관리”라는 한계가 남아 있었다. Fargate(2017 re:Invent 발표, 2018 GA)는 Task당 Firecracker 마이크로VM을 띄워 인스턴스 프로비저닝·OS 패치·Auto Scaling 그룹 운영을 AWS에 위임함으로써 그 한계를 한 단계 더 풀었고, 본문 3절의 Fargate 격리 모델이 이 해결의 기술적 구현이다.

AWS에서 빌려 쓰는 가상 서버(VM). 리눅스/윈도우 OS가 깔려 있고, SSH로 접속해서 직접 세팅한다.

Docker 컨테이너를 실행/관리해주는 서비스. “이 이미지를, 몇 개, 어떤 리소스로 실행해줘”라고 정의하면 ECS가 알아서 배포하고 관리한다.

비유로 이해하면: “EC2 vs ECS를 선택한다”는 것은 “핸들 vs 자동차를 선택한다”와 같다. EC2는 서버(자동차)이고, ECS는 그 서버를 운전하는 방식(핸들)이다. ECS + Fargate 조합에서는 자동차 자체도 AWS가 제공한다.

📖 더 보기: Amazon ECS vs Amazon EC2: Complete Comparison Guide — EC2와 ECS의 역할 차이, 비용 모델, 선택 기준을 실무 관점에서 정리

  1. Task Definition 등록: “어떤 이미지를 어떤 설정으로 실행할지” JSON으로 정의
  2. Service 생성: “이 Task를 N개 유지해줘”라고 ECS에 지시
  3. Scheduler 동작: ECS 스케줄러가 어떤 인프라(Fargate or EC2)에서 실행할지 결정
  4. Container Agent 실행: Fargate는 AWS가, EC2 모드는 EC2 인스턴스 위의 ECS Agent가 컨테이너를 시작
  5. Health Check 모니터링: ALB Health Check 또는 컨테이너 Health Check 결과에 따라 비정상 Task를 자동으로 교체

Fargate 격리 모델 — 보안과 성능

섹션 제목: “Fargate 격리 모델 — 보안과 성능”

Fargate는 각 Task가 고유한 마이크로VM(Firecracker 기술 기반) 위에서 실행된다. 즉, Task끼리 커널, CPU, 메모리를 공유하지 않는다. 이는 EC2 launch type(여러 컨테이너가 같은 EC2 인스턴스 커널을 공유)과 근본적으로 다른 격리 수준이다.

실무에서 의미하는 것:

  • Fargate는 다른 테넌트의 컨테이너로부터 격리가 강함 → 금융/의료 데이터 등 규정 준수 환경에 유리
  • 각 Task가 별도 ENI를 가짐 → Security Group을 Task 단위로 적용 가능
  • 단, 격리 오버헤드로 인해 동일 메모리 기준 EC2 launch type보다 약간 느릴 수 있음
  • EC2 launch type: EC2 인스턴스 위에 컨테이너를 올림. 서버를 직접 관리해야 하지만 비용 효율적.
  • Fargate launch type: 서버를 아예 신경 안 써도 됨. “컨테이너만 정의하면 AWS가 알아서 서버 관리.”

Fargate는 awsvpc 모드만 지원한다. 이 모드에서는 각 Task가 고유한 ENI(Elastic Network Interface)와 Private IP를 받는다.

  • 장점: Security Group을 Task 단위로 적용 가능
  • 장점: 포트 충돌 없음 (Task마다 독립된 네트워크 인터페이스)
  • 주의: Task 수가 많으면 VPC의 IP 주소 소진 가능 → Subnet CIDR 범위를 여유 있게 설계
  • Task Definition: 컨테이너 실행 스펙 (어떤 이미지, CPU/메모리, 포트, 환경변수 등)
  • Task: Task Definition을 실행한 인스턴스 (= 실행 중인 컨테이너 묶음)
  • Service: Task를 원하는 개수만큼 유지해주는 단위. 1개가 죽으면 자동으로 새로 띄움.
  • Cluster: Service와 Task를 묶는 논리적 그룹.
{
"family": "my-nest-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"containerDefinitions": [
{
"name": "my-nest-app",
"image": "123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-nest-app:latest",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [{ "name": "NODE_ENV", "value": "production" }],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/my-nest-app",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}

📖 더 보기: AWS 공식 - ECS Fargate 시작하기 — Task Definition 생성부터 Service 배포까지 공식 튜토리얼

인터넷 → ALB(로드밸런서) → Target Group → ECS Service → Task(컨테이너)

ALB 없이는 외부에서 ECS 컨테이너에 직접 접근할 수 없다.

ECS Service 생성 시 ALB와 Target Group을 연결하는 설정이 필요하다.

배포 흐름 — Rolling Update 내부 동작

섹션 제목: “배포 흐름 — Rolling Update 내부 동작”

ECS Service의 Rolling Update 방식 배포 과정:

  1. 새 Task Definition 등록 (이미지 태그 변경 등)
  2. ECS Service가 새 Task Definition으로 신규 Task 시작
  3. 신규 Task가 ALB Target Group에 등록되고 Health Check 통과 대기
  4. 신규 Task 정상 확인 → ALB가 신규 Task로 트래픽 전환
  5. 구버전 Task를 ALB에서 제거(Deregister) → Drain 후 종료

이 과정에서 minimumHealthyPercent(최소 유지 Task 비율)와 maximumPercent(최대 Task 비율) 설정으로 무중단 배포를 보장한다.

ECS Auto Scaling — 트래픽에 따른 자동 Task 수 조절

섹션 제목: “ECS Auto Scaling — 트래픽에 따른 자동 Task 수 조절”

ECS Service는 Application Auto Scaling을 통해 Task 수를 자동으로 늘리거나 줄인다. 실무에서 주로 사용하는 두 가지 방식이 있다.

Target Tracking Scaling (권장): 특정 메트릭의 목표값을 유지하도록 Task 수를 자동 조절한다. 예를 들어 “CPU 사용률 60%를 유지”로 설정하면, 사용률이 60%를 넘으면 Task를 늘리고 낮아지면 줄인다. 설정이 간단하고 AWS가 스케일링 계산을 대신 해주는 것이 장점이다.

// Target Tracking 정책 예시: CPU 60% 목표
{
"TargetValue": 60.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
},
"ScaleInCooldown": 300,
"ScaleOutCooldown": 60
}
// ScaleOutCooldown: 60초 — 급격한 트래픽 증가에 빠르게 반응
// ScaleInCooldown: 300초 — Scale In은 천천히 (플래핑 방지)

Step Scaling: 메트릭 값의 구간(Step)별로 Task 수 변화량을 직접 지정한다. 예를 들어 “CPU 70~85%이면 Task +2, 85% 이상이면 Task +4”처럼 세밀한 제어가 필요할 때 사용한다. 예측 가능한 트래픽 패턴이 있는 서비스에 적합하다.

실무 기준값: CPU 60%, Memory 70%를 Scale Out 기준으로 설정하는 것이 일반적이다. 최소 Task 수는 Multi-AZ를 위해 2개 이상, 최대 Task 수는 VPC Subnet의 가용 IP 수와 RDS max_connections를 고려해 설정한다.

Terminal window
# Auto Scaling 설정 등록 (ECS Service에 Scalable Target 연결)
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/my-cluster/my-service \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 2 \
--max-capacity 20
# Target Tracking 정책 생성
aws application-autoscaling put-scaling-policy \
--service-namespace ecs \
--resource-id service/my-cluster/my-service \
--scalable-dimension ecs:service:DesiredCount \
--policy-name cpu-target-tracking \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration file://scaling-policy.json

Fargate 비용은 vCPU/시간 + Memory GB/시간으로 청구된다. 잘못 설정된 Task 스펙은 가장 큰 낭비 원인이다.

  1. Right-Sizing (태스크 스펙 최적화)

    AWS Compute Optimizer가 실제 사용량을 분석해 CPU/메모리 권고안을 제시한다. 적절히 조정하면 30~70% 비용 절감이 가능하다.

    Terminal window
    # CloudWatch에서 실제 CPU/메모리 사용량 확인
    aws cloudwatch get-metric-statistics \
    --namespace AWS/ECS \
    --metric-name CPUUtilization \
    --dimensions Name=ClusterName,Value=my-cluster Name=ServiceName,Value=my-service \
    --start-time 2024-01-01T00:00:00Z \
    --end-time 2024-01-15T00:00:00Z \
    --period 3600 \
    --statistics Average
    # → 평균 CPU 사용률이 20% 미만이면 Task CPU를 절반으로 줄일 수 있음
  2. Fargate Spot 활용

    상태 비저장(Stateless), 중단 가능한 배치 작업이나 개발 환경은 Fargate Spot을 사용하면 최대 70% 절감된다.

    // capacityProviderStrategy에서 Spot 비중 설정
    {
    "capacityProviders": [
    { "capacityProvider": "FARGATE", "weight": 1, "base": 1 },
    { "capacityProvider": "FARGATE_SPOT", "weight": 3 }
    ]
    }
    // → 4개 Task 중 1개는 On-Demand(안정), 3개는 Spot(70% 저렴)
  3. Compute Savings Plans

    1년 또는 3년 약정으로 Fargate 비용을 최대 52% 절감. EC2, Lambda와 통합 적용된다. 예측 가능한 프로덕션 워크로드에 적합하다.

  4. Public IP 비용 제거

    ECS Task에 Public IP를 할당하면 IP당 시간 과금이 발생한다 (2024년부터 AWS 유료화). Private Subnet + ALB 구조로 전환하면 비용 절감과 보안 강화를 동시에 달성한다.

  • 웹 서비스 배포 (ECS Service + ALB로 컨테이너 운영)
  • 배치 작업 실행 (ECS Task로 1회성 실행)
  • 개발/스테이징/프로덕션 환경 분리 (Cluster 단위)
  • Auto Scaling (트래픽에 따라 컨테이너 수 자동 조절)
  • 팀 서비스가 ECS로 배포되고 있다면, 배포 구조를 이해하는 데 필수
  • 배포 실패 시 Task 상태, 로그, Service 이벤트를 확인해야 함
  • “서버 리소스가 부족하다”는 이슈 대응 시 Task Definition의 CPU/메모리 확인
  • 새 서비스 배포 시 ECS 설정을 이해하고 있어야 함

⚠️ Fargate에서는 docker exec이 안 된다

컨테이너에 직접 접속하려면 AWS ECS Exec을 써야 한다. 자주 환경변수를 확인하거나 내부 상태를 디버깅할 때 필요하다.

Terminal window
aws ecs execute-command \
--cluster my-cluster \
--task <task-id> \
--container my-container \
--interactive --command "/bin/sh"

5.5 컨테이너 오케스트레이션 원리의 전이

섹션 제목: “5.5 컨테이너 오케스트레이션 원리의 전이”

ECS를 이해하면 Kubernetes(K8s)나 Nomad 같은 다른 오케스트레이터로 전이하기 쉽다. 핵심 개념이 대응되기 때문이다.

ECS ↔ Kubernetes 핵심 개념 대응 표

섹션 제목: “ECS ↔ Kubernetes 핵심 개념 대응 표”
ECS 개념Kubernetes 대응역할
Task DefinitionPod Spec (manifest의 spec 섹션)컨테이너 이미지, CPU/메모리, 포트, 환경변수 등 실행 스펙 정의
TaskPod실제 실행 중인 컨테이너 인스턴스
ServiceDeployment + ServiceDesired State 유지(Deployment) + 네트워크 엔드포인트 노출(Service)
ClusterNamespace + Node 그룹워크로드를 묶는 논리적 단위
Task RoleServiceAccount + RBAC앱 코드가 AWS/Cloud 서비스에 접근할 때 사용하는 권한 ID
Execution Rolekubelet 자격증명컨테이너 런타임이 이미지 Pull, 로그 전송 등 플랫폼 작업 수행 시 사용
Service DiscoveryCoreDNS + Service ClusterIP서비스 이름으로 내부 통신 (ECS: <svc>.<namespace>.svc.cluster.local)
Health Check (ALB/컨테이너)Liveness/Readiness Probe비정상 컨테이너 자동 교체 트리거
FargateKnative / Fargate for EKS서버 프로비저닝 없이 컨테이너만 정의하는 서버리스 레이어

📖 참고: ECS Terminology for Kubernetes Users (Medium) — K8s 배경 엔지니어가 ECS로 전환할 때 참고

Desired State Reconciliation — 공통 원리

섹션 제목: “Desired State Reconciliation — 공통 원리”

ECS와 Kubernetes 모두 Desired State Reconciliation 방식으로 동작한다. 운영자가 “Service를 3개 유지”라고 선언(Declare)하면, 컨트롤 플레인이 실제 상태(Actual State)를 주기적으로 체크해 선언된 상태(Desired State)로 맞춘다.

[ECS 흐름]
Service desiredCount=3 선언
→ ECS Scheduler가 실행 중 Task 수 확인
→ Task 1개 죽음 → Scheduler가 새 Task 1개 시작 → desiredCount=3 복원
[K8s 흐름]
Deployment replicas=3 선언
→ ReplicaSet Controller가 실행 중 Pod 수 확인
→ Pod 1개 죽음 → Controller가 새 Pod 1개 스케줄 → replicas=3 복원

이 원리를 이해하면 ECS뿐 아니라 K8s, Nomad 어디서도 같은 사고방식을 적용할 수 있다.

새 오케스트레이터를 만났을 때 — 4가지 분석 질문

섹션 제목: “새 오케스트레이터를 만났을 때 — 4가지 분석 질문”

다음 도구(K8s, Nomad, Knative, HashiCorp Waypoint 등)를 처음 봤을 때 ECS 사고방식을 그대로 옮기려면 아래 4가지를 먼저 묻는다. 매핑 표를 외우는 것보다 이 질문에 답하는 게 빠르게 일반화된다.

  1. 선언형(Declarative)인가 명령형(Imperative)인가 — “원하는 상태를 선언하면 시스템이 알아서 맞추는가” vs “단계별 명령을 사용자가 내려야 하는가”. ECS Service desiredCount, K8s replicas는 선언형. docker run 한 번 띄우는 것은 명령형 — drift detection이 없다 (Hossein Kassaei - Reconciliation Patterns)
  2. Reconciliation 주체와 주기 — 무엇이 actual state와 desired state를 비교해 재동기화하는가. ECS는 ECS Scheduler, K8s는 각 컨트롤러(ReplicaSet/Deployment Controller). 주기는 fixed timer가 아니라 보통 이벤트 기반 + 주기적 resync 혼합
  3. Lifecycle 주체 — 컨테이너 시작/종료 신호를 누가 보내는가. ECS Fargate는 AWS가 SIGTERM 발사 후 stopTimeout 대기, K8s는 kubelet이 preStop hook → SIGTERM → terminationGracePeriodSeconds 대기. Graceful Shutdown 코드 위치가 여기서 갈린다
  4. 격리 단위와 네트워크 ID — Task/Pod 단위로 ENI/IP가 붙는가, 호스트 인터페이스를 공유하는가. ECS awsvpc는 Task당 ENI, K8s는 Pod당 IP, EC2 bridge 모드는 호스트 공유. 이 차이가 Security Group 적용 단위를 결정

적용 예 — Knative: Knative Service 매니페스트는 위 4가지로 읽으면 빠르다. 선언형(Service.spec.template), reconciliation은 Knative Serving Controller, lifecycle은 Queue Proxy가 SIGTERM 후 drain, 격리는 Pod-level. Fargate에서 onApplicationShutdown으로 5초 sleep을 넣은 것과 같은 자리에 Knative는 terminationGracePeriodSeconds를 쓴다 — 이름만 다르고 역할은 같다.

ECS와 EKS는 모두 컨테이너 오케스트레이터이지만 선택 기준이 명확히 다르다.

📖 출처: AWS 공식 - Choosing an AWS container service

선택 기준ECS 유리EKS 유리
팀 규모 / DevOps 전문성소~중규모, 전담 SRE 없음중~대규모, Kubernetes 전문 SRE 보유
운영 복잡도 감내 수준낮은 운영 부담 선호복잡도를 감내하고 세밀한 제어 필요
멀티클라우드 요구AWS 단일 환경GCP/Azure 혼용 또는 온프레미스 병행 (K8s 표준 활용)
오픈소스 생태계 필요불필요 (AWS 관리형으로 충분)Istio, Argo CD, OPA, KEDA 등 CNCF 생태계 활용 필요
클러스터 업그레이드 부담낮음 (AWS가 컨트롤 플레인 관리)높음 (K8s 연 3회 릴리스, 버전 만료 관리 필요)
멀티테넌시 / Namespace 격리제한적 (Cluster 단위)강력한 Namespace 기반 격리 지원
AWS 서비스 통합 간편성매우 높음 (IAM, ALB, ECR 네이티브 통합)높음 (추가 설정 필요할 수 있음)
추가 비용ECS 자체 무료 (Fargate/EC2 과금)EKS 클러스터당 $0.10/시간 (~$73/월) 추가 과금

실무 판단 기준: 팀이 Kubernetes 전문가 없이 AWS 워크로드를 빠르게 운영해야 한다면 ECS가 답이다. 멀티클라우드 이식성이나 CNCF 생태계 도구(Istio, Argo CD 등)가 필요하다면 EKS를 선택한다. AWS도 공식적으로 “대부분의 고객은 두 서비스를 혼용한다”고 밝히고 있으며, 선택은 단방향 결정이 아니다.

5.9 Fargate vs EC2 launch type 비용 비교

섹션 제목: “5.9 Fargate vs EC2 launch type 비용 비교”

Fargate는 서버 관리 비용을 없애는 대신 컨테이너 리소스에 프리미엄이 붙는다. 실제 비용을 파악하지 않으면 예상보다 훨씬 높은 청구서를 받을 수 있다.

항목FargateEC2 launch type
과금 단위vCPU/시간 + GB/시간 (초 단위 과금)EC2 인스턴스 시간 + EBS 볼륨
최소 과금1분인스턴스 타입에 따라 다름
서버 관리불필요 (AWS 관리)OS 패치, 에이전트 업데이트 직접 관리
인스턴스 공유불가 (Task별 격리)여러 Task가 인스턴스 공유 가능

비용 예시: 단일 Task 기준 (ap-northeast-2, Linux/x86)

섹션 제목: “비용 예시: 단일 Task 기준 (ap-northeast-2, Linux/x86)”

AWS Fargate 공식 요금 (참고: AWS Fargate Pricing)

  • vCPU: 약 $0.04048/시간 (서울 리전 기준, 환율/리전에 따라 변동)
  • 메모리: 약 $0.004445/GB/시간

Task 스펙: 0.25 vCPU / 0.5 GB — 24시간 × 30일 운영

항목계산월 비용(USD)
Fargate vCPU0.25 × $0.04048 × 720h~$7.29
Fargate 메모리0.5 × $0.004445 × 720h~$1.60
Fargate 합계~$8.89
t3.small On-Demand (2 vCPU, 2 GB)~$0.0208/시간 × 720h~$14.98
  • Fargate 0.25vCPU/0.5GB는 최소 스펙이므로, 인스턴스 비용만 보면 t3.small보다 저렴하다.
  • 그러나 실무에서는 Task 여러 개를 EC2 1개에 집약(bin packing)하므로, Task 수가 많아질수록 EC2 launch type이 유리해진다.

Task 10개 운영 시 비교 (0.5 vCPU / 1 GB per Task)

방식월 비용 추정
Fargate × 10 Task(0.5 × $0.04048 + 1 × $0.004445) × 720h × 10 ≈ $177
t3.large × 2대 (EC2)$0.0832/h × 720h × 2 ≈ $120 (서버 관리 비용 별도)

💡 실무 결론: Task 수가 적고 팀이 작다면 Fargate의 운영 편의성이 비용 차이를 상쇄한다. Task 수가 20개 이상이고 워크로드가 예측 가능하다면 EC2 launch type + Reserved Instance를 검토할 만하다.

📖 참고: AWS Blog - Theoretical cost optimization by Amazon ECS launch type: Fargate vs EC2

실제 도입 사례 — 위 기준으로 결정한 팀의 측정 결과

섹션 제목: “실제 도입 사례 — 위 기준으로 결정한 팀의 측정 결과”

위 trade-off 표는 결정 도구일 뿐, 실제 적용 후 무엇이 측정됐는지가 더 중요하다. 공개된 사례 3건을 수치 그대로 정리한다 (모두 출처 링크 명시).

  • NetworkLessons (K8s → ECS Fargate 마이그레이션, 1인 운영자 환경): EKS 클러스터 운영 부담이 너무 컸던 1인 팀이 ECS Fargate로 전환. 클러스터 업그레이드 부담 제거 + EKS 컨트롤 플레인 $73/월 절감이 결정 동기 (Towards The Cloud - NetworkLessons case study). 이는 위 표의 “팀 규모 / DevOps 전문성” 행이 그대로 적용된 사례 — Kubernetes 전문 SRE 없는 팀의 합리적 선택
  • IAMOPS (모니터링 워크로드 Fargate On-Demand → Fargate Spot 부분 전환): 비핵심 ECS 서비스를 단계적으로 Spot으로 이동. 월 ECS compute 비용 $275.39 → $143.74로 47.8% 감소 (IAMOPS - ECS Cost Optimization with Fargate Spot). 단, loki-write 같은 로그 ingestion은 중단되면 데이터 유실이 일어나므로 Spot 대상에서 제외 — 위 본문의 “상태 비저장·중단 가능”이 절대 조건임을 입증
  • Smartsheet (Fargate + AWS Graviton 도입): x86 Fargate에서 Graviton(arm64) Fargate로 전환 후 20% 비용 감소. 동시에 배포 빈도가 주 1회 → 하루 여러 회로 늘었고, 배포에 들던 엔지니어 시간이 “수 시간 → 수 분”으로 단축 (AWS - Smartsheet ECS Fargate case study)

판독 가이드: 비용 절감률은 워크로드 특성에 따라 20%(Graviton 단순 전환) ~ 47.8%(Spot 부분 전환) ~ 70%(IAMOPS가 인용한 Spot 이론 최대치) 범위로 분포한다. 동일 표를 보고도 결과가 다른 이유는 워크로드의 중단 허용도, 아키텍처 호환성(arm64), 베이스라인이 다르기 때문이다 — trade-off 표만 보고 “예상 절감률”을 약속하면 안 되는 이유.

Fargate의 절대 한계 — EC2 launch type을 강제하는 조건

섹션 제목: “Fargate의 절대 한계 — EC2 launch type을 강제하는 조건”

비용 표만으로 Fargate가 유리해 보여도, 다음 한계 중 하나라도 걸리면 EC2 launch type 또는 ECS Managed Instances 외에는 선택지가 없다. 수치는 AWS 공식 문서 기준 (Linux platform 1.4.0+).

한계Fargate 상한EC2 launch type
Task당 vCPU16 vCPU (8 vCPU 이상은 Linux only)0.25 ~ 192 vCPU (인스턴스 타입까지)
Task당 메모리120 GB (16 vCPU + 메모리는 8 GB 단위)인스턴스 메모리까지 (수백 GB+)
GPU 워크로드지원 안 함p3, g5 등 GPU 인스턴스 지원
Privileged container지원 안 함 (eBPF 보안 에이전트 등 일부 도구)지원
Task 콜드 스타트30~60초 (마이크로VM 프로비저닝 + 이미지 Pull)5~15초 (기존 인스턴스에 여유 있을 때)

출처: AWS 공식 - Task CPU/Memory 유효 조합, AWS 공식 - launch type 비교, OneUptime - Fargate vs EC2 결정 가이드.

한계에 부딪힌 시나리오 1 — 메모리 상한 초과 (decision 실패 사례): 임베딩 추론 서비스가 BERT-Large(~1.3 GB)를 메모리에 상주시킨 채 batch size=64로 처리하려면 working set이 Task당 30 GB를 쉽게 초과한다. 이 시점에 16 vCPU/120 GB Fargate 슬롯으로 올라가는 듯하지만, 실제로는 동시 추론 큐와 모델 워밍업까지 더해 OOMKilled가 빈번해진다. 강제로 좁아지는 선택지는 두 개 — (1) batch size를 16 이하로 줄여 latency를 늘리는 대신 메모리 안에 맞추기, (2) EC2 launch type + r6i.4xlarge(128 GB) 이상으로 전환해 인스턴스 관리 부담 부활. trade-off는 보통 (2)가 정답.

한계에 부딪힌 시나리오 2 — 콜드 스타트로 burst 트래픽 5xx: Fargate Task의 콜드 스타트는 30~60초이므로 spike에 취약하다. Auto Scaling이 트래픽 증가를 감지해 새 Task를 띄워도 30초 동안은 기존 Task만 부담을 받는다 — 큐가 쌓이고 ALB가 HTTPCode_Target_5XX_Count를 spike시킨다. 회피책은 baseline Task 수를 예측 피크의 70~80%로 두고, Auto Scaling은 안정 영역(Target Tracking)에서만 작동시키는 것 — cold start latency를 critical path에서 분리. spiky한 트래픽 패턴인데 baseline 비용을 아끼고 싶다면 EC2 launch type + warm pool이 더 합리적.

한계에 부딪힌 시나리오 3 — launch type 간 직접 전환 불가: 위 두 시나리오에 도달해 “Fargate → EC2”로 바꾸려 하면, AWS 공식 가이드는 launch type 간 직접 전환을 지원하지 않는다고 명시한다 (capacity provider 경유 필수, 공식 마이그레이션 표). 즉 초기에 Fargate launch type으로 서비스를 만들었다면 새 서비스로 마이그레이션해야 하고, 그 사이 ALB Target Group 재연결·DNS 컷오버 비용이 든다. 5.5절의 cross-pollination을 여기로 가져오면 — K8s에서 Pod의 nodeSelector를 잘못 잡았다가 다른 노드 풀로 옮길 때 똑같은 종류의 “선언적 시스템이지만 일부 속성은 immutable” 문제가 발생한다 (Pod 재생성). 처음부터 capacity provider 기반으로 잡으면 전환 비용이 훨씬 낮다.

개념 A개념 B차이점
EC2ECSEC2는 서버 자체, ECS는 컨테이너 관리 서비스
ECS + EC2ECS + FargateEC2 모드는 서버 직접 관리, Fargate는 서버리스
TaskServiceTask는 1회 실행 단위, Service는 Task를 원하는 수만큼 유지
ECSEKSECS는 AWS 전용 오케스트레이션, EKS는 Kubernetes 기반
Task RoleExecution RoleTask Role은 앱이 AWS 서비스 접근할 때, Execution Role은 ECS가 태스크 시작할 때

🔧 Task가 계속 STOPPED 상태로 반복 재시작되는 경우

섹션 제목: “🔧 Task가 계속 STOPPED 상태로 반복 재시작되는 경우”

증상: ECS Service 이벤트 탭에서 Task stopped (Exit Code: 1) 또는 Task stopped (Essential container exited) 메시지가 반복됨

원인: 컨테이너 애플리케이션이 시작 직후 오류로 종료되고 있음. 환경변수 누락, DB 연결 실패, 포트 바인딩 실패 등이 원인

해결:

  1. ECS 콘솔 → 해당 Service → Tasks 탭 → Stopped 상태의 Task 클릭
  2. Stopped Reason 필드 확인 (예: "Essential container exited")
  3. CloudWatch Logs에서 컨테이너 로그 확인 (/ecs/<task-definition-family> 로그 그룹)
  4. 로그에서 실제 오류 메시지 확인 후 애플리케이션 수정
경로: CloudWatch → Log Groups → /ecs/my-nest-app → 최신 로그 스트림
예상 출력 (NestJS 환경변수 누락 시):
Error: Config validation error: "DATABASE_URL" is required
at Object.<anonymous> (/app/dist/main.js:1:...)

🔧 “CannotPullContainerError” — 이미지 Pull 실패

섹션 제목: “🔧 “CannotPullContainerError” — 이미지 Pull 실패”

증상: Task가 시작되지 않고 CannotPullContainerError: failed to resolve... access denied 에러로 멈춤

원인 1: Task Definition에 설정된 Execution Role에 ECR Pull 권한(ecr:GetAuthorizationToken, ecr:BatchGetImage 등)이 없음

원인 2: Fargate Task가 Private Subnet에 있는데 NAT Gateway 또는 ECR VPC Endpoint가 없어서 ECR에 접근 불가

해결:

  1. Task Definition의 taskRoleArn vs executionRoleArn 구분 확인 (ECR Pull은 executionRoleArn)
  2. AmazonECSTaskExecutionRolePolicy 관리형 정책이 Execution Role에 붙어있는지 확인
  3. Fargate가 Private Subnet이면 NAT Gateway 또는 ECR/CloudWatch VPC Endpoint 설정

🔧 “OOMKilled” — 메모리 부족으로 컨테이너 종료

섹션 제목: “🔧 “OOMKilled” — 메모리 부족으로 컨테이너 종료”

증상: Task가 exit code 137 또는 OOMKilled로 종료됨. 트래픽이 몰릴 때 또는 서비스 시작 직후 발생

원인: Task Definition에 설정된 메모리 한도(Memory)보다 컨테이너가 더 많은 메모리를 사용하려 할 때 Linux 커널이 프로세스를 강제 종료

해결:

  1. CloudWatch → ECS 메트릭 → MemoryUtilization으로 최대 사용량 확인
  2. Task Definition의 memory 값을 현재 최대 사용량보다 20~30% 여유 있게 증가
  3. NestJS의 경우 --max-old-space-size Node.js 옵션 조정 검토

🔧 Fargate Task가 PROVISIONING 상태에서 멈추는 경우

섹션 제목: “🔧 Fargate Task가 PROVISIONING 상태에서 멈추는 경우”

증상: ECS Task가 PROVISIONING 상태에서 수 분이 지나도 RUNNING으로 넘어가지 않음

원인: Private Subnet에 배치된 Fargate Task가 ECR 이미지를 Pull하거나 Secrets Manager에 접근하기 위한 인터넷 경로(NAT Gateway 또는 VPC Endpoint)가 없음

해결:

확인 경로:
1. VPC → Route Tables → Private Subnet 연결된 Route Table
→ 0.0.0.0/0 → NAT Gateway 경로 존재 여부 확인
2. 또는 VPC Endpoint 확인:
→ VPC → Endpoints → com.amazonaws.ap-northeast-2.ecr.dkr
→ com.amazonaws.ap-northeast-2.ecr.api
→ com.amazonaws.ap-northeast-2.logs (CloudWatch)
위 세 Endpoint가 없으면 NAT Gateway 없이는 이미지 Pull 불가

🔧 ECS Service의 배포가 진행되지 않고 멈추는 경우

섹션 제목: “🔧 ECS Service의 배포가 진행되지 않고 멈추는 경우”

증상: 새 Task Definition으로 배포를 시작했는데 서비스가 오랫동안 in progress 상태. 구버전 Task는 계속 실행 중이고 신버전 Task가 교체되지 않음

원인 1: 신규 Task의 ALB Health Check가 통과되지 않아 ECS가 구버전을 제거하지 못함

원인 2: minimumHealthyPercent가 100%로 설정되어 새 Task가 올라올 여유 공간이 없음

원인 3: 새 Task 자체가 시작 직후 실패하고 있어 ECS가 계속 새 Task를 시도하다 포기하지 못함

해결:

Terminal window
# 1. Service 이벤트 탭에서 배포 상태 확인
# ECS → Clusters → <클러스터> → Services → <서비스> → Events 탭
# 예상 출력:
# service my-service has started 1 tasks: task abc123.
# service my-service (port 3000) is unhealthy in target-group ...due to (reason Health checks failed)
# 2. ALB Target Group 상태 확인
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:ap-northeast-2:123456789:targetgroup/my-tg/xxx
# → "State": "unhealthy", "Description": "Health checks failed" 나오면 Health Check 경로/포트 확인
# 3. Health Check 설정 점검
# ALB → Target Groups → <그룹> → Health checks 탭
# 확인: Health check path가 NestJS에서 실제 200을 반환하는 경로인지 (예: /health)

🔧 ALB 502 Bad Gateway 에러 — 프로덕션에서 간헐적으로 발생

섹션 제목: “🔧 ALB 502 Bad Gateway 에러 — 프로덕션에서 간헐적으로 발생”

증상: 하루에 수십 건 ALB에서 502 에러 반환. CloudWatch에서 HTTPCode_ELB_5XX_Count 지표가 산발적으로 올라감. 트래픽이 많지 않을 때도 발생

원인: ECS Task가 종료(스케일 다운 또는 배포)될 때 ALB가 아직 해당 Target을 De-register하기 전에 요청이 들어오는 경우. 또는 NestJS 앱이 응답 전에 연결을 닫는 경우

해결:

Terminal window
# 1. ALB Access Log 활성화 → 502 발생 시간과 Target IP 확인
# ALB → Attributes → Access logs → S3 버킷 지정
# 2. Target Group의 Deregistration delay 확인 (기본 300초)
aws elbv2 describe-target-group-attributes \
--target-group-arn arn:aws:elasticloadbalancing:ap-northeast-2:123456789:targetgroup/my-tg/xxx
# → "deregistration_delay.timeout_seconds": "300"
# 너무 길면 배포가 느려지고, 너무 짧으면 502 발생 가능
# 3. NestJS Graceful Shutdown 구현 (핵심 해결책)
# main.ts에 추가:
// main.ts — NestJS Graceful Shutdown
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// ALB가 De-register 신호 보낸 후 실제 연결이 드레인될 시간 확보
app.enableShutdownHooks();
await app.listen(3000);
}
// app.module.ts 또는 별도 모듈
@Injectable()
export class AppService implements OnApplicationShutdown {
async onApplicationShutdown(signal: string) {
console.log(`Shutting down on signal: ${signal}`);
// 진행 중인 DB 트랜잭션, SQS 처리 등을 완료할 시간 확보
await new Promise((resolve) => setTimeout(resolve, 5000));
}
}
Terminal window
# 4. ECS Task Definition에 Stop Timeout 설정 (Fargate 기본 30초)
# Task Definition → stopTimeout: 60 (최대 120초)
# → SIGTERM 수신 후 60초 동안 Graceful Shutdown 시간 부여

AWS 권고: ALB Deregistration delay(300초)와 ECS stopTimeout(120초 이하), NestJS Graceful Shutdown 3가지를 모두 맞춰야 502가 사라진다.

🔧 Silent Failure — 환경변수 키 오타로 디폴트 값이 적용되는 경우

섹션 제목: “🔧 Silent Failure — 환경변수 키 오타로 디폴트 값이 적용되는 경우”

증상: Task가 정상적으로 RUNNING 상태이고, ALB Health Check도 통과. 로그에 에러가 없음. 그러나 일부 비즈니스 로직(예: 결제 콜백 URL, 외부 API 키, feature flag)이 의도한 환경과 다르게 동작 — staging DB에 prod 트래픽이 쓰이거나, 실제로는 호출되지 않은 외부 API에 대한 폴백 응답이 반환됨.

원인: Task Definition의 environmentDATABSE_URL (오타) 또는 databaseURL (대소문자 다름)로 등록. NestJS ConfigService.get('DATABASE_URL', defaultValue)처럼 두 번째 인자를 받는 API는 키가 없을 때 default를 반환한다 — 에러를 던지지 않으므로 앱은 정상 부팅. Node.js의 process.env.X || 'fallback' 패턴도 동일.

왜 silent한가: Task Definition 변경은 새 revision 번호만 바뀌고, AWS 콘솔은 키 이름 오타를 검증하지 않는다. CloudWatch에는 “환경변수가 디폴트로 적용됨” 같은 신호가 없다. 결제 콜백이 잘못된 URL로 가도, 외부 시스템 응답까지 받아야 발견됨 — 짧으면 수 시간, 길면 수일.

감지 명령:

Terminal window
# 1. 실행 중 Task 안에서 실제 환경변수 dump (Fargate는 ECS Exec)
aws ecs execute-command \
--cluster my-cluster --task <task-id> \
--container my-container --interactive --command "/bin/sh"
# 컨테이너 내부에서:
# env | sort > /tmp/runtime-env.txt
# 예상 출력: DATABASE_URL=postgresql://prod-host:5432/...
# 만약 DATABASE_URL이 누락되어 있으면 default가 적용 중이라는 증거
# 2. Task Definition의 환경변수 키 목록을 실제 코드가 require하는 키와 diff
aws ecs describe-task-definition --task-definition my-nest-app:42 \
--query 'taskDefinition.containerDefinitions[0].environment[*].name' \
--output text | tr '\t' '\n' | sort > /tmp/td-keys.txt
# 코드에서 ConfigService.get('...') 호출된 키를 grep으로 추출 후 diff
diff /tmp/td-keys.txt /tmp/code-required-keys.txt

복구 절차:

  1. NestJS의 ConfigModulevalidationSchema(Joi/class-validator) 옵션과 함께 등록 — 필수 키 누락 시 부팅 자체를 실패시켜 silent를 loud failure로 바꾼다
  2. Secrets Manager에서 환경변수를 주입할 때 valueFrom에 ARN 대신 JSON 키 경로를 쓰면 Secret 자체가 없을 때 Task가 PROVISIONING에서 실패 — 이것도 silent → loud 변환
  3. CI에서 Task Definition 등록 전 키 목록 vs 코드 require 키 diff를 자동 검사 (위 명령을 lint 단계에 추가)

🔧 Silent Failure — deregistration_delay 오설정으로 일부 요청만 502

섹션 제목: “🔧 Silent Failure — deregistration_delay 오설정으로 일부 요청만 502”

증상: ALB 5xx 카운트가 0이 아니지만 매우 낮음(예: 분당 10-30건, 전체 트래픽 대비 0.05%). 사용자 신고는 산발적이고 재현이 안 됨. CloudWatch HTTPCode_ELB_5XX_Count만 보면 “노이즈 수준”으로 보여 무시되기 쉬움. 그러나 배포 직후나 Auto Scaling Scale-In 시점에 spike가 짧게 발생.

원인: deregistration_delay.timeout_seconds가 너무 짧게 설정(예: 10초)되어, 응답 시간이 긴 일부 요청(파일 업로드, 무거운 SQL, 외부 API 응답 대기)만 cut off됨. 짧은 요청은 정상 완료되므로 “거의 다 정상”처럼 보임. ALB Access Log에서 target_processing_time = -1, target_status_code = "-"로 나타나는 패턴 (DevOps Diaries - Resolving 502 errors).

왜 silent한가: 전체가 502가 나면 즉시 알람·롤백되지만, “긴 요청만 502, 짧은 요청은 정상” 패턴은 평균 latency·전체 5xx율 대시보드에서 묻힌다. 사용자도 “가끔 한 번 실패하고 새로고침하면 됨”으로 학습해 신고하지 않음.

감지 명령:

Terminal window
# 1. ALB Access Log에서 target_processing_time = -1 비율 추출
# (Athena로 ALB Access Log 쿼리 - S3에 활성화돼 있어야 함)
SELECT
date_trunc('minute', from_iso8601_timestamp(time)) AS minute,
COUNT(*) AS total,
SUM(CASE WHEN target_processing_time = -1 THEN 1 ELSE 0 END) AS aborted,
SUM(CASE WHEN elb_status_code = 502 THEN 1 ELSE 0 END) AS http_502
FROM alb_access_logs
WHERE date >= current_date - interval '7' day
GROUP BY 1
HAVING SUM(CASE WHEN target_processing_time = -1 THEN 1 ELSE 0 END) > 0
ORDER BY 1 DESC;
# 예상 출력: 배포 직후·Scale-In 시점에 aborted 컬럼이 spike
# 2. 현재 deregistration_delay 값과 p99 응답 시간 비교
aws elbv2 describe-target-group-attributes \
--target-group-arn <arn> \
--query "Attributes[?Key=='deregistration_delay.timeout_seconds'].Value"
# 위 값이 p99 latency × 2 미만이면 silent 502 가능성

복구 절차: deregistration_delaymax(60s, p99 latency × 2)로 상향 + ECS stopTimeout을 그보다 약간 짧게 설정 + 앱의 Graceful Shutdown 시간(예: NestJS OnApplicationShutdown sleep)을 둘 사이에 배치. 세 값의 부등식이 깨지면 다시 silent 502가 발생한다.

🔧 HealthCheckGracePeriod 미설정으로 Task 반복 재시작

섹션 제목: “🔧 HealthCheckGracePeriod 미설정으로 Task 반복 재시작”

증상: 새로 배포한 NestJS Task가 시작 직후 ALB Health Check에서 실패 → ECS가 Task를 교체 → 새 Task도 실패를 반복. 로그에는 정상 부팅 메시지가 찍히는데 Task가 계속 교체됨

원인: NestJS 앱이 완전히 부팅되기 전(DB 연결 초기화, 모듈 로딩 등)에 ALB Health Check가 먼저 실패 판정. ECS가 unhealthy로 판단해 Task를 교체

해결:

Terminal window
# ECS Service에 HealthCheckGracePeriod 설정 (초 단위)
# 앱이 완전히 부팅될 때까지 Health Check 실패를 무시하는 시간
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--health-check-grace-period-seconds 60
# → NestJS가 DB 연결 포함 완전 부팅에 30~45초 걸리면 60초 이상 설정
# 확인: Service 설정에서 healthCheckGracePeriodSeconds 값
aws ecs describe-services \
--cluster my-cluster \
--services my-service \
--query 'services[0].healthCheckGracePeriodSeconds'
# 예상 출력: 60

ALB Health Check 자체 설정도 함께 조정:

ALB → Target Groups → Health checks
- Healthy threshold: 2 (2회 연속 성공 시 healthy)
- Unhealthy threshold: 3 (3회 연속 실패 시 unhealthy)
- Interval: 30초
- Timeout: 5초
→ unhealthy 판정까지 최소 90초 소요 → NestJS 부팅 시간 커버 가능
  • EC2와 ECS의 차이를 한 문장으로 설명할 수 있다
  • Fargate와 EC2 launch type의 차이를 설명할 수 있다
  • Task Definition, Task, Service, Cluster의 관계를 설명할 수 있다
  • 인터넷 → ALB → ECS로 트래픽이 흐르는 구조를 설명할 수 있다
  • 팀 서비스의 배포 방식이 EC2인지 ECS인지 말할 수 있다
  • Task가 STOPPED되었을 때 로그를 어디서 확인하는지 안다

EKS(Kubernetes), Fargate Spot, Auto Scaling, ALB(Application Load Balancer), Target Group, Blue-Green 배포, Rolling 배포

  • AWS 콘솔에서 ECS Cluster 목록 확인

    경로: AWS 콘솔 → ECS → Clusters
    확인: 클러스터 이름, 실행 중인 서비스/태스크 수
  • 팀 서비스의 Task Definition 열어보기 (이미지, CPU/메모리, 환경변수 확인)

    경로: ECS → Task Definitions → <이름> → 최신 버전 클릭
    확인: CPU/Memory 할당, 컨테이너 이미지 주소, 환경변수, 로그 설정
  • ECS Service의 이벤트 탭에서 배포 이력 확인

    경로: ECS → Clusters → <클러스터> → Services → <서비스> → Events 탭
    예상 출력:
    2024-01-15 10:30:01 service my-service has reached a steady state.
    2024-01-15 10:29:45 service my-service has started 1 tasks: task abc123def.
    2024-01-15 10:28:30 service my-service deregistered 1 targets in target group arn:aws:...
  • 배포 시 ECS에서 무슨 일이 일어나는지 흐름 파악

  1. EC2는 가상 서버(인프라), ECS는 Docker 컨테이너 오케스트레이션(관리 방식)이다 — 둘은 비교 대상이 아니라 함께 쓰이는 계층이다
  2. ECS는 Fargate(서버리스, 비용 효율), EC2 launch type(고성능·특수 요구), Managed Instances(새 옵션, 특수 워크로드) 세 가지 실행 모드가 있다
  3. Task Definition(스펙) → Task(실행 인스턴스) → Service(개수 유지·배포) → Cluster(논리 그룹) 4계층 구조이다
  4. 외부 트래픽은 ALB → Target Group → ECS Service → Task 순으로 흐르며, 배포는 신규 Task 헬스체크 통과 후 구버전 교체 순서로 진행된다
  5. 배포 실패 시 확인 순서: ECS Events 탭 → CloudWatch 로그(컨테이너 오류) → ALB Target Group 헬스체크 → Task 메모리/CPU 설정

Docker Compose를 써봤다면 ECS Task Definition과 비교하기

로컬 개발에서 docker-compose.yml을 사용해봤다면 ECS Task Definition이 매우 친숙하게 느껴질 것이다:

# docker-compose.yml (로컬 개발)
version: "3"
services:
api:
image: my-nest-app:latest
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://...
depends_on:
- postgres
postgres:
image: postgres:14
environment:
POSTGRES_DB: mydb
// ECS Task Definition (프로덕션)
{
"family": "my-nest-app",
"cpu": "256",
"memory": "512",
"containerDefinitions": [{
"name": "api",
"image": "123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-nest-app:latest",
"portMappings": [{"containerPort": 3000}],
"secrets": [
{"name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:...:prod/db-url"}
],
"logConfiguration": {"logDriver": "awslogs", ...}
}]
}

차이점 요약:

docker-composeECS Task Definition
image:"image": (ECR URI)
ports:"portMappings":
environment:"environment": + "secrets": (Secrets Manager 참조)
depends_on:ECS Service + RDS (별도 리소스)
로컬 볼륨S3, EFS (ECS에서 관리)
로그: 터미널 출력CloudWatch Logs (awslogs 드라이버)

핵심 차이: 환경변수 값을 직접 넣지 않고 Secrets Manager ARN으로 참조한다. 그리고 RDS는 compose의 postgres 서비스가 아니라 별도 AWS 리소스로 분리된다.

프론트에서 배포 결과를 확인하는 흐름

CI/CD로 코드를 push했을 때 서비스에 반영되는 과정:

git push → GitHub Actions → Docker build → ECR push
→ ECS Service Update Deployment 시작
→ 새 Task 시작 (PROVISIONING → RUNNING)
→ ALB Health Check 통과 대기
→ 신규 Task에 트래픽 전환
→ 구버전 Task STOPPED
배포 확인 방법:
ECS → Clusters → Services → <서비스> → Events 탭
"service reached a steady state" 메시지 = 배포 완료
로그 확인:
CloudWatch → Log Groups → /ecs/<task-family> → 최신 로그 스트림

실무 아키텍처 패턴 — ECS 프로덕션 구성

섹션 제목: “실무 아키텍처 패턴 — ECS 프로덕션 구성”
[일반적인 프로덕션 ECS 아키텍처]
인터넷
└─ ALB (Public Subnet, HTTPS 443)
└─ Target Group (Health Check: /health)
└─ ECS Service (Private Subnet)
├─ Task (Fargate, 2개 이상 Multi-AZ)
│ └─ NestJS 컨테이너 (Port 3000)
└─ Auto Scaling Policy (CPU 60% 이상 시 Scale Out)
[주요 연결 서비스]
Task → RDS (Private Subnet, SG 허용)
Task → ElastiCache (Private Subnet, SG 허용)
Task → S3 (VPC Endpoint 경유, NAT 없이)
Task → CloudWatch (VPC Endpoint 경유)
Task → Secrets Manager (VPC Endpoint 경유, 환경변수 주입)

2025년 신기능 — ECS Managed Instances: re:Invent 2025에서 발표된 ECS의 새 컴퓨팅 옵션이다. Fargate처럼 서버 관리를 AWS에 맡기면서도, Fargate에서 지원하지 않는 GPU 워크로드, eBPF 기반 보안 에이전트, 특권 컨테이너(privileged container), 120GB 이상 메모리 작업에 사용할 수 있다. 2025년 신기능 — Fargate 태스크 종료 시 Stop Signal 지원: Fargate가 이제 컨테이너 이미지에 정의된 STOPSIGNAL 지시를 그대로 전달한다. 기존에는 SIGTERM만 보냈지만, 이제 SIGQUIT, SIGINT 등 컨테이너별 커스텀 종료 신호를 보내 Graceful Shutdown을 더 정확하게 구현할 수 있다.