Locust와 uv를 사용한 부하 테스트
이 스킬은 프로젝트를 "얼마나 버틸 수 있나?"에서 팀이 실행할 수 있는 수치가 포함된 보고서로 전환합니다. 각 단계가 다음 단계에 영향을 주므로 순서대로 수행하십시오. 사용자는 테스트 레벨과 트래픽 시나리오라는 두 가지 결정을 내리며, 코드에서 발견한 내용을 확인한 후에만 결정합니다.
$SKILL은 이 스킬의 폴더, 즉 이 SKILL.md를 읽어온 디렉터리입니다. 셸에서 한 번 설정하십시오(SKILL=/path/to/locust-load-test). 부하 테스트 자체는 대상 리포지토리의 loadtest/ 또는 사용자가 선택한 위치에 있습니다.
1 발견 -> loadtest/endpoints.json 모든 엔드포인트, 호출자 및 비용 힌트 포함
2 분류 -> loadtest/PLAN.md 핵심 / 지원 / 주변 표; 사용자에게 보여줌
3 질문 -> 계층 + 시나리오 + 환경 + 제약 조건
4 구축 -> loadtest/ (uv 프로젝트) harness.py, payloads/, 실행 스크립트
5 실행 -> loadtest/results/<run>/ 먼저 스모크, 그 다음 선택된 계층, 가장 위험한 것을 마지막으로
6 수집 -> results/<run>/metrics, profiles 서버 측 뷰, 접근 가능할 때
7 보고 -> loadtest/report.html + 일반 언어로 된 결과
1. 엔드포인트 발견
uv run $SKILL/scripts/discover_endpoints.py . -o loadtest/endpoints.json --markdown loadtest/endpoints.md
스캐너는 코드를 읽고 다른 것은 기록하지 않습니다. 일반적인 스택에서 경로 선언을 찾습니다: Python, Node, JVM, Go, Ruby, .NET 및 PHP 프레임워크, OpenAPI 파일, GraphQL 스키마, gRPC 프로토, WebSocket 및 SSE. 가능한 경우 라우터 접두사와 마운트를 확인합니다. 또한 각 엔드포인트에 대한 클라이언트 호출 사이트(fetch, axios, httpx, requests, 생성된 클라이언트, 테스트, 서버 렌더링 템플릿의 링크, 폼 및 htmx 속성)의 수를 계산하여 제품 자체 UI 및 서비스가 무엇을 사용하는지 확인할 수 있습니다. 각 항목에는 메서드, 경로, file:line, 핸들러, 인증 힌트, 본문 힌트, 스트리밍 힌트, 비용 힌트, 핸들러가 수행하는 호출 및 호출자가 포함됩니다.
프레임워크 자체의 경로 목록은 정적 스캔보다 우선합니다. 프레임워크가 이미 모든 접두사, 마운트 및 생성된 경로를 확인했기 때문입니다. 앱이 하나를 생성할 수 있는 경우, 이를 진실 공급원으로 사용하십시오. 즉, 실행 중인 개발 서버(/openapi.json), flask routes, bin/rails routes, php artisan route:list --json 또는 Spring의 /actuator/mappings의 OpenAPI 문서를 의미합니다:
uv run $SKILL/scripts/discover_endpoints.py . --route-dump http://localhost:8000/openapi.json -o loadtest/endpoints.json
그런 다음 출력을 신뢰하기 전에 비판적으로 읽으십시오:
unmatched_client_calls는 스캔이 놓친 경로이거나 다른 서비스에 대한 호출입니다.prefix_resolved: false및notes는 추측을 표시합니다.shared_handler: true는 하나의 함수가 여러 메서드를 제공하며 스캔이 해당 비용 힌트가 속한 메서드를 알 수 없음을 의미합니다. 해당 핸들러를 읽으십시오.existing_load_tests는 리포지토리에 이미 있는 locustfile 또는 k6 스크립트를 나열합니다. 페이로드를 재사용하십시오.- 소스 파일에 대해 5개의 항목을 점검하십시오.
인벤토리가 부실해 보이거나, 스택이 포함되지 않았거나, 프레임워크에 대한 경로 덤프 명령이 필요한 경우 references/discovery.md를 읽으십시오.
2. 분류: 핵심, 지원, 주변
엔드포인트를 판단에 따라 정렬하십시오. 키워드로 정렬하지 마십시오. 동일한 /search가 상점에서는 핵심이고 관리 콘솔에서는 지원입니다.
| 계층 | 정의 | 일반적인 예시 |
|---|---|---|
| 핵심 | 사용자가 찾아오는 이유이며, 일반적으로 무거운 작업(계산, 모델 호출, 데이터 처리, 주요 쓰기 경로)이 발생하는 곳 | 상점의 결제; 문서 서비스의 변환 또는 추출; 채팅 API의 완료 |
| 지원 | 핵심을 사용 가능하게 만드는 것 | 로그인, 세션, 설정, 사용자 자신의 데이터 목록 및 검색, 핵심을 공급하는 업로드, 관리자 CRUD |
| 주변 | 제품이 존재하지 않는 이유 | 상태, 메트릭, 문서, 정적 파일, 디버그 경로, 소유하지 않은 서비스의 웹훅 |
고려해야 할 증거:
- README와 문서가 제품의 목적에 대해 말하는 내용;
- UI가 가장 많이 호출하는 것(
client_calls)과 테스트가 실행하는 것(test_calls); - 핸들러 비용:
calls를 서비스 레이어까지 추적하십시오.cost_hints는 핸들러 본문만 볼 수 있기 때문입니다. 모델 호출, DB 쓰기, 외부 호출, CPU 작업(비밀번호 해싱, 압축, 이미지:cpu), 파일 처리 및 큐 게시는 엔드포인트를 비싸게 만드는 요소입니다. - 명명;
- 볼 수 있다면 팀 자체 모니터링 또는 로그가 가장 바쁘다고 말하는 것.
이 표와 함께 loadtest/PLAN.md를 작성하고, 부하를 가할 각 엔드포인트당 한 행, 그리고 생략한 엔드포인트와 그 이유에 대한 짧은 목록을 포함하십시오:
| 엔드포인트 | 계층 | 이유 | 비용 힌트 | 페이로드 출처 |
|---|---|---|---|---|
| POST /api/orders/{id}/checkout | 핵심 | 구매 경로; UI가 3곳에서 호출 | DB 쓰기, 결제 API 호출 | tests/fixtures/order.json |
질문을 하기 전에 이 표를 사용자에게 보여주고 수정을 요청하십시오. 잘못된 계층은 이후의 모든 숫자가 잘못된 질문에 답하게 만들며, 사용자는 어떤 스캔보다 자신의 제품을 잘 알고 있습니다.
3. 사용자에게 질문
AskUserQuestion 도구를 사용하십시오. 사용할 수 없는 경우, 동일한 질문을 일반 번호 목록으로 묻고 답변을 기다리십시오. 사용자가 계획 표를 본 후에만 질문하십시오.
- Q1 핵심 레벨. 계층 1, 2 또는 3.
- Q2 지원 레벨. 계층 1, 2 또는 3, 또는 지원 생략.
- Q3 사용자 부하 시나리오. 이 프로젝트에서 파생된 두 가지에서 네 가지 옵션(아래 참조). 먼저 권장하는 옵션에 "(권장)"을 표시하고, 왜 적합한지 한 줄로 설명하십시오.
- Q4 대상 환경. 부하를 가할 배포 환경(스테이징, 로컬 스택, 부하 테스트 복사본). 사용자가 명시적으로 동의하지 않는 한 프로덕션 시스템에 절대 부하를 가하지 마십시오. 부하는 공유 데이터베이스, 테넌트 및 비용에 영향을 미칩니다.
| 계층 | 실행 | 답변 | 비용 |
|---|---|---|---|
| 1 스모크/기준 | 몇 분 동안 낮은 정적 부하(1명에서 소수 사용자) | 모든 경로가 작동하는가, 경합 없는 지연 시간은 얼마인가? | 몇 분; 위험 무시할 수 있음 |
| 2 예상 부하 | 예상 최대치까지 단계별 램프업, 유지, 그런 다음 해당 수준에서 짧은 소크 | 가장 바쁠 때 지연 시간 목표를 충족하는가, 여유 용량은 얼마인가? | 약 1시간; 대상이 실제 최대 부하로 실행됨 |
| 3 스트레스/용량 | 정지 규칙이 발동할 때까지(한계점과 고장 지점) 단계별 램프업, 스파이크 포함 | 어디에서 고장 나는가, 어떻게 고장 나는가, 복구되는가? | 1~2시간; 대상이 포화되어 넘어질 수 있으므로 시간 창과 모니터링 담당자가 필요함 |
Q3의 옵션을 프로젝트가 트래픽에 대해 말하는 내용에서 파생하십시오: README와 문서(누가 사용하는지, 시트 수, 테넌트), 구성(속도 제한, 작업자 및 복제본 수, 오토스케일러 범위, 큐 크기, 연결 풀), cron 및 배치 작업, 호출자의 자체 타임아웃 및 재시도. 일반적인 형태는 업무 시간 단계적 램프, 아침 스파이크, 정적 소크, 다른 서비스의 통합 버스트, 배치 가져오기, 대화형 및 백그라운드 트래픽의 혼합입니다. 모든 옵션은 숫자(사용자, 생성 속도, 사고 시간, 기간 및 요청 혼합)를 명시합니다. 프로젝트가 트래픽에 대해 아무것도 말하지 않는 경우, 그렇게 말하고 각 숫자가 기반하는 가정을 명명한 다음 Q3에서 예상 최대치를 물으십시오. references/tiers-and-scenarios.md는 구성을 해당 숫자로 변환하는 방법을 보여줍니다. 질문하기 전에 읽으십시오.
답변 후, 일반 텍스트로 두 가지를 정리하십시오:
- 자격 증명. 하네스가 읽는 파일 또는 환경 변수에서 제공됩니다. 채팅에 절대 붙여넣지 마십시오.
- 제약 조건. 찾은 항목을 나열하고 나머지를 물으십시오: 속도 제한, 라이선스 또는 활성화 기간, 재시작 비용, 공유 테넌트, 요청당 비용(모델 토큰, 유료 API). 라이선스 기간 내 재시작은 서비스를 하루 동안 잠글 수 있으며, 하나의 거대한 입력을 가진 몇 개의 병렬 작업이 대상을 다운시킬 수 있습니다. 부하 전에 물으십시오.
- 데이터 상태. 실제 사용에서 목록과 검색이 보유하는 행 수, 실행 간에 데이터가 재설정될 수 있는지 여부, 테스트 사용자가 편집하거나 삭제할 수 있는 행. 빈 테이블은 목록 페이지를 무료로 만들고, 실행이 추가하는 행은 이후 각 단계가 이전보다 더 많은 데이터를 읽게 만듭니다.
4. 하네스 구축
mkdir -p loadtest && cd loadtest
cp $SKILL/templates/pyproject.toml . && uv sync # 자체 .venv 및 uv.lock; locust 고정, Python 3.11+
cp $SKILL/templates/{harness.py,run_local.sh,run_plan.sh,collect.sh,seed.py,loadtest.env.example} .
cp $SKILL/templates/run_docker.sh . # 생성기가 대상의 Docker 네트워크에서 실행될 때
cp -r $SKILL/templates/{run_k8s.sh,k8s} . # 대상의 클러스터 내에서 실행될 때
git -C .. status --short # loadtest/만 나타나야 함
여기서 uv init를 실행하지 마십시오. uv 프로젝트 자체인 리포지토리에서 loadtest를 앱의 [tool.uv.workspace]에 추가하고, locust를 앱의 .venv에 설치하고 앱 수준의 uv.lock을 작성합니다. 이런 일이 발생했다면, 앱에서 git checkout -- pyproject.toml을 실행하고, 새 uv.lock을 삭제한 다음, uv sync를 실행하십시오. 그런 다음 템플릿을 채우십시오:
REQUESTS. PLAN.md에서harness.py의 표를 채우십시오: 계층, 이름, 가중치, 혼합, 본문 및 예상 상태. 프로젝트의 테스트, 픽스처 및 스키마에서 페이로드를 가져와loadtest/payloads/에 넣으십시오. 프로덕션 데이터를 절대 사용하지 마십시오. 서버 렌더링 앱은 로그인 페이지와 오류 페이지 모두에 대해 200을 응답하므로, 올바른 페이지만이 가진 것을 확인하십시오:expect_text, 리디렉션이 답변인 경우expect_location. 그렇게 말하지 않고 리디렉션된 행은 실패합니다.REQ_TIMEOUT. 실제 호출자의 타임아웃으로 설정하십시오: 게이트웨이, 프론트엔드의 중단, 클라이언트 SDK 또는 프록시의 읽기 타임아웃. 하네스는 모든 요청을 전체 응답에 대한 데드라인으로 유지하므로, 늦은 응답은deadline exceeded로 실패하여 제품이 이미 실패로 보고했을 성공으로 계산되지 않습니다.MIX. 비용이 다른 각 트래픽 믹스에 고유한 태그를 부여하고, 각 믹스를 자체 시나리오로 실행하십시오(짧은 채팅 크기 입력, 문서 크기 입력, 추가 비용이 드는 옵션). 이들의 용량은 몇 배 차이가 날 수 있으며, 혼합 실행은 비싼 것을 숨깁니다.- 인증. 각 시뮬레이션된 사용자가 요청당 한 번이 아니라 한 번만 로그인하도록
AUTH를 설정하십시오. 세션 쿠키를 사용한 폼 로그인은LOGIN_EXPECT_LOCATION또는LOGIN_EXPECT_TEXT도 필요합니다. 잘못된 비밀번호도 200으로 응답하기 때문입니다(loadtest.env.example참조). 로그인은login단계를 얻고 단계 번호 및 정지 규칙에서 제외됩니다. 각 로그인은 해당 사용자를 추가한 단계의 시작에 도착하며, 느린 비밀번호 해시는 정적 부하가 결코 지불하지 않는 비용으로 실행을 종료시킵니다. 로그인 자체를 부하 테스트하려면 행으로 만드십시오. - 데이터. 앱에 대한
seed.py를 채우고python3 seed.py up --users N --rows M을 실행하십시오: 앱의 API를 통해 각 테스트 계정은 자체 행을 얻으며,SEED_MANIFEST는 이를 해당 사용자에게 전달합니다. 행 키capture및consume은 사용자가 생성하고 삭제하는 것을 추적합니다. 생성을 삭제와 쌍으로 만들거나 실행 간에 다시 시드하십시오. - 여정. 다단계 흐름(업로드 후 폴링, 생성 후 읽기)은 사용자 클래스의 메서드입니다. 템플릿에 예제가 있습니다.
references/harness.md는 모든 필드, 형태, 인증 모드, 스트리밍, 비HTTP 프로토콜 및 세 가지 배치를 다룹니다. 구축하는 동안 읽으십시오.
5. 안전하게 실행
- 먼저 스모크.
RUN_NAME=smoke SHAPE=smoke ./run_local.sh을 실행하십시오: 사용자 클래스당 한 명의 사용자가 각 행을 한 번씩 표 순서대로 보내고(생성하는 행을 사용하는 행 앞에 배치), 그런 다음 유휴 상태가 됩니다. 로그는NOTE smoke: every row was sent로 끝나거나 전송되지 않은 행의 이름을 지정합니다. 오류 제로, 타당한 지연 시간 및 올바른 본문을 확인하십시오. 부하가 발생하기 전에 연결이 올바른지 증명합니다. - 머신이 한가한지 확인하십시오. 용량을 측정하는 실행 전에 생성기 및 대상 호스트에서
uptime및vmstat 1 5를 실행하십시오. 동일한 코어의 다른 작업은 모든 숫자를 경합의 측정값으로 만듭니다. 하네스는 각 실행 전에 자체 호스트를 샘플링하여run.json(host_check및 메모)에 기록합니다. 다른 머신인 경우 대상 호스트의 숫자를NOTES에 넣으십시오. 보고서는 호스트 또는 생성기 CPU가 단계 중 90%에 도달한 실행에 배너를 배치하고 한계점에 주의를 표시합니다. 머신이 바쁜 경우, 기다리거나, 유휴 코어에 고정(taskset)하거나, 이동하고 그렇게 말하십시오. - 그런 다음 선택된 계층을 가장 안전한 것부터. 예상 부하, 그다음 소크, 그다음 스트레스, 그다음 스파이크 순으로 실행하십시오. 위험한 입력(가장 큰 페이로드, 추가 비용 옵션)은 마지막에 가므로, 대상에 해를 끼치더라도 이전 것은 손실되지 않습니다.
run_plan.sh는 목록을 순서대로 실행하고 대상의 상태 확인이 통과할 때만 각 실행을 시작합니다. - 네트워크 경로가 중요한 경우 생성기를 실제 호출자 옆에 배치하십시오: 동일한 클러스터, VNet 또는 compose 네트워크(
run_k8s.sh,run_docker.sh). 랩톱 실행은 서비스만큼 인터넷을 측정합니다. 모든 실행은GENERATOR를 기록하고, 생성기 자체 CPU는metrics/client.csv로 이동합니다. CPU 약 90% 이상에서는 숫자가 생성기를 설명합니다. - 정지 규칙을 준수하십시오. 하네스는 실패율이
STOP_FAIL_RATIO를 초과하거나, p95가STOP_P95_MS를 초과하거나, 마지막 20초 중 절반 이상이 실패하거나, 대부분의 로그인이 실패하면 실행을 종료합니다. 많은 서비스가 부하를 버리는 대신 큐에 넣습니다. 오류는 제로이고 지연 시간만 증가합니다. 따라서 p95 상한을 호출자의 타임아웃 또는 SLO로 설정하십시오. "보기 위해" 정지 규칙을 넘지 마십시오. - 실행 간 상태를 확인하고 큐를 비우도록 하십시오(
run_plan.sh의COOLDOWN). - 생성한 모든 것을 정리하십시오:
./run_k8s.sh down(파드, ConfigMap, Secret), 중지된 컨테이너, 테스트 사용자 및 테스트 데이터. 제거할 수 없는 항목(예: 소프트 삭제된 행 또는 감사 항목)을 사용자에게 알리십시오.
공유 시스템에 대한 첫 번째 실행(스모크 이후) 전과 모든 실행 전에 references/safety.md를 읽으십시오.
6. 서버 측 수집
클라이언트 숫자는 무슨 일이 일어났는지 알려줍니다. 서버 숫자는 그 이유를 알려줍니다. locust 전에 수집기를 시작하고, locust 후에 중지하며, 동일한 실행 디렉터리에 작성하십시오:
uv run $SKILL/scripts/collect_server_metrics.sh <run-dir> --target-host <host> --interval 5
collect.sh는 이미 이 스크립트를 run_plan.sh 내에서 호출하지만, 단일 실행의 경우 수동으로 실행할 수 있습니다. 수집기는 대상 호스트의 다음 항목을 수집합니다(SSH 또는 API 접근 권한이 있는 경우):
- CPU 사용률 (vmstat, /proc/stat)
- 메모리 사용량 (free, /proc/meminfo)
- 디스크 I/O (iostat)
- 네트워크 트래픽 (netstat, /proc/net/dev)
- 프로세스 목록 (ps aux)
- 애플리케이션 로그 (journalctl, tail)
대상에 접근할 수 없는 경우, 해당 숫자를 수동으로 수집하여 results/<run>/에 넣으십시오.
7. 보고
run_plan.sh는 마지막 단계로 보고를 자동으로 실행합니다. 수동으로 실행할 수도 있습니다:
uv run $SKILL/scripts/generate_report.py results/ -o loadtest/report.html
보고서는 다음을 포함합니다:
- 실행 개요: 각 실행의 요약 (날짜, 시간, 기간, 생성기, 대상).
- 지연 시간 분포: 각 계층 및 시나리오에 대한 p50, p95, p99, 최대값.
- 처리량: 요청/초, 사용자/초.
- 실패율: 각 실행에 대한 실패율 및 실패 원인.
- 용량 곡선: 부하 증가에 따른 지연 시간 및 처리량 그래프로 한계점(knee) 식별.
- 자원 사용량: 생성기 및 서버 CPU, 메모리, 네트워크.
- 결론: 요약 및 권장 사항 (용량, 한계점, 병목 현상, 개선 영역).
references/report.md는 보고서의 각 섹션을 해석하는 방법을 다룹니다. 보고서를 검토하고 주요 결과를 일반 언어로 사용자에게 제시하십시오.
참조
| 파일 | 내용 |
|---|---|
references/discovery.md | 엔드포인트 발견에 대한 자세한 내용, 프레임워크별 명령, 문제 해결 |
references/tiers-and-scenarios.md | 계층 및 시나리오 정의, 트래픽 프로필 구성 방법 |
references/harness.md | 하네스 구성 필드, auth 모드, 스트리밍, 배치 |
references/safety.md | 안전 점검 목록, 정지 규칙, 정리 절차 |
references/report.md | 보고서 해석 가이드, 주요 메트릭 이해 |
경고: 이 스킬은 부하 테스트를 용이하게 하기 위해 코드를 실행합니다. 프로젝트 또는 인프라에 피해를 줄 수 있는 작업을 승인하기 전에 항상 실행될 명령을 검토하십시오. 중요한 시스템에서 부하 테스트를 실행할 때는 적절한 허가를 받았는지 확인하십시오.