Development/[msa-perf-lab] Flask & Go MSA 성능 실험 프로젝트

[msa-perf-lab] Flask <-> Go(Gin) MSA 아키텍처 REST vs gRPC 성능 비교 및 InfluxDB + Grafana 시각화

양선규 2025. 11. 7. 02:15
728x90
반응형

드디어 성능 테스트에 필요한 모든 환경을 구축하고, 실제 테스트까지 해 보았다.

트래픽 발생은 JS k6라이브러리를 활용했고, 시계열 데이터를 InfluxDB에 담아 Grafana와 연동해 시각화했다.

 

InfluxDB + Grafana 컨테이너화

docker-compose.influx.yml

 

InfluxDB와 Grafana를 컨테이너로 올렸다. Docker에 준비된 이미지를 받아와 띄우고 기본 세팅만 하면 되기 때문에 크게 어렵진 않았다.

기존 컨테이너 (Flask + Go(Gin) + PostgreSQL)와 하나의 compose 파일로 묶을까 했지만, 네트워크는 공유하되 테스트용 컨테이너는 분리해 관리하는 것이 나을 것 같아 따로 구성했다.

 

InfluxDB, Grafana 세팅은 크게 건드린 건 없고 최대한 가볍게 띄웠다.

 

준비 완료

 

모든 컨테이너가 띄워졌다. 위부터 순서대로 Grafana, InfluxDB, Flask, PostgreSQL, Gin 이다.

위에서 말했듯 Grafana + InfluxDB, Flask + PostgreSQL + Gin을 각각 compose로 묶었다.

 

 

k6 코드 작성 (트래픽 생성기)

REST 테스트용 코드

 

자바스크립트 k6 라이브러리를 활용해 트래픽 생성 코드를 작성했다.

이게 무슨 코드냐면,

 

Flask서버의 /rest/ping 엔드포인트에,

{size: 10} payload를 담은 POST 요청을 보낸다.

1초에 100번, 30초동안.

 

이런 내용이다.

Flask가 요청을 받으면 size(byte단위) 크기 랜덤 문자열을 생성하고, 이것을 Gin 서버로 보낸다. Gin은 해당 데이터를 그대로 Flask에게 응답하는데, 이 때 Flask와 Gin의 통신 프로토콜이 REST이다.

 

공격(?) 시작!

 

명령어를 실행하면 이렇게 공격(?)을 시작하게 된다. WARN 뜬 건 무시해도 된다. 응답속도가 느려서 VU(가상 사용자) 최대수를 찍었다는 경고다.

 

 

REST 테스트 결과

 

http_req_duration이 평균 응답속도(latency)라고 보면 된다.

즉, 클라이언트(사용자)가 요청해서 응답을 받기까지의 총 지연시간을 의미한다.

 

보면 실패율은 0% (전부 성공)이지만, 평균 응답속도가 무려 2.22초다. 즉 매우 느리다.

최대 응답속도는 35초다. 이건 한번 튄 것 같긴 한데, 어쨌든 10byte 페이로드를 포함한 요청을 초당 100번, 30초간 보내는 것은 현재 내 시스템에게 확실히 무리라는 소리다.

 

이번엔 gRPC로 바꿔 테스트를 해보자.

 

 

gRPC 테스트 결과

 

보이는가? 단위가 바뀌었다.

 

REST: 2.22s

gRPC: 3.33ms (약 666배 감소!!!!)

 

정말 말도 안 되는 속도 향상이 이루어졌다. 어떻게 이렇게까지 차이가 날까?

원인은 REST와 gRPC의 파싱 비용과 직렬화/역직렬화 비용 차이 때문이다.

 

HTTP/1.1 Keep-Alive는 단순히 연결만 재사용할 뿐, 요청은 항상 직렬이다.

그래서 매 요청마다 HTTP의 긴 텍스트 헤더 파싱 + JSON 직렬화/역직렬화 비용을 다시 지불해야 한다.

즉, REST의 고정 오버헤드는 요청 수만큼 그대로 누적된다.

 

반면 HTTP/2는 하나의 커넥션에서 수백 개 스트림을 동시에(병렬) 처리할 수 있고, 헤더는 HPACK압축, 메시지는 바이너리 프레임 + Protobuf 기반이므로 파싱, 직렬화 비용 자체가 REST 대비 극단적으로 낮다.

 

-> 따라서 작은 payload일 수록 REST의 고정 파싱 비용이 상대적으로 커지며, gRPC는 오버헤드가 0에 가까워 수십~수백 배 까지 차이가 벌어진다.

 

 

테스트 결과 시각화 (payload: 10Byte)

10B기준 응답 시간 그래프

 

k6로 수집한 시계열 데이터를 InfluxDB에 넣고, InfluxDB를 Grafana와 연동하여 테스트 결과를 시각화 했다. 하긴 했는데... 속도 차이가 너무 많이 나서, 시각화가 의미가 있나? 싶은 수준이긴 하다. ㅋㅋ

 

초록색 그래프는 REST, 노란색 그래프는 gRPC고 좌측 숫자 단위는 ms이다. REST는 평균적으로 약 2.5s 정도에서 머물다가, 마지막에 20s 정도로 튄 모습이다. 반면 gRPC는 3ms 정도에서 처음부터 끝까지 아주 안정적으로 잘 버티고 있다.

 

그럼 이제 payload를 키워보면 어떻게 될까? 파싱, 직렬화 비용의 비중이 상대적으로 낮아져 REST와 gRPC의 속도 차이가 확연히 줄어들 것 같다. 그래서 해 보았다.

 

테스트 결과 시각화2 (payload: 10B ~ 1MB)

평균 응답속도 (단위: Byte)

 

payload 크기를 10B ~ 1MB까지 10배씩 늘려가며 응답속도(latency)를 확인해 보았다.

 

나는 payload가 커져도 격차는 줄어들 지언정 gRPC가 계속해서 우위일 줄 알았는데, 100KB 부분에서 REST와 응답속도가 비슷해지더니, 1MB가 되자 REST보다 2배 이상 느려지는 모습을 발견했다. (평균 3ms -> 15s 까지 상승)

 

반면 REST는 Payload크기와 무관하게 꾸준히 일정한 응답속도를 보였다.

 

 

초당 요청 처리량 (단위: 개수)

 

이번엔 초당 요청 처리량(RPS)를 확인해 보았다. 노란색이 REST다! 그래프 우측 정보는 잘못 표기했다.

처리량도 마찬가지로 gRPC가 꾸준히 우위에 있다가, 100KB에서 비슷해지더니 1MB가 되자 갑자기 곤두박질 쳤다. (평균 100 -> 15 까지 하락)

 

반면 REST는 꾸준히 80 정도의 일정한 처리량을 보인다.

 

 

총 요청 대비 드랍률

 

이건 총 요청 대비 드랍률이다.

100의 RPS로 30초 동안 요청을 보냈으니 총 3000번의 요청이 가야 하는데, 그 중에서 서버의 응답이 느렸거나 병목이 생겨 아예 보내지도 못한 요청의 비율이다.

 

즉 grpc 1000000(1MB) 의 경우 무려 3000개 요청 중 83.7%가 요청 전송조차 못 했다는 뜻이다. gRPC는 10, 100, 1000, 10000(10KB)까지는 드랍률이 0%였다가, 100KB부터 갑자기 드랍률이 REST를 상회하더니 1MB가 되자 드랍률이 하늘을 뚫어버렸다.

 

반면 REST는 payload가 커질수록 오히려 드랍률이 낮아지는 것 같이 보이기도 한다.

 

 

정리

payload 크기와 무관하게 항상 gRPC의 성능이 더 좋을 것이라는 내 예상과는 달리, 100KB부터 REST와 비슷해지는가 싶더니 1MB가 되자 아예 못 쓸 정도가 되어 버렸다. 반면 REST는 10B ~ 1MB까지 일정한 성능을 보였으며, 오히려 payload가 커질수록 안정적인 모습을 보였다.

 

왜 payload가 커질수록 gRPC의 성능이 급감할까? 반대로, 왜 REST는 안정적일까?

HTTP/2 기반의 gRPC는 데이터를 작은 프레임 단위로 쪼개서 전송한다.

payload가 작으면 프레임 개수도 적지만, payload가 커질수록 프레임 개수도 급증하게 된다. 이 때, 각 프레임에 대한 오버헤드는 고정적이기 때문에 payload가 커질수록 해당 오버헤드도 똑같이 누적되어 효율이 떨어진다.

여기서 말하는 오버헤드는 흐름 제어를 위한 window size 관리, 프레임 간 우선순위 조정 등의 복잡한 메커니즘들을 포함한다.

 

반면 REST는 payload 크기에 관계없이 각 요청이 독립된 TCP 세션에서 동일한 오버헤드를 가지기 때문에 항상 일정한 성능을 보인다.

즉, REST가 payload가 커질수록 빨라진다기보다는, REST는 일정한 성능을 유지하는 반면 gRPC는 payload가 커짐에 따라 급격히 비효율적이 되는 것.

 

-> 따라서 저용량(100KB 이하) payload에서는 gRPC가, 대용량에서는 REST가 효율적이다.

 

==========

 

msa-perf-lab 깃허브:

https://github.com/YangSunkue/msa-perf-lab

 

==========

 

테스트도 좋고, 구현도 좋다. 다 좋은데.. Grafana 시각화에서 이렇게 애를 먹을 줄 몰랐다.

 

InfluxQL도 처음이라 익숙하지 않았고, Grafana 사용법도 아예 몰라서 내가 원하는 데이터를 원하는 방식으로 시각화하는 게 정말 너무나도 힘들었다. 특히 드랍률 데이터... k6가 직접 집계하지 않는 데이터라 총 요청량을 계산한 후 드랍개수를 총 요청량으로 나눠야 했는데 (드랍개수 / 총 요청량), 이걸 Grafana에서 구현하는게 정말 힘들어서 하루를 통째로 썼다.

 

요새는 GPT, 클로드 병행해서 써보고 있는데, GPT는 한번 막히기 시작하면 똑같은 말을 반복하거나 해결책이 아닌것을 계속 시도하게 해서 괜히 시간 낭비하게 하는 경향이 있다. 이번에도 GPT때문에 하루를 통째로 날렸고.... 너무 답답해서 클로드로 옮겼는데, 옮긴지 2시간만에 해결했다. 적어도 내가 느끼기엔, 확실히 클로드가 낫다.

 

Grafana를 활용한 시각화는 그냥 뚝딱뚝딱 하면 될 줄 알았는데, msa-perf-lab을 진행하며 가장 많은 시간을 소모했다. 그래도 한번 해 봤으니, 다음 테스트에서는 쉽게 할 수 있을 거라 믿는다.

728x90
반응형