CI/CD 부하테스트 게이팅 — 성능 회귀를 배포 전에 차단하기
“이번 배포 후 왜 느려졌지?”를 배포 후에 발견하면 늦습니다. CI/CD 파이프라인에 부하테스트를 게이팅으로 추가하면 성능 회귀를 배포 전에 차단합니다.
파이프라인 게이팅 전략
섹션 제목: “파이프라인 게이팅 전략”[코드 Push] │ ▼[단위 테스트] → 실패 시 차단 │ ▼[통합 테스트] → 실패 시 차단 │ ▼[스테이징 배포] │ ▼[부하테스트 게이팅] → p95 > 300ms 또는 에러율 > 1% 시 차단 ← 핵심 │ ▼[프로덕션 배포]전체 파이프라인이 아닌 스테이징 이후 단계에 배치합니다. 단위/통합 테스트처럼 매 PR마다 실행하면 시간이 너무 깁니다.
k6 + GitHub Actions
섹션 제목: “k6 + GitHub Actions”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 });}JMeter + Jenkins
섹션 제목: “JMeter + Jenkins”// Jenkinsfilepipeline { 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' } } }}성능 베이스라인 비교
섹션 제목: “성능 베이스라인 비교”매번 절대값이 아닌 이전 배포 대비 회귀 여부를 감지합니다.
import jsonimport 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: 30TestForge와 연계
섹션 제목: “TestForge와 연계”TestForge 스캔으로 엔드포인트를 자동 추출하고, CI/CD 파이프라인에서 해당 엔드포인트를 대상으로 즉시 부하테스트를 실행할 수 있습니다.
# TestForge CLI (예시)testforge scan https://staging.example.com --output endpoints.json
# 추출된 엔드포인트로 k6 스크립트 생성testforge generate k6 --input endpoints.json --output tests/load/auto.js다음 단계
섹션 제목: “다음 단계”이 가이드를 내 서비스에 직접 적용해 보세요.
TestForge 무료 스캔 시작 →