안녕하세요. 아이들나라 Backend 팀 주니어 개발자 정연재입니다.
저는 입체북 서비스를 오픈 하기 전 진행했던 성능 테스트, 부하 테스트와 튜닝 과정과 느낀점에 대해 공유하는 글을 작성하고자 합니다.
입체북 서비스란, 아이들나라 책 읽어주는 TV 에서 더욱 생생하게 영어동화를 즐길 수 있는 서비스입니다. 3D로 책을 볼 수 있으며 360도 회전, 영어 단어 찾기 등 다양한 효과를 지원합니다.
들어가기에 앞서
야심차게 준비한 입체북 서비스를 출시하기 2달 전 무렵, 저에게 성능테스트라는 업무가 주어지게 되었습니다. 실제로 오픈하는 서비스인 만큼 무거운 책임감을 느꼈습니다.
성능 테스트가 대략적으로 무엇인지, 무엇을 확인하기 위함인지에 대한 개념은 있었지만 구체적인 지식은 거의 없었기에 “어디서부터 시작해야 할지”, “성능이 좋다는 것은 어떤 것을 의미하는지”, “어떤 도구를 사용할지” 등 많은 생각들이 들었습니다.
업무를 주셨던 파트 리더님께서는 저를 배려하여 충분히 서치하고 알아갈 시간을 주셨고, 팀 내 성능 테스트 경험자들이 많다고 언제든지 궁금한 거 있으면 물어보라고 말씀주셔서 자신감을 가지고 성능 테스트를 진행할 수 있었습니다.
본격적인 성능 테스트 과정에 대해 글을 작성하기 전 제가 알아보았던 성능 테스트란 무엇인지, 목적, 용어에 대해 설명드리도록 하겠습니다.
성능 테스트란?
성능 테스트란 시스템 구성 요소가 특정 상황에서 어떤 성능을 보이는지 확인하기 위해 수행되는 테스트입니다. 아래의 그림은 성능 테스트와 그 하위집합에 대한 그림입니다. 성능테스트는 부하테스트(load testing), 스트레스 테스트(stress testing) 등을 포함하고 있는 상위 집합으로 매우 광범위하게 사용되는 용어입니다.
성능 테스트가 왜 필요할까요?
좋은 서비스일수록 사용자가 증가하게 됩니다. 사용자가 계속해서 증가하다 특정 시점에 몰리게 될 경우 응답 속도에 대한 지연이 발생하고 더 나아가 서버에 장애가 발생하게 될 수 있습니다. 장애가 발생한다면 사용자에게는 안좋은 경험을 심어주게 되며, 서비스 신뢰도는 낮아지게 될 것 입니다.
이러한 상황을 방지하기 위해서 성능테스트를 통해 현재 우리 서비스가 수용할 수 있는 최대 사용자 수, 응답 속도, 처리량 등을 측정하고, 측정한 값이 목표로 한 성능에 부합하는지 사전에 확인합니다.
만약 테스트 결과가 목표한 성능에 부합하지 않는다면 왜 성능이 나오지 않는지, 병목이 발생한 지점이 어디인지를 파악하고 이를 개선함으로써 서비스가 정상적으로 제공될 수 있도록 가용성을 높이는 작업을 하기 위함입니다.
따라서 저는 성능 테스트를 진행하며 확인하고자 하는 목적을 다음과 같이 정의하였습니다.
•
입체북 서비스 오픈에 따른 CMS API 의 성능을 측정하고 병목지점이 발생한다면 이를 파악 및 튜닝 수행
•
부하를 처리하는 데 가장 적합한 서버 구성은 무엇인지 파악
•
기존 하드웨어가 충분한 지 또는 추가 하드웨어가 필요한지 여부 파악
•
CPU 사용률, 메모리 사용률, 네트워크 지연 등과 같은 병목 현상 식별 위함
성능이 좋다는 것은 무엇일까요?
그렇다면 과연 좋은 성능은 무엇이고, 어떤 지표로 성능의 좋고 나쁨을 판단할 수 있을까요? 일반적으로 성능테스트 시 사용되는 지표가 있습니다. 이에 대해 간략히 알아보겠습니다.
•
User (사용자)사용자란, 관점에 따라 다양한 형태로 존재하며, 성능테스트 관점에서 사용자는 서버를 사용중인 사용자를 의미합니다. 웹 페이지를 띄어놓은 사용자처럼 잠재적으로 언제든지 부하를 줄 수 있는 Concurrent User와 실제로 로그인을 하여 응답을 기다리는 서버에 실질적으로 부하를 주고 있는 Active User로 구분할 수 있습니다.
•
TPS (Transaction Per Second, 처리량)성능 테스트의 주요 지표로 활용되는 요소로 일정 시간동안 얼마나 많은 요청을 처리할 수 있는지를 나타냅니다. 이는 높을 수록 좋습니다.
위 그래프는 처리량에 대해 나타낸 것입니다. 위 그래프를 왼쪽부터 차례대로 설명하자면 다음과 같습니다.
1.
User 가 증가하면 TPS 가 증가하다가 어느순간 증가하지 않게 됩니다. 이는 User 가 늘어나면 처리량이 증가하게 되고, 더이상 처리하지 못하는 지점이 생기면서 TPS 가 늘어나지 않게 됩니다.
2.
사용자에게 일정한 수준의 응답시간으로 서비스를 제공하다가, Usre 가 증가하면 처리량이 많아지면서 응답시간(Time)이 점차적으로 증가합니다.
3.
TPS 가 증가하다보면 특정 지점에서 Time 이 급증합니다. 서버가 처리할 수 없는 대기 작업들이 누적이 되면서 급격히 증가하는 것인데요. 이 경우엔 리소스가 누수되고 있는 것은 아닌지 확인이 필요합니다.
TPS는 서버의 리소스 자체가 적어서 처리량이 낮은 경우 scale up 을, 부하가 늘어남에 따라 응답속도가 점차 느려진다면 scale out 을 이용하여 높일 수 있습니다.
•
Latency (시간)사용자가 요청을 보낸 시점부터 처리 결과가 사용자에게 보일 때까지의 시간을 의미합니다. 즉, 서비스가 작업을 얼마나 빠르게 처리할 수 있는지를 판단할 수 있는 지표입니다.
테스트 계획하기
성능 테스트를 하기 위해 저는 다음과 같이 테스트 계획을 세웠습니다.
1. 목표 성능 지표 설정
표는 참고용으로 임의의 값으로 지정한 것입니다 :)
2. 성능 테스트 대상 API 정의
•
service API 대상 모두
3. 시나리오 작성
다음으로 성능 테스트의 대상에 대해 어떻게 테스트를 진행할 것인지에 대한 시나리오를 작성했습니다. 지난 한 달간 가장 많이 호출되는 API 목록과 호출되는 빈도수를 집계하여 이를 퍼센티지로 변경하여 작성하였습니다.
•
가장 많은 요청이 발생하는 API 마다 단독 실행개별 API 마다 단독 실행하여 얼마나 견디는지 테스트 진행
•
호출하는 빈도에 따라 여러개의 API 에 가중치를 두어 실행요청 빈도 수에 따라 가중치를 두어 여러개의 시나리오를 동시에 던져 테스트 진행ex) 1번 API 70 %, 2번 API 20%, 3번 API 10%
테스트 진행하기
성능테스트 도구로 nGrinder 를 사용하였습니다. nGrinder 는 groovy 언어(다른 언어도 제공)로 스크립트 작성, 테스트 실행, 모니터링 및 결과 보고서 생성을 동시에 할 수 있는 성능 측정을 목적으로 한 오픈소스입니다.
테스트를 실행 시 위 그림과 같이 TPS , Errors 건 수 등의 정보들을 볼 수 있습니다.
위에서 작성했던 대로 테스트 설계를 마치고 과연 API 별 TPS 는 얼마나 될까? 하는 궁금증과 두근거리는 마음으로 성능테스트를 진행하였습니다. 하.지.만 무엇이 문제였을까요.... TPS 는 목표치보다 한참 아래로 나왔고, Error 역시 다 건 발생했습니다.
무엇이 문제인지 살펴보기 위해 pod 의 CPU 를 보니 위 그림처럼 CPU 가 100% 를 찍는 것을 확인할 수 있었습니다. 이로 인해 pod 이 죽게 되었고, 이 과정에서 Error 건 수가 발생하였습니다.
병목지점 찾기
테스트 과정에서 pod 이 깨지면서 정상적인 TPS 측정이 어려웠습니다. 무엇이 CPU 를 100% 치게 만들었는지를 파악하여 이를 튜닝하는 작업이 우선되어야 했습니다.
튜닝 포인트를 찾기 위해서 가장 중요한 것은 “변경 포인트는 하나”여야 한다는 것입니다. 여러 포인트를 변경하고 성능이 개선되었다면 어느 포인트가 문제였는지 파악하기 어려우니 반드시 “변경 포인트는 하나”로 두고 튜닝 작업을 진행해야 합니다. 따라서 튜닝하는 작업은 굉장히 오랜 시간이 소요됩니다. (마음의 여유를 가지고 해야합니다..)
저 역시 튜닝 포인트를 찾는 작업을 진행하였는데요. 이 과정에서 파트 내 시니어분이 아무런 비즈니스 로직을 타지않는 dummy API 를 하나 만들어서 TPS 가 얼마나 나오는지를 확인해보면 좋을 것 같다는 코멘트를 주셨습니다.
코멘트 주신 내용을 적극 반영하여 아무런 비즈니스 로직을 타지 않는 dummy API 하나, 애플리케이션 내 적용된 Spring Security 가 문제일까 싶어 Spring Security 만 타는 dummy API 하나 이렇게 두개를 만들어 다시 테스트 해보았습니다.
결과는..!! 아래 사진에서 CPU 사용률을 보시면 Spring Security 를 적용한 API 는 이전과 동일하게 100% 를 찍으며 pod 이 죽었고, Spring Security 를 적용하지 않은 API 는 CPU 를 안정적으로 사용하며 TPS 역시 높게 나왔습니다.
이 결과로 Spring Security 내 적용된 로직 중 튜닝 포인트가 있다는 것을 알 수 있었습니다. 아무래도 Spring Security 내 적용한 암호화 방식이 원인인 것 같았습니다.
따라서 저는 Spring Security 에 사용되는 암호화 방식을 변경하여 Spring Security dummy API 테스트를 다시 진행하였고, pod 이 깨지지 않고, TPS 도 높게 나오는 것을 확인할 수 있었습니다.
다시 테스트 진행
이제 성능을 측정할 수 있는 환경이 되었으니, 시나리오대로 테스트를 진행하며 성능을 측정하고, 점진적으로 부하를 주어 이벤트와 같은 상황에 발생할 수 있는 순간 트래픽 최대치와 한계치를 확인해보았습니다.
성능 테스트 설정 환경
성능 테스트 결과
위 표처럼 테스트 진행 전 테스트 설정 환경에 대한 정보를 작성하고, 이에 대한 테스트 결과를 표로 작성하며 현재 우리가 목표한 성능이 확인되는 지, 확인되지 않는다면 다른 설정값을 주어 테스트를 진행하며 우리에게 적합한 서버 구성은 무엇인지, 이벤트 같은 상황에서 어느 정도의 트래픽까지 받을 수 있는지 등을 확인할 수 있었습니다.
마치며
지금까지 성능 테스트를 진행하며 알아 보았던 내용들과 경험한 부분에 대해 글을 작성해보았습니다.
성능 테스트를 진행하며 우리 서비스가 어느정도의 트래픽까지 괜찮은지, 한계점을 넘어설 때 어떤 증상이 나타나는 지에 대해 파악할 수 있었습니다. 이를 통해 한계점에 도달하여 장애 발생시에 어떻게 대응하고 복구해야 할 지에 대해 계획할 수 있었습니다.
개인적으로 그동안 팀내에서 성능에 대해 고민하고 적용한 로직이 실제로 성능에 어떤 이점을 주는지 대해 다시 한 번 생각해보는 좋은 기회가 되었고, 이전에는 코드의 퀄리티에 대해서 신경을 많이 썼다면, 성능 테스트를 진행하며 리소스 관리에 대해서도 많은 고민을 해야겠다는 생각을 했습니다.
또한, 첫 테스트부터 pod 이 깨지는 상황이 발생하여 당황했지만 이를 잘 해결해나가는 과정을 경험하면서 저는 한걸음 더 성장한 것 같습니다.
성능테스트를 처음 경험해봐서 서툰 부분들이 많았지만, 팀내 도와주시는 분들이 계셔서 성공적으로 마칠 수 있었습니다. 감사합니다.
긴 글 봐주셔서 감사합니다!