콘텐츠로 이동

CI/CD 부하테스트 게이팅 — 성능 회귀를 배포 전에 차단하기

“이번 배포 후 왜 느려졌지?”를 배포 후에 발견하면 늦습니다. CI/CD 파이프라인에 부하테스트를 게이팅으로 추가하면 성능 회귀를 배포 전에 차단합니다.

[코드 Push]
[단위 테스트] → 실패 시 차단
[통합 테스트] → 실패 시 차단
[스테이징 배포]
[부하테스트 게이팅] → p95 > 300ms 또는 에러율 > 1% 시 차단 ← 핵심
[프로덕션 배포]

전체 파이프라인이 아닌 스테이징 이후 단계에 배치합니다. 단위/통합 테스트처럼 매 PR마다 실행하면 시간이 너무 깁니다.

.github/workflows/load-test.yml
name: Load Test Gate
on:
push:
branches: [main] # main 병합 시 (스테이징 배포 후)
workflow_dispatch: # 수동 실행
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: k6 설치
run: |
curl https://github.com/grafana/k6/releases/download/v0.50.0/k6-v0.50.0-linux-amd64.tar.gz \
-L -o k6.tar.gz
tar -xzf k6.tar.gz
sudo mv k6-v0.50.0-linux-amd64/k6 /usr/local/bin/
- name: 부하테스트 실행
env:
BASE_URL: ${{ secrets.STAGING_URL }}
run: |
k6 run \
--env BASE_URL=$BASE_URL \
--out json=results.json \
tests/load/smoke.js # 짧은 스모크 테스트 (2~3분)
# 임계치 초과 시 k6가 exit code 99 반환 → 스텝 실패 → 배포 차단
- name: 결과 업로드
if: always()
uses: actions/upload-artifact@v4
with:
name: k6-results
path: results.json
- name: Slack 알림
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "🚨 부하테스트 실패 - 배포가 차단됐습니다\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
// tests/load/smoke.js — CI용 경량 시나리오
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 20 }, // 워밍업
{ duration: '1m', target: 50 }, // 목표 부하
{ duration: '30s', target: 0 }, // 종료
],
thresholds: {
http_req_duration: ['p(95)<300'], // p95 300ms 이하
http_req_failed: ['rate<0.01'], // 에러율 1% 이하
},
};
const BASE_URL = __ENV.BASE_URL;
export default function () {
const res = http.get(`${BASE_URL}/api/products`);
check(res, { 'status 200': (r) => r.status === 200 });
}
// Jenkinsfile
pipeline {
agent any
stages {
stage('스테이징 배포') {
steps {
sh './deploy.sh staging'
}
}
stage('부하테스트') {
steps {
sh """
jmeter -n \
-t tests/load/stress-test.jmx \
-l results.jtl \
-e -o report/ \
-Jbase_url=${STAGING_URL} \
-Jusers=100 \
-Jduration=120
"""
}
post {
always {
perfReport 'results.jtl' // Performance Plugin
publishHTML(target: [
reportDir: 'report',
reportFiles: 'index.html',
reportName: '부하테스트 리포트',
])
}
}
}
stage('프로덕션 배포') {
when {
expression {
// JMeter 결과에서 에러율 추출
def errorRate = sh(
script: "awk -F, 'NR>1 {errors+=$8; total++} END {print errors/total}' results.jtl",
returnStdout: true
).trim().toDouble()
return errorRate < 0.01 // 에러율 1% 미만일 때만 배포
}
}
steps {
sh './deploy.sh production'
}
}
}
}

매번 절대값이 아닌 이전 배포 대비 회귀 여부를 감지합니다.

scripts/compare_baseline.py
import json
import sys
def load_results(path):
with open(path) as f:
return json.load(f)
def check_regression(current, baseline, threshold=0.15):
"""
현재 결과가 베이스라인 대비 15% 이상 나빠지면 실패
"""
metrics = ['http_req_duration', 'http_reqs']
regressions = []
for metric in metrics:
curr_p95 = current['metrics'][metric]['values']['p(95)']
base_p95 = baseline['metrics'][metric]['values']['p(95)']
if curr_p95 > base_p95 * (1 + threshold):
regressions.append({
'metric': metric,
'current': curr_p95,
'baseline': base_p95,
'delta': f'+{((curr_p95/base_p95 - 1) * 100):.1f}%',
})
if regressions:
print("❌ 성능 회귀 탐지:")
for r in regressions:
print(f" {r['metric']}: {r['baseline']:.0f}ms → {r['current']:.0f}ms ({r['delta']})")
sys.exit(1)
else:
print("✅ 성능 회귀 없음")
current = load_results('results-current.json')
baseline = load_results('results-baseline.json')
check_regression(current, baseline)
# GitHub Actions에서 베이스라인 비교
- name: 베이스라인 다운로드
uses: actions/download-artifact@v4
with:
name: k6-results-baseline
path: baseline/
- name: 회귀 비교
run: python scripts/compare_baseline.py
- name: 현재 결과를 베이스라인으로 저장
if: success() && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: k6-results-baseline
path: results.json
retention-days: 30

TestForge 스캔으로 엔드포인트를 자동 추출하고, CI/CD 파이프라인에서 해당 엔드포인트를 대상으로 즉시 부하테스트를 실행할 수 있습니다.

Terminal window
# TestForge CLI (예시)
testforge scan https://staging.example.com --output endpoints.json
# 추출된 엔드포인트로 k6 스크립트 생성
testforge generate k6 --input endpoints.json --output tests/load/auto.js

TestForge 스캔 시작하기 →

visitor count

이 가이드를 내 서비스에 직접 적용해 보세요.

TestForge 무료 스캔 시작 →