TL;DR

  • Валидируйте политики auto-scaling реальными нагрузочными тестами до production—K6 для JavaScript/TypeScript команд, Locust для Python команд
  • Используйте Terraform для provisioning эфемерной инфраструктуры нагрузочного тестирования: поднять, протестировать, уничтожить—платите только за время теста
  • Тестируйте минимум три сценария: устойчивая нагрузка (baseline), пиковая нагрузка (trigger auto-scaling) и восстановление (поведение scale-down)

Идеально для: Команд с auto-scaling инфраструктурой, которым нужно валидировать политики масштабирования и понимать лимиты ёмкости Не подходит если: Вы запускаете инфраструктуру фиксированной ёмкости без auto-scaling (фокусируйтесь на планировании ёмкости) Время чтения: 14 минут

Политики auto-scaling, которые работают в теории, часто отказывают под реальной нагрузкой. Политика, срабатывающая при 70% CPU, может масштабироваться слишком медленно, заставляя пользователей ждать. Или может масштабироваться слишком агрессивно, тратя бюджет. Единственный способ узнать, что ваша инфраструктура правильно обрабатывает нагрузку—протестировать её.

Для связанного тестирования инфраструктуры смотрите Стратегии Тестирования Terraform и Тестирование Сетевой Конфигурации.

Подходы с Использованием ИИ

Инструменты ИИ отлично справляются с генерацией скриптов нагрузочных тестов и анализом паттернов производительности.

Генерация сценариев нагрузочного теста K6:

Напиши скрипт нагрузочного теста K6, который валидирует поведение auto-scaling:

Целевое приложение: REST API с endpoints /api/users, /api/orders
Инфраструктура: AWS ALB + Auto Scaling Group (min: 2, max: 10, target CPU: 70%)

Включи три этапа теста:

1. Прогрев: Постепенное увеличение до 100 VUs за 2 минуты
2. Устойчивая нагрузка: Поддержание 500 VUs 10 минут (должен сработать scale-up)
3. Пик: Всплеск до 2000 VUs на 1 минуту, затем обратно к 500
4. Охлаждение: Постепенное снижение до 0 за 5 минут (должен сработать scale-down)

Добавь thresholds для:

- p95 время ответа < 500ms
- Частота ошибок < 1%
- Кастомные метрики для отслеживания событий scaling

Включи интеграцию с CloudWatch для корреляции нагрузки с количеством инстансов ASG.

Анализ поведения auto-scaling:

Проанализируй эти результаты нагрузочного теста и метрики auto-scaling:

Timeline нагрузочного теста:

- 0-2min: Рост до 100 VUs, p95=120ms
- 2-12min: 500 VUs устойчиво, p95 начался с 150ms, вырос до 800ms к минуте 8
- ASG масштабировался с 2 до 4 инстансов на минуте 6, до 6 инстансов на минуте 10
- 12-13min: Пик до 2000 VUs, p95=2500ms, частота ошибок 15%

Вопросы:

1. Политика scaling слишком медленная? Каким должно быть значение target tracking?
2. Почему latency выросла до того, как произошёл scaling?
3. Что объясняет высокую частоту ошибок во время пика?
4. Рекомендуй конкретные изменения в конфигурации auto-scaling.

Создание распределённых нагрузочных тестов Locust:

Создай нагрузочный тест Locust для flow checkout e-commerce:

1. Просмотр продуктов (70% трафика)
2. Добавление в корзину (20% трафика)
3. Checkout (10% трафика)

Включи:

- Реалистичные think times между действиями
- Обработку сессий для состояния корзины
- Кастомные метрики для каждого этапа flow
- Конфигурацию распределённого setup для запуска на Kubernetes

Покажи как запустить это с 10 worker подами для генерации 50,000 concurrent users.

Когда Использовать Разные Подходы к Тестированию

Фреймворк Принятия Решений по Стратегии Тестирования

Тип ТестаИнструментЦельКогда Запускать
Smoke testK6/LocustПроверить работу системы под минимальной нагрузкойКаждый deployment
Load testK6/LocustВалидировать производительность при ожидаемой нагрузкеЕженедельно, перед релизами
Stress testK6/LocustНайти точки отказаЕжемесячно, после изменений инфраструктуры
Spike testK6/LocustВалидировать поведение auto-scalingПосле изменений политик scaling
Soak testK6/LocustНайти memory leaks, исчерпание соединенийЕжеквартально

Чеклист Валидации Auto-Scaling

ВалидацияЧто ПроверятьКритерий Успеха
Trigger scale-upВремя от breach порога до нового инстанса< 3 минут
Ёмкость scale-upНовые инстансы обрабатывают трафик немедленноБез отказов запросов
Trigger scale-downИнстансы удаляются при снижении нагрузкиВ пределах cooldown периода
Безопасность scale-downНет преждевременного завершения во время трафикаНоль потерянных запросов
Максимальная ёмкостьСистема обрабатывает нагрузку максимума инстансовСоответствует SLA при макс. масштабе

K6 для Тестирования Масштабируемости

Базовый Тест Валидации Auto-Scaling

// tests/autoscaling-validation.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

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

export const options = {
  scenarios: {
    // Этап 1: Прогрев
    warmup: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 100 },
      ],
      gracefulRampDown: '0s',
      exec: 'defaultScenario',
    },
    // Этап 2: Устойчивая нагрузка (должен сработать scale-up)
    sustained: {
      executor: 'constant-vus',
      vus: 500,
      duration: '10m',
      startTime: '2m',
      exec: 'defaultScenario',
    },
    // Этап 3: Пик (stress test auto-scaling)
    spike: {
      executor: 'ramping-vus',
      startVUs: 500,
      stages: [
        { duration: '30s', target: 2000 },
        { duration: '1m', target: 2000 },
        { duration: '30s', target: 500 },
      ],
      startTime: '12m',
      exec: 'defaultScenario',
    },
    // Этап 4: Охлаждение (должен сработать scale-down)
    cooldown: {
      executor: 'ramping-vus',
      startVUs: 500,
      stages: [
        { duration: '5m', target: 0 },
      ],
      startTime: '14m',
      gracefulRampDown: '30s',
      exec: 'defaultScenario',
    },
  },
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% запросов под 500ms
    errors: ['rate<0.01'],              // Частота ошибок под 1%
    http_req_failed: ['rate<0.01'],     // Неудачные запросы под 1%
  },
};

const BASE_URL = __ENV.TARGET_URL || 'https://api.example.com';

export function defaultScenario() {
  // Симуляция реалистичного использования API
  const endpoints = [
    { path: '/api/users', weight: 0.5 },
    { path: '/api/orders', weight: 0.3 },
    { path: '/api/products', weight: 0.2 },
  ];

  const random = Math.random();
  let cumulative = 0;
  let selectedEndpoint = endpoints[0].path;

  for (const endpoint of endpoints) {
    cumulative += endpoint.weight;
    if (random <= cumulative) {
      selectedEndpoint = endpoint.path;
      break;
    }
  }

  const response = http.get(`${BASE_URL}${selectedEndpoint}`);

  const success = check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });

  errorRate.add(!success);

  // Реалистичное время ожидания
  sleep(Math.random() * 2 + 1);
}

Locust для Тестирования Масштабируемости

Конфигурация Распределённого Нагрузочного Теста

# locustfile.py
from locust import HttpUser, task, between, events
from locust.runners import MasterRunner
import time
import logging

class WebsiteUser(HttpUser):
    wait_time = between(1, 3)

    def on_start(self):
        """Инициализация сессии пользователя."""
        self.client.headers = {'Content-Type': 'application/json'}

    @task(5)
    def browse_products(self):
        """70% трафика - Просмотр продуктов."""
        with self.client.get("/api/products", catch_response=True) as response:
            if response.status_code == 200:
                response.success()
            else:
                response.failure(f"Got status {response.status_code}")

    @task(2)
    def view_product_detail(self):
        """20% трафика - Просмотр деталей продукта."""
        product_id = self.get_random_product_id()
        self.client.get(f"/api/products/{product_id}")

    @task(1)
    def checkout_flow(self):
        """10% трафика - Полный flow checkout."""
        # Добавление в корзину
        self.client.post("/api/cart", json={
            "product_id": self.get_random_product_id(),
            "quantity": 1
        })

        # Checkout
        with self.client.post("/api/checkout", json={
            "payment_method": "card"
        }, catch_response=True) as response:
            if response.status_code in [200, 201]:
                response.success()
            elif response.status_code == 503:
                response.failure("Сервис недоступен - проблема scaling?")

    def get_random_product_id(self):
        import random
        return random.randint(1, 1000)


# Кастомные метрики для анализа scaling
@events.request.add_listener
def track_response_time(request_type, name, response_time, response_length, **kwargs):
    if response_time > 1000:  # Логирование медленных запросов
        logging.warning(f"Медленный запрос: {name} занял {response_time}ms")

Kubernetes Deployment для Распределённого Locust

# locust-master.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: locust-master
spec:
  replicas: 1
  selector:
    matchLabels:
      app: locust
      role: master
  template:
    metadata:
      labels:
        app: locust
        role: master
    spec:
      containers:

        - name: locust
          image: locustio/locust:2.20.0
          args:

            - --master
            - -f
            - /mnt/locust/locustfile.py
            - --host
            - $(TARGET_HOST)
          ports:

            - containerPort: 8089
            - containerPort: 5557
---
# locust-worker.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: locust-worker
spec:
  replicas: 10  # 10 workers для распределённого тестирования
  selector:
    matchLabels:
      app: locust
      role: worker
  template:
    metadata:
      labels:
        app: locust
        role: worker
    spec:
      containers:

        - name: locust
          image: locustio/locust:2.20.0
          args:

            - --worker
            - --master-host=locust-master
            - -f
            - /mnt/locust/locustfile.py

Terraform для Инфраструктуры Нагрузочного Тестирования

Эфемерная Среда Нагрузочного Тестирования

# modules/load-test-infra/main.tf

variable "run_load_test" {
  description = "Установить в true для provisioning инфраструктуры тестирования"
  type        = bool
  default     = false
}

variable "worker_count" {
  description = "Количество K6/Locust workers"
  type        = number
  default     = 5
}

# ECS кластер для генераторов нагрузки
resource "aws_ecs_cluster" "load_test" {
  count = var.run_load_test ? 1 : 0
  name  = "load-test-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }

  tags = {
    Purpose   = "LoadTesting"
    AutoClean = "true"
  }
}

Интеграция CI/CD

GitHub Actions Workflow для Нагрузочного Тестирования

name: Тестирование Масштабируемости

on:
  schedule:

    - cron: '0 4 * * 1'  # Еженедельно Понедельник 4 утра
  workflow_dispatch:
    inputs:
      test_duration:
        description: 'Длительность теста (напр., 10m, 1h)'
        default: '20m'
      max_vus:
        description: 'Максимум виртуальных пользователей'
        default: '1000'

jobs:
  load-test:
    runs-on: ubuntu-latest
    environment: load-test

    steps:

      - uses: actions/checkout@v4

      - name: Настройка AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.LOAD_TEST_ROLE_ARN }}
          aws-region: us-east-1

      - name: Setup K6
        run: |
          sudo apt-get update && sudo apt-get install k6

      - name: Запуск нагрузочного теста
        run: |
          k6 run tests/autoscaling-validation.js \
            --env TARGET_URL=${{ vars.TARGET_URL }} \
            --duration ${{ inputs.test_duration || '20m' }} \
            --vus ${{ inputs.max_vus || '1000' }} \
            --out json=results/k6-results.json

Измерение Успеха

МетрикаЦельКак Отслеживать
Латентность scale-up< 3 минут от triggerМетрики CloudWatch ASG
P95 латентность во время scale< 500msРезультаты K6/Locust
Частота ошибок во время пика< 1%Результаты K6/Locust
Точность scale-downВ пределах 2x cooldown периодаЛоги активности ASG
Эффективность затратБез избыточного provisioningAWS Cost Explorer

Сигналы, что ваше тестирование масштабируемости не работает:

  • Тесты проходят, но production всё ещё имеет проблемы scaling
  • Политики scaling никогда не срабатывают во время тестов (слишком низкая нагрузка)
  • Тестовая среда не соответствует production (разные типы инстансов, лимиты)
  • Результаты сильно варьируются между запусками тестов (непостоянный baseline)

Заключение

Эффективное тестирование масштабируемости инфраструктуры требует реалистичных сценариев и подходящих инструментов:

  1. Тестируйте минимум три сценария: устойчивая нагрузка, пик и восстановление
  2. Используйте эфемерную инфраструктуру с Terraform для экономичного тестирования
  3. Интегрируйте с CI/CD для регулярной валидации
  4. Коррелируйте метрики между результатами нагрузочных тестов и scaling инфраструктуры
  5. Документируйте thresholds и оповещайте при провале тестов

Ключевая идея: политики auto-scaling нуждаются в валидации под реальными условиями нагрузки. Теоретических расчётов недостаточно—тестируйте вашу инфраструктуру до того, как это сделают ваши пользователи.

Смотрите также

Официальные ресурсы