왜 컨테이너 오케스트레이션을 배워야 할까?
여러분이 작은 온라인 쇼핑몰을 운영한다고 상상해보세요. 처음에는 EC2 인스턴스 1대에 Node.js 서버를 직접 설치하여 서비스했습니다. 고객이 늘어나자 서버를 2대로 늘렸습니다. 그런데 문제가 생깁니다.

서버마다 Node.js 버전이 다릅니다. 서버 1에는 18.17이, 서버 2에는 18.19가 설치되어 있습니다. OS 패치도 따로 관리해야 합니다. 새 개발자가 입사하면 로컬 환경 세팅에 하루가 걸립니다. "내 컴퓨터에서는 되는데"가 매주 반복됩니다.
Docker 컨테이너는 이 문제를 해결합니다. 애플리케이션과 실행 환경(OS, 라이브러리, 의존성)을 하나의 이미지로 패키징합니다. 이 이미지는 어떤 서버에서든 동일하게 실행됩니다. 개발 노트북, 테스트 서버, 프로덕션 서버 — 모든 곳에서 같은 동작을 보장합니다.
하지만 Docker만으로는 부족합니다. 컨테이너가 10개, 100개로 늘어나면 누가 관리할까요? 컨테이너가 죽으면 누가 다시 살릴까요? 트래픽이 몰리면 누가 컨테이너를 추가로 띄울까요?
이것이 오케스트레이션입니다. 오케스트라에서 지휘자가 수십 명의 연주자를 조율하듯, ECS(Elastic Container Service)가 수십 개의 컨테이너를 관리합니다.
이 실습에서는 다음을 직접 구축합니다:
- Docker 이미지를 빌드하고 ECR(Elastic Container Registry)에 푸시
- ECS Fargate 클러스터에 컨테이너를 배포하여 서버 관리 없이 실행
- ALB를 연동하여 여러 컨테이너에 트래픽을 자동 분산
- 오토 스케일링으로 트래픽 증가에 자동 대응
- CloudWatch Logs로 컨테이너 로그를 중앙 집중 모니터링
실습을 시작하기 전에 AWS 콘솔에 로그인되어 있는지 확인하세요. 리전은 ap-northeast-2 (서울) 을 사용합니다. Docker가 로컬에 설치되어 있어야 합니다.
아키텍처 개요

