좋은 아키텍처란 무엇인가
시스템 아키텍처를 설계할 때는 여러 가지 고려할 사항이 있는데요.
밑에는 좋은 아키텍처를 설계하기 위해 고려해야 할 주요 사항들입니다.
1. 가용성(Availability): 서비스가 정상적으로 동작하는 정도.
2. 성능(Performance): 빠른 응답 시간과 낮은 레이턴시를 위해 최적화된 시스템을 만드는 것이 중요.
3. 확장성(Scalability): 트래픽 규모에 맞추어 잘 확장할 수 있는지.
4. 신뢰성(Reliability): 시스템이 항상 정상적으로 동작하는지.
5. 관리성(Manageability): 시스템을 쉽게 운영할 수 있는지.
6. 비용(Cost): 하드웨어와 소프트웨어 비용, 시스템을 배포하고 관리하는 비용, 교육 비용 등. 시스템 운영에 필요한 모든 비용.
대규모 서비스에서는 고가용성은 매우 중요합니다. 고가용성이란 시스템이 오랜 시간 가용한 상태를 유지하는 것을 말합니다.
서비스에서 장애가 아예 없을 수는 없지만, 대비할 수 있는 부분은 잘 대비하면서 사용자가 납득할 수 있는 높은 가용성을 가진 안정적인 서비스를 구축하는 것이 중요하기 때문입니다.
따라서 서비스를 운영할 때는 미리 현재 시스템이 어느 정도의 부하를 견딜 수 있는지와 성능을 테스트해 볼 필요가 있었습니다.
고려해야 할 관점에 대해 정리하고 넘어가겠습니다.
- 부하
- 실행할 수 없어 대기하고 있는 프로세스의 수
- 성능
- Time: 얼마나 빠른 서비스인지
- TPS, RPS: 일정 시간 동안 얼마나 많이 처리할 수 있는지
- Users: 얼마나 많은 사람이 동시에 사용 가능한지
먼저, 부하 및 성능 테스트를 위해 테스트 종류 및 도구를 선정해야 합니다.
테스트의 종류
- 스트레스(STRESS) TEST
- 시스템이 과부하 상태에서 어떻게 동작하는지 검사하는 테스트(비정상적으로 높은 부하)
- 시스템의 오류를 확인하고, 어떻게 시스템이 정상적으로 복구되는지를 살펴보는 것이 목적
- 부하(LOAD) TEST
- 임계치의 한계에 도달할 때까지 시스템에 부하를 꾸준히 증가시키며 진행하는 테스트
- 서비스의 일반적인 트래픽과 예상되는 최대 트래픽 상황에서 성능이 어떤지 확인합니다.
- SMOKE TEST
- 시나리오대로 애플리케이션이 동작하는지 확인하는 테스트
테스트 도구
- JMeter
- 복잡하고 디테일한 부하테스트가 가능
- 시나리오 기반 테스트가 가능
- java로 이루어진 오픈소스의 성능테스트 도구
- 다양한 프로토콜을 지원(HTTP, HTTPS)
- 러닝 커브가 높은 편이다.
- nGrinder
- Groovy 기반의 스크립트
- 시나리오 테스트가 가능.
- 세밀한 성능 테스트 가능
- K6
- 시나리오 기반 테스트가 가능
- Javascript 기반의 스크립트
- 동시 접속자 수, 요청 간격, 최대 처리량 등 부하 조정이 가능
- 클라우드로 테스트시 50회 이전까지는 무료. CLI로 실행하면 무료.
- locust
- 로컬환경에서 천명 이상의 유저 테스트가 가능
- python 기반의 스크립트
저희 팀은 부하 테스트를 진행하며 성능 테스트를 진행하기로 하였고,
테스트 도구는 공식 문서에 설명이 자세하게 나와 있고 시나리오 기반의 테스트가 가능한 K6를 사용하여 테스트를 진행했습니다.
목표 VUser 정하기
우선 테스트에 앞서 VUser를 정해야 하는데요.
비슷한 서비스인 OKKY에서 스터디를 모집하는 사용자가 많은 것 같아, DAU는 OKKY를 기준으로 잡아 VUser를 계산해 보았습니다.
- 1일 사용자 수(DAU) = 26,000 = 780,000 / 30
- DAU * 1명당 1일 평균 접속수 = 1일 총 접속수 → 26,000 * 10 = 260,000
- 1일 총 접속수 / 86400(초 / 일) = 1일 평균 rps→ 260,000 / 86400 = 3.01
- 1일 평균 rps * (최대 트래픽 / 평소 트래픽) = 1일 최대 rps→ 3.01 * 4(평소의 4배) = 12.04
- VUser: (목표 rps * T) / R
- R: 시나리오에 포함된 요청의 수
- T: 시나리오 완료 시간보다 큰 값(VUser 반복을 완료하는데 필요한 시간보다 큰 값)
- T = (R * 왕복시간(http_req_duration)) + 지연시간
- 왕복 시간 = 얼마 안에 끝나야 하는지
- R = 3
- http_req_duration: 100ms
- T = (4 * 0.1 + 1(보정 시간)) = 1.4
- 최대 트래픽 VUser: (12.04 * 1.4) / 3 = 5.62
DAU는 방문자 통계를 알려주는 사이트에서 정보를 얻을 수 있었고, 이외의 값은 저희 서비스 값을 대입해서 계산을 진행했습니다.
하지만 테스트할 VUser의 수가 5.62로 나와서, 기대하는 VUser를 임의로 저희가 가정해서 계산하기로 했습니다.
모아모아 프로젝트 목표 VUser 설정
- 1일 사용자 수(DAU) = 100,000
- DAU * 1명당 1일 평균 접속수 = 1일 총 접속수 → 100,000 * 15 = 1,500,000
- 1일 총 접속수 / 86400(초 / 일) = 1일 평균 rps→ 1,500,000 / 86400 = 17.361
- 1일 평균 rps * (최대 트래픽 / 평소 트래픽) = 1일 최대 rps→ 17.361 * 4(평소의 4배) = 69.44
- VUser: (목표 rps * T) / R
- R: 시나리오에 포함된 요청의 수
- T: 시나리오 완료 시간보다 큰 값(VUser 반복을 완료하는 데 필요한 시간보다 큰 값)
- T = (R * 왕복시간(http_req_duration)) + 지연시간
- 왕복 시간 = 얼마 안에 끝나야 하는지
- R = 3
- http_req_duration: 500ms
- T = (3 * 0.5 + 1(보정 시간)) = 2.5
- 최대 트래픽 VUser: (69.44 * 2.5) / 3 = 57.86
VUser를 대략 50명 정도로 잡고 테스트 진행