Как стать автором
Обновить

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

Уровень сложностиСредний
Время на прочтение23 мин
Количество просмотров15K
Всего голосов 60: ↑57 и ↓3+62
Комментарии40

Комментарии 40

Вообще говоря, немного неожиданный результат для nginx. Я предполагал что nginx (ну и HAProxy, возможно), будет далеко впереди. А не пробовали поставить их всех без Docker?

А Caddy сам пошёл за сертификатами в LE? В какой момент?

С год назад выбирал прокси для Docker Swarm. В первую очередь нужна была гибкость и разные настройки. nginx я люблю, но подкупила реклама Traefik о том, что сам может обнаруживать смену конфигурации апстримов кластера. Envoy и Caddy что-то показались туповатыми с точки зрения конфигов. Traefik, честно говоря, тоже, но кое-как смог его завести. В итоге очень не нравится, серьёзно думаю поменять на nginx. Производительность не очень интересовала - у меня не такие большие нагрузки.

Caddy сам ходит за сертами на основе Caddyfile, в момент запуска docker compose

Без Docker, с тем же Caddy или Nginx, результаты лучше процентов на 5

А что касается результатов, как я уже писал, возможно с помощью грамотного тюнинга можно выжать и больше, но из коробки увы, мы сами удивились ))

Для Swarm плохи все прокси, основанные на файлах конфигов. Пришлось пилить свой на лейблах и событиях Докера, без редеплоя.

Любопытно, а можете показать что у вас получилось? Или хотя бы рассказать поподробнее?

Судя по логам Traefik, он не использует события докера, а раз в несколько секунд перечитывает весь список контейнеров. Ну, либо в логи зачем-то слишком (слишком!) часто пишет о том что нашёл изменения. Это мне в нём тоже не нравится.

В принципе, у меня есть вариант использовать nginx и резолвер докера, а для динамической смены конфига без редеплоя - хранить его в Redis и обновлять через Lua (upstreams).

nginx 1.27.3
  • в директиву server, используемую в блоке upstream, добавлена поддержка параметра resolve, включающего отслеживание изменения IP-адреса для используемого доменного имени и автоматическое обновление конфигурации блока upstream без необходимости перезапуска nginx в случае изменения адреса;

Пожалуйста, напишите статью, о текущих или итоговых результатах этой разработки. Будет невероятно интересно и полезно.

Статья, конечно, хорошо, но есть сомнения. Открывать код не планировал, а проприетарщину тут не любят...

С гибкостью и разными настройками у Envoy всë в порядке. Просто его формат конфигов определяется схемой на Protobuf. Это следствие того, что основной use case для Envoy - динамическое переконфигурирование на лету. Файл конфига, который указывается при старте - это просто bootstrap конфиг, который чаще всего просто подсказывает, где брать остальное - внешние процессы, знающие текущее состояние кластера (Cluster Discovery Service), роуты (Routes Discovery Service) и прочие xDS. Это причина, по которой именно на Envoy довольно часто строят сервисмэши, ингресс-контроллеры и API- гейтвеи. Не надо из темплейтов генерить файлы конфигов...

Ну так-то понятно - динамическая переконфигурация и нужна от всех. Вопрос лишь в том, где описывать тот же список сервисов и их личный роутинг с особенностями (routes, rewrite, авторизация и тп).

Traefik пытается брать всё из labels, но не всегда получается через них это задать.

nginx может всё и даже больше, но ему нужен конфиг, который, в принципе, можно генерировать и подсовывать прямо в контейнер для hot reload. Однако, на каждый новый контейнер это немного не динамически, да.

Спасибо за отличную стать Интересно было бы посмотреть результаты тестирования http2/grpc

И на h3, quic посмотреть бы.

Вот бы ещё Angie протестировали - это форк Nginx от бывших разработчиков nginx на стероидах.

Не ради рекламы спрашиваю, но ищу замену NPM

А как Вам Angie поможет заменить NPM? Это же вроде как троллейбус буханкой белого хлеба заменять, если вы понимаете о чем я.
Есть подозрения, что у Angie будут результаты такие же или хуже чем у Nginx, из-за количества дополнительных модулей в стандартной сборке.

Есть подозрения

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

А как Вам Angie поможет заменить NPM? 

И то и другое можно использовать в качестве реверсивного прокси, и то и другое умеет работать с LetsEncrypt, отличие в том, что NPN, являясь надстройкой над nginx имеем веб интерфейс для настройки сайтов (в что можно и лучше перенести на CI/CD), но не имеет никаким вменяемых мониторингов.

В моем случае, я не знаю зачем, но дома имею

  1. некоторую домашнюю сетевую лабу с несколькими мини ПК на проксмоксе

  2. одноплатник с NPN

  3. роутер, с dnat www трафика на NPN и локальным DNS

  4. Ряд сервисов (42 хоста), которые идут на NPM, а он уже стучится виртуалки и контейнеры внутри кластера.

Хочу: запустить кластер из реверс прокси, как минимум добавить по 1 экземпляру на каждом инстансе PVE, в локальном dns прописать для DNS имени не одноплатник, а проксик, находящийся на одном физическом хосте с сервисов, чтобы по локальной сети меньше гонять трафика. Как минимум вариант с конфигом в гите и деплоем на несколько машин выглядит решением

А что такое NPM/NPN?
Мои знания ограничиваются на https://www.npmjs.com/ как NPM и NPN/ALPN как NPN

Это Nginx Proxy Manager - попытка приделать веб-интерфейс к nginx, чтобы .. Непонятно чтобы что, но, наверное, кому-то проще кликать мышкой и не запоминать конфиг, хотя мне сложно представить чтобы это хоть как-то было близко к функционалу текстовых конфигов.

Я рекомендую его для несисадминов ну и сам использую в некоторых местах, на самом деле, проще накликать мышкой базовую конфигурацию для какого-то внутреннего сервиса: вбиваешь доменное имя, хост и порт бэкенда, по желанию выбираешь в списке wildcard ssl сертификат, можешь выбрать в списке правила безопасности - ошибиться с настройкой неподготовленному специалисту значительно сложнее, нежели при работе с конфигами напрямую.

"Сырой конфиг" тоже можно вставить, но NPN это не для извращений в конфигах, а для, например, недавнего варианта: настроил в контейнере свой nginx, там сложные правила, работа как с локальным кешем, так и с api бэком, все по классике, в общем. Далее, зашел в веб интерфейс и прописал, что по https://notes.mydomain.tld хостится на http://notes.lan и все

Ну и в плюсы NPM можно занести отличную работу в докере, новичок не всегда завернет nginx и certbot в docker-compose.yml с первой попытки, а тут полностью прозрачная работа с сертификатами, как это есть у конкурентов типа caddy

Nginx unit хорошо бы ещё..

Вот так вот, натестируют по 1й минуте, а потом на проде "ой, а у нас тут фигня какая-то"

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

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

А можно на примерах поподробнее? И вообще есть ли в этом смысл если релизы чаще раза в неделю (прокси можно рестартить по очереди)?

У нас нт проводится 12 часов. Большое количество проблем возникает в промежутках с 4 до 8 часов работы. Условно в первые 4х часа работает стабильно, если же 8+ то уже дальше всё стабильно . Причины самые невероятные возникали (кеши, коннекшены к базе, утечки памяти, подвисшие потоки и тд). Так что нт важно проводить в течении длительного времени. Особенно в высоконагруженных системах.

Но ведь в посте тестируются прокси, а не приложение, а вы описываете проблемы приложения

Спасибо за пост. Можете выложить результаты в виде таблицы?

Сам использую haproxy, от nginx отказался давно уже, простота конфигурации на haproxy подкупила. С 2.8 в связке с acme обновляет сертификаты без перезапуска, появился quic, не назвал бы его там готовым к проду, но оно работает и в целом quic внутри страны ходит исправно, в 3.0 версии добавили сохранение статистики и доработали quic, должен выйти 3.1 lts где quic довели до ума. И есть экспортер Prometheus в коробке, что куда проще чем с nginx, одна строка в frontend и пошли метрики.

Полезная нагрузка в виде "hello world" - не сказать что эталон по объему ответа. Результаты могли исказиться так или иначе - служебного трафика сильно больше полезного.

Но тут тогда сразу и разный объем ответа просится на тестирование - чтобы выявить разницу при разных объемах ответа. Что удлинит тест в разы.

А не пробовали сразу Golang-сервис запустить с HTTPS? Т.е. без обратного прокси. Как отправная точка тоже было бы полезно (помимо голого HTTP). Правда тогда бекенд тоже с 32 vCPU и 128 ГБ RAM надо было бы запускать...

Вероятно будет еще несколько статей с подобным тестированием, в разных вариациях + есть идея посмотреть на это все со стороны пентестов

Можете протестировать с таким конфигм nginx? Есть гипотеза что вся разница между проксирующими серверами свелась к включенному или выключенному keepalive по умолчанию. Как следствие - заканчиваются эфемерные порты и такое большое время на обработку и так много фейлов.

http {
  upstream backend {
    server 10.0.0.4:8080;
    keepalive 4;
  }
  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://backend;
        proxy_set_header Connection '';
        proxy_http_version 1.1;
        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;
    }
	}
}


Я думаю завтра потестирую с этим конфигом, спасибо за идею ;-)

Дополнил статью тестами, если кратко keepalive - не помог

Очень странный результат для nginx. Надеюсь, дело в неправильных настройках (как выше написали про keepalive).

Зачем включили multi_accept? Он выключен по умолчанию не просто так. Это лучший способ вызвать дисбаланс в бенчмарке. Скорее всего у вас большинство соединений в итоге приняли и обрабатывали всего несколько первых рабочих процессов. От того и такие результаты...

Что в логе ошибок было? nginx пишет обычно что пошло не так... возможно у вас закончились дескрипторы или уперлись в лимит как раз из-за multi_accept.

Перед тем, как тестировать, выровняли ли настройки TLS-шифрования? У nginx по умолчанию `ssl_ciphers HIGH:!aNULL:!MD5;`. У других серверов скорее всего шифрование использовалось более слабое и менее ресурсоемкое, так что CPU не стал для них бутылочным горлышком.

Одинаковы ли у серверов настройки логирования?

Крайне предвзятый тест, без попытки разобраться в причинах и изначально неравными настройками.

Моя гипотеза: из-за "multi_accept" соединения между рабочими процессами распределялись крайне неравномерно, а из-за тяжелой криптографии рабочие процессы, набрав соединения, заблокировались на CPU.

Затем и включили, что без него только хуже)
Результаты разных комбинаций добавил в статью

Настройки TLS выровняли в последнем тесте + включенный multi_accept - результаты стали немного получше, но увы до Envoy или HAProxy - очень и очень далеко

Какие ошибки в логах? Во всех тестах, что мы проводили на моей памяти, haproxy оказывался чуть хуже nginx, да в общем-то неоткуда там разнице особой взяться, тем более в разы - архитектура очень похожа. Поэтому есть все основания полагать, что что-то фундаментально не так с конкретной установкой nginx. Что все же в логах ошибок? И сам nginx, кстати, откуда? Из официального репозитория на `nginx.org`?

И кстати, ни один из серверов сам не обрабатывает TLS - эту задачу решают TLS-библиотеки. Учитывая, как изменились результаты с настройкой ssl_ciphers, возможно вы на самом деле протестировали производительность сборок конкретных библиотек. Для равноценного теста с участием TLS - сервера должны быть собраны с одной и той же библиотекой.

Если интересно все же разобрать в причинах, я бы предложил попробовать повторить тест без TLS в принципе. Если результаты nginx и haproxy окажутся в этом случае близки, то дальше нужно будет разбираться, а что не так с TLS.

И обращаю также внимание на то, что у nginx по умолчанию включен `access_log`, а у того же haproxy, насколько я помню, его нужно включать принудительно.

> Затем и включили, что без него только хуже)

По вашим данным с выключенным multi_accept число успешных запросов больше:

checks_succeeded...................: 30.91% 312910 out of 1012194 checks_failed......................: 69.08% 699284 out of 1012194

против с включенным:

checks_succeeded...................: 25.90% 244664 out of 944528 checks_failed......................: 74.09% 699864 out of 944528

или я неправильно интерпертирую данные?

Но должно быть вообще 100%. То, что nginx теряет запросы - это что-то ненормальное, в error_log должны быть причины этому.

Изначально цель статьи была — сравнить производительность “из коробки”, с минимальными настройками, что и было сделано.

Также я упомянул, что для nginx требуется дополнительный тюнинг.

Понятно, что нюансов масса. Если у вас, как у бывшего разработчика ядра nginx, есть понимание, как быстро и эффективно настроить nginx для обработки 50k RPS — будет круто, если поделитесь рабочим конфигом. Думаю, сообщество будет только благодарно.

Мое понимание заключается в том, что nginx из коробки не должен проигрывать haproxy в разы. Указанное число RPS для сервера с 32 CPU - это вообще крайне мало... и выглядит очень подозрительно.

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

Вопрос, у вас HTTPS соединения keepalive или на каждый запрос новое? А если на каждый запрос новое соединение, то используется ли возобновление TLS-сессий и каким механизмом? Полный хендшейк - это весьма CPU-нагруженная операция, и лишь разница в настройках TLS-сессий может полностью перевернуть картину между серверами. Опять же, каждая настройка хороша под конкретный стенд, поэтому выравнивать сервера по настройкам, тем более таким критичным, как TLS - необходимо, иначе будет просто необъективный результат, который применим только к конкретному стенду, а на другом стенде будет совсем другим. Так вы протестировали сервера на то, как их настройки по умолчанию подходят для вашего стенда и это будет иметь мало общего с производительностью в реальности, даже на тех же настройках - просто в силу того, что клиенты обычно не ведут себя как отдельно взятый тестовый стенд.

У haproxy, кстати, по умолчанию включен кэш SSL-сессий, а у nginx он выключен. И если клиент, например, не умеет в сессионные тикеты, то вот и разница - тогда в одном случае вы считаете полные хендшейки, а в другом возобновления сессий.

50k RPS для nginx - это вообще пустяки, но вот криптография, которой занимается TLS-библиотека - она может стать лимитирующим фактором. К примеру, вот такой sizing guide для nginx plus: https://www.f5.com/pdf/deployment-guide/Sizing-Guide-for-Deploying-NGINX-Plus-on-Bare-Metal-Servers-2019-11-09.pdf - он уже старенький, ещё от 2019 года, но можно заметить, что на 32 CPU легко может быть 1 000 000 RPS, но всего около 50к полных хендшейков.

Спасибо да статью!

А если использовать режим network_mode: host ?

Надо бы указывать версии продуктов.

haproxy 3.1.6

envoy 1.34.0-dev

nginx 1.27.4

traeffik 3.3.5

caddy 2.9.1

P.s. Добавил и в начало статьи

еще важна реализация SSL

haproxy и nginx по умолчанию билдятся с openssl 3.x (и у разработчиков haproxy есть претензии к производительности настолько что дают совет "не используйте openssl3 - используйте альтернативы, причем nginx меньше подвержен)

envoy использует boringssl - форк openssl 1.x еще до этих проблем

traeffik и caddy - не использовал, поэтому не стал закапываться.. какую-то гошную реализацию tls судя по всему

В итоге мало того что конфигурационные значения по умолчанию у всех проксей разные, есть еще внешние зависимости.

Имхо надо разделять тестирование чисто по http - оно бОльше скажет о производительности самой прокси(не учитывая проблему значений по умолчанию) и по https.

Да, в golang можно сказать чтобы использовал boringssl, например. Во время компиляции. Но автор упомянул, что тестирование "из коробки" было. Иначе бы было 1000 комбинаций наверное.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации