ECS와 EC2, 서버와 컨테이너 운영의 차이
EC2는 직접 관리하는 가상 서버이고, ECS는 Docker 컨테이너를 정의한 개수와 리소스로 실행하고 유지하는 관리 방식이다. Fargate, 네트워킹, 배포, 오토스케일링, 비용, 장애 대응 흐름까지 실무 관점에서 연결해 이해한다.
Script Companion
오디오와 함께 스크립트 보기
- 01
ECS와 EC2의 차이는 우리 서비스가 어디서 돌아가는지를 이해하는 질문에서 시작한다. EC2, Elastic Compute Cloud는 AWS에서 빌려 쓰는 가상 서버이고, 리눅스나 윈도우 운영체제가 깔린 VM에 SSH로 접속해 직접 세팅하는 방식이다. ECS, Elastic Container Service는 Docker 컨테이너를 실행하고 관리하는 서비스다. 어떤 이미지를 몇 개, 어떤 리소스로 실행할지 정의하면 ECS가 배포와 유지 관리를 맡는다.
- 02
여기서 중요한 점은 EC2와 ECS가 완전히 같은 층위의 비교 대상은 아니라는 것이다. 원문 비유를 빌리면, EC2는 서버라는 자동차에 가깝고 ECS는 그 서버를 운전하는 방식, 즉 핸들에 가깝다. EC2 launch type에서는 EC2 인스턴스 위에 컨테이너를 올리기 때문에 서버 관리 책임이 남지만 비용 효율을 챙길 수 있다. Fargate launch type에서는 서버 자체를 신경 쓰지 않고 컨테이너 정의에 집중한다.
- 03
ECS 내부 동작은 몇 가지 계층으로 볼 수 있다. 먼저 Task Definition에 어떤 이미지와 CPU, 메모리, 포트, 환경변수로 실행할지 JSON 형태의 스펙을 등록한다. 이 스펙을 실행한 것이 Task이고, Service는 이 Task를 원하는 개수만큼 유지한다. 하나가 죽으면 새로 띄우는 단위가 Service이며, Cluster는 Service와 Task를 묶는 논리적 그룹이다. 이후 Scheduler가 Fargate나 EC2 같은 실행 인프라를 정하고, Container Agent가 컨테이너를 시작한다.
- 04
Fargate의 핵심 특징은 격리 모델이다. Fargate에서는 각 Task가 Firecracker 기술 기반의 고유한 마이크로VM 위에서 실행되며, Task끼리 커널, CPU, 메모리를 공유하지 않는다. 여러 컨테이너가 같은 EC2 인스턴스 커널을 공유하는 EC2 launch type과 다른 지점이다. 그래서 다른 테넌트의 컨테이너로부터 격리가 강하고, 금융이나 의료 데이터처럼 규정 준수가 중요한 환경에 유리하다. 다만 격리 오버헤드 때문에 동일 메모리 기준으로 EC2 launch type보다 약간 느릴 수 있다.
- 05
네트워킹에서는 Fargate가 awsvpc 모드만 지원한다는 점이 중요하다. 이 모드에서는 각 Task가 고유한 ENI, Elastic Network Interface와 Private IP를 받는다. 장점은 Security Group을 Task 단위로 적용할 수 있고, Task마다 독립된 네트워크 인터페이스가 있어 포트 충돌이 없다는 것이다. 반대로 Task 수가 많아지면 VPC의 IP 주소가 소진될 수 있으므로 Subnet CIDR 범위를 여유 있게 설계해야 한다. 외부 트래픽은 보통 ALB에서 Target Group을 거쳐 ECS Service와 Task로 흐른다.
- 06
배포는 Rolling Update 흐름으로 이해하면 된다. 새 이미지 태그 같은 변경이 들어간 Task Definition을 등록하면, ECS Service가 새 Task Definition으로 신규 Task를 시작한다. 신규 Task는 ALB Target Group에 등록되고 Health Check를 통과해야 한다. 정상으로 확인되면 ALB가 신규 Task로 트래픽을 전환하고, 구버전 Task는 ALB에서 제거된 뒤 drain 후 종료된다. 이 과정에서 minimumHealthyPercent와 maximumPercent 설정이 무중단 배포의 여유 공간과 유지 비율을 결정한다.
- 07
ECS Service는 Application Auto Scaling으로 Task 수를 자동 조절할 수 있다. Target Tracking Scaling은 특정 메트릭 목표값을 유지하는 방식이며, 예를 들어 CPU 사용률 60%를 기준으로 넘으면 늘리고 낮아지면 줄인다. Step Scaling은 CPU 70에서 85%이면 Task를 2개 늘리고, 85% 이상이면 4개 늘리는 식으로 구간별 변화량을 직접 정한다. 실무 기준으로는 CPU 60%, Memory 70%를 Scale Out 기준으로 두는 경우가 일반적이며, 최소 Task 수는 Multi-AZ를 위해 2개 이상으로 잡는다. 최대 Task 수는 VPC Subnet의 가용 IP 수와 RDS max_connections까지 함께 봐야 한다.
- 08
비용 최적화에서는 Task 스펙을 맞추는 일이 먼저다. Fargate 비용은 vCPU 시간과 Memory GB 시간으로 청구되기 때문에, 잘못 잡은 CPU와 메모리 설정이 큰 낭비가 된다. AWS Compute Optimizer는 실제 사용량을 분석해 권고안을 제시하며, 원문은 적절한 조정으로 30에서 70% 절감이 가능하다고 설명한다. 상태 비저장 작업이나 중단 가능한 배치, 개발 환경은 Fargate Spot으로 최대 70% 절감할 수 있고, 예측 가능한 프로덕션 워크로드는 Compute Savings Plans로 최대 52% 절감할 수 있다. Public IP를 Task에 붙이면 IP당 시간 과금이 생기므로 Private Subnet과 ALB 구조가 비용과 보안 양쪽에 유리하다.
- 09
장애 대응은 증상에서 원인을 좁히는 순서가 중요하다. Task가 STOPPED 상태로 반복 재시작되고 Exit Code: 1 또는 Essential container exited가 보이면, 환경변수 누락, DB 연결 실패, 포트 바인딩 실패처럼 애플리케이션이 시작 직후 종료되는 상황을 의심한다. CannotPullContainerError와 access denied가 보이면 executionRoleArn에 ECR Pull 권한이 있는지, Private Subnet의 Fargate Task가 NAT Gateway 또는 ECR VPC Endpoint로 ECR에 접근할 수 있는지 본다. exit code 137이나 OOMKilled는 Task Definition의 Memory 한도보다 컨테이너가 더 많은 메모리를 쓰려 할 때 발생하며, CloudWatch의 MemoryUtilization을 확인한 뒤 memory 값을 최대 사용량보다 20에서 30% 여유 있게 늘리는 방식으로 접근한다.
- 10
더 조용한 실패도 있다. 환경변수 키를 DATABSE_URL처럼 오타 내거나 databaseURL처럼 대소문자를 다르게 등록하면 Task는 RUNNING이고 ALB Health Check도 통과하지만, NestJS ConfigService.get에 defaultValue가 있거나 Node.js의 fallback 패턴이 있으면 의도하지 않은 기본값으로 동작할 수 있다. 이 경우 CloudWatch에 환경변수가 디폴트로 적용됐다는 신호가 남지 않는다. 원문은 NestJS ConfigModule의 validationSchema로 필수 키 누락 시 부팅을 실패시키고, CI에서 Task Definition 등록 전 키 목록과 코드의 필수 키 차이를 검사해 silent failure를 loud failure로 바꾸라고 제안한다.
- 11
프론트엔드 경험과 연결하면 Docker Compose와 ECS Task Definition의 대응 관계가 도움이 된다. docker-compose.yml의 image, ports, environment는 ECS Task Definition의 image, portMappings, environment와 secrets로 이어지고, 터미널 로그는 CloudWatch Logs로 이동한다. 핵심 차이는 환경변수 값을 직접 넣기보다 Secrets Manager ARN으로 참조하고, RDS는 compose의 postgres 서비스가 아니라 별도 AWS 리소스로 분리된다는 점이다. 문서 말미에서는 ECS Managed Instances와 Fargate 태스크 종료 시 Stop Signal 지원도 언급한다. 정리하면 EC2는 인프라, ECS는 컨테이너 관리 방식이며, 배포 실패 시에는 ECS Events, CloudWatch 로그, ALB Target Group 헬스체크, Task 메모리와 CPU 설정 순서로 확인한다.
같은 레이어
L3에서 이어 듣기
- 오디오 파일
- /podcasts/l3-ecs-vs-ec2.mp3