В процессе администрирования веб‑сервера наступает момент, когда нужно протестировать производительность решения, оптимизировать настройки, проверить гипотезу. Для решения этих задач нужно выбрать правильный инструмент для бенчмарка, о чём и будем говорить в этой статье. Все примеры будут разобраны на основе сервера Angie, но могут применяться и к другим продуктам. В этой статье будут освещены простые и доступные утилиты для оперативных тестов. 

Навигация по циклу

  1. Почему стоит переходить на Angie.

  2. Установка Angie из пакетов и в докере.

  3. Переезд с Nginx на Angie. Пошаговая инструкция.

  4. Настройка location в Angie. Разделение динамических и статических запросов.

  5. Перенаправления в Angie: return, rewrite и примеры их применения.

  6. Сжатие текста в Angie: статика, динамика, производительность.

  7. Серверное кэширование в Angie: тонкости настройки.

  8. Настройка TLS в Angie: безопасность и скорость.

  9. Настройка Angie в роли обратного HTTP-прокси.

  10. Балансировка нагрузки для HTTP(S) в Angie.

  11. Мониторинг Angie с помощью Console Light и API.

  12. Балансировка и проксирование L4-трафика в Angie.

  13. Клиентское кэширование в Angie.

  14. Динамические группы проксируемых серверов в Angie.

  15. Мониторинг Angie с Prometheus и Grafana.

  16. Отказоустойчивый кластер Angie с VRRP и Keepalived.

  17. Контроль доступа в Angie.

  18. Аутентификация клиентов в Angie с помощью TLS-сертификатов.

  19. Кастомизация Angie (njs, Lua, Perl).

  20. Запуск CGI-скриптов в Angie.

  21. Защита от DoS-атак в Angie стандартными модулями.

  22. Защита от DoS-атак в Angie (дополнительные средства).

  23. Автоматические TLS-сертификаты в Angie с модулем ACME.

  24. HTTP/2 и HTTP/3: настройка, достоинства и недостатки.

  25. Работа с картинками в Angie.

  26. Инструменты для бенчмарка веб-сервера.

Видеоверсия

Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.

Подготовка веб-сервера и окружения

Повторяемость и надёжность результатов бенчмарка зависит от подготовки окружения и самого сервера. Также сразу оговоримся, что нагрузка должна подаваться на сервер под вашим контролем, иначе тестирование может обернуться атакой на отказ в обслуживании.

Применительно к Angie стоит контролировать базовые параметры работы сервера, которые значительно влияют на результаты тестирования (для HTTP):

  • локальное логирование запросов (access_log) и ошибок (error_log) создаёт значительную нагрузку на дисковую подсистему;

  • количество рабочих процессов (worker_processes);

  • настройки динамического сжатия (gzip, brotli, zstd);

  • набор шифров и тип сертификата (при использовании HTTPS);

  • используемый протокол (HTTP 1.1, HTTP/2, HTTP/3);

  • подключённые динамические модули;

  • настройки keep-alive соединений (в сторону клиента и бэкенда);

  • размер буферов при проксировании (proxy_buffers);

  • серверное кэширование (proxy_cache);

  • лимиты на запросы (limit_req и limit_conn);

  • базовая операционная система (используемые библиотеки);

  • настройки сетевого стека.

Указанные выше настройки и режимы работы должны повторяться и использоваться аналогично при сравнении различных конфигураций, версий или продуктов между собой. Также отсюда следует, что нельзя полагаться на настройки по умолчанию: они могут реализовать разную функциональность и направлены на различные задачи. Например, многие веб‑серверы и СУБД настройками по умолчанию заточены под экономию оперативной памяти и работу на слабом железе, что плохо сочетается с производительностью. 

Помимо серверной части также стоит контролировать клиентскую часть (источник нагрузки). Типичные проблемы: достижение предела производительности генератора нагрузки, исчерпание пропускной способности сетевых интерфейсов, фоновая нагрузка на систему. Также можно словить исчерпание эфемерных портов на клиенте или сервере (например, из‑за отключённой опции net.ipv4.tcp_tw_reuse), лимит открытых файловых дескрипторов и другие логические ограничения. Причём, первый прогон теста может уложиться в лимиты, а следующий будет сыпать ошибками из‑за сокетов в состоянии TIME‑WAIT. В результате бенчмарки показывают разгромные результаты в пользу одного из участников просто из‑за ошибок подготовки окружения.

Таким образом, любой бенчмарк это не только и не столько запуск тестов, а процесс анализа результатов и понимания тонкостей работы продукта и его взаимодействия со средой (операционной системой, железом, окружением). Именно поэтому процесс тестирования производительности крайне полезный и интересный вид деятельности.

Типы бенчмарков: closed-loop и open-loop

Перед обзором утилит для тестирования производительности веб‑серверов введём классификацию подобных тестов по обработке ответов:

  • закрытого цикла (closed-loop);

  • открытого цикла (open-loop).

Утилиты, работающие по закрытому циклу (closed‑loop) отправляют запросы c той скоростью, с которой получают ответы от сервера. Таким образом, когда сервер начинает отвечать медленее под нагрузкой, частота запросов снижается. По сути, закрытый цикл сильно зависит от времени ответа сервера (latency), но не всегда даёт полные данные о пропускной способности (throughput) сервера. Примеры утилит такого типа: ab, wrk, wrk2, h2load.

Средства тестирования с открытым циклом (open‑loop) создают нагрузку в соответствии с настройкой частоты запросов, при этом не ждут ответа и не зависят от времени ответа сервера. Если нагрузка будет превышать возможности сервера, стоит ожидать ошибок в ответах (таймауты, ошибки подключения и так далее) Такой режим позволяет более реалистично эмулировать нагрузку реальных пользователей. Примеры утилит с открытым циклом: Яндекс.Танк, httperf, Vegeta. Некоторые средства имеют несколько режимов работы, например: Grafana k6, Nighthawk.

Утилита ab (Apache Benchmark)

Apache Benchmark (ab) это простая утилита с неплохими возможностями. Утилита поставляется в пакете apache2-utils (для Ubuntu). 

Базовый тест можно запустить одной командой:

ab -c 10 -l -k -n 5000 http://localhost/

Разберём параметры этой команды:

  • ‑c 10 — уровень конкурентности запросов (эмуляция нескольких клиентов);

  • ‑l — допустить различную длину ответов (иначе отличия будут считаться ошибками);

  • ‑k — использовать keep‑alive соединения (по умолчанию отключены);

  • ‑n 5000 — количество запросов в тесте (50000);

  • http://localhost/ — URL запроса.

Так как ab это однопоточная утилита, то происходит именно эмуляция параллельных клиентов, а не реальный запуск нескольких потоков. Следовательно, утилита будет ограничена производительностью одного ядра процессора. В случае тестирования Angie в такой предел можно упереться даже при одном рабочем процессе на сервере.

Результат теста представлен в следующем виде:

Benchmarking localhost (be patient)
Completed 500 requests
...
Completed 5000 requests
Finished 5000 requests


Server Software:        Angie/1.11.4
Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        Variable

Concurrency Level:      10
Time taken for tests:   8.129 seconds
Complete requests:      5000
Failed requests:        0
Keep-Alive requests:    5000
Total transferred:      54581671 bytes
HTML transferred:       53641671 bytes
Requests per second:    615.08 [#/sec] (mean)
Time per request:       16.258 [ms] (mean)
Time per request:       1.626 [ms] (mean, across all concurrent requests)
Transfer rate:          6557.09 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     2   16  29.4     13     205
Waiting:        2   16  29.4     13     205
Total:          2   16  29.4     13     205

Percentage of the requests served within a certain time (ms)
  50%     13
  66%     14
  75%     14
  80%     15
  90%     17
  95%     36
  98%    158
  99%    167
 100%    205 (longest request)

Первая часть отчета показывает настройки теста и суммарные значения. Основной интерес представляет значение запросов в секунду (Requests per second) и время ответа (Time per request). Также важно сверять использованную пропускную способность (Transfer rate) с возможностями сетевого интерфейса. Если значение этого параметра приближается к максимальному для интерфейса, значит тест некорректен.

Также очень важно распределение времени ответа (перцентили). Эти значения показывают время ответа, в которое уложились x% ответов. Например, в результатах выше 95-й перцентиль равен 36 мс, то есть 95% ответов были получены за 36 мс или меньше. Соответственно, 100-й перцентиль означает время самого медленного ответа, 50-й это медиана.

При наличии ошибок утилита указывает на ответы с кодом, отличным от 200 (non-200 replies). Для игнорирования ошибок сокета можно добавить ключ ‑r, иначе тест заканчивается при первой такой ошибке.

Утилита ab поддерживает протокол HTTP/1.1 и TLS 1.2. Можно использовать различные HTTP‑методы (параметр ‑m), HTTP‑аутентификацию (параметр ‑A), а также mTLS (аутентификацию с помощью клиентского сертификата — параметр ‑E).

Часто при тестировании полезно задавать HTTP‑заголовки запроса (‑H), например, можно анонсировать поддержку сжатия:

ab -n 200 -c 4 -H "Accept-Encoding: gzip,deflate,br" https://localhost/

Подводя итог по утилите ab можно сказать, что она полезна для тестирования с использованием протокола HTTP/1.1 и TLS 1.2. При этом возможность создания нагрузки ограничена одним потоком исполнения, чего недостаточно для тестов быстрых сценариев использования Angie с использованием нескольких рабочих процессов. Частично это ограничение можно обойти за счет параллельного запуска нескольких экземпляров ab. Например, так:

apt install parallel
parallel --ungroup -j 2 'ab -n 1000 -c 10 {}' ::: 'http://localhost/' 'http://localhost/'

Однако, результаты теста придётся дополнительно обрабатывать. На этом перейдём к более продвинутому инструменту: wrk.

Утилиты wrk/wrkx

Проект wrk имеет богатую историю. Это многопоточная утилита для нагрузочного тестирования и бенчмарков веб‑серверов и приложений. На текущий момент проект не развивается более 5 лет, но есть активные форки с важными доработками относительно оригинальной версии. Версия wrk2 добавляет возможность контроля нагрузки (параметр ‑R, определяет RPS) и более точно измеряет время ответа. 

Вариант, который мы будем использовать далее это wrkx, форк wrk2 от Алексея Рыбака. В wrkx внедрены несколько патчей по повышению стабильности работы и возможности привязки потоков к процессорам (CPU affinity).

Для использования wrkx нужно собрать из исходников, благо это несложно. Пример процесса для Ubuntu 24.04:

apt install git build-essential libssl-dev zlib1g-dev
git clone https://github.com/devhands-io/wrkx.git
cd wrkx
make

Если всё прошло успешно, получим бинарный исполняемый файл wrk в директории клонированного проекта. При запуске важно не перепутать обычный wrk установленный в системе (из одноимённого пакета) и этот собранный файл.

Запустить простой тест с wrkx можно так (обратите внимание на указание пути к файлу):

./wrk -t8 -c48 -d20s -R2000 --timeout 5s http://localhost/

Разберём параметры этой команды:

  • ‑t8 — запустить 8 потоков для создания нагрузки;

  • ‑с48 — количество параллельных подключений на все потоки;

  • ‑d20s — длительность теста;

  • ‑R2000 — создать суммарную нагрузку в 2000 RPS (запросов в секунду);

  • ‑timeout 5s — время ожидания ответа от сервера

  • http://localhost/ — URL для запроса.

Таким образом, для теста определяется длительность и ограничение по частоте запросов. По умолчанию wrk создаёт keep-alive подключения. Благодаря многопоточности, wrk может создавать высокий уровень нагрузки. Как и ab, wrk может задавать заголовки запроса: 

./wrk -t2 -c48 -d30s -R2000 -H "Accept-Encoding: gzip" --timeout 5s http://localhost

Теперь результаты включают детальные параметры распределения времени ответа (часть вывода опущена):

Running 30s test @ http://localhost
  2 threads and 48 connections
  Thread calibration: mean lat.: 3844.563ms, rate sampling interval: 13623ms
  Thread calibration: mean lat.: 3788.619ms, rate sampling interval: 13557ms
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    14.90s     3.98s   22.95s    56.98%
    Req/Sec   244.50      0.50   245.00    100.00%
  Latency Distribution (HdrHistogram - Recorded Latency)
 50.000%   15.68s 
 75.000%   17.97s 
 90.000%   19.97s 
 99.000%   22.50s 
 99.900%   22.77s 
 99.990%   22.92s 
 99.999%   22.97s 
100.000%   22.97s 

  Detailed Percentile spectrum:
       Value   Percentile   TotalCount 1/(1-Percentile)

    7155.711     0.000000            1         1.00
    9789.439     0.100000          951         1.1
...   
   22970.367     0.999902         9421     10240.00
   22970.367     1.000000         9421          inf
#[Mean    =    14897.478, StdDeviation   =     3975.822]
#[Max     =    22953.984, Total count    =         9421]
#[Buckets =           27, SubBuckets     =         2048]
----------------------------------------------------------
  14874 requests in 30.04s, 150.82MB read
Requests/sec:    495.20
Transfer/sec:      5.02MB

Детальные данные о времени ответа можно использовать для визуализации HDR‑гистограммы.

Утилита wrk работает с протоколом HTTP/1.1, возможности TLS зависят от используемой при сборке версии библиотеки. Помимо простых тестов, wrk поддерживает скрипты на языке Lua для более сложных сценариев.

А что, если потребуется протестировать работу сервера с протоколом HTTP/2? Можно воспользоваться инструментом h2load.

Утилита h2load

Все разобранные выше инструменты могли работать только с HTTP/1.1. Однако, на сегодняшний день самым популярным протоколом для фронт-серверов является HTTP/2. Создать тест с применением HTTP/2 можно утилитой h2load из проекта nghttp2. Для Ubuntu 24.04 доступен пакет:

apt install nghttp2-client

Запуск теста аналогичен другим утилитам:

h2load -n 500000 -c 100 -t 4 https://localhost/

По умолчанию h2load использует протокол HTTP/2. Разберём параметры этого теста подробнее:

  • -n 500000 — количество запросов;

  • -c 100 — количество параллельных подключений;

  • -t 4 — количество потоков;

  • https://localhost/ — URI запроса.

Для использования протокола HTTP/1.1 можно указать параметр ‑h1. В экспериментальном режиме утилита поддерживает работу с протоколом HTTP/3, поддержка активируется ключом ‑h3.

Посмотрим на результаты теста:

starting benchmark...
spawning thread #0: 5 total client(s). 2500 total requests
spawning thread #1: 5 total client(s). 2500 total requests
TLS Protocol: TLSv1.3
Cipher: TLS_AES_128_GCM_SHA256
Server Temp Key: X25519 253 bits
Application protocol: h2
progress: 10% done
...
progress: 100% done

finished in 8.88s, 563.37 req/s, 5.75MB/s
requests: 5000 total, 5000 started, 5000 done, 4997 succeeded, 3 failed, 0 errored, 0 timeout
status codes: 4997 2xx, 0 3xx, 0 4xx, 3 5xx
traffic: 51.07MB (53546209) total, 371.03KB (379934) headers (space savings 43.70%), 50.53MB (52985839) data
                     min         max         mean         sd        +/- sd
time for request:      895us       1.13s     17.13ms     58.48ms    95.32%
time for connect:     2.23ms      6.40ms      4.30ms      1.46ms    60.00%
time to 1st byte:     7.09ms     27.95ms     13.57ms      7.88ms    80.00%
req/s           :      56.34       63.28       58.42        2.56    80.00%

В результатах можно найти общую статистику по кодам ответа сервера (status codes) и полученные значения RPS (req/s), пропускной способности (MB/s), а также статистику времени ответа (финальная таблица). Из дополнительных данных можно увидеть эффективность сжатия заголовков (space savings) в строке (traffic). 

Утилита полезна прежде всего возможностью сравнения работы протоколов HTTP/1.1 и HTTP/2, при этом обладает высокой эффективностью, сравнимой с wrkx.

Итоги

Каждая из рассмотренных утилит обладает своими достоинствами и недостатками, при этом хорошо решает свои задачи. Как наиболее универсальный инструмент можно выделить wrkx. Для работы с новыми протоколами можно рекомендовать h2load. За пределами статьи остались более сложные системы тестирования, например k6 от Grafana, Apache JMeter или Яндекс.Танк. Они могут реализовать более сложные сценарии тестирования и наделены широкими возможностями.

Комментарии (0)