Проксирование из коробки: сравнительный анализ HAProxy, Envoy, Nginx, Caddy и Traefik

от автора

Всем привет, меня зовут Стас, я техлид в Mish Product Lab.

Тема возникла не просто так: внутри команды у нас было немало споров и дискуссий о том, какой инструмент для проксирования и терминации SSL лучше использовать в различных ситуациях. Изначально все наши гипотезы были основаны больше на личных предпочтениях, чем на реальных данных. Мы долго спорили, надеясь, что истина будет где-то рядом с нашими любимыми решениями. Но в итоге пришли к выводу, что единственный способ получить действительно объективный ответ — это протестировать и сравнить различные варианты на практике.

Именно так родилась идея провести сравнительный анализ производительности HAProxy, Envoy, Nginx, Caddy и Traefik с поддержкой SSL/TLS. Мы хотели понять, какой из инструментов «из коробки» предоставляет наилучшую производительность и минимальные накладные расходы, особенно при обработке SSL-трафика, который, как известно, требует дополнительных ресурсов из-за шифрования и дешифрования.

Важно подчеркнуть, что наша задача не заключалась в поиске идеального или максимально оптимизированного решения. Для глубоких и тонких настроек всегда лучше обратиться к DevOps-инженерам, которые обладают необходимыми компетенциями и понимают нюансы каждого инструмента. Нашей целью было скорее провести «полевые испытания» и составить заметки, которые могли бы помочь нам самим и будущим поколениям разработчиков быстрее принимать решения на основе хотя бы приблизительных ориентиров.

Итак, мы готовы поделиться нашими наблюдениями и результатами тестов.

Работаем с тестовыми стендами

Для обеспечения объективности и прозрачности тестирования мы выбрали три отдельных стенда, каждый из которых отвечал за свою часть сценария нагрузки. Разделение ролей стендов было принципиально важным для минимизации возможных взаимных влияний и искажений результатов. Это позволило максимально точно зафиксировать поведение каждого инструмента и компонента инфраструктуры.

Все тестовые стенды были размещены в одной сети, что гарантировало стабильную и предсказуемую сеть без значительных задержек. Никаких настроек ядра, голые системы.

  • Тестовый стенд 1 (K6) предназначался для генерации и управления трафиком при помощи инструмента K6. Его ресурсы (32 vCPU, 128 ГБ RAM) обеспечивали возможность генерировать существенную нагрузку и давала нам уверенность в том, что сам инструмент генерации не станет узким местом.

  • Тестовый стенд 2 (Web) был основным объектом тестирования. Здесь были установлены различные инструменты для проксирования (HAProxy, Envoy, Nginx, Caddy, Traefik) и мониторинг штатными средствами хостера. Это позволило нам не только протестировать производительность каждого решения, но и подробно отслеживать потребление ресурсов. Ресурсы 32 vCPU, 128 ГБ RAM.

  • Тестовый стенд 3 (Backend) выполнял роль типичного приложения (backend), написанного на Golang, которое должно было принимать и обрабатывать запросы, проходящие через прокси. Ресурсы 8 vCPU, 32 ГБ RAM.

Такое разделение ролей и ресурсов дало возможность получить максимально чистые и объективные данные о производительности каждого инструмента.

Работаем с бэкэндом

Сервис делали на Golang, для обработки запросов использовали fasthttp — никаких излишеств.

package main  import (         "fmt"         "log"          "github.com/valyala/fasthttp" )  func main() {         fmt.Println("Listening on :8080")         requestHandler := func(ctx *fasthttp.RequestCtx) {                 switch string(ctx.Path()) {                 case "/":                         handleRoot(ctx)                 default:                         ctx.Error("Unsupported path", fasthttp.StatusNotFound)                 }         }          if err := fasthttp.ListenAndServe(":8080", requestHandler); err != nil {                 log.Fatalf("Error in ListenAndServe: %s", err)         } }  func handleRoot(ctx *fasthttp.RequestCtx) {         ctx.SetStatusCode(fasthttp.StatusOK)         ctx.SetBodyString("hello world") } 

Запускаем стресс-тест на этот сервис: 50 000 rps, 1 минута

