Docker로 앱을 잘 컨테이너화했습니다(Docker). 로컬에선 docker run 한 줄이면 됐는데, 운영에선 서버 3대에 컨테이너를 나눠 띄우고, 죽으면 살리고, 트래픽 늘면 더 띄우고, 무중단 배포까지 해야 합니다. 이걸 손으로 하면 곧 지옥입니다. 컨테이너 한두 개는 손으로, 수십 개는 오케스트레이터로 — 클라우드는 이 오케스트레이션을 관리형으로 제공합니다.
- 1컨테이너 오케스트레이터가 자동화하는 일을 설명할 수 있다
- 2ECS와 EKS(관리형 쿠버네티스)의 트레이드오프를 안다
- 3Fargate가 서버 관리를 어떻게 덜어주는지 이해한다
- 4서버리스 함수 대신 컨테이너를 고르는 기준을 안다
- 5컨테이너 이미지 저장소(레지스트리)의 역할을 안다
컨테이너를 '여러 대에 걸쳐' 운영하기
오케스트레이터가 대신 하는 일
컨테이너 한두 개는 docker run으로 충분하지만, 운영 규모가 되면 ① 어느 서버에 몇 개 띄울지 배치, ② 죽은 컨테이너 재시작, ③ 부하에 따른 수 조절, ④ 무중단 배포(롤링/블루그린), ⑤ 서비스 디스커버리·로드밸런싱이 필요합니다. 오케스트레이터(ECS/EKS)가 이를 자동화합니다.
이는 Kubernetes 트랙에서 배우는 개념을 클라우드가 관리형 서비스로 제공하는 것입니다 — 컨트롤 플레인을 직접 운영하지 않아도 됩니다.
ECS vs EKS — 단순함이냐, 표준·이식성이냐
- ECS: 클라우드 제공자 고유의 비교적 단순한 오케스트레이터. 학습 곡선 완만, AWS와 통합 매끄러움. K8s를 모르는 팀이 빠르게 운영.
- EKS: 표준 쿠버네티스 관리형. K8s 도구·생태계(Helm, GitOps 등)와 멀티클라우드 이식성을 얻지만 개념·운영 복잡도↑.
"K8s 생태계가 꼭 필요한가? 멀티클라우드를 갈 건가?"면 EKS, "AWS 안에서 단순하게"면 ECS가 출발점입니다.

EC2 모드 vs Fargate — 서버를 내가 관리하나
오케스트레이터(ECS/EKS) 위에서 컨테이너가 실제로 도는 곳은 두 가지입니다.
- EC2 모드: 컨테이너를 올릴 워커 노드(인스턴스)를 내가 관리(용량·패치·스케일). 노드를 세밀하게 통제하고 비용을 최적화할 여지가 큼.
- Fargate: 그 서버 계층을 제공자가 가림. 나는 컨테이너 사양(CPU/메모리)만 정함. 서버 관리에서 손을 떼는 대신 단가·제어는 다름.
서버리스(Lambda)가 '함수 단위 서버리스'라면 Fargate는 '컨테이너 단위 서버리스'에 가깝습니다.
컨테이너 레지스트리 — 이미지의 집
오케스트레이터가 컨테이너를 띄우려면 이미지를 어딘가에서 가져와야 합니다. 컨테이너 레지스트리(예: ECR)가 빌드한 이미지를 저장·버전 관리하는 곳입니다. CI(CI/CD 파이프라인)가 이미지를 빌드해 레지스트리에 푸시하면, 오케스트레이터가 그 이미지를 풀해 배포합니다. 이미지 태그 전략은 시맨틱 버저닝·릴리스 전략와 이어집니다.
오케스트레이터가 원하는 수(desired)만큼 실제로 돌고 있는지(running) 봅니다. 불일치는 배포·헬스 문제 신호입니다.
aws ecs describe-services --cluster prod --services web \
--query "services[0].{Desired:desiredCount,Running:runningCount,Pending:pendingCount}"
{ "Desired": 4, "Running": 4, "Pending": 0 }
aws ecs describe-services컨테이너가 자꾸 죽으면 stoppedReason에서 원인(이미지 풀 실패, OOM, 헬스체크 실패)을 봅니다.
aws ecs list-tasks --cluster prod --desired-status STOPPED --query "taskArns[0]" --output text \
| xargs -I{} aws ecs describe-tasks --cluster prod --tasks {} \
--query "tasks[0].{Reason:stoppedReason,Exit:containers[0].exitCode}"
{ "Reason": "Essential container in task exited", "Exit": 137 }
aws ecs describe-tasks- Desired vs Running 불일치가 지속되면 — 태스크 기동 실패 반복. stoppedReason·exitCode로 원인 추적
- exitCode 137(=128+9, SIGKILL) — 대개 메모리 초과(OOM). 태스크 정의의 메모리 상향 또는 누수 점검(Kubernetes OOMKilled와 동일 원리)
- stoppedReason에 'CannotPullContainerError' — 레지스트리 권한/태그/네트워크 문제. ECR 접근 IAM·이미지 태그 확인
- 롤링 배포 중 헬스체크 실패로 롤백 — 헬스 경로·기동 유예 점검(오토스케일링 + 로드밸런서의 헬스체크와 동일)
상황: 컨테이너가 기동조차 못 하고 이미지 풀 단계에서 실패.
원인: ① 태스크 실행 역할(execution role)에 레지스트리(ECR) 풀 권한이 없음, ② 이미지 태그·경로 오타, ③ 프라이빗 서브넷에서 레지스트리로 나가는 네트워크 경로(NAT/VPC 엔드포인트) 부재.
진단: 태스크 정의의 이미지 URI 확인 → 실행 역할의 ECR 권한 확인 → 서브넷의 아웃바운드 경로 확인(VPC와 서브넷).
해결: 실행 역할에 ECR 풀 정책 부여, 이미지 태그를 정확히, 프라이빗 서브넷이면 NAT 또는 ECR용 VPC 엔드포인트로 이미지 풀 경로 확보. 권한 문제는 계정과 IAM의 최소권한 설정과 직결됩니다.
"왜 ECS/EKS/Fargate 중 그걸 골랐나요?"는 클라우드 인프라 면접 단골입니다. 좋은 답은 기술 유행이 아니라 팀 역량·이식성 요구·운영 부담으로 설명하는 것입니다 — "K8s 경험이 없고 AWS 단일 클라우드라 ECS+Fargate로 운영 부담을 줄였다" 같은 식.
실무 흐름: Docker로 이미지를 만들고 → CI(CI/CD 파이프라인)가 레지스트리에 푸시 → 오케스트레이터가 롤링 배포 → 오토스케일링 + 로드밸런서로 스케일 → 관측과 거버넌스로 관측. 이 트랙의 조각들이 하나의 파이프라인으로 연결됩니다. 깊은 K8s 운영은 Kubernetes 트랙에서 다룹니다.
다음 모듈에서는 지금까지 손/콘솔로 만든 모든 자원을 코드로 선언해 재현하는 IaC와 Terraform 으로 들어갑니다.