TT Lab
시작하기
배우기 러닝패스 코스

부하 테스트

초당 500건을 걸었다고 적었지만 실제로는 48건이었다

TT Lab 에서 이어서 보기

목표

닫힌 루프 발생기의 처리량 상한을 리틀의 법칙으로 예측하고 실제로 재서 맞춰 본 뒤, hey 의 비율 옵션이 워커당 상한이라는 것을 실측하고, 목표율에 못 미친 결과가 서버 포화인지 발생기 한계인지 가르는 절차와 보고서 검사 스크립트를 만듭니다.

왜 중요한가

부하 시험 보고서에서 가장 자주 틀리는 숫자는 지연이 아니라 부하 그 자체다. 동시성 10에 응답 200밀리초인 닫힌 루프는 아무리 높은 목표를 적어도 초당 50건을 넘지 못한다. 그런데 도구는 경고 없이 예쁜 백분위를 출력하고, 실제 부하의 10분의 1만 겪은 서버는 당연히 건강해 보인다. 비율 옵션의 정의가 도구마다 달라서 한 낱말을 잘못 읽으면 실제 부하가 워커 수만큼 부풀거나 줄어들고, 열린 파일 수 같은 발생기 자신의 자원이 한계가 되면 그 실패가 서버의 오류율에 섞여 들어간다. 그래서 결과를 읽기 전에 요청한 부하와 실제로 걸린 부하가 같았는지부터 확인해야 하고, 그 확인을 사람의 기억이 아니라 보고서 양식과 검사 스크립트에 맡겨야 한다.

단계

  1. /opt/lab/lt/lt-generator-limits/slow.py 를 포트 8090 에 응답 0.2초로 띄우세요. 그다음 curl -s -o /dev/null -w '%{time_total}\n' 로 / 를 다섯 번 불러 그 다섯 줄을 /root/lt-generator-limits/01-probe.txt 에 그대로 남기고, /root/lt-generator-limits/01-service.txt 에 두 줄 port= 과 service_ms= 를 적으세요. service_ms 는 다섯 줄의 중앙값을 밀리초 정수로 적습니다.
  2. /root/lt-generator-limits/predict.tsv 를 만드세요. 머리글 없이 네 줄이고 각 줄은 탭으로 나눈 두 칸 <동시성> <초당 요청 수 상한> 입니다. 동시성은 순서대로 1 · 2 · 5 · 10 이고, 상한은 동시성 ÷ 서비스 시간(초) 을 소수 둘째 자리까지 적습니다. 서비스 시간은 1단계에서 잰 값입니다.
  3. hey -t 60 -o csv 로 두 번 재세요. 동시성 1 은 -n 30, 동시성 5 는 -n 125 로 하고 원본 CSV 를 각각 /root/lt-generator-limits/c1.csv 와 /root/lt-generator-limits/c5.csv 에 그대로 남깁니다. 그다음 /root/lt-generator-limits/03-measured.tsv 에 두 줄을 탭으로 나눈 세 칸 <동시성> <예측 RPS> <측정 RPS> 로 적으세요(소수 둘째 자리까지). 총 RPS 는 요청 수 ÷ (max(offset + response-time) − min(offset)) 로 계산합니다 — hey 의 CSV 두 칸만 있으면 구할 수 있습니다.
  4. hey -n 100 -c 5 -q 2 -t 60 -o csv 로 재서 원본을 /root/lt-generator-limits/q.csv 에 남기세요. 그다음 /root/lt-generator-limits/04-q.txt 에 다섯 줄 q_flag= · workers= · expected_total_rps= · measured_rps= · per_worker= 를 적습니다. expected_total_rps 는 -q 값과 워커 수로 예측한 총 비율, measured_rps 는 q.csv 에서 다시 계산한 값(소수 둘째 자리), per_worker 는 -q 가 워커당 상한이면 yes, 총 상한이면 no 입니다.
  5. hey -n 100 -c 2 -q 25 -t 60 -o csv 로 재서 원본을 /root/lt-generator-limits/shortfall.csv 에 남기세요. -q 25 에 워커 2개이므로 요청한 부하는 초당 50건입니다. /root/lt-generator-limits/05-shortfall.txt 에 세 줄 requested_rps= · achieved_rps= · achieved_ratio= 를 적으세요. 달성률은 achieved_rps ÷ requested_rps 를 소수 셋째 자리까지 적습니다.
  6. /root/lt-generator-limits/verdict.txt 에 여섯 줄을 적으세요. baseline_service_ms= 는 c1.csv 의 응답 시간 중앙값, loaded_service_ms= 는 shortfall.csv 의 중앙값(밀리초 정수), service_ratio= 는 둘의 비(소수 둘째 자리), rps_ratio= 는 5단계의 달성률, verdict= 는 아래 규칙으로 고른 값, reason= 은 60자 이상의 근거입니다. 규칙: rps_ratio 가 0.8 이상이면 ok, 그렇지 않은데 service_ratio 가 1.3 이상이면 server, 둘 다 아니면 generator 입니다.
  7. 열린 파일 수 한도를 64 로 낮춘 셸에서 hey -n 400 -c 200 -t 5 를 돌려 사람이 읽는 출력을 /root/lt-generator-limits/fdlimit.txt 에 그대로 남기세요(-o csv 없이). 그다음 /root/lt-generator-limits/07-fd.txt 에 다섯 줄 nofile= · concurrency= · ok_count= · error_count= · limit= 을 적습니다. 성공 수와 오류 수는 fdlimit.txt 에서 세고, limit= 에는 이 실패가 어느 쪽 한계였는지 server 또는 generator 로 적습니다.
  8. /root/lt-generator-limits/check-report.sh 를 만드세요. 보고서 파일 경로를 첫 인자로 받아 requested_rps= · achieved_rps= · achieved_ratio= 세 줄이 모두 숫자와 함께 있으면 0 으로, 하나라도 없으면 0 이 아닌 값으로 끝나야 합니다. 그리고 그 검사를 통과하는 보고서 /root/lt-generator-limits/report.md 를 쓰되, 세 값은 5단계에서 잰 값과 같아야 합니다.

