102 Docker Compose 운영 - 100-hours-a-week/KTB4-3rd-CLOUD GitHub Wiki
EC2 인스턴스 스펙
DAU가 약 1500명인 초기 서비스 규모에 피크 타임에 커뮤니티는 약 1.25 RPS의 조회 트래픽과 매칭 및 정산은 약 0.075 RPS의 쓰기 트래픽이 발생한다. 하나의 EC2 인스턴스에서 Next.js·Spring Boot·MySQL 컨테이너로 독립적인 애플리케이션 공간에서 EC2의 인스턴스의 자원을 공유한다.
현재 모여타는 Dev와 Prod 가 각각의 인스턴스로 독립적으로 운영한다. Dev 는 실제 사용자 트래픽 없이 개발자들의 공동 테스트 환경이고, Prod는 실제 사용자의 서비스 환경으로 안정적인 서빙을 위해서는 추가 메모리나 리소스가 필요하다.
모여타는 V1 단계에서 일반적인 API 웹 서비스와 동일하게 평균 CPU 사용량이 높지 않고 출퇴근 시간대에 짧은 피크가 발생하는 트래픽 특성을 가지고 있다. 따라서 지속적인 CPU의 높은 성능이 필요하지 않기 때문에 T 계열의 버스트형 인스턴스를 선택했다.
| 인스턴스 | 사양 |
|---|---|
| t3.large | 2 vCPU / 8 GiB |
| t3.medium | 2 vCPU / 4 GiB |
| t3.medium + swap | 2 vCPU/ 4 GiB + |
위의 인스턴스 중에 최종 선택은 Dev는 t3.medium, Prod는 t3.medium + swap 이다.
결정 이유1. 인스턴스 비용
결정 이유2. swap: 인스턴스 OOM 대비책
컨테이너 구성
모여타를 구성하는 애플이케이션 서비스는 독립적인 컨테이너로 분리해 아래의 역할을 수행한다.
- Nginx : 외부 HTTPS 진입점과 Reverse Proxy
- Next.js: SSR과 프론트엔드 제공
- Spring Boot: 회원·게시글·매칭·정산·WebSocket
- MySQL: 영속적인 서비스 상태 저장
- FastAPI: AI 요청과 외부 AI API 연동(V1에서 제외)
컨테이너별 초기 리소스 가정
| 컨테이너 | CPU 제한 | RAM 제한 | Swap |
|---|---|---|---|
| Nginx | 0.25 vCPU |
128MB |
사용 금지 |
| Next.js SSR | 0.5 vCPU |
512MB |
사용 금지 |
| Spring Boot | 0.75 vCPU |
768MB |
최대 256MB |
| MySQL | 0.5 vCPU |
768MB |
사용 금지 |
전체 컨테이너 RAM의 상한는 2.2 GB로 나머지의 경우 Docker, SSM, CloudWatch Agent, 파일 캐시, 여유 공간으로 사용한다.
도커 네트워크 설정
V1에서의 컨테이너간 통신 흐름은 다음과 같다. 여기서 보안성이 중요한 MySQL 컨테이너는 외부 인터넷이나 Nginx, Next.js에 직접 노출되면 안된다. SpringBoot 만 DB에 접근할 수 있는 권한을 가진다. 따라서 네트워크 세그멘테이션 패턴으로 DB와 다른 서비스의 네트워크를 격리한다.
사용자
│
│ HTTPS :443
▼
Nginx
├── / ──> Next.js :3000
├── /api ──> Spring Boot :8080
└── /ws ──> Spring Boot WebSocket :8080
Next.js ────────> Spring Boot :8080
Spring Boot ────> MySQL :3306
도커 네트워크 네이밍 규칙은 프로젝트명 - 용도/목적 - 환경 으로 작성한다.
moyeota-app-dev
- nginx
- next.js
- spring
moyeota-db-dev
- spring
- mysql
컨테이너 간 의존성과 재시작
브라우저에서 API를 호출하는 경우 실제 네트워크 흐름은 다음과 같을 수 있다.
브라우저 → Nginx → Spring Boot
Next.js의 서버 사이드 렌더링 과정에서 API를 호출한다면 다음 의존성도 발생한다.
Next.js 서버 → Spring Boot
따라서 프론트엔드 → 백엔드는 항상 컨테이너 시작 의존성을 의미하지 않는다. 브라우저가 Nginx를 통해 백엔드에 직접 접근하는 구조라면 Next.js 컨테이너는 백엔드가 없어도 시작할 수 있다.
호스트 즉 인스턴스가 리부팅할 때 모든 컨테이너가 멈추거나, 컨테이너에서 오류가 발생해서 재시작할 수 있다.
모든 컨테이너는 unless-stopped 정책을 도입해 수동 중지 이외의 어떤 이유로 멈추든 재시작하도록 한다.
컨테이너 실행 순서
MySQL 컨테이너 → DB 마이그레이션 → Spring Boot 컨테이너 → Next.js 컨테이너 → Nginx 컨테이너 순으로 depends_on을 하면 된다.
services:
spring:
depends_on:
mysql:
condition: service_healthy
next:
depends_on:
spring:
condition: service_healthy
nginx:
depends_on:
next:
condition: service_healthy
spring:
condition: service_healthy
이미지 태그 방식
ECR 리포지토리에 프로젝트명/컴포넌트로 작성하고 이미지 태그는 sha로 관리한다.
- ECR Repository
moyeota/api
moyeota/nginx
moyeota/next
- Image
moyeota/api:sha-a1b2c3d4