K6 (генерация трафика и проверка ответа, http code и body):

import http from 'k6/http'; import { check } from 'k6';  export let options = {   scenarios: {     constant_request_rate: {       executor: 'constant-arrival-rate',       rate: 50000,       timeUnit: '1s',       duration: '60s',       preAllocatedVUs: 1000,       maxVUs: 100000,     },   }, };  export default function () {   let res = http.get('http://10.0.0.8:8080');    check(res, {     'status is 200': (r) => r.status === 200,     'body contains expected content': (r) => typeof r.body === 'string' && r.body.includes('hello world'),   }); } 
scenarios: (100.00%) 1 scenario, 100000 max VUs, 1m30s max duration (incl. graceful stop):          * constant_request_rate: 50000.00 iterations/s for 1m0s (maxVUs: 1000-100000, gracefulStop: 30s)    █ THRESHOLDS       checks     ✓ 'rate>=0.95' rate=100.00%      http_req_duration     ✓ 'p(95)<500' p(95)=387.29µs      http_req_failed     ✓ 'rate<0.01' rate=0.00%    █ TOTAL RESULTS       checks_total.......................: 5993052 99880.644605/s     checks_succeeded...................: 100.00% 5993052 out of 5993052     checks_failed......................: 0.00%   0 out of 5993052      ✓ status is 200     ✓ body contains expected content      HTTP     http_req_duration.......................................................: avg=283.17µs min=110.92µs med=234.52µs max=235.41ms p(90)=324.36µs p(95)=387.29µs       { expected_response:true }............................................: avg=283.17µs min=110.92µs med=234.52µs max=235.41ms p(90)=324.36µs p(95)=387.29µs     http_req_failed.........................................................: 0.00%   0 out of 2996526     http_reqs...............................................................: 2996526 49940.322303/s      EXECUTION     dropped_iterations......................................................: 3476    57.931271/s     iteration_duration......................................................: avg=349.05µs min=142.88µs med=277.87µs max=1.06s    p(90)=374.7µs  p(95)=453.85µs     iterations..............................................................: 2996526 49940.322303/s     vus.....................................................................: 15      min=10           max=183      vus_max.................................................................: 1044    min=1044         max=1044      NETWORK     data_received...........................................................: 438 MB  7.3 MB/s     data_sent...............................................................: 246 MB  4.1 MB/s 

Результат — тест выполнен успешно.
Максимальная задержка — max=47.52ms

Пропускная способность — 49964.433616/s

Настройка и тестирование проксирования

K6 (генерация трафика и проверка ответа, http code и body):

