Skip to content

k6 부하테스트 스크립트 - #106

Merged
jaebeom79 merged 7 commits into
mainfrom
develop
May 29, 2026
Merged

k6 부하테스트 스크립트#106
jaebeom79 merged 7 commits into
mainfrom
develop

Conversation

@jaebeom79

Copy link
Copy Markdown
Contributor

📢 기능 설명

필요시 실행결과 스크린샷 첨부

연결된 issue

연결된 issue를 자동을 닫기 위해 아래 {이슈넘버}를 입력해주세요.

close #{이슈넘버}

✅ 체크리스트

  • PR 제목 규칙 잘 지켰는가?
  • 추가/수정사항을 설명하였는가?
  • 이슈넘버를 적었는가?

johe00123 and others added 7 commits May 29, 2026 00:30
3-Phase 구조로 SADD/SMEMBERS/TTL 각각 측정
- PHASE 1: POST /api/v1/vacancy 100회 호출로 SADD 부하
- PHASE 2: k6/experimental/redis 로 SMEMBERS 응답시간 측정
- PHASE 3: TTL/EXISTS 로 키 자동 삭제 확인
- @transactional 제거: 자동 롤백으로 시드 유저 미반영 문제 해결
- Redis 클라이언트 조건부 초기화: subscribe phase 단독 실행 가능
- Date.now() -> performance.now(): sub-ms SMEMBERS 측정 정확도 개선
…cribe-load-test

Chore/#101 vacancy subscribe load test
- 01·03·05·06·07번: event_spike(워밍업 후 스파이크) + ramp_up(점진 증가) 두 시나리오 적용
- 04번: ramp_up 단독으로 서킷브레이커 OPEN 시점 탐색 시나리오로 개선
- docker-compose.prod.yml: Prometheus remote write 수신 활성화 및 포트 외부 노출
- run.ps1: 기본값 운영 서버 주소로 변경
- k6-dashboard.json: event_spike 시나리오명 동기화, 심화 지표 패널 4개 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 05: district 파라미터 한글 encodeURIComponent 적용
- 07: setDate/getDate 타임존 혼용 → ms 연산으로 통일

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chore: k6 부하테스트 시나리오 고도화 및 모니터링 대시보드 개선

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates and adds several k6 load testing scripts to compare event spike and ramp-up scenarios, introduces a Grafana dashboard, and adds a token generator utility. Feedback on these changes highlights a critical security issue in docker-compose.prod.yml where Prometheus port 9090 is exposed publicly. Additionally, multiple k6 script issues were identified, including incorrect string-based timeout values in 04-circuit-breaker.js and 06-store-nearby.js, and a dynamically generated options block in 11-vacancy-test.js that violates k6's static analysis requirements.

Comment thread docker-compose.prod.yml
Comment on lines +83 to +84
ports:
- "9090:9090"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

운영 환경(docker-compose.prod.yml)에서 Prometheus 포트 9090을 외부(0.0.0.0:9090)에 그대로 노출하는 것은 심각한 보안 위험입니다.

특히 현재 설정에는 --web.enable-lifecycle--web.enable-remote-write-receiver 옵션이 활성화되어 있어, 인증되지 않은 외부 사용자가 다음과 같은 악의적인 행위를 수행할 수 있습니다:

  1. POST /-/reload 또는 POST /-/quit 호출을 통한 Prometheus 강제 종료 및 설정 변경
  2. 원격 쓰기 수신기(/api/v1/write)에 대량의 허위 메트릭을 전송하여 디스크 고갈 및 서비스 거부(DoS) 유발
  3. 내부 시스템 메트릭 및 민감한 인프라 정보 무단 조회

해결 방안:

  • 외부 노출이 불필요하다면 루프백 주소로 바인딩하여 외부 접근을 차단하세요: 127.0.0.1:9090:9090
  • 외부(예: 로컬 k6)에서 원격 쓰기가 필요하다면, 이미 동일한 compose 파일에 정의된 Nginx 프록시를 통해 기본 인증(Basic Auth) 및 SSL을 적용하여 안전하게 라우팅하는 것을 권장합니다.
    ports:
      - "127.0.0.1:9090:9090"

Comment thread k6/04-circuit-breaker.js
const res = http.post(`${BASE_URL}/api/v1/chat/messages`, payload, {
headers: HEADERS_AUTH,
timeout: 65000, // LLM 응답 최대 대기 (65초)
timeout: '65s',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

k6의 http 요청 매개변수에서 timeout 옵션은 **밀리초(ms) 단위의 숫자(number)**를 기대합니다.

'65s'와 같이 문자열로 지정할 경우, k6가 이를 올바르게 파악하지 못해 기본 타임아웃(60초)이 적용되거나 런타임 오류가 발생할 수 있습니다. LLM 응답 대기를 위해 65초를 설정하려면 기존처럼 65000 숫자로 지정해야 합니다.

Suggested change
timeout: '65s',
timeout: 65000,

Comment thread k6/06-store-nearby.js
`&centerLat=${lat}&centerLng=${lng}&limit=50`;

const res = http.get(url, { headers: HEADERS_JSON, timeout: 15000 });
const res = http.get(url, { headers: HEADERS_JSON, timeout: '15s' });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

k6의 http 요청 매개변수에서 timeout 옵션은 **밀리초(ms) 단위의 숫자(number)**를 지정해야 합니다.

'15s' 문자열 대신 15000 숫자를 사용해 주세요. (참고로 106라인의 nearby 요청에서는 15000 숫자로 올바르게 설정되어 있습니다.)

Suggested change
const res = http.get(url, { headers: HEADERS_JSON, timeout: '15s' });
const res = http.get(url, { headers: HEADERS_JSON, timeout: 15000 });

Comment thread k6/11-vacancy-test.js
Comment on lines +183 to +196
export const options = (() => {
if (PHASE === 'subscribe') {
return { vus: N, iterations: N };
}
if (PHASE === 'smembers') {
// 단일 VU 가 N 번 반복 - SMEMBERS 자체 응답시간 분포만 본다.
// 동시성 부하를 보려면 vus 를 늘릴 것.
return { vus: 1, iterations: N };
}
if (PHASE === 'ttl') {
return { vus: 1, iterations: 1 };
}
throw new Error(`Unknown PHASE: ${PHASE}`);
})();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

k6에서 options 객체는 정적 분석(Static Analysis)이 가능하도록 **정적 객체 리터럴(Plain Object)**로 내보내야(export) 합니다.

현재와 같이 즉시 실행 함수(IIFE)를 사용하여 options를 동적으로 생성하면, k6가 실행 전 테스트 계획을 수립하는 단계에서 구문 분석에 실패하거나 설정을 무시하여 스크립트가 정상적으로 실행되지 않을 수 있습니다.

개선 방향:
k6에서 여러 테스트 시나리오나 단계를 실행하는 가장 표준적이고 권장되는 방법은 Scenarios를 정적으로 모두 정의해 두고, 실행 시 CLI 플래그(--scenario)를 사용하여 원하는 시나리오만 선택적으로 실행하는 것입니다.

export const options = {
    scenarios: {
        subscribe: {
            executor: 'shared-iterations',
            vus: N,
            iterations: N,
        },
        smembers: {
            executor: 'shared-iterations',
            vus: 1,
            iterations: N,
        },
        ttl: {
            executor: 'shared-iterations',
            vus: 1,
            iterations: 1,
        },
    },
};

Comment thread k6/11-vacancy-test.js
Comment on lines +147 to +150
import http from 'k6/http';
import redis from 'k6/experimental/redis';
import { check } from 'k6';
import { Counter, Trend } from 'k6/metrics';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

시나리오별 분기를 위해 k6/execution 모듈을 임포트합니다.

Suggested change
import http from 'k6/http';
import redis from 'k6/experimental/redis';
import { check } from 'k6';
import { Counter, Trend } from 'k6/metrics';
import http from 'k6/http';
import redis from 'k6/experimental/redis';
import exec from 'k6/execution';
import { check } from 'k6';
import { Counter, Trend } from 'k6/metrics';

Comment thread k6/11-vacancy-test.js
Comment on lines +258 to +262
export default async function () {
if (PHASE === 'subscribe') subscribe();
else if (PHASE === 'smembers') await smembers();
else if (PHASE === 'ttl') await ttl();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

options를 정적 시나리오 구조로 변경한 후, 실행 중인 시나리오 이름(exec.scenario.name)에 따라 동적으로 실행할 함수를 분기하도록 개선할 수 있습니다.

이렇게 하면 환경 변수(PHASE)에 의존하지 않고 k6의 표준 시나리오 제어 방식을 활용할 수 있어 훨씬 깔끔하고 견고해집니다.

Suggested change
export default async function () {
if (PHASE === 'subscribe') subscribe();
else if (PHASE === 'smembers') await smembers();
else if (PHASE === 'ttl') await ttl();
}
export default async function () {
const phase = exec.scenario.name;
if (phase === 'subscribe') subscribe();
else if (phase === 'smembers') await smembers();
else if (phase === 'ttl') await ttl();
}

@jaebeom79
jaebeom79 merged commit 200c37b into main May 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants