Реализация Continuous Load Testing в CI/CD пайплайне

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Continuous Load Testing в CI/CD пайплайне
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Непрерывное нагрузочное тестирование в CI/CD

Нагрузочное тестирование в CI/CD — запуск базовых performance тестов при каждом деплое. Цель не в симуляции пикового трафика, а в обнаружении регрессий производительности: если новый код замедлил ключевой endpoint на 30% — пайплайн должен сломаться до попадания в продакшен.

Инструменты и их место в CI

k6 — лучший выбор для CI: JS-скрипты, встроенная статистика, threshold-based pass/fail, нативная интеграция с GitHub Actions и GitLab CI.

Artillery — YAML-конфигурация, удобен для описания сценариев без кода.

Gatling — Scala/Java, детальные HTML-отчёты, удобен для Java-команд.

Базовый k6 скрипт

// tests/performance/api-smoke.js
import http from 'k6/http'
import { check, sleep } from 'k6'
import { Rate, Trend } from 'k6/metrics'

// Кастомные метрики
const errorRate = new Rate('errors')
const postCreateDuration = new Trend('post_create_duration')

export const options = {
  // Профиль нагрузки для CI: быстро, не разрушительно
  stages: [
    { duration: '30s', target: 10 },  // разогрев
    { duration: '1m',  target: 10 },  // устойчивая нагрузка
    { duration: '10s', target: 0 },   // остывание
  ],

  // Пайплайн сломается если порог не достигнут
  thresholds: {
    http_req_duration: [
      'p(95)<500',   // p95 < 500мс
      'p(99)<1000',  // p99 < 1000мс
    ],
    errors: ['rate<0.01'],            // ошибок < 1%
    http_req_failed: ['rate<0.01'],   // HTTP ошибок < 1%
    post_create_duration: ['p(95)<800'],
  }
}

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000'
const AUTH_TOKEN = __ENV.AUTH_TOKEN

export function setup() {
  // Один раз: получить токен или подготовить данные
  const res = http.post(`${BASE_URL}/api/auth/login`, JSON.stringify({
    email: '[email protected]',
    password: 'testpassword'
  }), { headers: { 'Content-Type': 'application/json' } })

  return { token: res.json('token') }
}

export default function(data) {
  const headers = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${data.token || AUTH_TOKEN}`
  }

  // Сценарий 1: список постов (70% трафика)
  const postsList = http.get(`${BASE_URL}/api/posts?limit=20`, { headers })
  check(postsList, {
    'posts list: status 200': (r) => r.status === 200,
    'posts list: has items': (r) => r.json('data').length > 0
  })
  errorRate.add(postsList.status !== 200)

  sleep(Math.random() * 0.5)  // случайная пауза 0-500мс

  // Сценарий 2: создание поста (20% трафика)
  if (Math.random() < 0.2) {
    const start = Date.now()
    const createPost = http.post(`${BASE_URL}/api/posts`, JSON.stringify({
      title: `Test post ${Date.now()}`,
      content: 'Load test content'
    }), { headers })

    postCreateDuration.add(Date.now() - start)
    check(createPost, {
      'create post: status 201': (r) => r.status === 201,
    })
    errorRate.add(createPost.status !== 201)
  }

  sleep(0.3)
}

GitHub Actions интеграция

# .github/workflows/performance.yml
name: Performance Tests

on:
  push:
    branches: [main, staging]
  pull_request:
    branches: [main]

jobs:
  performance:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_DB: testdb
          POSTGRES_PASSWORD: testpass
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready
          --health-interval 10s

    steps:
      - uses: actions/checkout@v4

      - name: Start application
        run: |
          docker compose -f docker-compose.test.yml up -d api
          npx wait-on http://localhost:3000/health --timeout 60000

      - name: Run k6 smoke test
        uses: grafana/[email protected]
        with:
          filename: tests/performance/api-smoke.js
          flags: --out json=results.json
        env:
          BASE_URL: http://localhost:3000
          K6_PROMETHEUS_RW_SERVER_URL: ${{ secrets.PROMETHEUS_URL }}

      - name: Parse results
        if: always()
        run: |
          # Показать summary в PR комментарии
          jq -r '.metrics | {
            p95: .http_req_duration["p(95)"],
            p99: .http_req_duration["p(99)"],
            errors: .http_req_failed.rate
          }' results.json

      - name: Comment PR with results
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs')
            const results = JSON.parse(fs.readFileSync('results.json'))
            const p95 = results.metrics.http_req_duration['p(95)'].toFixed(0)
            const errorRate = (results.metrics.http_req_failed.rate * 100).toFixed(2)

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Performance Test Results\n\n| Metric | Value | Threshold |\n|--------|-------|-----------|\n| p95 latency | ${p95}ms | <500ms |\n| Error rate | ${errorRate}% | <1% |`
            })

Baseline сравнение между деплоями

#!/bin/bash
# scripts/compare-performance.sh

CURRENT_BRANCH=$(git branch --show-current)
BASELINE_BRANCH="main"

# Тест текущего кода
k6 run --out json=current.json tests/performance/api-smoke.js

# Переключиться на baseline
git stash
git checkout $BASELINE_BRANCH
docker compose up -d --build api
sleep 10
k6 run --out json=baseline.json tests/performance/api-smoke.js

# Сравнение
node - <<'EOF'
const current = require('./current.json')
const baseline = require('./baseline.json')

const metrics = ['http_req_duration']
for (const m of metrics) {
  const cp95 = current.metrics[m]['p(95)']
  const bp95 = baseline.metrics[m]['p(95)']
  const delta = ((cp95 - bp95) / bp95 * 100).toFixed(1)

  if (cp95 > bp95 * 1.2) {  // регрессия > 20%
    console.error(`REGRESSION: ${m} p95 degraded by ${delta}%`)
    process.exit(1)
  }
  console.log(`${m} p95: ${cp95}ms vs ${bp95}ms baseline (${delta}%)`)
}
EOF

# Вернуться на текущую ветку
git checkout $CURRENT_BRANCH
git stash pop

Artillery для описания сценариев

# tests/performance/user-journey.yml
config:
  target: "{{ $processEnvironment.BASE_URL }}"
  phases:
    - duration: 60
      arrivalRate: 5
      rampTo: 20
      name: "Ramp up"
    - duration: 120
      arrivalRate: 20
      name: "Sustained load"

  ensure:
    thresholds:
      - http.response_time.p95: 500
      - http.request_rate: 15

scenarios:
  - name: "Browse and purchase"
    weight: 70
    flow:
      - get:
          url: "/api/products"
          expect:
            - statusCode: 200
      - post:
          url: "/api/cart"
          json:
            productId: "{{ $randomInt(1, 100) }}"
            quantity: 1

  - name: "Search only"
    weight: 30
    flow:
      - get:
          url: "/api/search?q={{ $randomString(5) }}"

Срок выполнения

Настройка k6 smoke-тестов в CI/CD с пороговыми значениями и PR-комментариями — 1–2 рабочих дня.