import http from 'k6/http'; import { check } from 'k6';  export let options = {   scenarios: {     constant_request_rate: {       executor: 'constant-arrival-rate',       rate: 50000,               // количество запросов в секунду       timeUnit: '1s',            // единица времени для rate       duration: '60s',           // длительность теста       preAllocatedVUs: 1000,     // количество предварительно выделенных виртуальных пользователей       maxVUs: 100000,            // максимальное количество VU, которое может быть задействовано     },   },   thresholds: {     checks: ['rate>=0.95'],     http_req_failed: ['rate<0.01'],     http_req_duration: ['p(95)<500'],   }, };  export default function () {   let res = http.get('https://test-backend.mish.design');    check(res, {     'status is 200': (r) => r.status === 200,     'body contains expected content': (r) => typeof r.body === 'string' && r.body.includes('hello world'),   }); } 

Получаем сертификат:

sudo apt install certbot sudo certbot certonly --standalone -d test-backend.mish.design 

Сертификаты появятся тут:

/etc/letsencrypt/live/test-backend.mish.design/fullchain.pem /etc/letsencrypt/live/test-backend.mish.design/privkey.pem 

Теперь совместим их — это нужно для HAProxy:

cd /etc/letsencrypt/live/test-backend.mish.design/ cat fullchain.pem privkey.pem > haproxy.pem 

HAProxy

Начать решили именно с HAProxy, так как он позволяет максимально быстро и эффективно протестировать работоспособность архитектуры без сложной предварительной настройки инфраструктуры.

docker-compose.yml

  • HAProxy запускается через Docker Compose, что позволяет легко воспроизводить окружение на разных серверах и машинах разработчиков.

  • Порты 80 и 443 прокидываются наружу, то есть сервис явно ориентирован на обработку HTTP и HTTPS-запросов.

  • SSL-сертификат Let’s Encrypt подключается напрямую через volume, таким образом сервис сразу готов к работе в защищённом режиме (HTTPS).

services:   haproxy:     image: haproxy:latest     container_name: haproxy     ports:       - "80:80"       - "443:443"     volumes:       - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro       - /etc/letsencrypt/live/test-backend.mish.design/haproxy.pem:/usr/local/etc/haproxy/certs/haproxy.pem:ro 

haproxy.cfg

HAProxy настроен в режиме http, и весь трафик, поступающий на порт 443, перенаправляется в backend-сервис, указанный в конфигурации (10.0.0.8:8080).

defaults     mode http  frontend main     bind *:443 ssl crt /usr/local/etc/haproxy/certs/haproxy.pem     default_backend test_backend  backend test_backend     server test_backend_server 10.0.0.8:8080 check 

Запускаем созданный выше сценарий

scenarios: (100.00%) 1 scenario, 100000 max VUs, 1m30s max duration (incl. graceful stop):          * constant_request_rate: 50000.00 iterations/s for 1m0s (maxVUs: 1000-100000, gracefulStop: 30s)    █ THRESHOLDS       checks     ✓ 'rate>=0.95' rate=100.00%      http_req_duration     ✓ 'p(95)<500' p(95)=1.16ms      http_req_failed     ✓ 'rate<0.01' rate=0.00%    █ TOTAL RESULTS       checks_total.......................: 5967620 99455.517508/s     checks_succeeded...................: 100.00% 5967620 out of 5967620     checks_failed......................: 0.00%   0 out of 5967620      ✓ status is 200     ✓ body contains expected content      HTTP     http_req_duration.......................................................: avg=759.38µs min=330.64µs med=571.94µs max=1.07s p(90)=861.18µs p(95)=1.16ms       { expected_response:true }............................................: avg=759.38µs min=330.64µs med=571.94µs max=1.07s p(90)=861.18µs p(95)=1.16ms     http_req_failed.........................................................: 0.00%   0 out of 2983810     http_reqs...............................................................: 2983810 49727.758754/s      EXECUTION     dropped_iterations......................................................: 16192   269.8536/s     iteration_duration......................................................: avg=984.55µs min=360.44µs med=643.34µs max=1.36s p(90)=968.37µs p(95)=1.35ms     iterations..............................................................: 2983810 49727.758754/s     vus.....................................................................: 34      min=28           max=357      vus_max.................................................................: 1293    min=1293         max=1293      NETWORK     data_received...........................................................: 374 MB  6.2 MB/s     data_sent...............................................................: 108 MB  1.8 MB/s 

Нагрузка на систему

Основные выводы:

CPU

  • Загрузка до ~900% (9 ядер), стабильна весь тест → CPU справился.

K6 Test Summary

  • RPS (успешные): 49,728 / сек

  • Ошибки: 0% (http_req_failed = 0.00%)

  • Дропнутые итерации: 16,192 (≈0.5%)

  • Время ответа p(95): 1.16 ms

  • Макс. задержка: 1.07s

  • Объём данных:

    • Получено: 374 MB (6.2 MB/s)

    • Отправлено: 108 MB (1.8 MB/s)

  • VUs использовано: 28–357

  • Макс. допустимо: 1293 VUs

🟢 Отличная производительность, минимальные задержки, всё стабильно.

Envoy

Высокопроизводительный прокси-сервер и балансировщик нагрузки, разработанный компанией Lyft, поддерживающий динамическое конфигурирование, observability и множество современных протоколов.

docker-compose.yml

  • Envoy разворачивается через Docker Compose для простоты повторяемости окружения.

  • Порты 80 и 443 открываются для обработки входящего трафика.

  • Используется сертификат Let’s Encrypt, напрямую подключённый в конфигурацию.

services:   envoy:     image: envoyproxy/envoy-dev:latest     container_name: envoy     environment:      - "ENVOY_UID=0"     volumes:      - /etc/letsencrypt:/etc/certs:ro      - ./envoy.yaml:/etc/envoy/envoy.yaml     ports:      - "80:80"      - "443:443" 

envoy.yaml

  • Запросы, поступающие на домен test-backend.mish.design, перенаправляются на backend-сервис по адресу 10.0.0.4:8080 с балансировкой по алгоритму round-robin.

  • Envoy автоматически терминирует TLS-соединения и маршрутизирует трафик по HTTP.

static_resources:   secrets:     - name: server_cert       tls_certificate:         certificate_chain:           filename: /etc/certs/live/test-backend.mish.design/fullchain.pem         private_key:           filename: /etc/certs/live/test-backend.mish.design/privkey.pem    listeners:   - name: back_listener     address:       socket_address:         address: 0.0.0.0         port_value: 443     filter_chains:     - filters:       - name: envoy.filters.network.http_connection_manager         typed_config:           "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager           codec_type: AUTO           stat_prefix: ingress_http           upgrade_configs:           - upgrade_type: websocket           route_config:             name: local_route             virtual_hosts:             - name: main               domains:               - "test-backend.mish.design"               routes:               - match:                   prefix: "/"                 route:                   cluster: back           http_filters:           - name: envoy.filters.http.router             typed_config:               "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router        transport_socket:         name: envoy.transport_sockets.tls         typed_config:           "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext           common_tls_context:             tls_certificate_sds_secret_configs:              - name: server_cert    clusters:   - name: back     connect_timeout: 5s     type: STRICT_DNS     lb_policy: ROUND_ROBIN     load_assignment:       cluster_name: back       endpoints:       - lb_endpoints:         - endpoint:             address:               socket_address:                 address: 10.0.0.4                 port_value: 8080  

Запускаем созданный выше сценарий

scenarios: (100.00%) 1 scenario, 100000 max VUs, 1m30s max duration (incl. graceful stop):          * constant_request_rate: 50000.00 iterations/s for 1m0s (maxVUs: 1000-100000, gracefulStop: 30s)    █ THRESHOLDS       checks     ✓ 'rate>=0.95' rate=100.00%      http_req_duration     ✓ 'p(95)<500' p(95)=3.34ms      http_req_failed     ✓ 'rate<0.01' rate=0.00%    █ TOTAL RESULTS       checks_total.......................: 5970208 99497.954679/s     checks_succeeded...................: 100.00% 5970208 out of 5970208     checks_failed......................: 0.00%   0 out of 5970208      ✓ status is 200     ✓ body contains expected content      HTTP     http_req_duration.......................................................: avg=1.38ms min=421.73µs med=1ms    max=131.98ms p(90)=2.11ms p(95)=3.34ms       { expected_response:true }............................................: avg=1.38ms min=421.73µs med=1ms    max=131.98ms p(90)=2.11ms p(95)=3.34ms     http_req_failed.........................................................: 0.00%   0 out of 2985104     http_reqs...............................................................: 2985104 49748.977339/s      EXECUTION     dropped_iterations......................................................: 14895   248.236248/s     iteration_duration......................................................: avg=1.63ms min=454.49µs med=1.08ms max=1.15s    p(90)=2.33ms p(95)=3.87ms     iterations..............................................................: 2985104 49748.977339/s     vus.....................................................................: 59      min=49           max=291      vus_max.................................................................: 1262    min=1246         max=1262      NETWORK     data_received...........................................................: 597 MB  9.9 MB/s     data_sent...............................................................: 344 MB  5.7 MB/s 

Нагрузка на систему

Основные выводы:

CPU

  • Загрузка до ~1600% → использовано до 16 ядер, держалось стабильно.

K6 Test Summary

  • RPS (успешные): 49,749 / сек

  • Ошибки: 0%

  • Дропнутые итерации: 14,895 (≈0.5%)

  • Время ответа p(95): 3.34 ms

  • Макс. задержка: 132 ms

  • Объём данных:

    • Получено: 597 MB (9.9 MB/s)

    • Отправлено: 344 MB (5.7 MB/s)

  • VUs использовано: 49–291

  • Макс. допустимо: 1262 VUs

🟢 Отличная производительность, минимальные задержки, всё стабильно.

Traefik

Современный обратный прокси и балансировщик нагрузки с встроенной поддержкой автоматической выдачи и обновления SSL-сертификатов через Let’s Encrypt. Он разработан для динамической работы с контейнерными средами, такими как Docker и Kubernetes.

docker-compose.yml

  • Запуск через Docker Compose — Traefik работает как отдельный сервис в контейнере.

  • Порты 80 и 443 — открыт для приёма HTTP и HTTPS-трафика.

  • Сертификаты Let’s Encrypt — полностью автоматическая выдача и хранение сертификатов.

services:   traefik:     image: traefik:latest     container_name: traefik     ports:       - "80:80"       - "443:443"     volumes:       - /var/run/docker.sock:/var/run/docker.sock       - ./traefik_data:/etc/traefik       - ./traefik_data/dynamic_conf.yml:/etc/traefik/dynamic_conf.yml     command:       - "--log.level=ERROR"       - "--accesslog=false"       - "--api.dashboard=false"       - "--providers.docker=true"       - "--providers.docker.exposedbydefault=false"       - "--providers.file.filename=/etc/traefik/dynamic_conf.yml"       - "--entrypoints.web.address=:80"       - "--entrypoints.websecure.address=:443"       - "--certificatesresolvers.myresolver.acme.httpchallenge=true"       - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"       - "--certificatesresolvers.myresolver.acme.email=example@yandex.com"       - "--certificatesresolvers.myresolver.acme.storage=/etc/traefik/acme.json" 

dynamic_conf.yml

  • Запросы на test-backend.mish.design по HTTPS идут на backend-сервис по адресу http://10.0.0.8:8080.

  • Используется entryPoint websecure (порт 443).

  • Балансировка выполняется на уровне Traefik.

http:   routers:     myrouter:       rule: "Host(`test-backend.mish.design`)"       entryPoints:         - websecure       tls:         certResolver: myresolver       service: myservice    services:     myservice:       loadBalancer:         servers:           - url: "http://10.0.0.4:8080" 

Запускаем созданный выше сценарий

scenarios: (100.00%) 1 scenario, 100000 max VUs, 1m30s max duration (incl. graceful stop):          * constant_request_rate: 50000.00 iterations/s for 1m0s (maxVUs: 1000-100000, gracefulStop: 30s)    █ THRESHOLDS       checks     ✗ 'rate>=0.95' rate=67.15%      http_req_duration     ✗ 'p(95)<500' p(95)=8.46s      http_req_failed     ✗ 'rate<0.01' rate=32.84%    █ TOTAL RESULTS       checks_total.......................: 723606 9317.72162/s     checks_succeeded...................: 67.15% 485906 out of 723606     checks_failed......................: 32.84% 237700 out of 723606      ✗ status is 200       ↳  67% — ✓ 242953 / ✗ 118850     ✗ body contains expected content       ↳  67% — ✓ 242953 / ✗ 118850      HTTP     http_req_duration.......................................................: avg=2.11s min=0s       med=70.12ms  max=32.98s p(90)=6.25s  p(95)=8.46s        { expected_response:true }............................................: avg=2.47s min=622.32µs med=289.6ms  max=32.98s p(90)=6.68s  p(95)=9.22s      http_req_failed.........................................................: 32.84%  118850 out of 361803     http_reqs...............................................................: 361803  4658.86081/s      EXECUTION     dropped_iterations......................................................: 2088749 26896.379681/s     iteration_duration......................................................: avg=4.42s min=1.1ms    med=358.05ms max=1m0s   p(90)=10.28s p(95)=27.16s     iterations..............................................................: 361803  4658.86081/s     vus.....................................................................: 26      min=26               max=29182     vus_max.................................................................: 29243   min=2532             max=29243      NETWORK     data_received...........................................................: 126 MB  1.6 MB/s     data_sent...............................................................: 24 MB   308 kB/s 

Нагрузка на систему

Основные выводы:

CPU

  • Загрузка до ~3000% → задействовано ~30 ядер.

Network Traffic

  • Периоды падения, из-за ошибок и дропов.

K6 Test Summary (Traefik)

  • Успешные запросы: 4,659 RPS (в 10 раз ниже цели)

  • Ошибки: ❌ 32.84%

  • Дропы: ❌ 2,088,749 итераций (‼️ огромный уровень отказов)

  • p(95) ответа: ❌ 8.46 сек

  • Максимальная задержка: 32.98 сек (!)

  • Передано:

    • Получено: 126 MB (1.6 MB/s)

    • Отправлено: 24 MB (308 KB/s)

  • VUs: до 29,243 → рост числа VUs указывает на тяжёлую деградацию под нагрузкой

❗ Вывод:

  • Traefik не справился с 50k RPS.

  • Наблюдаются:

    • Массовые дропы итераций

    • Высокие задержки

  • Требуется оптимизация, либо он не подходит для такого уровня нагрузки в текущей конфигурации.

Nginx

Классический веб-сервер и обратный прокси, известный своей стабильностью, высокой производительностью и минимальным потреблением ресурсов. Один из самых популярных инструментов для терминации TLS, балансировки нагрузки и проксирования трафика

docker-compose.yml

  • Запуск через Docker Compose — контейнер с NGINX разворачивается быстро и удобно.

  • Открыты порты 80 (HTTP) и 443 (HTTPS) для обработки входящего трафика.

  • Подключены сертификаты от Let’s Encrypt для обеспечения HTTPS.

services:   nginx:     image: nginx:latest     volumes:       - ./default.conf:/etc/nginx/nginx.conf       - /etc/letsencrypt/live/test-backend.mish.design/fullchain.pem:/etc/letsencrypt/live/test-backend.mish.design/fullchain.pem:ro       - /etc/letsencrypt/live/test-backend.mish.design/privkey.pem:/etc/letsencrypt/live/test-backend.mish.design/privkey.pem:ro     ports:       - "80:80"       - "443:443" 

default.conf

  • Прописаны все основные заголовки для правильной передачи оригинального IP и схемы клиента на backend.

  • worker_processes auto; — количество воркеров подстраивается под доступные ядра.

  • worker_rlimit_nofile 100000; — увеличен лимит на количество открытых файлов (важно при высоких нагрузках).

  • worker_connections 5000; и multi_accept on; — рассчитано на большое количество одновременных соединений.

  • Используется epoll — эффективный режим для Linux-систем с высоким количеством соединений.

worker_processes auto; worker_rlimit_nofile 300000;  events {     worker_connections 5000;     multi_accept on;     use epoll; }  http {   server {     listen 443 ssl;     server_name test-backend.mish.design;      ssl_certificate /etc/letsencrypt/live/test-backend.mish.design/fullchain.pem;     ssl_certificate_key /etc/letsencrypt/live/test-backend.mish.design/privkey.pem;      location / {         proxy_pass http://10.0.0.4:8080;         proxy_set_header Host $host;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header X-Forwarded-Proto $scheme;     } } } 

Запускаем созданный выше сценарий

scenarios: (100.00%) 1 scenario, 100000 max VUs, 1m30s max duration (incl. graceful stop):          * constant_request_rate: 50000.00 iterations/s for 1m0s (maxVUs: 1000-100000, gracefulStop: 30s)    █ THRESHOLDS       checks     ✗ 'rate>=0.95' rate=89.79%      http_req_duration     ✗ 'p(95)<500' p(95)=3.82s      http_req_failed     ✗ 'rate<0.01' rate=10.20%    █ TOTAL RESULTS       checks_total.......................: 1649228 18316.01633/s     checks_succeeded...................: 89.79%  1480978 out of 1649228     checks_failed......................: 10.20%  168250 out of 1649228      ✗ status is 200       ↳  89% — ✓ 740489 / ✗ 84125     ✗ body contains expected content       ↳  89% — ✓ 740489 / ✗ 84125      HTTP     http_req_duration.......................................................: avg=1.72s min=0s      med=1.73s max=30.09s p(90)=3.32s p(95)=3.82s       { expected_response:true }............................................: avg=1.91s min=4.27ms  med=1.81s max=30.09s p(90)=3.42s p(95)=3.86s     http_req_failed.........................................................: 10.20%  84125 out of 824614     http_reqs...............................................................: 824614  9158.008165/s      EXECUTION     dropped_iterations......................................................: 1154367 12820.183033/s     iteration_duration......................................................: avg=1.81s min=14.05ms med=1.81s max=30.09s p(90)=3.4s  p(95)=3.9s      iterations..............................................................: 824614  9158.008165/s     vus.....................................................................: 1       min=1               max=29297     vus_max.................................................................: 29354   min=2620            max=29354      NETWORK     data_received...........................................................: 231 MB  2.6 MB/s     data_sent...............................................................: 95 MB   1.1 MB/s 

Нагрузка на систему

Основные выводы:

CPU

  • Загрузка до ~3000% → задействовано ~30 ядер.

Network Traffic

  • Трафик стабильнее, чем у Traefik, но ниже, чем у Envoy.

K6 Test Summary (NGINX)

  • Успешные RPS: 9,158 / сек (х2 выше Traefik, но всё ещё ниже цели)

  • Ошибки: ❌ 10.2% (http_req_failed)

  • Дропы: ❌ 1,154,367 итераций

  • p(95) задержка: ❌ 3.82 сек, медиана 1.73 сек

  • Макс. задержка: 30 сек (таймаут)

  • Передано:

    • Получено: 231 MB (2.6 MB/s)

    • Отправлено: 95 MB (1.1 MB/s)

  • VUs: до 29,354 → высокая нагрузка на уровне Traefik.

⚠️ Вывод:

  • NGINX работает лучше Traefik, но сильно уступает HAProxy и Envoy.

  • Присутствуют таймауты, высокие задержки и дропы.

Caddy

Это современный веб-сервер и обратный прокси с нативной поддержкой HTTPS и автоматического получения сертификатов от Let’s Encrypt. Главные преимущества Caddy — простота конфигурации, автоматическое управление TLS и минимальные усилия по обслуживанию.

docker-compose.yml

  • Запуск через Docker Compose — контейнерный запуск с удобным управлением жизненным циклом.

  • Открыты порты 80 и 443 (включая 443/udp для HTTP/3).

  • Используется Caddyfile — минималистичная и читаемая конфигурация.

services:   caddy:     image: caddy:latest     container_name: caddy     hostname: caddy     ports:       - "80:80"       - "443:443"       - "443:443/udp"     restart: unless-stopped     volumes:       - caddy_data:/data       - caddy_config:/config       - ./Caddyfile:/etc/caddy/Caddyfile     logging:       driver: "json-file"       options:         max-size: "200k"         max-file: "10"     cap_add:       - NET_ADMIN  volumes:   caddy_data:   caddy_config: 

Caddyfile

  • Caddy автоматически берёт и обновляет сертификаты Let’s Encrypt без дополнительных скриптов и настроек.

  • Логирование настроено на уровень ERROR для минимальной нагрузки на диск.

  • Проксирование запросов на backend-сервис по адресу http://10.0.0.4:8080.

test-backend.mish.design {     log {       level ERROR     }      reverse_proxy http://10.0.0.4:8080 } 

Запускаем созданный выше сценарий

scenarios: (100.00%) 1 scenario, 100000 max VUs, 1m30s max duration (incl. graceful stop):          * constant_request_rate: 50000.00 iterations/s for 1m0s (maxVUs: 1000-100000, gracefulStop: 30s)    █ THRESHOLDS       checks     ✗ 'rate>=0.95' rate=64.12%      http_req_duration     ✗ 'p(95)<500' p(95)=17.03s      http_req_failed     ✗ 'rate<0.01' rate=35.87%    █ TOTAL RESULTS       checks_total.......................: 603996 6708.595341/s     checks_succeeded...................: 64.12% 387296 out of 603996     checks_failed......................: 35.87% 216700 out of 603996      ✗ status is 200       ↳  64% — ✓ 193648 / ✗ 108350     ✗ body contains expected content       ↳  64% — ✓ 193648 / ✗ 108350      HTTP     http_req_duration.......................................................: avg=4.29s min=0s       med=377.57ms max=34.92s p(90)=12.88s p(95)=17.03s       { expected_response:true }............................................: avg=5.03s min=917.98µs med=2.73s    max=31.94s p(90)=12.95s p(95)=16.93s     http_req_failed.........................................................: 35.87%  108350 out of 301998     http_reqs...............................................................: 301998  3354.29767/s      EXECUTION     dropped_iterations......................................................: 1861627 20677.127362/s     iteration_duration......................................................: avg=5.14s min=999.47µs med=477.38ms max=56.43s p(90)=16.17s p(95)=19.59s     iterations..............................................................: 301998  3354.29767/s     vus.....................................................................: 1       min=1                max=28943     vus_max.................................................................: 29170   min=2432             max=29170      NETWORK     data_received...........................................................: 104 MB  1.2 MB/s     data_sent...............................................................: 23 MB   250 kB/s 

Нагрузка на систему

Основные выводы:

CPU

  • Загрузка до ~3000% → задействовано ~30 ядер.

Network Traffic

  • Пик входящего трафика: ~6–7 MBps, исходящего: ~3–4 MBps

  • Колебания присутствуют, особенно в начале — возможны ошибки или нестабильная отдача.

K6 Test Summary (Caddy)

  • Успешные RPS: 3,354 / сек → в 15 раз ниже цели

  • Ошибки: ❌ 35.87% (http_req_failed)

  • Дропы: ❌ 1,861,627 итераций

  • p(95) задержка: ❌ 17.03 сек (!), медиана ≈0.4 сек

  • Макс. задержка: 34.92 сек

  • Трафик:

    • Получено: 104 MB (1.2 MB/s)

    • Отправлено: 23 MB (250 KB/s)

  • VUs: до 29,170 — почти на пределе лимита

❌ Вывод:

  • Caddy не выдерживает нагрузку в 50k RPS

  • Большая доля ошибок, дропов и задержек

  • Работает хуже, чем NGINX и Traefik, ближе к антирекорду

Итоги

✅ HAProxy — лидер по стабильности

  • Уверенный лидер по результатам тестов: стабильно держит 50 000 rps при минимальной нагрузке на CPU. Прост в запуске и конфиге.

🟢 Envoy — почти наравне с HAProxy

  • Нагрузка на CPU выше, но приемлемая. Удобен в сценариях, где нужны гибкие настройки маршрутизации.

🟡 NGINX — середняк

  • Нужен fine-tuning или другой режим работы

  • Пропускная способность в 5 раз ниже ожидаемой

  • Аномальная нагрузка на CPU

🔴 Traefik — нестабилен

  • Почти 33% ошибок, сильные дропы, p95 = 8.5 сек.

  • Пропускная способность в 5 раз ниже ожидаемой

  • Аномальная нагрузка на CPU

🔻 Caddy — худший результат

  • Самые высокие задержки (p95 = 17 сек), ошибки 36%, дропов >1.8 млн.

  • Аномальная нагрузка на CPU

  • Явно не справляется с такой нагрузкой в дефолтной конфигурации.

Что важно понять из результатов?

  • HAProxy и Envoy — лучшие выборы, если нужна готовая к бою система без плясок с бубном.

  • Traefik, NGINX, Caddy — требуют дополнительной настройки и оптимизации для высоконагруженных сценариев.

Для кого подойдёт и зачем всё это использовать?

Когда можно брать HAProxy или Envoy:

  • Когда нужен стабильный и производительный балансировщик «здесь и сейчас».

  • Минимальные усилия на настройку — запустил и работает.

Когда стоит смотреть на Traefik / NGINX / Caddy:

  • Для небольших и средних проектов, где нагрузки ниже и можно позволить себе чуть сложнее конфиг или доработку.

  • Если в команде есть человек, который умеет настраивать и оптимизировать эти инструменты под конкретные задачи.

Спасибо что дочитали до конца 😉


ссылка на оригинал статьи https://habr.com/ru/articles/900438/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *