인공지능 기능들이 구현이 완료된 상태에서 부하 테스트를 진행했고,
현재 한 서버에 여러 컨테이너로 분리된 서비스들을 각각의 서버로 분리해야할 필요성을 느꼈다, (마이크로서비스 아키텍쳐를 구현해야했다.)
일전에 Locust 패키지를 통해 부하테스트를 진행하는 방법을 소개했다.
Locust 를 사용해서 테스트 시나리오를 정의했고, 그 시나리오로 테스트를 진행했을 때 예상보다,
시간이 괭장히 오래걸렸다.
30명의 유저가 3~4초 사이 랜덤 간격으로 요청
목표 : 평균 응답시간 2초 이내
* 리퀘스트는 최악의 복잡도를 가지는 예시로 테스트
단일 리퀘스트는 0.1초안에 응답했지만,
위 시나리오를 적용했을때 7~8초가 결렸다.
*챗봇에게 질문을 했을때 7~8초에 응답한다면 그건 아주 심각한 문제다.
여러 개의 서비스 컨테이너와 복잡한 알고리즘으로 구성된
챗봇 응답에서 어느 부분에서 시간이 많이 소요되는지 확인이 필요했고,
단계별로 쪼개서 시간측정을 진행했다.
Task1 에서 시간이 몰리는 것을 확인했고,
실제 Task1 만 따로 API로 만들어서 부하테스트를 진행했지만, 응답시간이 늘어나지 않았다.
(삽질 + 1)
계속된 로그 분석을 통해서 하나의 독립된 서버에 요청할때
같은 VPC에 있었지만, 서로 외부아이피로 통신하고 있음을 알아채고
내부 아이피로 변경했다(이게 문제였구나!!!)
하지만, 내부 아이피로 변경했음에도 서버 부하는 그대로 였다.
(삽질 + 2)
그래도 위 삽질 과정에서 알아낸 것이 있었다.
하나의 독립된 Task는 부하가 안걸리지만, 여러 Task를 한꺼번에 처리할때
부하가 발생하는구나!
문제는 아주 단순한 문제였다. 문제 발견 후 엄청난 현타가...
문제는 CPU의 부하였다. 여러 Task를 2core CPU가 한번에 처리하려고 하니까 처리량이 버티질 못했던 것이다. CPU 사용률을 보니 90%가 훌쩍 넘었다.
그리고 T3 인스턴스는 CPU 사용 크레딧이 있다는 사실을 간과했던 것이다.
이 당연한 사실을 모르고 있었다니, 다음 부터 위 같은 실수를 하지 않아야겠다.
현재 인스턴스
변경 진행
r6g.large 인스턴스를 m6g.large 로 변경했다.
: 2core 는 그대로 메모리는 16GB -> 8GB
c6g.medium 인스턴스를 새로 추가했다.
: 1core 2 RAM
T3.small -> T4g.micro
: 2core 1 RAM
변경 후
메모리 최적화 r6g.large 를 사용했던 이유는 임베딩 벡터의 크기가 컸기 때문이였지만, 우리는 Faiss 벡터 양자화를 통해 벡터의 크기를 줄였기 때문에 16GB 까지 고스펙이 필요없어졌다. 그래서 m6g.large 로 바뀌어도 검색기반 챗봇은 충분히 동작했고, 부하가 몰렸던 Task2를 새로운 인스턴스
c6g.medium 에 따로 분리시켜 CPU 부하를 분산시켰다. 신기했던게 c6g.medium은 CPU가 1개 뿐임에도 버텨내는 놀라운 모습을 보여줬다.
T3.small 인스턴스도 코어 수를 유지한채 메모리를 낮추면서 T4g.micro로 변경했고
크레딧이 만료되면 성능을 줄이는 것이 아닌 추가 요금을 발생하도록 변경했다.
인스턴스를 변경하고 다시 Locust 를 통해 시나리오를 진행시켰더니.
목표했던 2초 보다도 훨씬 적은 평균 0.5 초의 응답속도를 보여줬고,
12시간 동안 유지되었다.
거의 비슷하지만 결과 적으로 요금은 아주 조금 줄어들었다 (시간당 0.05 usd)
요즘 챗봇에 성능에만 신경을 너무 썼더니 서빙에 대한 고민을 많이 안했던 것 같다.
결과적으로 서버 스펙을 최적화해서 기분은 좋고 이 경험을 통해 앞으로 처음 설계할때 잘 설계해야겠다. + r6g.large 는 예약인스턴스로 선결제 했는데.
이런 결정은 좀 더 신중해야할 필요가 있어보인다.