Всем привет, меня зовут Стас, я техлид в 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/
Добавить комментарий