하루 트래픽이 5배 이상 늘어난 날, 서버 개발팀의 대응 기록
안녕하세요. 아테나스랩 서버챕터 제우스입니다.
저희 아테나스랩의 오늘학교는 초·중·고등학생과 학부모 140만 명이 사용하는 앱으로, 3월 새학기 개학을 앞두고 2월 말부터 여러 광고와 이벤트를 진행했습니다.
이로 인해 트래픽이 평소보다 증가하게 되었고, 평균 4~5만명을 기록하던 DAU가 3월 1일 약 18만명을 기록하면서 오늘학교 역대 최고 수치의 DAU를 기록했습니다.
최소 5배 증가한 트래픽 이슈를 대응하기 위해 어떤 노력을 했는지, 어떤 숙제가 생겼는지에 대해 공유하려 합니다.
3월 1일 오늘학교 트래픽 변화
3월 1일 저희의 일정은 아래와 같습니다. 최소 한번에 수십만 단위의 메시지를 발송하는 것들이 여러 건 스케줄링되어 있었습니다.
3월 1일 트래픽 그래프를 보면, 푸시 메시지 및 카카오톡 광고 메시지 발송 후 해당 시간대에 사용자가 몰리면서 트래픽이 증가했습니다.
아래 학기 중의 일 평균 트래픽과 비교해서 3월 1일에 트래픽이 급증한 것을 확인 할 수 있습니다.
트래픽이 급증했던 동일 시간대에 RDS CPUUtilization과 데이터베이스 Connection이 증가했음을 확인할 수 있습니다.
또한, 초당 요청수는 최대 639건까지 발생하고, Latency*의 최대 값도 약 30초에 임박했습니다.
*Latency : 사용자가 요청을 한 시점부터 해당 사용자에게 요청에 대한 응답을 받기까지 걸리는 시간
Scale-Up vs. Scale-Out
트래픽이 증가함에 따라 서버가 한계치에 도달하게 되었고, 이에 대한 대응을 어떻게 해야 할 지 의사결정이 필요했습니다.
CPU, I/O등을 확인하여 부하를 줄이는 방법을 택하기에는 작업을 위한 시간이 부족하다고 판단했으며, 지금 할 수 있는 가장 빠르고 효과적인 대응 방법은 Scale-Up과 Scale-Out이라고 생각했습니다.
Scale-Up은 기존 서버의 CPU, 메모리, 디스크 등의 사양을 확장하는 방법으로, 서버의 크기가 그림과 같이 수직형태로 커진다고 하여 Vertical Scaling이라고 불립니다.
Scale-Up은 아래와 같은 상황일 때 채택하는 문제 해결 방법입니다.
- 트래픽이 적은 상황임에도 현재 서버의 Resource가 100%에 도달하여 요청을 처리할 수 없다.
- 기존 성능으로 해결할 수 없는 문제를 대응해야 한다.
- 우리 서버는 모놀리식* 아키텍처이다.
*모놀리식: 모든 비즈니스 관련 사항을 함께 결합하는 하나의 코드 베이스를 갖춘 대규모 단일 컴퓨팅 네트워크
3번의 경우, Scale-Up을 적용하면 한 번의 변화로 서버 전체에 고성능을 적용시킬 수 있기 때문에 상대적으로 MSA보다 모놀리식 구조로 이루어진 서비스에 적절합니다.
Scale-Out은 서버를 여러 대 추가하여 시스템을 확장하는 방법으로, 동일 성능 서버가 병렬 형태로 증가한다고 하여 Horizontal Scaling이라고 불립니다.
Scale-Out은 아래와 같은 상황일 때 채택하는 문제 해결 방법입니다.
- 트래픽이 급증하여 생기는 수 많은 요청을 처리해야 한다.
- Scale-Up에 비해 상대적으로 유연한 대응을 해야 한다.
- 우리 서버는 MSA* 아키텍처이다.
*MSA : 독립적으로 배포 가능한 일련의 서비스를 이용하는 아키텍처 방법
3번의 경우, 특정 서비스에 문제가 생길 경우 해당 서버만 Scale-Out을 시켜줘서 저비용으로 빠르게 대응할 수 있기 때문에 상대적으로 모놀리식보다 MSA 구조로 이루어진 서비스에 적절합니다.
선택의 시간
RDS : Scale-Up
빠르게 올라가는 CPUUtilization을 쿼리 최적화로 수정 및 배포하여 대응하는 건 늦다고 판단했고, RDS를 Scale-Up하면 인스턴스의 성능만 높여 적용하면 되어 빠르게 대응할 수 있다고 판단했습니다.
다만, Scale-Up이 적용되기까지 약 10분이 소요되고, 그 동안 서버와 연결이 끊기기 때문에 사용자가 앱에 접속할 수 없게 됩니다.
그래서 아래와 같이 Scale-Up을 적용하는 동안 사용자가 앱을 이용할 수 없음을 알리기 위해 서버 점검 공지를 띄웠습니다.
EC2 : Scale-Out
트래픽이 급증했지만, 언제든 감소할 가능성이 있었습니다.
그래서 트래픽 증가와 감소에 대한 두 가지 상황에 대해 Scale-Out을 통해 빠르게 대응하려고 했습니다.
EC2에서 Scale-Up이 아닌 Scale-Out을 적용했던 이유는 생산성 저하 때문이었습니다.
EC2의 Scale-Up의 절차는 아래와 같습니다.
- 기존 인스턴스의 이미지 복사
- 복사한 이미지로 신규 인스턴스 생성
- 생성 후 기존 인스턴스 삭제
필요한 서버는 많은데, 한 개씩 서버를 만드는 작업이 생산성을 해치는 것이라 판단했습니다.
Scale-Out을 선택했지만, 서버를 몇 대까지 늘려야 최소한의 비용으로 대응할 수 있는지에 대한 기준이 없었기에, 일단 서버 개수를 순차적으로 늘리며 상황을 지켜보기로 했습니다.
이 과정에서 제가 놓치고 있는 상황이 있었습니다.
오늘학교는 로그 저장 작업을 비롯한 일부 작업들은 Celery*를 이용하여 비동기형태로 이루어져 있는데, 트래픽이 늘어나면 비동기 요청도 늘어나는 걸 생각하지 못 했습니다.
그래서 작업이 이루어지던 Worker에서 Queue가 쌓이게 됩니다.
*Celery : 분산 메시지 전달에 기반을 둔 오픈 소스 비동기 태스크 큐로 스케줄링을 지원하지만 실시간 운영에 초점을 두고 있다.
Concurrency : 하나의 Worker에서 동시에 처리할 수 있는 작업의 양
Worker의 경우, 위에서 언급한 것처럼 로그 저장과 같이 작은 단위의 작업들이 수행되는 곳이어서 처리할 수 있는 서버를 늘리는 게 빠른 대응에 적합하다고 판단했습니다.
그래서 EC2에서 대응한 것과 같이 Scale-Out을 적용하였습니다.
대응 종료
3월 1일 6시 50분부터 시작된 새학기 대응 작업은 약 12시간에 걸쳐 진행되었고, 18시에 종료되었습니다.
하지만 대응 작업 종료 이후 저녁 시간대의 트래픽을 대비하여 AutoScaling을 어떻게 설정해야 할 지 고민이 되었습니다.
왜냐하면 일반적으로 학생들이 공부를 끝내고 저녁부터 개인의 시간을 가지다 보니, 주로 저녁이 앱의 활성도가 가장 높은 시간이기 때문이었습니다.
서비스의 속도가 조금만 느려지면 답답했던 경험이 있어서, 사용자들도 저와 같은 경험을 하지 않았으면 하는 마음을 담아 TargetResponseTime*의 임계값을 정한 뒤 임계값 이상으로 넘어갈 경우 서버를 추가하도록 AutoScaling을 적용했습니다.
서버를 추가하여 사용자가 정상적으로 사용할 수 있도록 하고, TargetResponseTime이 설정한 임계값을 넘지 않을 경우 서버를 감소시켜 관리하도록 설정했습니다.
*TargetResponseTime : 로드 밸런서에서 요청 신호를 전송한 후 대상에서 응답 신호가 수신될때까지 경과된 시간(초)
Summary
하루의 대응 기록을 요약하자면 다음과 같습니다.
1. 트래픽 급증으로 인하여 서버와 데이터베이스가 한계에 도달
2. 대응을 위해 RDS는 Scale-Up, EC2는 Scale-Out을 진행
3. RDS의 Scale-Up을 진행한 이유
- 데이터베이스 과부하가 빠르게 진행되고 있어, 쿼리 최적화 등 코드 수정으로 해결하기에 시간이 촉박했음
- RDS Scale-Up의 경우, 서버 성능을 상향시킨 뒤 적용을 해주면 돼서 빠른 대응 가능
4. EC2의 Scale-Out을 진행한 이유
- EC2의 Scale-Up은 기존 인스턴스의 이미지 복사 → 신규 인스턴스 생성 및 적용 → 기존 인스턴스 삭제로 RDS와 절차가 다름
- 서버의 빠른 확충이 필요한 데, 한 개씩 추가하기에 시간이 촉박했음
5. Worker의 Scale-Out을 진행한 이유
- Worker는 로그 저장 같이 작고 빠르게 일어나는 작업이 많아 서버 개수 증가를 통해 빠른 대응에 적합하다고 판단
6. 퇴근 후 트래픽 증가에 유연한 대응을 위해 TargetResponseTime을 기준으로 서버 증가/감소되도록 설정
Lesson Learned
이번 경험을 통해 서버 대응 시야가 넓어졌습니다.
트래픽 증가에 대한 AutoScaling은 설정되어 있었지만 저희가 설정한 기준점은 평소 트래픽이었습니다.
급박한 대응 상황을 겪다 보니, 다음부터는 지금의 한계 수치가 아닌 미래까지 고려하여 기준을 정하는 것이 유연한 대응을 할 수 있는 방법 중 하나라는 걸 느꼈습니다.
그리고 트래픽 뒤에 숨은 부하 원인을 볼 수 있었습니다.
트래픽이 증가했다는 DB Hits 증가를 의미하기도 합니다.
대응 후 RDS 지표를 통해 호출이 불필요하게 많이 된 쿼리나 응답시간이 긴 쿼리 등을 발견했고, 쿼리 최적화나 캐싱 등 데이터베이스 부하를 줄일 방법들을 적용하여 실제로 부하를 줄여 나아가고 있습니다.
이번 새학기 대응 작업을 거치면서 앞으로 생각해볼 점들에 대해 아래와 같이 정리해보았습니다.
- 서버를 Scale-Out으로 n대씩 확장한다고 할 때 어떤 기준으로 확장할 것인지
- 서버의 최소 대수 및 최대 대수를 설정한다면, 각각 몇 대로 정할 것인지
- 지표가 아닌 경험을 바탕으로 서버의 Scale-Out 기준을 TargetResponseTime으로 설정했는데, 이것이 맞는 기준인지
- RDS에 Scale-Up을 진행할 때, 왜 많은 옵션 중에서 16xlarge를 선택했는지
현재 위 사항들에 대한 저희 팀만의 기준을 만들어가는 중이며, 더 많은 사용자들이 언제 앱을 이용해도 서버개발자 누구든 대응 가능하도록 대응 프로세스를 구축하고 있습니다.
이러한 대응 과정을 블로그로 남기고, 아직 해결하지 못한 다른 문제들을 해결해가는 과정을 블로그로 남김으로써 더 나아가는 서비스를 제공할 수 있도록 노력하겠습니다.
저희 아테나스랩과 함께 성장하실 분을 찾고 있습니다! 🕶
좋은 서비스를 만드는 것에 더불어, 저희 아테나스랩과 더 나은 기술을 고민하고 서로 자극이 될 수 있는 조직을 만들며 성장하고 싶은 분들은 아래 채용 중 포지션 페이지를 참고해 주세요 🎉
채용 중 포지션 : https://prompie.com/s/alq3upis/