Troubleshooting - CLB, SurgeQueueLength, SpilloverCount
서버가 죽었습니다.
잠자는 와중 또는 퇴근 후 가장 무서운 슬랙 중 하나이다. 사실 이번 서버 장애는 내가 즉각적으로 조치하지 못했다.
그 이유는 바로... 너무 깊게 잠들어서 서버장애 알림을 받지 못했다.. 아니 받았으나 보지 못했다..! 다행히도 request가 몰려 발생한 장애로 request가 감소되면서 다시 서버는 정상 동작을 할 수 있었고, 원인 파악을 하면서 파악된 원인에 대해 포스팅하려 한다.
서버.. 왜 죽었니?
회사에서 내가 담당하고 있는 서비스는 사실상 가장 큰 수익모델을 담당하고 있는 서버로 아주 보수적으로 AutoScaling수치를 설정 해두었다. 다만 00시 기준으로 AWS beanstalk지표를 보았을 때 00시에 request 요청이 몰리고 있고, 해당 요청과 연관된 DB의 쓰기 사용량도 폭증하는 것을 확인할 수 있었다.
이렇게 request가 급격하게 틱으로 튀어버리면 서버에서는 Aucoscaling 될 시간적 여유가 없다. 다만, Aucoscaling은 cpu 사용량 기반으로 이루어지는데 서버 장애가 있던 시각에 cpu사용량은 평이했다.
서버가 죽은 이유
우리 서버는 부하분산을 위해 로드밸런서 ELB를 사용중이다. ELB에는 CLB, NLB, ALB로 나누어져 있다. 우리는 이중 CLB를 사용 중에 있다. CLB를 사용하는 이유는 사실 딱히 없고, 우리 서버의 Beanstalk를 생성했던 시기에는 CLB만 지원하고 있었다...ㅎㅎ
여하튼 이 CLB에만 있는 개념이 있는데, SurgeQueueLength, SpilloverCount이 두가지 이다.
- SurgeQueue: 로드 밸런서는 SurgeQueue를 이용해서 최대 1024개의 요청까지만 큐에 저장할 수 있다. 로드밸런서의 처리속도보다 요청이 더 많아서 큐가 꽉 찼는데도 요청이 더 올 경우, 큐가 꽉 찬 이후로 오는 요청은 모두 처리되지 못한다. 처리되지 못한 요청은 5xx 에러가 발생하며, ELB지표에서 확인할 수 있다.
- SurgeQueueLength: Classic Load Balancer에 의해 대기열에 대기 중인 총 요청 수를 측정한다.
- SpilloverCount: SurgeQueueLength가 1024를 넘은 이후 처리되지 못한 요청의 수를 측정한다.
서버의 인스턴스가 장애 시각에 3대 떠있었고, 서버에서 Ulimit설정을 통해 최대 동시에 처리할 수 있는 최대 요청을 200,000으로 설정해두었다. 사실 동시에 처리할 수 있는 request가 200,000이 되지는 못할 것이다. 다만 편하게 계산하기 위해 하나의 인스턴스당 200,000으로 계산을 해보자.
서버 3대 * 200,000 = 60,000
최대 600,000의 요청을 받을 수 있다. 다만 한 번에 633465개의 request가 00시에 몰렸고, 동시간에 633,465 - 600,000 정도의 5xx 에러와 SpilloverCount를 확인할 수 있었다.
다만, 위의 개념들은 CLB에만 있고, ALB에는 없는 개념이다. Beanstalk의 로드밸런서를 CLB에서 ALB로 바꿔치기하는 방법은 없는 것으로 알고 있다.(혹시 알고 계시면 공유 부탁드립니다.) 따라서 우리는 Beanstalk을 새로 만들면서 ALB로 생성하고, Linux2, arm64 환경으로 변경하면서 그라비톤 인스턴스로 변경하려고 한다.(변경하면서 경험했던 삽질은 추후에 포스팅 예정이다.)
해결 방안
- 리눅스 서버의 최대 처리량을 늘려준다.
- 00시에 떠있는 인스턴스를 늘려준다.
- 위에서 말한 로드밸런서 변경
우리는 2번의 사항을 우선적으로 적용하고, 지표를 조금 더 확인 후 1번의 조치도 동시에 취하려 한다.
이후 서버를 안정화시키고 3번을 적용해서 가중치를 조금씩 늘려가려 한다.