☁️ 🏗️

클라우드/인프라 엔지니어가 되는 과정을 기록합니다.

홈서버 구축 일지

[10] k6로 서버 맷집 테스트하기 🥊

d4eh0 2026. 4. 29. 22:17
키워드 : VU, RPS, p95, 에러율, Tomcat Thread Pool, HikariCP Connection Pool

[9]편에서 모니터링 스택을 갖췄다 !

 

Prometheus가 메트릭을 긁어오고,

Grafana 대시보드에서 서버의 CPU와 메모리를 실시간으로 볼 수 있게 됐다.

이제 실제로 때려볼 차례다.


🤔  왜 맥북에서 k6를 돌리나?

k6는 맥북에서 실행한다. 갤북(서버)이 아니라.

MacBook (k6 실행) → 트래픽을 생성하는 클라이언트
갤북 → 트래픽을 받아서 처리하는 서버
 
k6를 갤북에 설치하면 자기 자신한테 요청을 보내는 구조가 된다.

네트워크 구간도 없고, k6가 CPU를 갉아먹어서 서버 측정값도 오염이 된다.

 

반면 맥북에서 공인 IP로 쏘면 실제 인터넷 → 공유기 → nginx 전체 경로를 탄다.

즉, 실제 사용자가 접속하는 것과 동일한 구조 !


💥 그래서 얼마나 때릴건데?

CloudAttend는 출석 시스템이다.

서비스의 핵심 트래픽은 출석이 오픈되고, 학생들이 동시에 출석 버튼을 누르는 그 순간에 집중된다.

 

이 부하테스트에서 가장 먼저 결정해야 할 건 목표 VU다.

정확한 트래픽 수치는 실제 운영 데이터 없이는 알 수 없다.

 

재학생 수가 20,000명인 상황을 가정을 했지만,

어느 시간대에 트래픽이 얼마나 몰릴지 정확히 알 수 없기 때문에 가장 보수적인 기준을 잡았다.

VU 500명을 안정적으로 처리할 수 있는가?

 

출석 가능 시간은 수업 시작 시간 전후로 10분이다.

500명의 학생이 동시에 출석 버튼을 누르는 상황은 현실에서 거의 발생하지 않을 듯 하다.


⚙️ 테스트 환경 구성

🔹 테스트 계정

학번이 서로 겹치지 않도록 2000001 ~ 2000500.

500개 계정을 DB에 직접 insert했다.

 

각 계정은 모두  동일하게 3개 과목을 수강 중인 상태로 구성했다.

출석 시도 API는 "이 학생이 이 수업을 수강 중인가"를 먼저 확인한다.

takes 테이블에 수강 데이터가 없으면 출석시도 자체가 에러가 나기 때문에 사전 구성이 필요했다.

 

🔹 k6 stages

 

총 10분. 갑자기 500명을 때리는 게 아니라 서서히 올라가는 구조다.

export const options = {
  stages: [
    { duration: '1m',  target: 10  }, // 워밍업
    { duration: '2m',  target: 100 }, // 가벼운 부하
    { duration: '2m',  target: 200 }, // 적당한 부하
    { duration: '2m',  target: 350 }, // 현실적인 피크
    { duration: '2m',  target: 500 }, // 최대 부하
    { duration: '1m',  target: 0   }, // 쿨다운
  ],
};

 

📊 k6 테스트 결과

k6 테스트 결과

 

주요 지표는 4가지다.

지표 결과 평가
로그인 에러율 7.23% 문제있음
출석시도 에러율 0% Good
p95 응답시간 658ms 양호
RPS 227/s 홈서버 처리 한계 수준

 

*RPS(Request Per Second)

: 서버가 1초에 처리하는 요청 수.

: 227 RPS는 1초에 227번 요청을 처리했다는 뜻

 

*p(95) 응답시간

: 전체 요청 중 95%가 이 시간 안에 처리됐음을 의미

: 658ms라는건 대부분의 사용자가 0.65초 안에 응답을 받았다는 뜻


📈 Grafana 서버 메트릭

k6 테스트와 동시에 Grafana로 서버 자원을 모니터링했다.

 

순서대로 VU 200, VU 350, VU500 구간

 

구간 VU CPU Busy Sys Load RAM Used
적당한 부하 200명 12% 8.9% 29.1%
현실적 피크 350명 18.8% 13% 29.3%
최대 부하 500명 27.3% (양호) 30.1% 29.7%
종료 후 0명 1% 2.3% 28.5%

 

🔍 병목의 원인은 서버의 CPU가 아니었다

VU 500명 → CPU 27.3% (자원은 충분히 남아있음)
VU 500명 → 로그인 에러 7.23% (에러는 발생)

 

CPU가 문제가 아니다.

자원은 남아있는데 에러가 난다는 건 설정값이 병목이라는 뜻이다.

 

원인은 일단 두 가지로 보인다.

(1) Tomcat thread pool 기본값 : 200개
   → VU가 늘어날수록 thread 소진
   → 대기 큐 초과 → 요청 튕겨냄 → 로그인 에러

(2) HikariCP connection pool 기본값 : 10개
   → thread가 DB connection을 못 잡고 대기
   → connection-timeout 초과 → 에러 발생 

 

CPU 여유가 있음에도 에러가 발생하는 패턴은 thread/connection pool 병목의 전형적인 증상이다.

출석시도 에러율이 0%인걸 보니 이걸 더 뒷받침한다.

 

로그인을 통과한 요청은 정상 처리됐다.

문제는 로그인 단계에서 thread가 소진되면서 요청 자체가 튕겨나간 것


📌 다음 편 예고

원인은 파악했다. CPU가 아니라 설정값이 병목이다.

그러면 pool 값을 그냥 올리면 해결되는 거 아닐까?

 

단순한 생각이었지만, 그게 아니었다.

pool size에는 기준이 있다고 한다.

무작정 늘리면 오히려 DB가 터진다.

어떤 기준으로 얼마나 조정해야 하는지, 다음 편에서 튜닝을 진행한다.