4단계 컨테이너 기반 서비스 확장 및 운영 자동화 설계(v2) - 100-hours-a-week/KTB4-17th-wiki GitHub Wiki

서비스 소개

앱 종류 : 소개팅 앱(*23#)

  • 우리 서비스의 타겟층 : 20~30대
  • 우리 서비스의 핵심 서비스 : AI 연습대화(매칭 성공률 높여주기)
  • 1일 사용 시간 : 10~12분
  • 피크 시간 : 오후 8시~10시
image

목차

  1. 현재 서비스의 성장 상황
  2. 기존 인프라의 한계
  3. 서비스 확장 전략
  4. v2 아키텍처
  5. v2 CI/CD 파이프라인
  6. 장애 및 한계

1. 현재 서비스의 성장 상황

소개팅 앱을 출시한 뒤, 목표로 삼았던 AI 소개팅 앱 '미러'를 제치고 국내 2~3위 소개팅 앱 '글램'에 준하는 규모로 성장했다.

  • v1보다 사용자가 160배 증가했다.
  • v2에서는 사주분석, 관상분석, 채팅자동 종료의 기능이 추가된다.
  • 인프라팀은 사용자를 유지하기 위해 가용성, 트래픽이 몰릴때를 대비한 확장성을 챙겨야 한다.

1-1 v2 예상 사용자 및 트래픽

지표 v1 v2
DAU 1,775명 280,000명
MAU 2,795명 420,000명
평균 RPS 6 RPS 272 RPS
피크 RPS 33 RPS 1,470 RPS
지표 산정 근거

DAU·MAU 산정

글램 창업 시점 DAU: 500명
2022년 8월 글램 DAU: 80,000명

DAU 증가 배수
= 80,000 ÷ 500
= 160배

v2 예상 DAU
= 1,775 × 160
= 약 280,000명

v2 예상 MAU
= 280,000 × 1.5
= 420,000명

RPS 산정

평균 RPS
= 280,000 × 6 × 14 ÷ 86,400
= 약 272 RPS

피크 RPS
= 272 × 5.4
= 약 1,470 RPS

동시 사용자 산정

평균 동시 사용자
= DAU × 1일 세션 수 × 평균 세션 시간 ÷ 86,400
= 280,000 × 6 × 120초 ÷ 86,400초
= 약 2,333명

피크 동시 사용자
= 2,333 × 5.4
= 약 12,598명

참고 자료

1-2. 사용자 행동과 기능별 사용량

사용자 행동 가정

  • 사용자는 하루 평균 6회 앱에 방문한다.
  • 사용자 1인당 월평균 매칭 횟수는 15.6회이다.
  • 전체 채팅방에서 월간 약 6.5TB의 이미지가 전송된다.
사용자 활동 시작
      │
      ├─ 전체 프로필 조회 2회
      │  └─ 회당 10명, 총 20명 확인
      │
      ├─ 상세 프로필 조회 4회
      │
      ├─ 좋아요 등록 6회
      │  └─ 프로필 확인 인원의 30%
      │
      └─ 세션 연결 요청 4회
기능별 사용량 및 트래픽 계산

일반 기능

기능 사용자 1인당 사용량 평균 처리량 피크 처리량
전체 프로필 조회 API 2회/일 38 RPS 205 RPS
상세 프로필 조회 API 4회/일 77 RPS 415 RPS
좋아요 등록 API 6회/일 116 RPS 626 RPS
매칭 및 채팅방 생성 API 15.6회/월 38 RPS 205 RPS
세션 연결 요청 4회/일 77 req/s 420 req/s
채팅 메시지 월 6,552만 건 25.3 msg/s 136.5 msg/s

채팅 사용량

사용자 한 명은 하나의 채팅방에서 평균 9건의 텍스트 메시지와 1건의 이미지 메시지를 전송한다고 가정한다.

항목 계산식 월간 예상량
채팅방 생성 수 매칭 수와 동일 3,276,000개
텍스트 메시지 수 3,276,000 × 9건 29,484,000건
이미지 메시지 수 3,276,000 × 1건 3,276,000건
전체 메시지 수 텍스트 + 이미지 32,760,000건
텍스트 본문 저장량 29,484,000 × 10자 × 3B 약 884.52MB
이미지 저장량 3,276,000 × 2MB 약 6,552GB
이미지 저장량 환산 6,552GB ÷ 1,000 약 6.5TB

참고 자료


AI 기능 이용 제한

기능 사용자 1인당 최대 사용량
AI 연습 대화 30회
AI 시뮬레이션 5회

AI 기능 시나리오별 예상 처리량

기능 시나리오 사용자 1인당 사용량 평균 처리량 피크 처리량
AI 연습 대화 낙관 10회 32 RPS 175 RPS
AI 연습 대화 기준 20회 64 RPS 350 RPS
AI 연습 대화 최악 30회 97 RPS 525 RPS
AI 시뮬레이션 낙관 1회 3 RPS 17.5 RPS
AI 시뮬레이션 기준 3회 9 RPS 52 RPS
AI 시뮬레이션 최악 5회 16 RPS 87.5 RPS

↑ 목차로 이동


2.기존 인프라의 한계

  • 모든 애플리케이션이 단일 인스턴스에서 실행되어 장애와 자원 경합이 서비스 전체로 전파되는 단일 장애 지점이 존재한다.
  • 모든 인스턴스가 퍼블릭 서브넷에 있어 외부 공격에 대한 위험이 있다.
  • 부하 테스트 결과 애플리케이션과 DB의 CPU 한계가 확인되어 현재 구조로는 v2의 목표 트래픽을 안정적으로 처리할 수 없다.

2.1 증가한 트래픽에 대한 부하 테스트

v2에서는 사용자와 트래픽이 다음과 같이 증가할 것으로 예상된다.

  • v2에서는 DAU가 1,775명에서 28만 명으로 증가하면서 평균 RPS는 6에서 272로, 피크 RPS는 33에서 1,470으로 증가할 것으로 예상된다. 피크 시간대의 동시 사용자 역시 약 12,598명까지 증가한다.

  • 기존 인프라는 백엔드와 데이터베이스가 각각 단일 t3.medium 인스턴스로 구성되어 있으며, 트래픽 증가에 따라 인스턴스를 자동으로 확장하거나 요청을 여러 인스턴스로 분산할 수 없는 구조이다.

1차 부하 테스트 결과

결론: 현재 백엔드 구조로는 v2의 목표 트래픽을 안정적으로 처리할 수 없다.

- 테스트 범위: 272~1,470 RPS
- 동시 사용자: 2,333~12,598명
- 테스트 대상: 단순 읽기 및 조회 API

1차 부하 테스트 결과

테스트 결과

실제 비즈니스 로직이 포함되지 않은 단순 읽기 및 조회 API를 대상으로 테스트했음에도 애플리케이션 CPU가 목표 트래픽을 처리하지 못했다. 테스트 당시 DB 상태를 수집하지 못해 데이터베이스 부하는 확인할 수 없었지만, 애플리케이션 계층만으로도 단일 인스턴스의 처리 용량이 한계에 도달했음을 확인했다.


2차 부하 테스트 결과

결론 : 애플리케이션뿐만 아니라 데이터베이스 역시 v2 트래픽을 처리하기 위한 확장과 최적화가 필요하다.

- 테스트 범위: 200~800 RPS
- 동시 사용자: 500~5,600명
- DB CPU 최대 사용률: 76.2%

2차 애플리케이션 부하 테스트 결과
2차 애플리케이션 부하 테스트 결과


2차 부하 테스트 DB CPU 사용량
DB CPU 사용량

테스트 결과

1차 테스트보다 RPS와 동시 사용자 수를 줄였음에도 DB CPU 사용률이 최대 76.2%까지 상승했다. 이는 현재 데이터베이스가 목표 피크 트래픽인 1,470 RPS에 도달하기 전부터 가용 자원의 상당 부분을 사용하고 있음을 의미한다. 향후 복잡한 쿼리와 신규 기능이 추가되면 데이터베이스가 주요 병목 지점이 될 가능성이 높다.



## 2.2 단일 장애 지점과 가용성 부족

결론: 단일 인스턴스에 여러 애플리케이션이 결합되어 있어 장애 격리가 어렵고, 인스턴스 장애가 서비스 전체 장애로 이어지는 단일 장애 지점이 존재한다.

v1에서는 백엔드와 AI 애플리케이션이 단일 인스턴스 내부의 컨테이너로 실행된다. 이로 인해 하나의 장애가 같은 인스턴스의 다른 애플리케이션으로 전파될 수 있다.

v1 인프라 구성

확인된 문제

  • 백엔드 애플리케이션이 중단되면 AI, 채팅, 프로필 조회 등 모든 기능을 사용할 수 없다.
  • 특정 컨테이너가 CPU나 메모리를 과도하게 사용하면 같은 인스턴스에서 실행되는 다른 컨테이너에도 영향을 준다.
  • 인스턴스 자체에 장애가 발생하면 백엔드와 AI 컨테이너가 동시에 중단된다.
  • 장애가 발생한 인스턴스를 대체할 서버가 없어 서비스 전체의 가용성이 저하된다.

2.3 보안성 부족

결론: 외부에 공개할 구성 요소를 최소화하고, 애플리케이션과 DB를 프라이빗 서브넷으로 분리하며, 컨테이너에는 최소 권한 원칙을 적용해야 한다.

확인된 문제

  • 외부 접근이 필요하지 않은 애플리케이션과 데이터베이스도 퍼블릭 서브넷에 배치되어 있다.
  • 컨테이너가 root 권한으로 실행되면 취약점이나 잘못된 권한 설정과 결합되어 호스트 권한으로 확대될 위험이 커진다.
  • 컨테이너에 호스트 파일 시스템이나 과도한 권한이 부여되면 같은 인스턴스의 다른 컨테이너와 호스트 자원까지 영향을 받을 수 있다.
  • 읽기 전용 파일 시스템, 비루트 사용자, CPU·메모리 제한 등의 컨테이너 보안 정책이 적용되어 있지 않다.

↑ 목차로 이동


3. 서비스 확장 전략

3-1 서버의 수평 확장

3-2 데이터베이스의 수평 확장

**[DB확장에서 고려해야 할 사항]**

- **데이터베이스 확장 및 축소**: 트래픽이 오후 8시부터 10시 사이에 집중되며, 피크 시간과 비피크 시간의 트래픽 차이가 약 9배에 달한다. 따라서 평상시에는 작은 사양을 유지하고, 트래픽 증가 시 확장한 뒤 감소 시 다시 축소할 수 있는 구조를 고려해야 한다.

- **읽기·쓰기 비율**: 읽기와 쓰기의 비율은 약 4:3으로, 일반적인 서비스에 비해 쓰기 비중이 높은 편이다. 따라서 데이터베이스 구조를 설계할 때 읽기 성능뿐만 아니라 쓰기 트래픽과 쓰기 I/O를 여러 노드로 분산할 수 있는 방안을 중점적으로 고려해야 한다.

(1) Replication

결론: writeDB의 스펙을 고정하기 떄문에 중단시간이나 리샤딩에서 자유롭게 축소,확장이 가능해 해당 방식을 채택했다. ┌──────────────┐ │ Backend │ └──────┬───────┘ │ ┌────────────┴────────────┐ │ │ Write Read │ │ ▼ ┌────────┴────────┐ ┌──────────────┐ ▼ ▼ │ Write DB │ 복제 ┌──────────────┐ ┌──────────────┐ │ 상시 고정 │ ─────▶ │ Read DB 1 │ │ Read DB 2 │ └──────────────┘ │ 상시 │ │ 피크 때만 │ │ └──────────────┘ └──────▲───────┘ └──────────────────── 복제 ─────────────┘

image

[replica 동작 방식]

  1. 사용자 write 요청
  2. write DB가 데이터를 변경
  3. write DB가 변경 내용을 log에 기록
  4. write DB가 클라이언트에 성공 응답 전송 (읽기 DB를 기다리지 않음)
  5. read DB가 log를 가져와 relay log에 저장
  6. replica의 스레드가 log를 순서대로 적용
  7. 시간이 지나면 같은 상태로 맞춰짐

(2) 쓰기 DB만 샤딩

결론: 샤딩을 통한 확장은 가능하지만 리샤딩(데이터 재분배)을 통한 축소가 어렵고 전환 과정에서 쓰기 중단이 필요해 채택하지 않았습니다.

쓰기 DB 샤딩 구조
  • Read/Write DB를 분리한 뒤 Write DB만 샤딩하여 쓰기 I/O 부하를 여러 노드로 분산하는 구조이다.
  • 현재 서비스에는 여러 DB 테이블을 함께 조회해야 하는 경우가 있다.
  • 피크 시간과 비피크 시간의 트래픽 차이가 약 9배 발생한다.
  • 피크 시간에 맞춰 확장된 상태를 계속 유지하면 리소스 낭비가 크기 때문에, 비피크 시간에는 샤드를 축소하는 리샤딩 과정이 필요하다.

현재 구조에서 샤드 축소가 어려운 이유

리샤딩을 통한 샤드 축소 순서

  1. 제거할 샤드를 선정하고 축소 작업을 준비한다.
  2. 제거할 샤드가 담당하던 키 범위를 어느 샀드로 이전할지 결정한다.
  3. 기존 데이터를 새로운 담당 샤드로 복사한다.
  4. 복사 중 제거 대상 샤드에 들어온 추가 쓰기를 새로운 담당 샤드에 동기화하고 데이터를 검증한다.
  5. 제거 대상 샤드에 대한 쓰기를 일시적으로 중단한다. 이때 요청을 큐에 보관하는 등의 처리가 필요하다.
  6. 트래픽을 새로운 담당 샤드로 전환한다.
  7. 데이터와 트래픽 전환을 확인한 후 기존 샤드를 제거한다.
샤드 축소 및 데이터 마이그레이션 과정

주요 문제점

  1. 샤딩 키 재배치가 필요하다.

    제거할 샤드가 담당하던 샤딩 키를 어느 샤드로 이전할지 결정해야 한다. 일반적인 해시 샤딩은 리샤딩 시 많은 데이터가 이동할 수 있다. 일관된 해싱(Consistent Hashing)을 적용하면 키의 이동 범위를 줄일 수 있지만, 데이터 재배치 자체를 완전히 없앨 수는 없다.

  2. 무중단 축소가 어렵다.

    현재 구조에서는 리샤딩 작업을 완전한 무중단 방식으로 수행하기 어렵다. 전환 과정에서 발생하는 쓰기 요청을 처리하기 위해 별도의 큐 구성과 재처리 방식을 고려해야 한다.

  3. 마이그레이션 중 발생하는 쓰기를 동기화해야 한다.

    기존 데이터를 다른 샤드로 옮기는 동안에도 새로운 데이터가 계속 들어온다. 따라서 초기 데이터 복사 이후에 발생한 변경 사항을 새로운 담당 샤드에 지속적으로 동기화해야 한다.

    특히 쓰기 DB는 데이터 변경이 계속 발생하므로 동기화 지연이 완전히 사라지는 시점을 보장하기 어렵다. 결국 최종 트래픽 전환 시점에는 데이터 정합성을 보장하기 위해 짧은 시간이라도 쓰기를 중단해야 한다.

참고: MongoDB의 샤드 제거 절차에서도 데이터 이동이 완료된 후 남은 데이터와 데이터베이스를 수동으로 이전해야 하며, 관련 작업 과정에서 애플리케이션 및 운영 절차에 대한 별도 고려가 필요하다.
MongoDB 공식 문서 - 샤드 클러스터에서 샤드 제거

3-3 데이터베이스의 수직 확장

(1) AWS Aurora Serverless v2

결론 : 시점을 알 수 없는 경우가 아니라면 비용이 매우 비싸 비효율적인데 우리 서비스는 2시간 오후 8~10시 피크 시간이라는 것이 명확하다.

  • 부하에 따라 용량이 자동으로 조절되는 관리형 aws 서비스.
  • 다운타임 없이 부하에 따라 자동 스케일링.
  • 부하 발생 시점을 특정할 수 없는 경우에 적합.
  • 예상 비용 :(1 ACU × 22h + 8 ACU × 2h) × 30일 × $0.20 = $228/월 (30만 원)

참고자료

(2)RDS를 Blue-Green 방식으로 확장

결론: RDS의 서비스를 사용하는 경우에도 blue-green방식으로 수직 확장 전환 시 60~120초의 다운타임이 발생하는데 v2기준으로 매일 240명의 유저가 장애를 겪기 때문에 해당 방법을 사용할 수 없다.

image
  • 평소 22시간은 작은 사이즈 DB로 운영하다가 피크 직전에만 큰 DB로 복제해 blue-green방식으로 트래픽을 전환해준다.
  • 특정 시각 동작하게 예약스케줄링이 별도로 필요하다.
  • RDS 구현 예상 비용: db.t3.small 22h + db.t3.xlarge 2h = 83,616원
  • EC2 구현 예상 비용: t3.small 22h + t3.xlarge 2h =41,808원

참고 자료


실제 사례-LG ThinQ 서비스

read는 중단 없이 계속 가능, write만 30초 중단, 총 switch-over 1~2분 소요


4. v2 아키텍처

4-1 전체 아키텍처 요약

4-2 서버 아키텍처

4-3 DB 아키텍처

4-4 Frontend 서빙

결론: v1과 동일하게 정적 파일을 S3에 저장하고 CloudFront를 통해 제공하는 구조로 결정했다.

(1) CDN vs EC2 인스턴스

  • 단일 장애 지점: Frontend를 단일 EC2 인스턴스에서 제공하면 해당 인스턴스나 웹 서버에 장애가 발생했을 때 페이지를 제공할 수 없다. 이를 방지하려면 여러 인스턴스와 로드 밸런서를 별도로 구성해야 한다. CloudFront는 AWS 관리형 CDN으로, 콘텐츠를 여러 엣지 로케이션에 분산하여 제공한다.

  • 트래픽 병목: 접속이 몰리면 단일 Frontend 인스턴스의 CPU, 네트워크 등의 자원이 병목 지점이 되어 전체 응답 시간이 증가할 수 있다. CloudFront는 캐시된 콘텐츠를 여러 엣지 로케이션에서 분산 제공하므로 갑작스러운 트래픽 증가에 대응하기 쉽다.

  • 운영 및 관리 비용: EC2에서 Frontend를 제공하려면 웹 서버 운영, 보안 패치, 장애 대응, 오토 스케일링 및 배포 파이프라인 등을 직접 관리해야 한다. S3와 CloudFront를 사용하면 서버 관리가 필요 없으며, 배포와 캐시 정책 및 캐시 무효화 등을 중심으로 관리할 수 있다.

  • 비용: 동일한 데이터 전송량을 기준으로 하면 CloudFront가 EC2의 직접 인터넷 전송보다 저렴할 수 있다. 참고 자료 작성 시점의 서울 리전 기준으로 EC2의 인터넷 데이터 전송 요금은 GB당 0.126달러, CloudFront는 GB당 0.120달러이다. 무료 데이터 전송량은 AWS 리전이 월 100GB, CloudFront가 월 1TB로 안내되어 있다.

    EC2는 트래픽이 없어도 인스턴스, EBS 및 퍼블릭 IPv4 주소 등에 대한 비용이 발생한다. S3와 CloudFront 조합도 S3 저장 용량과 요청 등에 대한 비용은 발생하지만, 별도의 서버 인스턴스를 상시 운영하는 것보다 고정 비용 부담이 작다.

AWS 요금과 무료 제공량은 리전 및 정책에 따라 변경될 수 있으므로 실제 도입 시점의 공식 요금표를 다시 확인해야 한다.

(2) S3 직접 서빙 vs CDN

  • 버킷 공개 범위: S3 정적 웹사이트 호스팅 엔드포인트를 직접 사용하려면 콘텐츠를 공개적으로 읽을 수 있도록 설정해야 한다. 이 경우 실수로 업로드한 파일이 외부에 노출될 가능성이 있어 보안 정책에 위배될 수 있다.

    CloudFront를 사용하면 S3 버킷을 비공개로 유지하고, OAC(Origin Access Control)를 통해 특정 CloudFront 배포에만 객체 읽기 권한을 부여할 수 있다. 따라서 사용자가 CloudFront를 우회하여 S3 객체에 직접 접근하는 것을 차단할 수 있다.

  • HTTPS 지원: S3 정적 웹사이트 호스팅 엔드포인트는 HTTPS를 지원하지 않는다. CloudFront에서는 ACM에서 발급한 인증서를 연결하여 자체 도메인에 HTTPS를 적용할 수 있다.

  • 전송 성능: S3에서 직접 제공하면 사용자 요청이 매번 S3 버킷이 위치한 리전까지 전달된다. CloudFront를 사용하면 콘텐츠가 사용자와 가까운 엣지 로케이션에 캐싱되므로 네트워크 지연과 S3 원본 요청을 줄일 수 있다.

  • 전송 비용: CloudFront를 사용하면 S3에서 CloudFront로 전송되는 원본 데이터에 별도의 인터넷 데이터 전송 요금이 부과되지 않는다. 캐시에 저장된 콘텐츠는 CloudFront 엣지 로케이션에서 직접 제공되므로 반복 요청에 대한 S3 요청 횟수도 줄일 수 있다.

참고 자료


5. v2 CI/CD 파이프라인


6. 장애 및 한계

6-1 모니터링 방식

6-2 v2의 한계 지점

frontend 서빙방식

현재 구조의 한계

CDN은 미리 만들어진 파일을 그대로 전달하는 구조이므로 요청 시점에 서버가 페이지를 생성해야 하는 경우는 처리할 수 없다.검색 노출(SEO)이나 링크 미리보기를 위해 SSR이 필요한 페이지가 생기면 렌더링을 수행할 별도의 인스턴스를 두고 직접 관리해야 한다.

⚠️ **GitHub.com Fallback** ⚠️