4. 컨테이너 기반 서비스 확장 및 운영 자동화 설계 - 100-hours-a-week/KTB4-17th-wiki GitHub Wiki
목차
1. 서비스 성장에 따른 트래픽 변화
- v2 예상 사용자 규모: DAU 28만 명, MAU 42만 명
- 전체 요청량: 평균 272 RPS, 피크 1,470 RPS
- 동시 접속자: 평균 2,333명, 피크 12,598명
- 채팅 트래픽: 월 3,276만 건의 메시지, 평균 약 12.6건/초·피크 약 70건/초
- AI 요청량: 피크 시 연습 대화 350 RPS, 시뮬레이션 52 RPS 예상
2. v1 인프라의 한계
2-1. v1의 인프라 한계
- 단일 서버 장애점: 백엔드와 AI가 한 EC2에서 실행되어 서버 장애 시 함께 중단될 수 있다.
- 트래픽에 따른 확장 어려움: 서버 수를 자동으로 늘릴 수 없었고 부하 테스트에서도 애플리케이션의 처리 한계가 확인됐다.
2-2. 데이터베이스 한계
- 단일 DB 인스턴스 의존: DB가 EC2 한 대에만 배치되어 있어 장애 발생 시 데이터 읽기,쓰기 기능이 중단될 수 있다.
- DB 처리량 확장 제약: 백엔드의 DB 요청이 단일 인스턴스로 집중되어 트래픽 증가 시 CPU와 메모리,스토리지 I/O가 처리량의 한계가 될 수 있다.
2-3. 네트워크 보안 한계
- 네트워크 분리 부족: 애플리케이션과 DB가 모두 퍼블릭 서브넷에 있고 사용자가 백엔드 EC2에 직접 접속하는 구조라 외부 요청을 받는 영역과 내부 서비스를 분리하기 어렵다. 인프라 한계 및 보안 취약점 추가
2-4. 증가한 트래픽에 대한 부하 테스트 요약
v2 예상 트래픽(DAU 28만 명, 피크 RPS 1,470)을 바탕으로 부하 테스트를 진행한 결과, 현재 인프라는 애플리케이션과 데이터베이스 모두 한계에 도달함이 확인되었습니다.
- 1차 부하 테스트: 단순 조회 API 테스트에서도 애플리케이션 CPU가 목표 트래픽을 처리하지 못하고 단일 인스턴스 용량이 한계에 도달했습니다.
- 2차 부하 테스트: 트래픽을 낮추어 테스트했음에도 DB CPU 사용률이 최대 76.2%를 기록하여, DB가 주요 병목 지점이 될 가능성을 확인했습니다.
- 1차 및 2차 부하 테스트 상세 결과 보기
3. v2아키텍처
| 구성 요소 | 실행 환경 | 인스턴스 사양 |
|---|---|---|
| 프론트엔드 | Amazon S3 + CloudFront | - |
| 메인 백엔드 | Amazon ECS Fargate | Task당 1 vCPU / 2 GiB |
| 채팅 서버 | Amazon ECS Fargate | Task당 0.5 vCPU / 1 GiB |
| AI 백엔드 | Amazon ECS Fargate | Task당 0.5 vCPU / 1 GiB* |
| 메인 데이터베이스 | Amazon RDS for MySQL | db.r6g.large (2 vCPU / 16 GiB) |
| 채팅 메시지 브로커 | Amazon ElastiCache for Valkey | cache.t4g.small (약 1.4 GB) |
3-1. v2 한계 개선
인프라
- 단일 서버 장애점 해결 : v2에서는 ALB와 두 가용 영역의 ECS Fargate Task를 추가해 요청을 분산하도록 구성했다.
- 확장 어려움 해결 : v2에서는 백엔드와 채팅 Task에 각각 Auto Scaling을 설계했다.
데이터베이스
- DB의 단일 장애점 해결 : v2 그림에는 다른 가용 영역의 RDS 대기 인스턴스를 추가해 장애 시 전환할 수 있는 구성이 표시되어 있다.
- DB 수직 확장 경로 확보: v2는 RDS Multi-AZ로 전환해 향후 DB 성능이 부족해지면 대기 인스턴스를 먼저 증설하고 주 인스턴스로 전환하는 방식으로 사양을 높일 수 있다.
네트워크 보안
- 네트워크 분리 문제 해결 : v2에서는 내부 서버의 퍼블릭 서브넷 배치하는 방식으로 외부 요청을 ALB가 받고 애플리케이션과 DB를 각각 프라이빗 서브넷에 배치하도록 분리했다.
3-2 애플리케이션 구조 설계
결론: ECS Fargate를 선택한다.
아키텍처 근거: EC2 Capacity 관리, 인스턴스 운영 및 Task 배치에 대한 관리 부담을 AWS에 위임하고, 애플리케이션의 Task와 서비스 운영에 집중하기 위해 Fargate를 선택하였다.
3-3 DB 구조 설계
결론: 초기 DB는 읽기·쓰기를 하나의 RDS Writer에서 처리하며 인스턴스 사양은 db.t4g.medium(2 CPU / 4 GiB)로 결정했다. 가용성을 위해 다른 AZ에 대기 인스턴스를 둔다.
아키텍처 근거: 로컬 DB 직접 부하 테스트에서 피크 읽기 2,214 QPS와 쓰기 982 QPS를 처리했다. DB CPU는 평균 0.57 vCPU와 최대 1.55 vCPU를 사용했으며 2 vCPU 기준 평균 사용률은 28.5%였다. 이를 근거로 초기에는 읽기 DB를 별도로 확장하기보다 단일 RDS Writer에서 읽기와 쓰기를 처리하는 구성을 선택했다. 고가용성을 우선해 다른 가용 영역에 대기 인스턴스를 두는 RDS Multi-AZ 구성을 적용한다.
인스턴스 사양 선택 근거: 로컬 테스트에서 DB 메모리 최대 사용량은 1.18GiB였다. 프로젝트가 정한 메모리 사용률 기준 50%를 적용하면 2.36GiB가 필요해 4GiB RAM을 제공하는 db.t4g.medium 을 선택하였다.
3-4 메세지 브로커 도입
결론: 두 채팅 서버 사이에 메시지 브로커를 도입하고 ElastiCache for Valkey의 Pub/Sub으로 온라인 메시지 알림을 전달한다.
도입 근거: 서로 다른 채팅 서버에 접속한 두 사용자에게 메시지를 즉시 알리려면 서버 간 이벤트 전달 경로가 필요하다.
선택 근거: 메시지 원본은 RDS에 저장하고 Valkey Pub/Sub은 각 서버에 온라인 알림을 전달하는 용도로 사용한다.
그외 대안 비교: DB polling, SQS, Kafka, redis OSS를 간단하게 비교했다.
3-5 frontend 구성
결론: v1과 동일하게 정적 파일을 S3에 저장하고 CloudFront를 통해 제공하는 구조로 결정했다.
4. 서비스 확장 전략
4-1. 서버 확장 방식
결론: BE와 WebSocket은 부하 특성이 다르기 때문에 각각 다른 지표를 기준으로 확장한다.
- BE: 요청량을 나타내는 RPS를 기준으로 확장한다.
- WebSocket: 실제 연결 부하를 나타내는 Task당 동시 Connection 수를 기준으로 확장한다.
- CPU와 Memory는 Primary Metric이 포착하지 못하는 자원 부하에 대응하기 위한 Secondary Metric으로 사용한다.
확장 방식 선택 근거: 이를 통해 일반적인 트래픽 증가는 Primary Metric으로 대응하고, 동일한 요청량 또는 Connection에서도 처리 비용이 증가하여 자원이 먼저 포화되는 상황은 Secondary Metric으로 대응한다.
4-2. DB확장 방식
확장 방식: 초기 사이징을 초과하면 기존에 복제해서 대기 상태이던 RDS를 수직확장 후 Blue/Green으로 전환을 적용한다.
확장 방식 선택 근거: 초기 인스턴스를 피크 트래픽 기준으로 산정했으므로 매일 부하에 따라서 크기를 변경할 필요는 없다. 트래픽이 처리 용량을 초과하면 복제된 RDS의 사양을 높이고 blue-green방식으로 전환해서 사용자 영향을 줄인다.(전환중 쓰기 중단이 발생할 수 있다)
그외 고려한 확장 방식
5. v2 CI/CD 파이프라인
5-1. V2 CI 파이프라인 설계
Workflow: Backend CI
└─ Job: backend-ci
│
├─ Step 1 정적 검사
│ ↓
├─ Step 2 단위 테스트
│ ↓
├─ Step 3 테스트 DB 기동
│ ↓
├─ Step 4 핵심 API 테스트
│ ↓
└─ Step 5 빌드