테마
13. Docker와 ECS Fargate 배포
VM 배포의 한계를 넘어가려면 "서버에 명령을 친다"가 아니라 "이미지와 선언으로 배포한다"는 발상으로 넘어가야 한다. Docker와 ECS Fargate는 그 전환점이다.
학습 목표
- Docker가 VM 배포의 어떤 문제를 줄여주는지 설명할 수 있다
- ECS의 핵심 단위인 Cluster, Task Definition, Service를 구분할 수 있다
- ECR, ALB, Service Connect, Secrets, Auto Scaling이 ECS 배포에서 어떻게 연결되는지 이해한다
- GitHub Actions와 OIDC를 사용한 컨테이너 배포 자동화의 큰 흐름을 설명할 수 있다
- 언제 Lightsail을 유지하고, 언제 ECS Fargate로 넘어가야 하는지 판단할 수 있다
1. 왜 VM에서 컨테이너로 넘어갈까?
Lightsail이나 EC2에 직접 배포할 때의 문제는 보통 같다.
- 서버마다 환경 차이가 생긴다
- 배포 명령이 늘어난다
- 여러 서비스가 섞일수록 관리가 복잡해진다
- 롤백과 헬스 체크를 구조적으로 관리하기 어렵다
Docker는 이 문제를 "실행 환경 자체를 이미지로 묶는다" 는 방식으로 줄여 준다.
2. Docker에서 알아야 할 최소 개념
| 개념 | 의미 |
|---|---|
| Image | 실행에 필요한 파일 시스템과 설정의 묶음 |
| Container | 이미지를 실제로 실행한 프로세스 |
| Dockerfile | 이미지를 어떻게 만들지 적는 설계도 |
| Registry | 이미지를 저장하는 저장소 |
| Compose | 여러 컨테이너를 함께 다루는 로컬/CI용 정의 |
원문의 Node.js/Redis 예제는 여기서도 중요 포인트만 남긴다.
- 앱과 Redis를 서로 다른 서비스로 분리한다
.dockerignore로 민감 파일과 불필요한 파일을 이미지에 넣지 않는다- 환경 변수는 이미지에 박지 않고 실행 시 주입한다
컨테이너 하나에 프로세스 하나를 기본으로 본다
VM에서 쓰던 PM2 다중 프로세스 발상은 컨테이너 오케스트레이션 환경과는 잘 맞지 않는다. ECS에서는 보통 컨테이너 하나 = 메인 프로세스 하나로 보고, 확장은 서비스 레벨에서 해결한다.
3. 샘플 앱은 왜 컨테이너화하는가?
이 시리즈에서 샘플 앱 자체는 주인공이 아니다.
컨테이너화에서 중요한 것은 다음 세 가지다.
- 빌드 결과물이 재현 가능해야 한다
- 테스트 환경과 실행 환경 차이를 줄여야 한다
- 외부 서비스 연결이 환경 변수와 네트워크로 분리되어야 한다
즉, Node.js든 Java든 Go든 핵심은 같다.
- Dockerfile 작성
- 런타임 이미지 선택
- 환경 변수 주입
- CI에서 동일한 방식으로 테스트
4. 2026 기준 Dockerfile 기본 감각
원문은 Node.js 18과 단순한 Docker 흐름을 다루지만, 현재 기준에서는 아래를 같이 보는 편이 낫다.
- Node.js는 20 또는 22 LTS 계열 기준으로 본다
- 기본 베이스 이미지는 무조건 Alpine보다
slim계열이 더 무난한 경우가 많다 - 멀티 스테이지 빌드로 빌드 도구와 런타임을 분리하는 것이 일반적이다
.env를 이미지에 복사하지 않는다
왜 Alpine을 무조건 기본으로 두지 않을까?
작고 빠르다는 장점은 있지만, 네이티브 모듈이나 musl libc 이슈 때문에 운영에서 예상치 못한 문제를 만날 수 있다.
특별한 이유가 없으면 node:22-slim 같은 균형 잡힌 베이스가 더 실용적이다.
5. ECR은 AWS 안의 이미지 저장소다
ECR(Elastic Container Registry)은 Docker Hub와 비슷하지만 AWS 안에서 권한, 네트워크, 배포 흐름을 더 자연스럽게 묶기 좋다.
운영 감각으로 중요한 것
- Private repository를 기본으로 본다
latest태그만 믿지 않는다- 커밋 해시 기반 태그를 쓴다
- 가능하면 immutable tag 전략을 사용한다
- 이미지 스캔을 활성화한다
즉, 이미지는 "무엇을 실행했는지 다시 설명 가능한 단위"여야 한다.
6. ECS 핵심 구성요소
| 구성요소 | 역할 |
|---|---|
| Cluster | ECS 리소스를 논리적으로 묶는 단위 |
| Task Definition | 컨테이너 이미지, 포트, CPU/메모리, 시크릿, 로그 설정 등 실행 정의 |
| Task | Task Definition을 실제로 한 번 실행한 것 |
| Service | 원하는 개수의 Task를 유지하고 롤링 업데이트/자가 복구를 담당 |
| Launch Type | Fargate 또는 EC2 |
Fargate를 기본으로 보는 이유
- 서버를 직접 관리하지 않는다
- 오토 힐링과 배포 모델이 단순하다
- 작은 팀이 운영하기 좋다
EC2 Launch Type은 GPU, 특수 커널, 더 낮은 단가 최적화 같은 특수 이유가 있을 때 고려하는 편이 자연스럽다.
7. ECS Fargate 기본 아키텍처
이 구조로 넘어오면 배포 단위가 "서버"가 아니라 "서비스가 유지해야 할 Task 집합"으로 바뀐다.
8. Task Definition에서 특히 중요한 항목
Task Definition은 ECS 배포의 핵심 문서다.
운영에서 특히 중요하게 보는 항목은 아래다.
- 이미지 URI
- CPU / 메모리
- 포트 매핑
- 로그 설정
executionRoleArntaskRoleArnsecretsawsvpc네트워크 설정
여기서 중요한 감각은 두 가지다.
- 실행을 위한 권한과 앱 코드 권한을 분리한다
- 환경 변수는 plain text보다
secrets주입을 우선한다
Log Group 미생성은 흔한 태스크 실패 원인이다
CloudWatch Logs 설정을 켜 놓고 Log Group을 준비하지 않으면 태스크가 뜨지 않는 경우가 있다. ECS는 앱 코드보다 주변 설정이 빠지면 실패하는 경우가 많다.
9. Public ALB, Private Task가 운영 기본값이다
학습 데모에서는 Public IP를 가진 태스크를 잠깐 띄워볼 수 있다.
하지만 운영 구조는 보통 이렇게 간다.
- ALB는 Public Subnet
- ECS Task는 Private Subnet
- 외부 공개는 ALB 한 지점만
이렇게 하면:
- 태스크가 직접 인터넷에 노출되지 않고
- Security Group 경계가 분명해지고
- 교체와 확장이 쉬워진다
다만 Private Subnet 태스크가 이미지 pull, 로그 전송, 시크릿 조회를 해야 한다면 NAT Gateway나 VPC Endpoint 설계를 같이 봐야 한다.
10. Service Connect는 내부 서비스 이름을 안정적으로 만든다
서비스가 하나일 때는 잘 안 느껴지지만, 앱과 Redis, 워커, API가 나뉘기 시작하면 내부 통신 주소 관리가 바로 복잡해진다.
Service Connect의 가치는 다음에 있다.
- 서비스 이름으로 내부 통신 가능
- 동적으로 뜨고 지는 태스크의 IP를 직접 추적하지 않아도 됨
- 내부 서비스 디스커버리를 ECS 안에서 더 자연스럽게 처리
즉, 예전의 "IP 고정 + 환경 변수" 감각에서 "서비스 이름 기반 내부 연결" 로 넘어가는 지점이다.
11. 시크릿 주입은 이미지가 아니라 런타임에서
운영에서는 이 흐름이 기본이다.
- 이미지 빌드 시 시크릿을 넣지 않는다
- ECS Task Definition에서 시크릿 ARN을 참조한다
- 런타임에 컨테이너에 주입한다
이렇게 하면:
- 이미지를 안전하게 재사용할 수 있고
- 환경별로 다른 값을 쉽게 바꿀 수 있고
- 시크릿 회전과 감사 추적이 쉬워진다
Secrets Manager와 Parameter Store의 선택 기준은 12장에서 본 내용 그대로 이어진다.
12. Auto Scaling은 "서버 늘리기"보다 Desired Count 관리다
ECS에서 가장 흔한 오토 스케일링 대상은 ecs:service:DesiredCount다.
즉, 원하는 Task 수를 지표에 따라 자동 조정한다.
대표 지표:
- CPU 사용률
- 메모리 사용률
- ALB target당 요청 수
여기서 중요한 것은 임계값 그 자체보다 쿨다운과 과민 반응 방지다.
- 너무 빠르게 scale out/in 하면 흔들린다
- 앱 시작 시간이 긴데 헬스 체크가 너무 급하면 실패로 본다
- grace period를 충분히 줘야 한다
13. 배포 실패를 자동으로 되돌리는 장치도 같이 본다
ECS 운영에서는 새 태스크가 떴다고 끝이 아니다.
- 헬스 체크 통과 여부
- 새 버전 태스크가 안정적으로 떠 있는지
- 실패 시 이전 정상 버전으로 되돌릴 수 있는지
이런 이유로 deployment circuit breaker와 롤링 업데이트 설정이 중요하다.
배포 자동화는 "새 버전 올리기"보다 실패를 안전하게 멈추는 설계까지 포함해야 한다.
14. GitHub Actions에서 AWS까지의 현대적 CI/CD 흐름
2026년 기준 추천 흐름은 아래와 같다.
핵심은 두 가지다.
- AWS API 호출은 OIDC로 임시 권한을 받아서 한다
- 이미지 태그는 커밋 해시처럼 설명 가능한 값으로 남긴다
실무에서는 여기에 buildx 기반 멀티 아키텍처 빌드, ECR 스캔, 이미지 캐시 최적화 등이 붙을 수 있다.
15. Graviton, Fargate Spot, 관측성도 같이 본다
운영 단가와 품질을 생각하면 아래도 같이 알아 두면 좋다.
Graviton / ARM64
- 많은 런타임이 ARM64를 안정적으로 지원한다
- 비용 효율이 좋은 경우가 많다
Fargate Spot
- 중단 허용 가능한 작업에는 비용 절감 효과가 크다
- 핵심 API 트래픽보다는 배치·비동기 작업에 더 잘 맞는다
CloudWatch Container Insights
- 태스크 레벨 CPU, 메모리, 로그 흐름을 보는 기본 도구
- ECS에서는 배포 성공보다 이후 관측이 더 중요해지는 순간이 빠르게 온다
16. 언제 Lightsail을 유지하고, 언제 ECS로 가야 할까?
| 질문 | Lightsail 유지 | ECS Fargate 전환 |
|---|---|---|
| 서버 수 | 1대 중심 | 여러 태스크/서비스 |
| 배포 복잡도 | 낮음 | 중간 이상 |
| 서비스 디스커버리 | 거의 불필요 | 필요 |
| 자동 복구/확장 | 단순 수준 | 중요 |
| 팀 운영 성숙도 | 초기 | 중기 이상 |
즉, ECS는 "무조건 더 좋은 것"이 아니라 운영 복잡도를 더 잘 흡수할 수 있는 구조다.
17. PR 리뷰 체크리스트
- Dockerfile이 빌드 도구와 런타임을 적절히 분리하는가
- 이미지에
.env나 민감 파일이 들어가지 않는가 - ECR 태그 전략이
latest에만 의존하지 않는가 - Task Execution Role과 Task Role이 섞여 있지 않은가
- ALB는 Public, ECS Task는 Private라는 기본 구조를 지키는가
- 시크릿이 런타임 주입되는가
- Auto Scaling 기준과 grace period가 과하게 공격적이지 않은가
- GitHub Actions가 장기 Access Key 대신 OIDC를 사용하는가
핵심 정리
- Docker는 실행 환경을 표준화하고, ECS Fargate는 그 컨테이너를 서비스 단위로 복구·확장·배포하게 해 준다
- 운영 기본 구조는
ECR -> ECS Service -> ALB -> Private Task -> Managed data services에 가깝다 - 시크릿, 권한, 로그, 오토스케일링까지 같이 설계해야 진짜 배포가 된다
- GitHub Actions와 AWS 자동화는 OIDC 기반 임시 권한 모델이 현재 기본이다
관련 문서
출처
- AWS 공식 문서
- 원문 자료:
ing-0019-aws-배포.md