Jinho.Hong(홍진호) | Ayaan.Park(박찬영) | Jully.Han(한주리) | Milo.Kim(김민제) | Winter.Park(박현혜) | Dorothy.Kim(김도윤) |
팀장 클라우드 |
클라우드 | 풀스택 | 풀스택 | AI | AI |
- 기존에 만들어져 있는 서비스를 각 팀들에게 제공하고,해당 서비스를 최적화하여 최대한의 부하를 견딜 수 있도록 설계
- 토너먼트 형태로 대회 진행
- 각 팀에 대하여 부하테스트를 진행 후 보다 많은 접속자에게 서비스를 문제 없이 제공한 팀이 승리
24/12/09 ~ 24/12/13(총 5일)
- 동시 접속자 수 증가 (변경될 수도 있음)
- 초기 100명에서 시작하여 매 30초마다 100명씩 증가
- 최대 3,000명까지 순차적 증가 목표
- 각 사용자당 최소 1개 이상의 WebSocket 연결 유지
- 제한된 리소스로 최대한의 효율 달성
- 최대 30대의 t3.small 인스턴스 사용
- 다양한 아키텍쳐 시도
- 사용 가능한 AWS 권한
- EC2
- Elastic Load Balancing
- Auto Scaling
- Route 53
- ACM
- S3
- CloudFront
- CloudWatch
- 주어진 도메인에 CloudFront와 서브 도메인에 ELB 연결
- 프론트
- 클라우드 프론트와 S3 버킷을 활용하여 호스팅
- 백엔드
- 인스턴스 총 20개 생성
- 오토스케일러를 활용해 각 인스턴스의 개수가 일정하게 유지되도록 설정
- 각 인스턴스에 도커 컨테이너를 5개씩 띄우고 각각의 애플리케이션에 로드벨런싱
- 총 100개의 서비스에 로드밸런싱
- 파일 서비스와 같은 경우는 S3 버킷에 업로드하고 메타데이터를 레디스에 저장하는 방식
- DB
- MongoDB로 구축되어 있던 서비스를 Redis로 변경
- Redis Cluster을 통한 정합성 보장
- 인스턴스의 성능과 개수 제한이 존재함
- 최대한 부가기능들은 인스턴스를 띄우지 않고 AWS의 서비스로 대체
- 프론트 호스팅을 S3+CloudFront(CDN)을 활용
- 부하 테스트의 최대 요청 수를 2~3만 단위로 잡고, 클라우드 프론트에서 감당 가능한지 확인 후 도입
- 마찬가지로 로드밸런싱도 Nginx를 사용하지 않고 ELB 사용
- AutoScailig Group
- 인스턴스 탬플릿 제작 후 등록
- 최대 30개 인스턴스 제한이 있었기에 20개 이상 늘어나서 실격패 되지 않도록 정책 설정
- ELB
- 각 인스턴스에 20개의 컨테이너를 띄워서 총 100개의 서비스 로드 밸런싱
- 총 4개의 타깃 그룹을 제작 후 각각 연결 포트 변경으로 대응
- CloudWatch
- 부하가 걸렸을 때, 병목지점을 파악하고 조치할 수 있도록 각 인스턴스에 CloudAgent 설치하여 메트릭 수집
- 대시보드를 제작하여 대회진행 중에 지속적 모니터링
- 각 인스턴스 뿐만 아닌 ELB 등등의 서비스들도 모니터링
- CloudFront
- 클라우드 프론트를 활용한 프론트 호스팅
- S3버킷을 연결
- 만약 S3에서 에러를 반환할 경우 index.html 로 라우팅 되도록 설정
- Route 53
- 주어진 도메인을 기반으로, 서브도메인을 설정하여 라우팅 설정
- 대용량 트래픽 처리를 해야 하기에 메세지 큐를 사용할까 논의
- Kafka vs RabbitMQ 고려
- RabbitMQ는 Push 모델 기반 설계이기 때문에 Kafka보단 레이턴시가 낮으며 메세지 전달 보장을 하기 떄문에 소켓 통신에선 RabbitMQ가 유리
- 하지만 RabbitMQ를 활용했을 때, t3.small 이라는 성능 한계에 의해서 병목지점이 될까 우려
- 최대 3000명까지 WebSocket 연결이 이루어지고 메세지가 지속적으로 송수신되어 메세지가 RabbitMQ 큐에 쌓임
- 큐에 쌓인 메세지가 많아질수록 소비자가 해당 메세지를 가져갈 때까지의 시간이 발생 -> 실시간 통신 시스템에서 큰 문제가 우려가 되기 때문에 RabbitMQ는 사용안하기로 결정
- 최종적으로 메시지큐를 활용하지않고 Redis Cluster을 통한 샤딩으로 대체
- Kafka vs RabbitMQ 고려
- Redis Cluster
- t3.small은 싱글 코어기 때문에 한 서버 내에 여러 Redis를 두는 것은 그리 효율적이지 않을 것이라 생각
- 저장 공간
- 영어는 1바이트, 한글은 3바이트 정도로 두었을 때 한 채팅의 메시지나 유저의 토큰 값 등을 저장하는데 하나의 Key 당 100바이트를 사용한다고 가정
- 인스턴스의 RAM은 2GB이지만 OS, 프로세스 등의 사용 공간을 가정하여 Redis가 사용할 수 있는 공간은 1.5GB라고 가정하여 데이터의 갯수를 1500만개 저장할 수 있고 넉넉하게 잡아도 500 ~ 1000만개를 저장할 수 있다는 결론을 도출 -> 부하 테스트에서 아무리 작업을 많이 해도 1000만개를 넘길 일은 없을 것이라 생각하였고 데이터 저장 갯수는 클러스터 갯수 선정 기준에서 제외
- 작업 처리 효율
- 저장 공간을 제외하면 클러스터의 갯수를 선정하는 기준 중 가장 중요한 것은 Single Thread인 Redis에서의 작업 처리 효율이라고 생각하였습니다. -> 마스터 5 + 레플리카 5로 구성
- 마스터 노드 5대로 구성하여 각 노드에 대한 해싱은 백엔드 서버에서 하고 마스터 노드는 작업 처리만 담당
- 레플리카 노드는 Read 작업 수행
- 부하 테스트 대회이기 때문에 레플리카 노드의 데이터 정합성은 고려 x
- 고가용성
- 마스터 노드 당 레플리카 노드를 한 개씩 두어 failover 발생 대비
- 클러스터 모드에서 각 노드가 서로를 감시하며 HA를 보장
- 파일 시스템
- 디스크에 저장하던 파일 시스템을 S3 버킷에 저장하도록 변경
- Redis에서 메타파일을 저장하고 필요할 때 프론트에 URL 전달
- 대상 수상
- 카카오 대표이사 상장 수여
- 빕스 30만원 상품권
- 최종 3400명의 부하 테스트 중 150개 실패
- 이외 수상팀(2,3등)에 비해서 약 400~500개 정도 테스트를 더 많이 통과
- 이외에도 이벤트 전으로 우승자에게 도전으로 몇몇 팀들과 대결후 전부 압도적 승리
- 백엔드 서버 하나에 도커 컨테이너를 추가로 배포하여 처리량을 늘리려 했으나, t3.small 인스턴스의 CPU가 1개뿐이라 성능 향상이 미미했음
- JS 코드를 Java로 리팩토링하고 쓰레드 풀을 조정하여 멀티 쓰레드의 이점을 활용했다면 더 좋았을 것
- S3 파일 업로드 로직을 백엔드에서 구현했으나, 프론트엔드에서 처리하면 더 효율적일 듯