참고

서비스 시간이 일정한 부하 대상을 띄운다

/opt/lab/lt/lt-generator-limits/slow.py 를 포트 8090 에 응답 0.2초로 띄우세요. 그다음 curl -s -o /dev/null -w '%{time_total}\n' 로 / 를 다섯 번 불러 그 다섯 줄을 /root/lt-generator-limits/01-probe.txt 에 그대로 남기고, /root/lt-generator-limits/01-service.txt 에 두 줄 port= 과 service_ms= 를 적으세요. service_ms 는 다섯 줄의 중앙값을 밀리초 정수로 적습니다.

이 대상은 자물쇠가 없어서 동시에 들어온 만큼 함께 처리합니다 — 이 실습에서 한계는 서버가 아니라 부하를 만드는 쪽에 있습니다. 백그라운드 실행은 nohup ... & 이고 뜰 때까지 1초쯤 기다리세요. 중앙값은 sort -n 뒤 가운데 줄입니다.

리틀의 법칙으로 상한을 먼저 예측한다

/root/lt-generator-limits/predict.tsv 를 만드세요. 머리글 없이 네 줄이고 각 줄은 탭으로 나눈 두 칸 <동시성> <초당 요청 수 상한> 입니다. 동시성은 순서대로 1 · 2 · 5 · 10 이고, 상한은 동시성 ÷ 서비스 시간(초) 을 소수 둘째 자리까지 적습니다. 서비스 시간은 1단계에서 잰 값입니다.

닫힌 루프에서는 워커 하나가 응답을 기다리는 동안 다음 요청을 보내지 않습니다. 그래서 워커 C 개가 만들 수 있는 최대 비율은 C ÷ 응답시간 입니다(리틀의 법칙). 이 상한은 서버 성능과 무관합니다 — 서버가 아무리 빨라도 기다리는 동안에는 요청이 나가지 않기 때문입니다.

예측이 맞는지 실제로 재 본다

hey -t 60 -o csv 로 두 번 재세요. 동시성 1 은 -n 30, 동시성 5 는 -n 125 로 하고 원본 CSV 를 각각 /root/lt-generator-limits/c1.csv 와 /root/lt-generator-limits/c5.csv 에 그대로 남깁니다. 그다음 /root/lt-generator-limits/03-measured.tsv 에 두 줄을 탭으로 나눈 세 칸 <동시성> <예측 RPS> <측정 RPS> 로 적으세요(소수 둘째 자리까지). 총 RPS 는 요청 수 ÷ (max(offset + response-time) − min(offset)) 로 계산합니다 — hey 의 CSV 두 칸만 있으면 구할 수 있습니다.

hey 의 CSV 는 첫 줄이 머리글이고, 첫 칸이 response-time, 마지막 칸이 offset(시험 시작부터 그 요청을 보낸 시각)입니다. 측정값이 예측값을 넘지 못하는 것이 정상입니다 — 넘었다면 서비스 시간을 잘못 쟀거나 대상이 응답을 일찍 끊은 것입니다.

-q 는 총 목표율이 아니라 워커당 상한이다

