부하 테스트 · 부하 모델과 지표(RPS·p95·에러율) · 실습
기준선 만들기
이 실습은 진짜 VM 에서 돕니다
이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이
따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라
진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가
되고 docker exec 도 docker logs 도 그대로 동작합니다.
예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라
컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어
보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.
알아 둘 것이 둘 있습니다.
- 처음 뜨는 데 1분 남짓 걸립니다. VM 이 부팅하고 도커를 설치하기
- 브라우저 미리보기가 없습니다. VM 으로 들어오는 연결은 채점 포트
때문입니다. 파드 실습(보통 40초)보다 느립니다.
하나만 열려 있습니다. 웹 서버를 띄웠다면 VM 안에서 curl 로 확인하세요.
목표
실제 컨테이너에 부하를 걸어 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 가 들어 있습니다. 출력에는 세 절이 있고, 이후 단계는 모두 이 절들에서 값을 읽습니다.
Summary:절의Requests/sec:줄 → 처리량. 줄의 마지막 필드가 값입니다.Summary:절의Average:줄 → 평균 지연(초). 두 번째 필드가 값입니다.Latency distribution:절의95% in 0.0086 secs같은 줄 → 분위수. 세 번째 필드가 값입니다.Status code distribution:절의[200] 2000 responses같은 줄 → 상태 코드별 건수. 두 번째 필드가 건수입니다.
출력 전체를 통째로 저장하세요. 채점기가 여러분이 적은 값을 이 원본과 다시 대조합니다.
단계
먼저 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 이하여야 합니다(에러가 나는 상태의 숫자는 기준선이 될 수 없습니다).
참고
- 실행 예:
hey -n 100 -c 2 http://127.0.0.1:8085/ > /root/lt1/smoke.txt 2>&1.-n은 총 요청 수,-c는 동시성입니다. - 값 뽑기:
grep -i 'Requests/sec' run1.txt | awk '{print $NF}',grep '95% in' run1.txt | awk '{print $3}',grep 'Average:' run1.txt | awk '{print $2}'. - 상태 코드 합계는
awk '/Status code distribution/{f=1;next} f && /responses/ {gsub(/[][]/," "); t+=$2} END{print t}' run1.txt로 셀 수 있습니다. - 중앙값은
cut -d, -f2 runs.csv | sort -g후 가운데 값입니다. - 흔한 실수 1:
runs.csv헤더를 숫자로 시작하게 쓰는 것. 데이터 행이 3개를 넘겨 실패합니다. - 흔한 실수 2: 값을 반올림해 적당히 적는 것. 채점기가 원본 출력에서 다시 계산해 5% 이내인지 확인합니다.
단계 8개
- 부하 대상 컨테이너 띄우기
- 스모크 실행으로 도구 출력 확인
- 워밍업과 본 측정 분리
- 핵심 지표 5개를 JSON 으로 정리
- 에러율을 손으로 계산
- 평균과 p95 의 배수 비교
- 3회 반복 측정과 변동폭
- 기준선 확정