잘 만든 Server란 무엇일까? 목적에 부합하는 기술로 만들어진 Server? 유지보수에 유리한 코드로 작성된 Server? 사실 가장 중요한 건 "좋은 성능"을 내는 Server다. 하지만 "좋은 성능"이란 참 어려운 얘기다. 그래서 성능 테스트를 공부해야겠다 생각했다. 하지만 개인 프로젝트를 중도 포기하다 보니 Server를 완성해 본 적이 없었고 늘 꿈으로만 남아 있었다.
그리고 오늘, 벼르고 별렀던 그 테스트를 맛봤다.
어떤 tool을 사용해야 할까? nGrinder가 간단하다던데? 하지만 찾아보니 java 계열(java 생태계의 거대함을 느끼는 요즘이다...)로 보였다. k6? 괜찮아 보인다. 하지만 이미 e2e test에 postman을 사용하고 있는 만큼 기술을 통일하고 싶었다. postman으로 성능 테스트라. 아마 생소하게 여길 사람이 많을지도 모르겠다.
postman의 단점은 명확하다. 우선 performance test를 지원한지 얼마 안 되었는지 자료가 많지 않다. 그리고 cli로는 performance test를 할 수 없다. test flow(scenario)도 애매하고 iteration도 불가능하다. pm.response.responseTime
으로 latency check를 기대했는데, performance test에서는 test fail을 보여주지 않더라... virtual user의 ramp up은 지원하지만, ramp down은 지원하지 않는다. 그래서 spike test에는 부적합할지 모르겠다. 다만 load test나 endurance test에는 충분해 보인다.
기존 functionality test의 scenario를 그대로 가져왔다.
flow를 더 정교하게 설정할 수 있었더라면 100회 정도 반복하되 첫 iteration에만 sign up을 하도록 했을 테지만, postman.setNextRequest()
로는 이 정도가 한계인 듯 하다.
솔직히 성능이 좋을거라 생각했다. "A-ToDo는 그래봤자 todo list인데 기초 CRUD밖에 없는 프로그램이 성능이 안 좋을 수가 있겠어?" 그런데 안 좋을 수가 있더라...
이날, 너무 충격받아서 그냥 누워버렸다. rps가 85인데 avg가 227이라니...? 동접자가 고작 20명이란 말이다!
물론 안다. 나는 think time을 고려하지 않았다. 그리고 7가지 api의 request 빈도도 고르지 않을 것이다. 아마 실 traffic에서 대부분을 차지하는 건 sign in
과 get task list
, register new task
와 같은 read 위주의 비교적 가벼운 request일 것이다. 하지만 고작 20명의 virtual user란 말이다!
그날 잠들기 전까지 왜 내 Server가 구린지 고민했다. 알 수 없었다. 생각해 보니 resource monitoring을 하지 않았다. 이런;;
resource monitoring을 어떻게 해야 할까? 잘 모르겠어서 일단 시스템 관리자를 켜 봤다.
오? 측정 결과가 이전보다 좋다! rps가 220인데 avg가 120이면 나쁘지 않은데? 아무래도 local(사랑스러운 내 노트북)에서 측정하다 보니 정확히 측정되지 않았나 보다.
시스템 관리자로 확인해 보니 memory는 괜찮아 보인다. 예상외로 부하는 cpu에 걸린 듯하다. 중요한 건 시간이 흐를수록 latency가 선형적으로 증가하고 있다는 것이다. 잠 못 이룰 정도로 심각한 성능은 아닌 거 같지만, 여전히 대책이 필요하다.
resource를 배포 환경과 유사하게, 그리고 local에 영향을 덜 받게 설정해 봤다.
#! /bin/sh
# Exit local mysql before running this script
# $ sudo service mysql stop
# At the end of this script, run the commands below
# $ mysql --protocol=tcp --user=root < a_todo.sql
# $ run the docker stats app db
# Then do the performance test!
# After the performance test, exit the test containers
# $ docker stop app db
docker run --detach --network="host" --rm \
--memory="1g" --cpus=1 \
--env MYSQL_ALLOW_EMPTY_PASSWORD=true \
--name db mysql
docker run --detach --network="host" --rm \
--memory="1g" --cpus=1 \
--name app goldentrash/a-todo-server
예상외로 db가 안정적이었다. (다만 처음 40%였던 cpu rate이 마지막에는 60%까지 증가했다) express의 cpu rate이 항시 100%일 거라고는 생각 못 했는데, 어쩌면 사용자 인증 module에서 실수가 있었는지 모르겠다.
db 부하는 filter와 pagination을 도입하면 괜찮아질 거라 예상한다. express의 cpu 부하는 어떻게 할지 더 공부해 봐야겠다.
마지막으로 정말 좋은 포스팅을 발견해서 공유하고자 한다. 최근 몇 년간 본 포스팅 중 손꼽히는 글이다.