Когда я писал статью про HAProxy, у меня возникла идея сравнить его с другим популярным proxy-сервером, например с Envoy. Но тогда мне показалось, что простое сравнение в виде таблицы или пары абзацев будет неинформативным — и я решил сделать полноценный разбор в отдельной статье. Если вам интересно — добро пожаловать! Здесь рассмотрены не все возможности каждого решения, но ключевые — те, которые действительно важны на практике.

Сегодня я разберу три популярных прокси, сравню их и расскажу: что, где и когда лучше применять. Под «популярными» я имею в виду те, с которыми работал сам и изучил их устройство «под капотом». Прокси существует гораздо больше, но о других говорить не буду — либо не копал глубоко, либо знаю слишком мало, чтобы включать их в разбор. Отдельно отмечу важность документации: если она запутана или неполна, приходится гадать, что и где настраивать, а это быстро отбивает желание работать с инструментом.

HAProxy 3.3, NGINX 1.29 и Envoy 1.35 — три open source-прокси с разной архитектурой и моделью управления. Enterprise-версии рассматривать не буду — капитализм делает свое дело: серьёзных отличий почти нет, а вот в OSS-вариантах есть что сравнить — в ряде моментов конкуренция пошла на пользу.

Итак, начинаем. Рассматриваем самые свежие релизы на дату публикации.

План разбора:

  1. Цель и версии (только open source, последние доступные stable-ветки)

  2. Архитектура (Модель потоков/процессов, event-loop, статическая vs динамическая конфигурация (xDS, Data Plane API).

  3. Поддерживаемые протоколы L4/L7 (HTTP/1.1, HTTP/2, HTTP/3/QUIC, gRPC, WebSocket, TCP/UDP)

  4. Балансировка и маршрутизация (Алгоритмы round-robin, least-conn, hash, L7-маршруты, аффинити)

  5. Надежность и отказоустойчивость (Health-check, retries, circuit-breaking, rate-limiting)

  6. Наблюдаемость (Метрики Prometheus/StatsD, логи, OpenTelemetry/Jaeger, трассировка)

  7. Безопасность (TLS 1.3, mTLS, RBAC, OAuth/JWT)

  8. Переменные (Область видимости, встроенные, динамические)

  9. Таймауты (виды, где\как применять, принцип работы)

  10. Kubernetes-интеграция (gateway, ingress, service mesh)

Архитектура

HAProxy. Высокопроизводительный L4/L7-прокси с моделью master–worker и неблокирующим event-loop. Мастер руководит процессами, воркеры обрабатывают трафик — как шеф-повар, который не трогает еду, но следит, чтобы повара не спали. Поддерживает TCP/HTTP, TLS-терминацию, health-checks и продвинутые алгоритмы балансировки. Конфигурация статична, но есть Runtime API для живых правок; полный reconfigure делается через бесшовный reload. Типовой кейс: frontend принимает HTTPS, терминирует TLS, выбирает backend по leastconn и отправляет запрос — pipeline прозрачнее, чем бюджет в стартапе.

NGINX. Архитектура «master + workers». Воркеры принимают TLS/HTTP, парсят протокол, применяют фильтры, кеш и проксируют на upstream. Хорошо подходит для статических и программируемых конфигураций, если «динамика» нужна только на уровне переменных и Lua. Перезагрузка конфигурации обычно бесшовная: старые воркеры завершают запросы, новые уже готовы — никакой драмы, кроме как у админа в 3 ночи. Типовой кейс: server с proxy_pass на пул API, с кешем и ограничениями скорости по ключу. JWT/OAuth? Или Plus, или Lua — выбирайте, что меньше ломает мозг.

Envoy. Динамический L4/L7-прокси с фильтровой архитектурой и управлением через xDS. Конфигурация подтягивается из контрольной плоскости в реальном времени, маршрутизация гибкая до абсурда: можно отправить трафик хоть на кофемашину, если она объявилась в EDS. Поддерживает HTTP/1.1, HTTP/2, HTTP/3, gRPC, ретраи, метрики и обновляется через hot-restart без обрыва сокетов — хоть разворачивайся каждую минуту. Типовой кейс: listener с TLS, далее цепочка фильтров (ALPN → HTTP/2 → router) маршрутизирует gRPC-потоки по кластерам, которые сами приходят и уходят, как разработчики после демо.

Итого по архитектуре:

  • HAProxy — минимализм, высокая производительность, минимум магии.

  • NGINX — стабильность и зрелая экосистема, но с ограниченной гибкостью.

  • Envoy — динамика и API-центричность, но сложнее в эксплуатации.


Поддерживаемые протоколы

HAProxy работает как TCP/UDP-прокси и полнофункциональный HTTP-прокси. Поддерживает HTTP/1.1, HTTP/2 и HTTP/3 (QUIC), включая ALPN (Application-Layer Protocol Negotiation) и SNI (Server Name Indication). Может терминировать TLS или проксировать TLS passthrough. gRPC работает в режиме HTTP/2 без сторонних плагинов.


frontend fe
  mode http
  bind :80
  bind :443  ssl crt /etc/haproxy/certs/foo.com/cert.crt alpn h2
  bind quic4@:443 ssl crt /mycert.pem alpn h3
  http-request redirect scheme https unless { ssl_fc }
  http-after-response add-header alt-svc 'h3=":443"; ma=60'

backend be_api
    mode http
    option httpchk GET /health
    server s1 10.0.0.31:443 ssl verify none sni str(api.example.com) alpn h2 check
    server s2 10.0.0.32:443 ssl verify none sni str(api.example.com) alpn h2 check

Входящий TLS-трафик принимается на порту 443, ALPN выбирает HTTP/2 или HTTP/3, затем HAProxy терминирует соединение, балансирует по серверам с поддержкой h2 и шифрует трафик заново. Плюсы: высокая производительность на L4 и L7, зрелая поддержка HTTP/2. Минусы: настройка HTTP/3/QUIC требует проверки и ручной конфигурации; для сложных L7-правил доступны только ACL и Lua. Downstream/Upstream: HAProxy может принимать HTTP/1.1, HTTP/2 или HTTP/3 от клиентов (downstream) и независимо использовать любой протокол при подключении к бэкенду (upstream). Возможны сценарии h1→h2, h2→h1, h2→h2, h3→h1 и т.д., что позволяет менять протокол между клиентом и сервером без ограничений.

NGINX работает как TCP-stream прокси и как HTTP-прокси. Поддерживает HTTP/1.1 и HTTP/2 из коробки. HTTP/3/QUIC доступен с версии 1.25+ через директивы quic и http3. gRPC поддерживается через отдельный модуль grpc_pass, который работает поверх HTTP/2.

server {
    listen 443 ssl http2;
    ssl_certificate     /etc/nginx/certs/site.pem;
    ssl_certificate_key /etc/nginx/certs/site.key;

    location / {
        proxy_pass https://backend_pool;
        proxy_http_version 1.1;
    }
}

server {
    listen 8443 quic reuseport;
    listen 8443 ssl;
    ssl_certificate     /etc/nginx/certs/site.pem;
    ssl_certificate_key /etc/nginx/certs/site.key;

    location /api {
        add_header Alt-Svc 'h3=":8443"; ma=86400';
        proxy_pass https://backend_pool;
        proxy_http_version 1.1;
    }
}

upstream backend_pool {
    least_conn;
    server 10.0.0.31:443;
    server 10.0.0.32:443;
}

NGINX принимает TLS-соединения и включает HTTP/2 или HTTP/3 для клиентов, но к backend всё равно ходит по HTTP/1.1 (кроме gRPC). Плюсы: стабильный HTTP/2, простой конфиг. Минусы: HTTP/3 менее зрелый и требует ручного включения; stream-режим TCP не даёт детальной L7-логики; расширенные функции доступны только в Plus-версии. Downstream/Upstream: клиенты могут подключаться по HTTP/1.1, HTTP/2 или HTTP/3, но к backend соединение будет по HTTP/1.1 (кроме gRPC-location).

Envoy изначально построен как L4/L7-прокси с поддержкой TCP/UDP HTTP/1.1, HTTP/2, HTTP/3 и gRPC. Протоколы выбираются автоматически через ALPN, а фильтры позволяют обрабатывать как TCP, так и полный HTTP-поток. Для upstream на TLS/443 необходимо включить http2_protocol_options и настроить transport_socket для TLS.

Как примерно выглядит конфиг с поддержкой h1/h2/h3
admin:
  address:
    socket_address:
      address: 127.0.0.1
      port_value: 9901

static_resources:
  listeners:
  - name: main_listener
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 443
    # TLS конфигурация для HTTPS
    listener_filters:
    - name: envoy.filters.listener.tls_inspector
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector

    filter_chains:
    - filter_chain_match:
        application_protocols: ["h2", "http/1.1"]
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsConfig
          common_tls_context:
            tls_certificates:
            - certificate_chain: {filename: "/etc/envoy/cert.pem"}
              private_key: {filename: "/etc/envoy/privkey.pem"}
          require_client_certificate: false
      filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          http2_protocol_options: {}
          http_protocol_options: {}
          codec_type: AUTO

          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: {prefix: "/"}
                route: {cluster: backend_cluster}

          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

    # Отдельный filter chain для HTTP/3
    - filter_chain_match:
        transport_protocol: "quic"
      transport_socket:
        name: envoy.transport_sockets.quic
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.quic.v3.QuicDownstreamTransportConfig
          downstream_tls_context:
            common_tls_context:
              tls_certificates:
              - certificate_chain: {filename: "/etc/envoy/cert.pem"}
                private_key: {filename: "/etc/envoy/privkey.pem"}
      filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http_quic
          http2_protocol_options: {}
          http_protocol_options: {}
          codec_type: HTTP3

          route_config:
            name: local_route_quic
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: {prefix: "/"}
                route: {cluster: backend_cluster}

          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

    # UDP listener для QUIC
    udp_listener_config:
      quic_options: {}

  clusters:
  - name: backend_cluster
    connect_timeout: 5s
    type: STATIC
    lb_policy: ROUND_ROBIN
    typed_extension_protocol_options:
      envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
        "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
        explicit_http_config:
          http2_protocol_options: {}
    load_assignment:
      cluster_name: backend_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8080

Envoy listener принимает соединения на 443, сам определяет протокол (h1/h2/h3), маршрутизирует поток на пул с тем же протоколом или переводит его в нужный режим. gRPC обрабатывается нативно через HTTP/2. Плюсы: полная поддержка современных протоколов, гибкость через фильтры, без перезапуска можно менять маршруты. Минусы: выше потребление ресурсов, сложнее конфигурация для простых TCP-сценариев. Downstream/Upstream: Envoy автоматически конвертирует любые протоколы — h1↔h2↔h3 в любых комбинациях. Можно принимать h3 downstream и ходить к бэкенду по h1, или наоборот.

HAProxy — чемпион по скорости, будто с допингом. Глотает TCP, HTTP/1.1, HTTP/2, HTTP/3 и даже gRPC без костылей. Протокол downstream/upstream можно миксовать как коктейли на корпоративе — хоть h3→h1, хоть h1→h2. Минус: для QUIC придётся повозиться с ручной настройкой, а за сложные L7-правила — только ACL и Lua, никакой встроенной магии. Но зато latency минимальная, как у хорошего эспрессо.

NGINX — надёжен как дедовский жигуль: HTTP/2 держит стабильно, HTTP/3 прикручен «на синюю изоленту», а сложной L7-логики не жди. TCP через stream идёт, но апгрейд backend до h2 не завезут — хочешь комфорта, смотри NGINX Plus.

Envoy — как инженер с немецким дипломом: жонглирует h1/h2/h3 фильтрами, не потея и не перезапускаясь. Downstream хоть HTTP/3, backend хоть HTTP/1.1 — всё под контролем. Но ресурсы ест как браузер с сотней вкладок и требует знаний на уровне DevOps-соревнований.

Вывод:

  • HAProxy — брать, если нужна минимальная задержка и зрелый HTTP/1/2 без плясок.

  • NGINX — выбрать для стабильного HTTP/1/2 и простого конфига, но помнить про слабый upstream-функционал.

  • Envoy — король gRPC и любых протокольных миксов, но для банального L4-прокси это как на Porsche картошку возить.


Балансировка и маршрутизация

HAProxy поддерживает алгоритмы балансировки: roundrobin, leastconn, source, uri, hdr, consistent-hash. Маршрутизация уровня L7 реализуется через ACL: можно матчить по URI, заголовкам, TLS-отпечаткам (JA3/JA4) с использованием Lua.

frontend fe_api
    bind :443 ssl crt /etc/haproxy/certs/api.pem
    acl v1 path_beg /v1/
    use_backend be_v1 if v1
    default_backend be_v2

backend be_v1
    balance leastconn
    server s1 10.0.0.41:443 ssl verify none check
    server s2 10.0.0.42:443 ssl verify none check

backend be_v2
    balance uri
    hash-type consistent
    server s3 10.0.0.43:443 ssl verify none check
    server s4 10.0.0.44:443 ssl verify none check

Фронтенд принимает HTTPS-запросы, проверяет путь /v1/ и отправляет их в пул be_v1 с балансировкой по наименьшему числу активных подключений. Остальные запросы попадают в пул be_v2 с консистентным хешированием URI.

NGINX поддерживаемые алгоритмы: round_robin (по умолчанию), least_conn, ip_hash, hash с опцией consistent. Маршруты уровня L7 задаются через location. Конфигурация обновляется только через reload.

upstream api_v1 {
    least_conn;
    server api1.local:443;
    server api2.local:443;
}
upstream api_v2 {
    hash $request_uri consistent;
    server api3.local:443;
    server api4.local:443;
}
server {
    listen 443 ssl http2;
    ssl_certificate     /etc/nginx/certs/api.pem;
    ssl_certificate_key /etc/nginx/certs/api.key;
    proxy_ssl_server_name on;
    location /v1/ { proxy_pass https://api_v1; }
    location /    { proxy_pass https://api_v2; }
}

Запросы к /v1/ перенаправляются на пул api_v1 с алгоритмом least_conn. Остальные — на api_v2 с консистентным хешированием URI.

Envoy поддерживает алгоритмы: round_robin, least_request, maglev, ring_hash, а также веса, канареечные релизы, shadow-трафик. Управление и маршрутизация меняются динамически через xDS.

static_resources:
  listeners:
  - name: https_listener
    address: { socket_address: { address: 0.0.0.0, port_value: 443 } }
    filter_chains:
    - transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificates:
            - certificate_chain: { filename: "/etc/envoy/tls/api.crt" }
              private_key:       { filename: "/etc/envoy/tls/api.key" }
      filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress
          route_config:
            virtual_hosts:
            - name: api
              domains: ["*"]
              routes:
              - match: { prefix: "/v1/" }
                route: { cluster: api_v1 }
              - match: { prefix: "/" }
                route: { cluster: api_v2 }
          http_filters:
          - name: envoy.filters.http.router
  clusters:
  - name: api_v1
    lb_policy: LEAST_REQUEST
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        sni: "api.internal"
    load_assignment:
      cluster_name: api_v1
      endpoints:
      - lb_endpoints:
        - endpoint: { address: { socket_address: { address: api1.local, port_value: 443 }}}
  - name: api_v2
    lb_policy: MAGLEV
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        sni: "api.internal"
    load_assignment:
      cluster_name: api_v2
      endpoints:
      - lb_endpoints:
        - endpoint: { address: { socket_address: { address: api3.local, port_value: 443 }}}

Envoy слушает HTTPS-трафик на порту 443, маршрутизирует запросы по префиксу URI: /v1/ → кластер api_v1 (least_request), / → кластер api_v2 (Maglev). Все соединения к апстримам идут по TLS с проверкой SNI.

HAProxy — работает как швейцарский механизм: поддерживает roundrobin, leastconn, source, uri, hdr, consistent-hash — всё стабильно и предсказуемо. L7-маршруты через ACL: можно матчить по URI, заголовкам, даже по TLS-отпечаткам (JA3/JA4). Конфигурация статична, но понятна и прозрачна.

NGINX — минимализм без сюрпризов: доступны только round_robin, least_conn, ip_hash. HTTP-маршруты задаются через location, но без динамических алгоритмов или ring_hash. Sticky-сессии — только через ip_hash, API для внешнего управления нет, расширенные возможности балансировки есть только в NGINX Plus. Хорош, если конфиг «разложил и забыл», а трафик стабилен.

Envoy — тяжёлая артиллерия: поддерживает round_robin, least_request, maglev, ring_hash, веса, канареечные релизы, shadow-трафик. Всё управляется динамически через xDS — можно менять маршруты хоть каждую секунду. Но YAML-схемы перегружены и требуют опыта, иначе будет больно. Для сложной продакшен-динамики — это лучший выбор, но придётся вложиться в автоматизацию и обучение.

Выводы:

  • HAProxy — низкая задержка и предсказуемый контроль L4/L7. Настроил ACL — и работает.

  • NGINX — статические маршруты без динамических требований. Конфиг «разложил и забыл».

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


Надежность и отказоустойчивость

HAProxy обеспечивает встроенные механизмы отказоустойчивости на L4 и L7: активные health-check, повторные попытки при ошибках, circuit breaker через лимиты соединений, а также защиту от перегрузки через stick-tables.

global
  maxconn 20000 # верхний предел соединений на процесс HAProxy

defaults
  maxconn 10000 # лимит для фронтендов/бэкендов по умолчанию

backend be_api
  mode http
  option httpchk GET /health
  retry-on all-retryable-errors
  retries 3
  http-request disable-l7-retry if METH_POST METH_PUT METH_DELETE
  default-server inter 2s fall 3 rise 2
  server s1 10.0.0.51:443 ssl verify none maxconn 500
  server s2 10.0.0.52:443 ssl verify none maxconn 500
  stick-table type ip size 1m expire 10m store conn_rate(10s)
  tcp-request connection track-sc0 src
  tcp-request connection reject if { sc0_conn_rate gt 100 }

Серверы проверяются каждые две секунды по пути /health. Они исключаются из пула после трёх неудачных ответов и возвращаются обратно после двух успешных. Если лимит подключений к серверу (500 соединений) превышен, лишние запросы становятся в очередь и ожидают timeout queue (по умолчанию — от timeout connect), после чего возвращается 503. Параметр retries 3 выполняет до трёх попыток соединения (исходная + до двух повторов), а опция retry-on all-retryable-errors и директива http-request disable-l7-retry гарантируют, что HAProxy будет повторять только идемпотентные методы и не будет ретраить POST, PUT, DELETE. Параметры global maxconn и server maxconn задают верхние пределы соединений на процесс и на каждый backend. Практический предел зависит также от лимита файловых дескрипторов (ulimit -n), поскольку одно проксируемое соединение обычно требует два FD(≈1–2 FD на сессию). Дополнительно stick-tables отслеживают частоту подключений и блокируют источник при превышении порога, например 100 соединений за 10 секунд.

NGINX использует пассивные health-check (max_fails/fail_timeout) и proxy_next_upstream для fallback. Важно понимать нюанс подсчёта повторов: proxy_next_upstream_tries N считается на всю upstream-группу, то есть при proxy_next_upstream_tries 3 реально получается исходный запрос + до 2 повторов по остальным серверам (итого 1 + 2), а не 1 + 3 для каждого backend.

worker_processes auto;
worker_rlimit_nofile 4096; # максимальное количество файловых дескрипторов, которые может использовать один worker-процесс 

events {
  worker_connections 2048; # максимальное число одновременных соединений, которые может обработать один worker-процесс 
  multi_accept on;
}

http {
  upstream api_pool {
    least_conn;
    server 10.0.0.51 max_fails=3 fail_timeout=5s;
    server 10.0.0.52 max_fails=3 fail_timeout=5s;
  }

  limit_req_zone $binary_remote_addr zone=req_limit:10m rate=100r/s;

  server {
    listen 443 ssl;
    limit_req zone=req_limit burst=50;
    proxy_pass https://api_pool;
    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 3;
  }
}

При трёх ошибках подряд сервер исключается из пула на пять секунд. Для ограничения нагрузки используются limit_req и limit_conn, которые регулируют частоту и количество запросов на клиента или глобально. Максимальное число соединений на процесс определяется как минимум из worker_connections и worker_rlimit_nofile, а реальный предел для всех воркеров равен произведению числа процессов на лимит воркера, с учётом того, что одно проксируемое соединение использует около двух файловых дескрипторов. В Open-Source NGINX нет per-backend лимитов, аналогичных server maxconn; эти возможности (max_conns и очередь на апстриме) есть только в NGINX Plus.

Envoy предоставляет полный набор инструментов: активные health-check (HTTP/TCP/gRPC), circuit breaking с лимитами по соединениям, запросам и ошибкам, retries с гибкой политикой, outlier detection для автоматического исключения проблемных узлов и фильтры для ограничения входящих соединений

static_resources:
  listeners:
  - name: https_listener
    address: { socket_address: { address: 0.0.0.0, port_value: 443 } }
    filter_chains:
    - filters:
      - name: envoy.filters.network.connection_limit
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit
          stat_prefix: conn_limit
          max_connections: 10000
          delay: 0s
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress
          route_config: { ... }
          http_filters: [ { name: envoy.filters.http.router } ]

  clusters:
  - name: api_pool
    lb_policy: LEAST_REQUEST
    circuit_breakers:
      thresholds:
      - max_connections: 500
        max_requests: 1000      
    health_checks:
    - timeout: 1s
      interval: 2s
      unhealthy_threshold: 3
      healthy_threshold: 2
      http_health_check: { path: /health }
    outlier_detection:
      consecutive_5xx: 5
      interval: 5s
      base_ejection_time: 30s

Фильтр connection_limit ограничивает входящие соединения на listener, а circuit breakers задают пределы подключений и одновременных запросов к каждому кластеру. Механизм retries настраивается в retry_policy: параметр num_retries определяет только повторные попытки, всего выполняется исходный запрос + num_retries, а условия повтора можно задавать детально (например, только при отказе соединения или только при ответах 503) с использованием экспоненциального backoff. Количество файловых дескрипторов управляется на уровне ОС через ulimit -n, а перегрузка отдельных серверов контролируется с помощью outlier detection, автоматически исключающего узлы с избыточными ошибками 5xx.

HAProxy — как строгий охранник у клуба: лишние соединения не пустит, пьяных клиентов выгонит (stick-tables), сердцебиение проверяет каждую секунду. Circuit breaker настраивается вручную, но работает чётко. Минус: сложные сценарии придётся писать на Lua или подключать внешнюю автоматику — без этого всё руками, как в старой школе DevOps.

NGINX — охранник без рации: может заметить, что клиент упал, но только если он реально свалился на пол. Активных health-check нет (оставили для платной версии), circuit breaker в зачатке. Зато лимиты соединений и запросов задаются парой директив, без магии и Lua. Хорош для стабильных пулов, где никто не бегает и свет не мигает.

Envoy — охранник с камерой, тепловизором и аналитикой на ИИ: активные проверки, circuit breaker на всё подряд, outlier detection, retries с умной задержкой. Встроенный или внешний rate limiting — на выбор. Минус: он просит больше зарплату (CPU/memory) и инструктаж посложнее, иначе случайно закроет дверь даже перед хозяином.

Вывод:

  • HAProxy — лучший выбор для L4/L7-балансировщиков, где нужна быстрая защита от перегрузки и детальная ручная настройка.

  • NGINX — подойдёт, если достаточно пассивных проверок и простых лимитов без динамики.

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


Наблюдаемость

HAProxy выводит подробные логи через syslog (RFC5424/3164), поддерживает structured logging (JSON) через log-format и экспорт метрик в Prometheus через встроенный prometheus-exporter или stats socket. Встроенный веб-статус (stats uri) есть, но распределённой трассировки нет — требуются внешние агенты (Jaeger, Zipkin).

global
    log stdout format raw local0
    stats socket /var/run/haproxy.sock mode 600 level admin
    stats timeout 30s

defaults
    log global
    option httplog
    log-format { "time":"%t", "frontend":"%f", "backend":"%b", "srv":"%s", "status":%ST, "bytes":%B }

frontend prometheus
    bind *:8405
    mode http
    http-request use-service prometheus-exporter if { path /metrics }
    no log

Логи пишутся в stdout, метрики публикуются по http://<host>:8405/metrics. Плюсы: минимальные накладные расходы, детальные логи. Минусы: нет встроенной трассировки запросов, приходится интегрировать вручную.

NGINX поддерживает access/error логи, может логировать в JSON для экспорта в ELK/Graylog. Метрики — только через сторонние модули (nginx-module-vts, nginx-prometheus-exporter). Встроенной распределённой трассировки нет (в отличие от NGINX Plus, где есть OpenTelemetry).

http {
    log_format json_combined escape=json
        '{ "time":"$time_iso8601",'
        '"remote":"$remote_addr",'
        '"host":"$host",'
        '"request":"$request",'
        '"status":$status,'
        '"bytes":$body_bytes_sent }';

    access_log /var/log/nginx/access.json json_combined;

    server {
        listen 80;
        location / {
            proxy_pass http://10.0.0.52:8080;
        }
    }
}

Трафик логируется в JSON, готов для парсинга внешними системами. Плюсы: гибкий формат логов, легко подключать ELK. Минусы: нет нативных метрик и трассировки, нужен внешний экспортер.

Envoy из коробки поддерживает structured logging, метрики в Prometheus, а также OpenTelemetry для распределённой трассировки (Jaeger, Zipkin, Datadog). Каждый фильтр может генерировать собственные статистики.

admin:
  access_log_path: /dev/stdout
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

tracing:
  http:
    name: envoy.tracers.opentelemetry
    typed_config:
      "@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig
      grpc_service:
        envoy_grpc:
          cluster_name: otel_collector

Envoy пишет логи и метрики напрямую(http://<host>:9901/stats/prometheus), трассировка передается в OpenTelemetry Collector. Плюсы: нативная поддержка Prometheus и трассировки, детализированные метрики L4/L7.

HAProxy — пишет логи быстро и честно, как будто ведёт дневник без цензуры. Легко отдаёт метрики Prometheus через экспортер, а встроенный веб-статус даёт картинку без лишних зависимостей. Но трассировку придётся прикручивать самому — Lua, агенты, шаманский бубен.

NGINX — логи делает удобоваримыми в JSON и сразу готовыми для ELK, но метрики и трассировка — только через сторонние модули или экспортеры. Конфигурация простая, но никакой нативной магии для L7-метрик или распределённого следа. То есть видно, кто зашёл, но не куда он потом пошёл.

Envoy — наблюдает за всем, как параноидальный сосед с биноклем на пенсии. Логи структурированы, метрики идут прямо в Prometheus, а OpenTelemetry встроен из коробки: можно смотреть трассировку запроса от клиента до базы без внешних костылей. Минус: съедает больше ресурсов и требует аккуратной настройки, иначе легко утонуть в собственных графиках.

Вывод:

  • HAProxy — лёгкий и быстрый, но наблюдаемость только базовая.

  • NGINX — нормальные логи, но за метрики и трассировку придётся платить сторонними модулями и временем.

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


Безопасность

HAProxy — поддерживает TLS 1.3 и mTLS прямо на фронтендах, с версии 2.5 умеет проверять JWT и OAuth2 без внешних сервисов (http_auth_bearer, jwt_verify, jwt_payload), а для RBAC применяют ACL и переменные. Zero Trust-сценарии реализуемы через SPIFFE/SPIRE, но требуют дополнительного glue-кода для ротации ключей.

frontend https_in
  bind :443 ssl crt /etc/haproxy/certs.pem ca-file /etc/haproxy/ca.pem verify required
  http-request set-var(txn.jwt)       http_auth_bearer
  http-request set-var(txn.verified)  var(txn.jwt),jwt_verify(alg=RS256,key=/etc/haproxy/jwks.json)
  http-request deny if { var(txn.verified) -m int 0 }
  http-request set-var(txn.role)      var(txn.jwt),jwt_payload,query(role)
  acl role_admin var(txn.role) -m str admin
  http-request deny unless role_admin
  default_backend web

Производительность высокая, функционал политик менее гибкий, чем в Envoy, но достаточно для edge-защиты. За подробностями можно глянуть здесь. В OSS нативного WAF нет. Обычно используют ModSecurity через SPOE-агент или выносят проверку во внешний сервис с правилами OWASP CRS. Это добавляет операционную сложность и накладные расходы.

NGINX — прост в эксплуатации, но без встроенной проверки JWT в OSS-версии. TLS 1.3 и mTLS поддерживаются через стандартный ssl-модуль. JWT, OIDC и сложные RBAC-сценарии реализуют через внешний авторизатор (чаще всего oauth2-proxy) с auth_request или через Lua/OpenResty. Zero Trust возможен, но интеграция со SPIFFE/SPIRE полностью ручная

server {
  listen 443 ssl;
  ssl_certificate     /etc/nginx/ssl/server.crt;
  ssl_certificate_key /etc/nginx/ssl/server.key;
  ssl_client_certificate /etc/nginx/ssl/ca.pem;
  ssl_verify_client on;

  location / {
    auth_request /oauth2/auth;
    error_page 401 = /oauth2/sign_in;
    proxy_set_header X-Auth-Request-User  $upstream_http_x_auth_request_user;
    proxy_set_header X-Auth-Request-Email $upstream_http_x_auth_request_email;
    proxy_pass http://backend;
  }
  location = /oauth2/auth { internal; proxy_pass http://oauth2-proxy; }
  location /oauth2/       {           proxy_pass http://oauth2-proxy; }
}

Прост в эксплуатации, надёжен, но без внешнего авторизатора сложные политики реализовать трудно. В OSS встроенного WAF нет. Чаще всего применяют libmodsecurity + модуль ModSecurity-nginx с профилем OWASP CRS. Всё настраивается вручную, нужно следить за обновлением правил и тюнинговать под нагрузку.

Envoy — оптимален для Zero Trust. Поддерживает TLS 1.3, mTLS, динамическую загрузку сертификатов через SDS, встроенные фильтры JWT, RBAC и OAuth2. Интеграция со SPIFFE/SPIRE нативная, сертификаты обновляются автоматически. OAuth2/OIDC решают через внешний IdP совместно с jwt_authn или ext_authz

Очень большой конфиг (danger)
static_resources:
  listeners:
  - name: main_listener
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 8080
    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
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: service_cluster

          http_filters:
          - name: envoy.filters.http.jwt_authn
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
              providers:
                example_provider:
                  issuer: https://example.com
                  audiences:
                  - api.example.com
                  remote_jwks:
                    http_uri:
                      uri: https://example.com/.well-known/jwks.json
                      cluster: jwks_cluster
                      timeout: 1s
                    cache_duration:
                      seconds: 300

          - name: envoy.filters.http.rbac
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
              rules:
                policies:
                  "require-valid-jwt":
                    permissions:
                      - any: true
                    principals:
                      - metadata:
                          filter: envoy.filters.http.jwt_authn
                          path:
                            - key: example_provider
                          value:
                            string_match:
                              exact: verified

          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: service_cluster
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8081

  - name: jwks_cluster
    connect_timeout: 5s
    type: LOGICAL_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: jwks_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: example.com
                port_value: 443
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        sni: example.com

JWT проверяется через jwt_authn filter с поддержкой локальных и удалённых JWKS, а RBAC-фильтр позволяет описывать allow/deny-политику прямо в конфигурации. WAF: встроенного решения нет. Используют WASM-фильтры на базе Coraza (правила совместимы с OWASP CRS) или интегрируют Curiefense как Envoy-фильтр. Подход открытый, но требует тестов под прод-нагрузкой и ручной настройки правил.

HAProxy — шифрует быстро, JWT/OAuth2 проверяет нативно с версии 2.5 без лишних зависимостей. TLS 1.3 и mTLS на фронтендах, ACL и переменные для RBAC. Zero Trust через SPIFFE/SPIRE возможен, но придётся дописывать собственный «клей» для ротации ключей и распределения SVID. Хорош, когда важны скорость и простые политики без лишней магии.

NGINX — настраивается так же легко, как добавить новый upstream. TLS 1.3 и mTLS встроены, но JWT и OIDC в OSS нет — всё решается через внешний авторизатор (чаще oauth2-proxy). Сложный RBAC потребует вынесенной логики. Zero Trust сценарии возможны, но интеграция полностью ручная. Конфиг прост, но возможностей меньше, чем у HAProxy и Envoy.

Envoy — Zero Trust в чистом виде. TLS 1.3, mTLS, SDS для динамики ключей, встроенные фильтры JWT, RBAC, OAuth2. SPIFFE/SPIRE подключается нативно, сертификаты обновляются сами. Можно строить политику хоть под микроскопом, без лишних костылей.

Вывод:

  • HAProxy — быстрый минимализм: простые ACL и встроенная JWT/OAuth2-проверка.

  • NGINX — надёжен и понятен, но для сложных политик нужны внешние сервисы.

  • Envoy — всё для Zero Trust из коробки: TLS, mTLS, JWT, RBAC, OAuth2, SDS и SPIFFE.


Переменные

HAProxy — переменные бывают как коты в коробке Шрёдингера: живут то в транзакции (txn.*), то в сессии (sess.*), то в запросе (req.*) или ответе (res.*). Можно хранить что угодно: IP, токены, промежуточные флаги. Эти переменные легко помещать в ACL, логи или Lua.

frontend fe_https
    bind :443 ssl crt /etc/haproxy/certs/site.pem alpn h2,http/1.1
    mode http
    http-request set-var(txn.client_ip) src
    http-request set-header X-Client-IP %[var(txn.client_ip)]
    default_backend be_api

backend be_api
    mode http
    http-request set-var(sess.backend_name) be_api
    server s1 10.0.0.31:443 ssl verify none
    server s2 10.0.0.32:443 ssl verify none

Здесь IP клиента записывается в переменную транзакции и сразу же уезжает в заголовок. Переменная sess.backend_name живёт дольше — пока TCP-сессия не умрёт. Плюсы: быстрые ACL, всё можно хранить прямо в прокси. Минусы: сложные сценарии часто требуют Lua — готовься писать код прямо в конфиге, как в 2000-х.

NGINX — переменные как студенты на паре: появляются только на время запроса, потом испаряются. Есть встроенные ($host, $remote_addr) и те, что задаёшь руками через set. Между запросами память не держат — хочешь что-то протащить дальше, прокидывай заголовки или ставь cookie.

server {
    listen 443 ssl http2;
    ssl_certificate     /etc/nginx/certs/site.pem;
    ssl_certificate_key /etc/nginx/certs/site.key;

    set $client_ip $remote_addr;

    location / {
        proxy_set_header X-Client-IP $client_ip;
        proxy_pass https://backend_pool;
    }
}

upstream backend_pool {
    least_conn;
    server 10.0.0.31:443;
    server 10.0.0.32:443;
}

Переменная $client_ip живёт ровно один запрос, как батарейка в китайском фонарике. Плюсы: просто, стабильно, никаких утечек состояния. Минусы: никакой «долгой памяти», всё руками через заголовки.

Envoy — вместо переменных тут подстановки и метаданные. %DOWNSTREAM_REMOTE_ADDRESS%, %REQ(:authority)%, %UPSTREAM_HOST% и десятки других. Можно хранить временные данные прямо внутри фильтров — хочешь память на уровень TCP, хочешь на уровень HTTP-запроса.

static_resources:
  listeners:
  - name: https_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
          stat_prefix: ingress
          route_config:
            virtual_hosts:
            - name: api
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: 
                  cluster: api_pool
                  request_headers_to_add:
                  - header:
                      key: X-Client-IP
                      value: "%DOWNSTREAM_REMOTE_ADDRESS%"
          http_filters:
          - name: envoy.filters.http.router

Envoy сам добавляет X-Client-IP из встроенной подстановки. А если захочешь хранить ещё что-то — используй Dynamic Metadata, и у тебя будет «карманная база данных внутри прокси». Плюсы: гибкость и фильтры как LEGO. Минусы: сложность конфига, настраивать без кофе опасно.

Вывод:

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

  • NGINX — переменные как одноразовые стаканчики: удобно, но без повторного использования.

  • Envoy — это как владение магией: невероятная мощь, но один неверный символ в метаданных или переменной и ты уже не повелеваешь трафиком, а тушишь фаервол.


Таймауты

HAProxy таймаутов столько, что можно потеряться. Таймауты обычно указывают в секциях defaults/frontend/backend. Главные параметры: timeout connect (тайм-аут на установку TCP), timeout client (неактивность со стороны клиента), timeout server (неактивность со стороны бэкенда), timeout http-request (время ожидания заголовков запроса), timeout http-keep-alive и timeout tunnel для туннелей/вебсокетов. Таймауты часто задаются явно в defaults; без корректных значений соединения могут «залипать» или падать. Каждый отвечает за свой этап: соединение, запрос, очередь, туннель. Можно гибко настроить поведение и на L4, и на L7.

defaults
  timeout connect 5s
  timeout client 1m
  timeout server 1m
  timeout http-request 10s
  timeout queue 15s
  timeout http-keep-alive 15s
  timeout tunnel 1h

Соединение к backend ждём 5 секунд, клиентский запрос и ответ сервера — по 1 минуте, запросы на уровне HTTP проверяем 10 секунд, очередь не держим дольше 15 секунд. Плюсы: полный контроль, можно «настроить до болтика». Минусы: легко запутаться, если лепить всё подряд, конфиг превращается в «таймаутное болото».

NGINX даёт набор директив для клиента и прокси. Ключевые: client_header_timeout, client_body_timeout — время чтения заголовков/тела от клиента; keepalive_timeout — время удержания keep-alive; proxy_connect_timeout, proxy_read_timeout, proxy_send_timeout — поведение при proxy_pass: подключение, чтение ответа и отправка запроса учитываются между последовательными операциями, а не за весь ответ. То есть proxy_read_timeout измеряет паузу между чтениями, а не общий срок передачи.

http {
  client_header_timeout 60s;
  client_body_timeout   60s;
  keepalive_timeout     75s;
  proxy_connect_timeout 10s;
  proxy_read_timeout    300s;  # увеличить для долгих ответов/streaming
  proxy_send_timeout    300s;
}

Для потоковых gRPC/HTTP2 соединений нужно поднять proxy_*_timeout и следить за http2_recv_timeout/настройками upstream. Минусы: меньше гранулярности, чем у HAProxy, например, нет отдельного таймаута очереди или туннеля — всё «оптом».

Envoy оперирует таймаутами на уровне connection manager, route и cluster. Важные пункты: route timeout по умолчанию 15s (это время ожидания полного upstream-ответа; стартует после получения всего downstream-запроса), stream_idle_timeout у HTTP connection manager по умолчанию 5 минут (закрывает бездействующие стримы), cluster.connect_timeout/transport_socket_connect_timeout по умолчанию ~5s. Таймауты можно задавать глобально и на уровне маршрута; per-route timeout: 0s отключает таймаут маршрута. Для стриминговых API нужно отключать/увеличивать route/stream таймауты.

http_connection_manager:
  stream_idle_timeout: 300s   # обычно 5m по умолчанию
route_config:
  virtual_hosts:
  - name: default
    domains: ["*"]
    routes:
    - match: { prefix: "/" }
      route:
        cluster: service_a
        timeout: 0s         # отключить route timeout для стриминга
        idle_timeout: 0s    # отключить per-route idle

В Envoy поведение таймаутов сложнее из-за retry/retry-timeout и взаимодействия connection-manager ↔ route ↔ cluster. Тестируйте сценарии с retry и streaming. Плюсы: гибкость, можно делать очень тонкие сценарии. Минусы: конфиг превращается в «головоломку на скорость», без документации самому себе — через неделю не разберёшься.

HAProxy — с таймаутами как со швейцарскими часами. Всё задаётся явно: connect, client, server, http-request, keep-alive, tunnel. Хочешь стриминг — поставь timeout tunnel и забудь. Минимум магии, максимум контроля. Если соединение зависло — это твоя вина, а не сюрприз в дефолтах.

NGINX — таймауты как бытовая техника. Client, proxy, keepalive — всё просто и понятно, но надо помнить, что proxy_read_timeout меряет паузы между байтами, а не всю загрузку. Для долгих gRPC или стриминга таймауты придётся крутить руками. Работает надёжно, если не забыть про все ручки.

Envoy — таймауты как корпоративные дедлайны. По умолчанию route timeout 15 секунд, stream_idle_timeout 5 минут — и Envoy безжалостно разрывает стриминг, если не объяснить, что проект ещё не готов. Гибкости море: можно отключить таймауты, настроить idle отдельно на route и cluster, но придётся думать, что именно отключать.

Вывод:

  • HAProxy — максимальная детализация: можно настроить всё, даже «таймаут на таймаут». Но легко перестараться.

  • NGINX — базовый набор: быстро, понятно, но без редких «спецрежимов».

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


Kubernetes-интеграция

HAProxy. Есть два OSS-варианта:

  • haproxytech/kubernetes-ingress — классический Ingress с аннотациями, поддержка Gateway API флагом. Метрики для Prometheus из коробки. Плавная миграция на новые API.

  • jcmoraisjr/haproxy-ingress — тоже Ingress v1, частичная поддержка Gateway API (HTTPRoute/Gateway в v0.14). Подойдёт, если ближе его модель конфигурации.

Минимум магии, максимум контроля. Если что-то не работает — виноваты не «умные дефолты», а конкретные настройки.

NGINX Ingress Controller — фокус на Ingress v1. TLS/mTLS через аннотации (auth-tls-*), внешняя авторизация (auth-url), глобальные параметры через ConfigMap. Всё просто, но за сложные сценарии придётся платить количеством аннотаций. Работает стабильно, но «особые режимы» придётся включать руками.

Envoy Gateway (OSS) — нативный Gateway API, расширения SecurityPolicy для mTLS/JWT и авторизации по клеймам, метрики Prometheus.

Contour — управляет Envoy, поддерживает Gateway API и свои CRD для HTTP/gRPC/TCP. Гибкости море: route, cluster, policy — всё настраивается. Но за каждую опцию придётся думать.

Istio — mesh как отдельный отдел. Envoy выполняет роль data plane, политика и телеметрия централизуются через контроль-плейн. Если mesh уже есть или планируется — Ingress лучше строить на Istio или Gateway API с Envoy Gateway, чтобы не плодить зоопарк контроллеров.

Кратко:

  • Хотите «всё под контролем» и минимум сюрпризов — HAProxy.

  • Хотите «просто работает» — NGINX.

  • Хотите «тонкая политика и современные API» — Envoy Gateway или Contour.

  • Хотите «единая политика на весь трафик» — Istio.

Вывод

  • HAProxy — максимальная детализация, можно настроить всё. Но легко утонуть в параметрах.

  • NGINX — базовый и понятный путь. Быстро стартовать, без редких «сюрпризов».

  • Envoy/Contour — гибкость и глубокая интеграция с Gateway API. Требует продуманной конфигурации.

  • Istio — если mesh уже в планах, Ingress лучше сразу строить на нём.


Выводы и рекомендации

Подойдёт, когда важна минимальная латентность, высокая пропускная способность и предсказуемое поведение без control-plane. Нативная сильная сторона — L4/L7 балансировка с детальной ручной настройкой (таймауты, maxconn, stick-tables, httpchk). QUIC/H3 в активной доработке — уже можно тестировать, но планируйте нагрузочные тесты.

Когда брать:

  • Edge TLS-терминация с высокими SLA по latency.

  • Большие объёмы TCP/HTTP трафика, где нужна малая задержка.

  • Нужен контроль на уровне соединений и простая схема автозамены бэкендов (seamless reload).

Операционные ограничения и подводные камни:

  • Сложные L7-политики требуют Lua или внешней логики.

  • HTTP/3 требует тестов и знаний о QUIC.

  • Нет встроенной «из коробки» распределённой трассировки — нужно подключать OTEL/агрегаторы.

Лучше для простых и стабильных веб-фронтов, кэширования и ситуаций, где конфиг «разложил и забыл». HTTP/2 зрел, HTTP/3 стабилизируется в mainline. Для WAF/JWT/metrics/tracing — внешние модули или Plus

Когда брать:

  • Статический контент, reverse-proxy с кешем, rate-limiting на уровне edge.

  • Быстрый rollout и простая конфигурация без control-plane.

  • Нужен модульный моддинг (lua, njs) для лёгкого расширения.

Операционные ограничения и подводные камни:

  • Активные health-checks, advanced LB и enterprise-функции в основном в Plus.

  • Метрики и трассировка требуют внешних экспортеров/модулей.

Если система динамическая, микросервисная, с gRPC, канареечными релизами, shadow-трафиком и требованием к глубокой телеметрии — Envoy даёт наилучшую платформу. Требует control-plane (xDS) и автоматизации.

Когда брать:

  • Service mesh или централизованное управление L7-политиками.

  • gRPC-first архитектуры и обширная трассировка/OTel.

  • Нужно гибкое управление канареями, retry/circuit-breaker, rate-limit через внешние сервисы.

Операционные ограничения и подводные камни:

  • Высокая сложность конфигурации и поддержания YAML-политик.

  • Больше CPU/RAM на экземплах по сравнению с NGINX/HAProxy.

  • Требует CI/CD для xDS/контроль-плейна и хорошей наблюдаемости.


Итого по синтетическому разбору:

  • Низкая латентность и грубая производительность L4/L7 → HAProxy

  • Простой веб-фронт, кеш, статический контент, минимум динамики → NGINX

  • Динамичная среда, gRPC, mesh, канареи/трафик-менеджмент → Envoy

Практические советы по внедрению

  • Test-first: для HTTP/3 и QUIC задействуйте этап нагрузочного теста и SLO-валидацию

  • Observability: если выбираете Envoy — проектируйте OTEL-пайплайн сразу. Для HAProxy/NGINX готовьте Prometheus → OTEL/Jaeger интеграцию

  • K8s: для простого ingress — ingress-nginx или haproxy-ingress; для политики и mesh — Envoy Gateway/Contour/Istio. Планируйте единый control-plane, чтобы не плодить контроллеров.

  • Ресурсы: закладывайте больше CPU/RAM под Envoy и автоматизацию для его конфигураций.

NGINX — как велосипед с корзинкой: завёл и забыл, но далеко не уедешь. HAProxy — как надёжный жигуль: предсказуемо, стабильно, но без наворотов. Envoy — как BMW: летит быстро и уверенно, но вечно приходится ковыряться под капотом. Всё едет, вопрос только — куда и с какой скоростью.

Кто победил?

По сумме плюсов — Envoy, остаётся лучшим выбором для динамических, telemetry-heavy и gRPC-проектов. HAProxy держит первенство по сырой производительности и простоте L4/L7 в высоких нагрузках. NGINX остаётся лучшим выбором для традиционных web/edge сценариев с простым управлением и кешированием.

P.S. Если я в процессе написания статьи где-то что-то упустил, ошибся в конфиге или описании — присылайте исправления в личку, чтобы я мог внести их в статью.

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