Масштабирование Kubernetes и тестирование производительности контейнеров
Updated Jul 2026
Валидация автомасштабирования Kubernetes
Kubernetes Horizontal Pod Autoscaler (HPA) обещает автоматическое масштабирование на основе CPU, памяти или пользовательских метрик. Но «настроено» не означает «работает». Тестирование производительности должно валидировать, что автомасштабирование ведёт себя корректно при реальных условиях трафика: масштабируется вверх достаточно быстро для поглощения всплесков трафика и масштабируется вниз корректно, не нарушая активные соединения.
Тест k6 для валидации HPA
// k6-container-scaling-test.js
// Verify Kubernetes HPA responds correctly to traffic spikes
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Trend } from 'k6/metrics';
const scalingLatency = new Trend('scaling_response_time', true);
export const options = {
scenarios: {
spike: {
executor: 'ramping-arrival-rate',
startRate: 10,
timeUnit: '1s',
preAllocatedVUs: 50,
maxVUs: 500,
stages: [
{ duration: '1m', target: 10 }, // baseline
{ duration: '30s', target: 200 }, // sudden spike
{ duration: '5m', target: 200 }, // sustain spike (HPA should scale)
{ duration: '30s', target: 10 }, // drop back
{ duration: '5m', target: 10 }, // verify scale-down
],
},
},
thresholds: {
// Even during spike, 95th percentile should stay under 2s
// (once HPA has scaled, which may take 1-2 minutes)
http_req_duration: ['p(95)<2000'],
http_req_failed: ['rate<0.05'],
},
};
export default function () {
const res = http.get('https://app.example.com/api/heavy-computation');
scalingLatency.add(res.timings.duration);
check(res, {
'status is 200': (r) => r.status === 200,
'no 503 (service unavailable)': (r) => r.status !== 503,
});
}
Что мониторить во время теста
Пока k6 работает, мониторьте кластер Kubernetes в параллельном терминале или на дашборде:
# Watch pod count change in real-time
kubectl get pods -l app=my-service -w
# Watch HPA status
kubectl get hpa my-service-hpa -w
# Check HPA events for scaling decisions
kubectl describe hpa my-service-hpa
Ожидаемая хронология
T+0:00 - 10 req/s, 3 pods (baseline)
T+1:00 - Spike to 200 req/s, latency increases immediately
T+1:30 - HPA detects CPU > target, begins scaling
T+2:00 - New pods scheduled, pulling images
T+2:30 - New pods running, latency begins to decrease
T+3:00 - Full scale-up complete (e.g., 15 pods), latency normalized
T+6:30 - Traffic drops to 10 req/s
T+7:00 - HPA begins scale-down (cooldown period)
T+11:30 - Scale-down complete, back to 3 pods
Критический вопрос: Что происходит с пользователями в период T+1:00 — T+3:00 (разрыв масштабирования)? Именно здесь вы обнаружите, достаточна ли ваша конфигурация HPA.
Конфигурация HPA для производительности
Базовый HPA на основе CPU
# hpa-cpu-based.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: checkout-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-service
minReplicas: 3
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # scale up when avg CPU > 60%
behavior:
scaleUp:
stabilizationWindowSeconds: 30 # react to spikes quickly
policies:
- type: Percent
value: 100 # can double pod count per scaling event
periodSeconds: 60
- type: Pods
value: 5 # or add 5 pods, whichever is larger
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300 # wait 5 min before scaling down
policies:
- type: Percent
value: 10 # remove at most 10% of pods per interval
periodSeconds: 60
HPA на пользовательских метриках (запросы в секунду)
HPA на основе CPU часто слишком медленный для масштабирования, управляемого трафиком. Пользовательские метрики на основе частоты запросов могут быть более отзывчивыми:
# hpa-custom-metrics.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: checkout-hpa-custom
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-service
minReplicas: 3
maxReplicas: 50
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: 100 # target 100 req/s per pod
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Матрица решений по тестированию производительности архитектур
Разные архитектуры имеют разные проблемы производительности. Используйте эту матрицу для выбора правильной стратегии тестирования:
| Архитектура | Ключевая проблема | Основной инструмент | Ключевая метрика |
|---|---|---|---|
| Монолит | Исчерпание пула потоков | k6 / JMeter | Одновременные соединения |
| Микросервисы | Межсервисная задержка, каскадные отказы | k6 + распределённая трассировка | Сквозная задержка p99 |
| Serverless | Холодные старты, лимиты конкурентности | k6 + CloudWatch | TTFB, одновременные выполнения |
| Edge/CDN | Доля попаданий в кеш, нагрузка на origin | k6 из нескольких регионов | % попаданий в кеш, req/s на origin |
| На базе LLM | Пропускная способность токенов, лимиты запросов | k6 с пользовательскими метриками | TPS, TTFT, срабатывания лимитов |
| Событийная | Глубина очереди, отставание потребителя | k6 + метрики очередей | Отставание потребителя, задержка обработки |
Тестирование каскадных отказов в микросервисах
В микросервисной архитектуре медленный нижестоящий сервис может вызвать каскадные отказы вышестоящих. Тестируйте этот сценарий явно:
// k6-cascading-failure-test.js
// Test: What happens when the payment service is slow?
import http from 'k6/http';
import { check } from 'k6';
import { Trend, Rate } from 'k6/metrics';
const orderLatency = new Trend('order_creation_latency', true);
const cascadeErrorRate = new Rate('cascade_errors');
export const options = {
scenarios: {
normal_traffic: {
executor: 'constant-arrival-rate',
rate: 50,
timeUnit: '1s',
duration: '10m',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
thresholds: {
order_creation_latency: ['p(95)<5000'], // total order flow under 5s
cascade_errors: ['rate<0.1'], // under 10% cascade errors
},
};
export default function () {
// This test runs against the order service while separately
// injecting latency into the payment service (via Litmus/Chaos Mesh)
const res = http.post('https://staging.example.com/api/orders',
JSON.stringify({
items: [{ sku: "TEST-1", qty: 1 }],
payment: { method: "card", token: "tok_test" },
}),
{ headers: { 'Content-Type': 'application/json' }, timeout: '30s' }
);
orderLatency.add(res.timings.duration);
check(res, {
'order created or gracefully degraded': (r) =>
r.status === 201 || r.status === 202 || r.status === 503,
'no 500 internal errors': (r) => r.status !== 500,
});
// A 503 with a retry-after header is acceptable (circuit breaker open)
// A 500 is a cascading failure bug
cascadeErrorRate.add(res.status === 500);
}
Запускайте этот тест k6 одновременно с экспериментом Litmus по внедрению сетевой задержки в сервис платежей для валидации корректной работы circuit breaker, таймаутов и логики отката.
Ресурсные лимиты и производительность
Ресурсные запросы и лимиты Kubernetes напрямую влияют на производительность. Недостаточно обеспеченные ресурсами контейнеры тротлятся в самый неподходящий момент:
# resource-config-for-performance.yaml
resources:
requests:
cpu: 500m # guaranteed CPU allocation
memory: 512Mi # guaranteed memory allocation
limits:
cpu: 2000m # burst capacity (4x request)
memory: 1Gi # hard memory limit (OOMKill if exceeded)
Тестирование производительности конфигураций ресурсов
Протестируйте ваш сервис с разными конфигурациями ресурсов для поиска оптимальных настроек:
| Конфигурация | CPU запрос/лимит | Память запрос/лимит | Результат теста |
|---|---|---|---|
| Минимальная | 100m/500m | 128Mi/256Mi | 50 req/s, p99=2,5 с, OOMKill под нагрузкой |
| Консервативная | 250m/1000m | 256Mi/512Mi | 100 req/s, p99=800 мс, стабильно |
| Оптимальная | 500m/2000m | 512Mi/1Gi | 200 req/s, p99=400 мс, стабильно |
| Щедрая | 1000m/4000m | 1Gi/2Gi | 200 req/s, p99=350 мс, убывающая отдача |
«Оптимальная» конфигурация достигает целевой производительности без избыточного расхода ресурсов. Нагрузочное тестирование — единственный способ найти эту точку баланса.
Интеграция в CI-пайплайн: полный рабочий процесс производительности и хаос-инженерии
# .github/workflows/performance-chaos.yml
name: Performance & Chaos Pipeline
on:
push:
branches: [main]
jobs:
performance-budget:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v11
with:
configPath: ./lighthouserc.json
load-test-staging:
needs: performance-budget
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run k6 load tests
uses: grafana/k6-action@v0.4.0
with:
filename: tests/performance/k6-load-test.js
flags: --out json=results.json
env:
K6_TARGET_URL: ${{ secrets.STAGING_URL }}
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: k6-results
path: results.json
chaos-staging:
needs: load-test-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Litmus
run: |
kubectl apply -f https://litmuschaos.github.io/litmus/litmus-operator-v3.0.0.yaml
- name: Run chaos experiment
run: |
kubectl apply -f chaos/pod-delete-experiment.yaml
kubectl apply -f chaos/network-delay-experiment.yaml
- name: Wait for chaos completion
run: |
kubectl wait --for=condition=complete chaosresult/checkout-chaos \
--timeout=600s
- name: Verify SLOs held during chaos
run: |
python scripts/verify_slo_during_chaos.py \
--prometheus-url ${{ secrets.PROMETHEUS_URL }} \
--slo-config slo/checkout-api.yaml \
--chaos-window 10m
Этот пайплайн гарантирует, что каждый мерж в main валидируется на производительность (Lighthouse + k6) и устойчивость (хаос Litmus). Система должна быть не только быстрой — она должна оставаться быстрой, когда что-то идёт не так.