hey -n 100 -c 5 -q 2 -t 60 -o csv 로 재서 원본을 /root/lt-generator-limits/q.csv 에 남기세요. 그다음 /root/lt-generator-limits/04-q.txt 에 다섯 줄 q_flag= · workers= · expected_total_rps= · measured_rps= · per_worker= 를 적습니다. expected_total_rps 는 -q 값과 워커 수로 예측한 총 비율, measured_rps 는 q.csv 에서 다시 계산한 값(소수 둘째 자리), per_worker 는 -q 가 워커당 상한이면 yes, 총 상한이면 no 입니다.

-q 가 총 상한이라면 총 비율은 2 근처여야 합니다. 실제로 몇이 나오는지 보고 판단하세요. hey 의 문서에 이 옵션이 어떻게 적혀 있는지도 확인해 보세요 — 한 낱말 차이가 시험 전체를 다른 실험으로 만듭니다. 동시성 5 의 상한(25)보다 낮게 걸어야 제한이 실제로 걸립니다.

목표를 걸었는데 절반도 못 걸린 시험을 만든다

hey -n 100 -c 2 -q 25 -t 60 -o csv 로 재서 원본을 /root/lt-generator-limits/shortfall.csv 에 남기세요. -q 25 에 워커 2개이므로 요청한 부하는 초당 50건입니다. /root/lt-generator-limits/05-shortfall.txt 에 세 줄 requested_rps= · achieved_rps= · achieved_ratio= 를 적으세요. 달성률은 achieved_rps ÷ requested_rps 를 소수 셋째 자리까지 적습니다.

동시성 2 에 응답 0.2초면 리틀의 법칙 상한은 초당 10건입니다. 비율 상한을 아무리 높게 걸어도 그 위로는 올라갈 수 없습니다 — 비율 옵션은 '이보다 빠르게 보내지 말라' 는 뜻이지 '이만큼 보내라' 는 뜻이 아닙니다.

서버 포화인가 발생기 한계인가

/root/lt-generator-limits/verdict.txt 에 여섯 줄을 적으세요. baseline_service_ms= 는 c1.csv 의 응답 시간 중앙값, loaded_service_ms= 는 shortfall.csv 의 중앙값(밀리초 정수), service_ratio= 는 둘의 비(소수 둘째 자리), rps_ratio= 는 5단계의 달성률, verdict= 는 아래 규칙으로 고른 값, reason= 은 60자 이상의 근거입니다. 규칙: rps_ratio 가 0.8 이상이면 ok, 그렇지 않은데 service_ratio 가 1.3 이상이면 server, 둘 다 아니면 generator 입니다.

서버가 포화되면 처리 시간이 늘어납니다. 처리 시간이 그대로인데 총 비율만 목표에 못 미친다면 서버는 아직 놀고 있고 한계는 부하를 만드는 쪽에 있습니다. 이 구분을 하지 않으면 멀쩡한 서버에 인스턴스를 더 붙이게 됩니다.

발생기 자신이 한계가 되는 순간

열린 파일 수 한도를 64 로 낮춘 셸에서 hey -n 400 -c 200 -t 5 를 돌려 사람이 읽는 출력을 /root/lt-generator-limits/fdlimit.txt 에 그대로 남기세요(-o csv 없이). 그다음 /root/lt-generator-limits/07-fd.txt 에 다섯 줄 nofile= · concurrency= · ok_count= · error_count= · limit= 을 적습니다. 성공 수와 오류 수는 fdlimit.txt 에서 세고, limit= 에는 이 실패가 어느 쪽 한계였는지 server 또는 generator 로 적습니다.

ulimit -n 64 는 그 셸과 자식 프로세스에만 적용됩니다 — bash -c 'ulimit -n 64; hey ...' 처럼 한 줄로 묶으세요. hey 의 출력에는 Status code distribution 과 Error distribution 두 절이 있습니다. 오류 문구를 읽어 보면 이 실패를 누가 냈는지 알 수 있습니다.

보고서 양식이 스스로 묻게 만든다

/root/lt-generator-limits/check-report.sh 를 만드세요. 보고서 파일 경로를 첫 인자로 받아 requested_rps= · achieved_rps= · achieved_ratio= 세 줄이 모두 숫자와 함께 있으면 0 으로, 하나라도 없으면 0 이 아닌 값으로 끝나야 합니다. 그리고 그 검사를 통과하는 보고서 /root/lt-generator-limits/report.md 를 쓰되, 세 값은 5단계에서 잰 값과 같아야 합니다.

채점기는 이 스크립트에 일부러 항목을 뺀 보고서를 물려 떨어지는지 봅니다 — 무조건 0 으로 끝나는 스크립트는 통과하지 못합니다. 이런 작은 검사 하나가 '초당 500건을 걸었습니다' 를 스스로 증명하게 만듭니다. 보고서에는 시험 조건도 함께 적어 두세요.