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

부하 테스트

기준선 만들기

TT Lab 에서 이어서 보기

이 실습은 진짜 VM 에서 돕니다

이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이 따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라 진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가 되고 docker exec 도 docker logs 도 그대로 동작합니다.

예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라 컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어 보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.

알아 둘 것이 둘 있습니다.

목표

실제 컨테이너에 부하를 걸어 RPS·p50·p95·p99·에러율을 뽑고, 세 번 반복해 변동폭까지 포함한 재현 가능한 기준선을 /root/lt1/ 에 만듭니다.

왜 중요한가

기준선이 없으면 "느려졌다"는 말은 감상입니다. 그리고 기준선은 숫자만으로는 부족합니다. 동시성이 몇이었는지, 몇 건을 보냈는지, 언제 쟀는지가 없으면 다음 달에 비교할 수 없기 때문입니다. 지표를 고르는 기준도 분명합니다. 요청 10,000건 중 9,900건이 50ms, 100건이 3,000ms 일 때 평균은 79.5ms 이고, 실제로 79.5ms 에 응답받은 요청은 한 건도 없습니다. 게다가 느린 100건이 두 배 나빠져도 평균은 79.5 → 109.5ms 밖에 움직이지 않습니다. 그래서 평균 하나로 판단하지 않고 p50·p95·p99 를 함께 보고, 마지막에는 세 번 재서 변동폭보다 큰 차이만 '변화'라고 부르기로 합니다.

부하 도구 출력 읽는 법

이 이미지에는 hey 가 들어 있습니다. 출력에는 세 절이 있고, 이후 단계는 모두 이 절들에서 값을 읽습니다.

출력 전체를 통째로 저장하세요. 채점기가 여러분이 적은 값을 이 원본과 다시 대조합니다.

단계

먼저 mkdir -p /root/lt1 을 해 두세요.

  1. 부하 대상 컨테이너를 띄웁니다. 이름은 정확히 lt-web, 포트는 호스트 127.0.0.1:8085 → 컨테이너 80 이고, curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8085/ 가 200 이어야 합니다.\n 대상은 응답 시간에 꼬리가 있어야 합니다. 대부분 빠르고 열두 건에 한 번쯤 느린 서버를 파이썬으로 만들어 python:3.12-alpine 컨테이너에 담으세요. nginx 로 정적 파일을 돌려주면 한 건이 1ms 도 걸리지 않아 평균과 p95 가 같은 값이 되고, 6단계에서 쓸 근거가 사라집니다.
  2. 스모크 실행 결과를 /root/lt1/smoke.txt 에 저장합니다. 총 응답이 50건 이상이어야 하고, Requests/sec · Latency distribution · Status code distribution 세 절이 모두 파일에 있어야 합니다.
  3. 워밍업과 본 측정을 나눕니다. /root/lt1/warmup.txt(워밍업)과 /root/lt1/run1.txt(본 측정)를 각각 저장하고, run1.txt 의 총 응답은 200건 이상이어야 합니다.
  4. /root/lt1/metrics.json 에 다섯 필드를 씁니다: rps, p50, p95, p99, error_rate. 앞의 네 값은 run1.txt 에서 읽은 값 그대로(초 단위), error_rate 는 2xx 가 아닌 응답 수 ÷ 전체 응답 수 의 비율(0~1) 입니다.
  5. /root/lt1/errors.txt 에 세 줄을 씁니다: non2xx=(정수), total=(정수), rate=(백분율, 소수 둘째 자리). 앞의 두 값은 상태 코드 분포에서 센 값과 정확히 같아야 합니다.
  6. /root/lt1/why.md 에 세 줄과 설명을 씁니다: average_s=(run1.txt 의 Average), p95_s=(run1.txt 의 95% 분위수), ratio=(p95 ÷ 평균, 소수 둘째 자리). 그리고 평균만 보면 무엇을 놓치는지 본문에 '평균'이라는 단어를 포함해 설명하세요.
  7. 같은 조건으로 두 번 더 측정해 /root/lt1/run2.txt, /root/lt1/run3.txt 를 만들고, /root/lt1/runs.csv 에 정리합니다. 형식은 첫 줄 헤더 run,rps,p95, 이어서 데이터 행 정확히 3개(1,<rps>,<p95> 형태), 마지막에 spread= 줄을 둡니다. spread 는 (최대 RPS − 최소 RPS) ÷ 최소 RPS × 100, 소수 첫째 자리입니다.
  8. /root/lt1/baseline.json 에 여섯 필드를 씁니다: rps, p95, error_rate, concurrency, requests, measured_at. rps 는 3회 측정의 중앙값(가운데 값)이어야 하고, error_rate 는 0.01 이하여야 합니다(에러가 나는 상태의 숫자는 기준선이 될 수 없습니다).

참고

부하 대상 컨테이너 띄우기

부하 대상 컨테이너를 띄웁니다. 이름은 정확히 lt-web, 포트는 호스트 127.0.0.1:8085 → 컨테이너 80 이고, curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8085/ 가 200 이어야 합니다.\n 대상은 응답 시간에 꼬리가 있어야 합니다. 대부분 빠르고 열두 건에 한 번쯤 느린 서버를 파이썬으로 만들어 python:3.12-alpine 컨테이너에 담으세요. nginx 로 정적 파일을 돌려주면 한 건이 1ms 도 걸리지 않아 평균과 p95 가 같은 값이 되고, 6단계에서 쓸 근거가 사라집니다.

이름은 정확히 lt-web 이고 호스트 127.0.0.1 의 8085 포트를 컨테이너 80 에 연결해야 합니다. 호스트 쪽이 1024 미만이면 바인딩할 수 없으니 높은 포트를 쓰고, 컨테이너 안쪽은 root 라서 80 을 그대로 써도 됩니다. 대상은 정적 파일 서버가 아니라 응답 시간에 꼬리가 있는 서버여야 합니다 — 그래야 6단계에서 평균과 p95 가 갈라지는 것을 자신의 측정값으로 볼 수 있습니다.

스모크 실행으로 도구 출력 확인

스모크 실행 결과를 /root/lt1/smoke.txt 에 저장합니다. 총 응답이 50건 이상이어야 하고, Requests/sec · Latency distribution · Status code distribution 세 절이 모두 파일에 있어야 합니다.

결과를 /root/lt1/smoke.txt 에 통째로 저장합니다. 요약·지연 분포·상태 코드 분포 세 절이 모두 남아야 하므로 리다이렉션할 때 일부만 잘라 내면 안 됩니다. 최소 50건 이상 보내세요.

워밍업과 본 측정 분리

워밍업과 본 측정을 나눕니다. /root/lt1/warmup.txt(워밍업)과 /root/lt1/run1.txt(본 측정)를 각각 저장하고, run1.txt 의 총 응답은 200건 이상이어야 합니다.

warmup.txt 와 run1.txt 를 따로 만듭니다. 첫 요청들은 캐시도 커넥션도 없는 상태를 측정하므로 본 측정에 섞으면 분포가 오염됩니다. 본 측정은 200건 이상이어야 분포를 볼 수 있습니다.

핵심 지표 5개를 JSON 으로 정리

/root/lt1/metrics.json 에 다섯 필드를 씁니다: rps, p50, p95, p99, error_rate. 앞의 네 값은 run1.txt 에서 읽은 값 그대로(초 단위), error_rate 는 2xx 가 아닌 응답 수 ÷ 전체 응답 수 의 비율(0~1) 입니다.

/root/lt1/metrics.json 의 값은 run1.txt 에서 그대로 읽은 값이어야 합니다. 지어내면 원본과 대조하는 과정에서 걸립니다. 에러율은 비율(0~1)이고 상태 코드 분포에서 2xx 가 아닌 건수를 세어 구합니다.

에러율을 손으로 계산

/root/lt1/errors.txt 에 세 줄을 씁니다: non2xx=(정수), total=(정수), rate=(백분율, 소수 둘째 자리). 앞의 두 값은 상태 코드 분포에서 센 값과 정확히 같아야 합니다.

/root/lt1/errors.txt 에 non2xx / total / rate 세 줄을 씁니다. 앞의 두 값은 정수로 정확히 일치해야 하고 rate 는 백분율입니다. 상태 코드 분포 절의 건수를 모두 더하면 total 입니다.

평균과 p95 의 배수 비교

/root/lt1/why.md 에 세 줄과 설명을 씁니다: average_s=(run1.txt 의 Average), p95_s=(run1.txt 의 95% 분위수), ratio=(p95 ÷ 평균, 소수 둘째 자리). 그리고 평균만 보면 무엇을 놓치는지 본문에 '평균'이라는 단어를 포함해 설명하세요.

/root/lt1/why.md 에 average_s / p95_s / ratio 세 줄과 평균이 왜 부족한지에 대한 설명을 씁니다. ratio 는 p95 를 평균으로 나눈 값입니다. 값은 run1.txt 에서 읽으세요.

3회 반복 측정과 변동폭

같은 조건으로 두 번 더 측정해 /root/lt1/run2.txt, /root/lt1/run3.txt 를 만들고, /root/lt1/runs.csv 에 정리합니다. 형식은 첫 줄 헤더 run,rps,p95, 이어서 데이터 행 정확히 3개(1,<rps>,<p95> 형태), 마지막에 spread= 줄을 둡니다. spread 는 (최대 RPS − 최소 RPS) ÷ 최소 RPS × 100, 소수 첫째 자리입니다.

같은 조건으로 run2.txt, run3.txt 를 더 만들고 runs.csv 에 정리합니다. 변동폭은 최대와 최소의 차이를 최소로 나눈 백분율입니다. 이 값보다 작은 차이는 개선이라고 부를 수 없습니다.

기준선 확정

/root/lt1/baseline.json 에 여섯 필드를 씁니다: rps, p95, error_rate, concurrency, requests, measured_at. rps 는 3회 측정의 중앙값(가운데 값)이어야 하고, error_rate 는 0.01 이하여야 합니다(에러가 나는 상태의 숫자는 기준선이 될 수 없습니다).

/root/lt1/baseline.json 에는 숫자뿐 아니라 측정 조건도 함께 넣어야 나중에 비교할 수 있습니다. 대표값은 3회 중 가운데 값이고, 에러가 나는 상태의 숫자는 기준이 될 수 없습니다.