컨테이너 배포 흐름
비용 예측
비용 계산기
시간
시간
시간
GB/월
* 실제 비용은 AWS 요금 정책에 따라 달라질 수 있습니다.
Step 1: Dockerfile 작성
Dockerfile은 컨테이너 이미지의 "레시피"입니다. 어떤 기반 이미지를 사용하고, 어떤 파일을 복사하고, 어떤 명령을 실행할지를 순서대로 정의합니다. 좋은 Dockerfile을 작성하면 이미지 크기가 줄어들고, 빌드 속도가 빨라지고, 보안이 향상됩니다.
node:18-alpine을 기반 이미지로 사용하는 이유는 일반 node:18 이미지(약 900MB)에 비해 Alpine 기반 이미지(약 120MB)가 훨씬 작기 때문입니다. 이미지가 작을수록 ECR에서 Pull하는 시간이 줄어들고, 컨테이너 시작 속도가 빨라집니다.
COPY package*.json ./을 COPY . .보다 먼저 실행하는 것은 Docker의 레이어 캐싱을 활용하기 위해서입니다. 소스 코드가 변경되어도 package.json이 변경되지 않았다면 npm ci 단계를 캐시에서 재사용할 수 있습니다.
- 로컬에서 간단한 Node.js 애플리케이션 생성
Dockerfile작성:FROM node:18-alpine, EXPOSE 3000docker build -t lab-app .로 이미지 빌드docker run -p 3000:3000 lab-app로 로컬 테스트
# Dockerfile 생성
cat <<'DOCKERFILE' > Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
DOCKERFILE
docker build -t lab-app .
docker run -p 3000:3000 lab-appStep 2: ECR 리포지토리 생성 및 이미지 푸시
ECR(Elastic Container Registry)은 Docker Hub와 유사한 AWS의 컨테이너 이미지 저장소입니다. 프라이빗 저장소이므로 IAM 인증이 필요하고, 이미지가 AWS 네트워크 내에 저장되므로 ECS에서 Pull하는 속도가 매우 빠릅니다.
ECR에 Push하기 전에 aws ecr get-login-password로 인증 토큰을 받아야 합니다. 이 토큰은 12시간 동안 유효합니다. CI/CD 파이프라인에서는 매 빌드마다 새로 인증하도록 설정하세요.
- AWS 콘솔 → ECR → 리포지토리 생성 클릭
- 리포지토리 이름:
lab-app - 생성 후 푸시 명령 보기 클릭하여 안내 따르기
- 인증 → 태그 → 푸시 순서대로 실행
aws ecr create-repository --repository-name lab-app --region ap-northeast-2
aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin <ACCOUNT_ID>.dkr.ecr.ap-northeast-2.amazonaws.com
docker tag lab-app:latest <ACCOUNT_ID>.dkr.ecr.ap-northeast-2.amazonaws.com/lab-app:latest
docker push <ACCOUNT_ID>.dkr.ecr.ap-northeast-2.amazonaws.com/lab-app:latestdocker push가 "no basic auth credentials" 에러로 실패하는 경우
aws ecr get-login-password명령의 출력이 정상인지 확인하세요. 빈 문자열이 반환되면 AWS CLI 자격 증명이 만료된 것입니다.- Docker Desktop이 실행 중인지 확인하세요. Docker 데몬이 꺼져있으면 login 명령 자체가 실패합니다.
- 이미지 태그의 ECR URI에 오타가 없는지 확인하세요. 계정 ID와 리전이 정확해야 합니다.
ECS vs EKS 비교
컨테이너 오케스트레이션 서비스를 선택할 때 가장 많이 받는 질문은 "ECS를 쓸까, EKS를 쓸까?"입니다. 두 서비스의 핵심 차이를 이해하면 올바른 선택을 할 수 있습니다.
- ECS (Elastic Container Service): AWS 네이티브 오케스트레이터. AWS 콘솔에서 직접 관리. Fargate와의 통합이 매끄럽고, 학습 곡선이 낮습니다. 소규모~중규모 워크로드에 적합합니다.
- EKS (Elastic Kubernetes Service): Kubernetes 기반 오케스트레이터. 표준 Kubernetes API를 사용하므로 멀티 클라우드 이식성이 높습니다. 학습 곡선이 가파르지만, 복잡한 마이크로서비스 아키텍처에 적합합니다.
이 실습에서는 ECS Fargate를 사용합니다. AWS 환경에서 컨테이너를 시작하는 가장 빠르고 간단한 경로이며, Kubernetes 지식 없이도 컨테이너 오케스트레이션의 핵심 개념(서비스 디스커버리, 오토 스케일링, 헬스 체크)을 학습할 수 있습니다.
Step 3: ECS 클러스터 생성
ECS 클러스터는 컨테이너를 실행하는 논리적 그룹입니다. Fargate를 선택하면 EC2 인스턴스를 직접 관리할 필요가 없습니다. AWS가 컨테이너를 실행할 서버를 자동으로 할당하고, 사용한 만큼만 비용을 청구합니다.
Fargate의 가장 큰 장점은 서버 관리의 완전한 제거입니다. OS 패치, 보안 업데이트, 용량 계획 모두 AWS가 담당합니다. 개발자는 "어떤 서버에서 실행할지"가 아니라 "얼마나 많은 CPU/메모리가 필요한지"만 결정하면 됩니다.
EC2 시작 유형과 Fargate 시작 유형의 비용 분기점도 알아두면 좋습니다. 24시간 상시 실행되는 워크로드에서는 EC2 + Reserved Instance가 Fargate보다 저렴할 수 있습니다. 하지만 트래픽이 불규칙하거나 운영 인력이 부족한 환경에서는 Fargate의 서버리스 특성이 총 비용(TCO) 관점에서 더 유리합니다.
- 1AWS 콘솔 → ECS → 클러스터 생성 클릭
- 2클러스터 이름: lab-cluster
- 3인프라: AWS Fargate 선택
- 4생성 클릭 후 상태가 ACTIVE가 될 때까지 대기
Step 4: Task Definition 작성
Task Definition은 컨테이너를 어떻게 실행할지 정의하는 "설계도"입니다. Docker 이미지 위치, CPU/메모리 할당량, 포트 매핑, 환경 변수, 로그 설정 등을 포함합니다.
CPU와 메모리 조합에는 제한이 있습니다. 예를 들어 0.25 vCPU를 선택하면 메모리는 0.5GB, 1GB, 2GB 중에서만 선택 가능합니다. 실습에서는 최소 사양인 0.25 vCPU / 0.5 GB로 시작하여 비용을 절약합니다.
awslogs 로그 드라이버를 설정하면 컨테이너의 stdout/stderr가 자동으로 CloudWatch Logs에 전송됩니다. SSH 접속 없이도 로그를 확인할 수 있으므로, Fargate 환경에서는 반드시 설정해야 합니다.
- 1ECS → 태스크 정의 → 새 태스크 정의 생성
- 2패밀리 이름: lab-app-task
- 3시작 유형: Fargate, OS: Linux/X86_64
- 4CPU: 0.25 vCPU, 메모리: 0.5 GB
- 5컨테이너 이름: lab-app, 이미지 URI: ECR 이미지 주소 입력
- 6포트 매핑: 3000 (TCP)
- 7로그 드라이버: awslogs (CloudWatch 자동 생성)
본인의 말로 설명해 보세요
ECS Task와 ECS Service의 차이를 설명해보세요.
💡 Task는 실행 중인 컨테이너의 단일 인스턴스(설계도 기반). Service는 원하는 수의 Task를 유지하고, 실패 시 자동 복구, ALB 연동, 오토 스케일링 등을 관리하는 상위 개념...
Step 5: 서비스 생성 (ALB 연동)
ECS 서비스는 "항상 N개의 태스크가 실행되고 있도록 보장"하는 역할을 합니다. 태스크가 비정상 종료되면 자동으로 새 태스크를 시작합니다. ALB와 연동하면 여러 태스크에 트래픽을 자동으로 분산하고, 비정상 태스크로의 라우팅을 차단합니다.
원하는 태스크 수를 2 이상으로 설정하는 것이 중요합니다. 태스크가 1개뿐이면 해당 태스크가 종료되는 순간(업데이트, 장애 등) 서비스가 중단됩니다. 2개 이상이면 항상 최소 1개는 트래픽을 처리할 수 있습니다.
- 1클러스터 → 서비스 생성 클릭
- 2시작 유형: Fargate, 태스크 정의: lab-app-task
- 3서비스 이름: lab-app-service, 원하는 태스크 수: 2
- 4네트워킹: VPC 및 서브넷 선택, 보안 그룹에서 포트 3000 허용
- 5로드 밸런싱: Application Load Balancer 선택
- 6ALB 이름: lab-alb, 타겟 그룹 자동 생성
- 7헬스 체크 경로: /health
서비스의 원하는 태스크 수를 2 이상으로 설정하면 ALB가 트래픽을 분산하여 고가용성을 확보할 수 있습니다.
Step 6: 오토 스케일링 설정
오토 스케일링은 트래픽 변화에 따라 태스크 수를 자동으로 조절합니다. 쇼핑몰 세일 기간에 트래픽이 5배로 늘어나면 태스크도 자동으로 늘어나고, 세일이 끝나면 줄어듭니다. 수동으로 서버를 늘리고 줄이는 것에 비해 비용 효율과 대응 속도가 크게 향상됩니다.
대상 추적 정책(Target Tracking Policy)은 가장 직관적인 스케일링 방식입니다. "CPU 사용률을 70%로 유지하라"고 설정하면, 사용률이 70%를 넘으면 태스크를 추가하고, 70% 미만이면 태스크를 줄입니다. 쿨다운(60초)은 스케일링 후 다음 스케일링까지 대기하는 시간으로, 불필요한 진동을 방지합니다.
- 1서비스 → 서비스 업데이트 → 오토 스케일링 섹션
- 2최소 태스크 수: 2, 최대 태스크 수: 6
- 3스케일링 정책: 대상 추적 선택
- 4지표: ECSServiceAverageCPUUtilization, 대상 값: 70
- 5스케일 아웃/인 쿨다운: 60초
Step 7: CloudWatch 로그 확인
Fargate 환경에서는 컨테이너에 직접 SSH로 접속할 수 없습니다. 따라서 CloudWatch Logs가 유일한 로그 확인 경로입니다. 로그 인사이트(Log Insights) 기능을 사용하면 SQL과 유사한 쿼리로 대량의 로그를 빠르게 분석할 수 있습니다.
- 1AWS 콘솔 → CloudWatch → 로그 그룹
- 2/ecs/lab-app-task 로그 그룹 확인
- 3로그 스트림에서 각 태스크별 로그 확인
- 4로그 인사이트에서 에러 로그 필터링 쿼리 실행: fields @timestamp, @message | filter @message like /error/i | sort @timestamp desc | limit 20
트러블슈팅 가이드
태스크가 계속 PENDING 상태인 경우
- 보안 그룹에서 인바운드(ALB → 태스크 포트)와 아웃바운드(ECR 이미지 Pull)가 열려있는지 확인하세요.
- Fargate 태스크에 퍼블릭 IP를 할당했거나, NAT Gateway가 있는 프라이빗 서브넷을 사용하고 있는지 확인하세요. ECR에서 이미지를 Pull하려면 인터넷 접근이 필요합니다.
- Task Definition의 CPU/메모리 조합이 유효한지 확인하세요. Fargate는 특정 조합만 지원합니다.
태스크가 RUNNING 후 즉시 STOPPED 되는 경우
- CloudWatch Logs에서 컨테이너 로그를 확인하세요. 대부분 애플리케이션 시작 오류(포트 충돌, 환경 변수 누락 등)입니다.
docker run으로 로컬에서 동일한 이미지를 실행해보세요. 로컬에서 정상 동작하면 환경 변수나 네트워크 설정이 원인입니다.- 헬스 체크 경로(
/health)가 정상 응답하는지 확인하세요. 헬스 체크 실패 시 ALB가 태스크를 비정상으로 판단하여 교체합니다.
ECR 이미지 Pull이 실패하는 경우
- Fargate 태스크의 실행 역할(Execution Role)에
ecr:GetAuthorizationToken,ecr:BatchGetImage권한이 있는지 확인하세요. - ECR 리포지토리와 ECS 클러스터가 같은 리전에 있는지 확인하세요.
- VPC 엔드포인트(ECR, S3, CloudWatch Logs)를 사용하면 NAT Gateway 없이도 프라이빗 서브넷에서 ECR에 접근할 수 있습니다.
핵심 개념 확인
완성 후 테스트 가이드
- 1기본 동작 확인: ALB의 DNS 이름으로 브라우저에서 접속하여 애플리케이션이 정상 응답하는지 확인합니다.
- 2고가용성 테스트: ECS 콘솔에서 태스크 하나를 수동으로 중지합니다. 서비스가 자동으로 새 태스크를 시작하고, 중단 시간 없이 ALB가 나머지 태스크로 트래픽을 라우팅하는지 확인합니다.
- 3오토 스케일링 테스트: ab -n 10000 -c 100 http://<ALB-DNS>/ 또는 hey 도구로 부하를 주고, CloudWatch 지표에서 CPU 사용률 증가와 태스크 수 변화를 관찰합니다.
- 4이미지 업데이트 테스트: 코드를 수정하고 새 이미지를 ECR에 Push한 뒤, ECS 서비스를 "새 배포 강제"로 업데이트합니다. 롤링 업데이트가 다운타임 없이 진행되는지 확인합니다.
- 5로그 확인: CloudWatch Logs Insights에서 에러 쿼리를 실행하여 정상적으로 로그가 수집되고 있는지 검증합니다.
본인의 말로 설명해 보세요
Docker 컨테이너를 EC2에 직접 배포하는 것과 ECS Fargate에 배포하는 것의 차이를 설명해보세요.
💡 EC2 직접 배포: SSH 접속 → docker pull → docker run → 수동 관리. ECS Fargate: 서버 프로비저닝/패치/스케일링 모두 AWS가 관리, 개발자는 Task Definition만 정의...
실무에서 자주 묻는 질문
Q: Fargate 태스크의 CPU/메모리를 어떻게 결정하나요?
처음에는 최소 사양(0.25 vCPU / 0.5 GB)으로 시작하고, CloudWatch 메트릭에서 CPU/메모리 사용률을 모니터링합니다. 사용률이 지속적으로 80%를 넘으면 한 단계 올리고, 20% 미만이면 한 단계 내립니다. Fargate는 정해진 조합만 지원하므로 AWS 문서에서 유효한 조합을 확인하세요.
Q: 컨테이너 이미지를 업데이트할 때 다운타임이 발생하나요?
ECS 서비스의 롤링 업데이트 설정에 따라 다릅니다. 기본 설정에서는 새 태스크가 먼저 시작되고 헬스 체크를 통과한 후, 이전 태스크가 종료됩니다. minimumHealthyPercent=100으로 설정하면 항상 기존 태스크가 유지된 상태에서 새 태스크가 추가되므로 다운타임이 발생하지 않습니다.
Q: 프라이빗 서브넷에서 ECR 이미지를 Pull하려면 어떻게 하나요?
두 가지 방법이 있습니다. (1) NAT Gateway: 프라이빗 서브넷에서 NAT Gateway를 통해 인터넷에 접근. 간단하지만 NAT Gateway 비용(월 $32+)이 발생합니다. (2) VPC 엔드포인트: ECR, S3, CloudWatch Logs에 대한 VPC 엔드포인트를 생성. 인터넷 접근 없이 AWS 네트워크 내에서 통신하므로 보안성이 높고, 데이터 전송 비용이 절감됩니다.
확장 아이디어
- 멀티 컨테이너 태스크: 하나의 Task Definition에 웹 서버(nginx)와 앱 서버(node)를 함께 배치하는 사이드카 패턴을 구현합니다.
- 서비스 디스커버리: AWS Cloud Map을 연동하여 마이크로서비스 간 DNS 기반 통신을 구현합니다.
- ECS + CI/CD 연동: CodePipeline에서 ECR 이미지를 빌드하고, ECS 서비스를 자동 업데이트하는 파이프라인을 구축합니다.
- EKS 비교 실습: 동일한 애플리케이션을 EKS(Kubernetes)에 배포하고, ECS Fargate와 관리 복잡성/비용을 비교합니다.
- Fargate Spot 활용: 내결함성이 있는 배치 처리 태스크에 Fargate Spot을 사용하여 비용을 최대 70% 절감합니다.
Fargate CPU/메모리 조합 참고
Fargate는 아래의 유효한 CPU/메모리 조합만 지원합니다. 잘못된 조합으로 Task Definition을 생성하면 오류가 발생합니다.
| vCPU | 메모리 옵션 |
|---|---|
| 0.25 | 0.5 GB, 1 GB, 2 GB |
| 0.5 | 1~4 GB (1 GB 단위) |
| 1 | 2~8 GB (1 GB 단위) |
| 2 | 4~16 GB (1 GB 단위) |
| 4 | 8~30 GB (1 GB 단위) |
학습 정리
핵심 치트시트
리소스 정리
실습 완료 후 반드시 아래 순서대로 리소스를 정리하여 불필요한 과금을 방지하세요.
- ECS 서비스 삭제 (원하는 태스크 수 → 0 후 삭제)
- ECS 클러스터 삭제
- ALB 및 타겟 그룹 삭제
- ECR 리포지토리 삭제 (이미지 포함)
- CloudWatch 로그 그룹 삭제
- 관련 보안 그룹 삭제
- 태스크 정의 등록 해제