company_banner

Как мы в Dropbox перешли с Nginx на Envoy

Автор оригинала: Alexey Ivanov, Oleg Guba
  • Перевод

В этой статье мы будем говорить о нашей старой инфраструктуре, основанной на Nginx, ее болячках, а также выгоде, которую мы получили после миграции на Envoy. Мы сравним Nginx и Envoy различными способами. Также кратко коснемся процесса миграции, текущего состояния, а также проблем, возникших при переходе.



Когда мы переключили большую часть трафика на Envoy — у нас получилось бесшовно мигрировать систему, обрабатывающую десятки миллионов открытых соединений, миллионы запросов в секунду и терабиты пропускной способности. По факту мы стали одним из крупнейших пользователей Envoy в мире.


Отказ от ответственности: мы пытаемся оставаться объективными, достаточно много сравнений относятся только к Dropbox и нашим принципам разработки ПО: мы ставим на Bazel, gRPC, С++ и Golang.


Просим обратить внимание, что мы рассмотрим версию Nginx с открытым исходным кодом, а не коммерческую с дополнительными функциями.


Наша старая инфраструктура, основанная на Nginx


Наши настройки Nginx были статичными, обновлялись с помощью комбинации Python2, Jinja и YAML. Любое изменение требовало полной раскатки с нуля. Все динамические части, к примеру управление upstream и экспорт статистики, были написаны на Lua. Любая достаточно сложная логика была перемещена на следующий уровень проксирования, написанный на Go. Наша статья также имеет раздел, посвященный нашей старой инфраструктуре на Nginx.


Nginx служил нам верой и правдой уже порядка десяти лет. Но он перестал устраивать наши лучшие практики по разработке:


  • Наши внутренние и (закрытые) внешние API постепенно переходили с REST на gRPC, который требует кучу различных перекодировок от прокси.
  • Protocol buffers стали стандартом de facto для определения сервисов и настроек
  • Все программное обеспечение, независимо от языка программирования, собирается и тестируется с помощью Bazel.
  • Большая вовлеченность наших инженеров ключевых инфраструктурных проектов в качестве участников сообществ ПО с открытым исходным кодом.

Также Nginx был достаточно сложным в плане обслуживания:


  • Сборка конфигурационных файлов была слишком гибкой и была разбита между YAML, Jinja2 и Python.
  • Мониторинг был смесью Lua, анализа журналов, и мониторингом системного уровня
  • Повышенная зависимость от сторонних модулей влияла на стабильность, производительность и стоимость последующих обновлений.
  • Раскатка и управление процессом сильно отличались от остальных сервисов, поскольку они достаточно сильно зависели от настройки других систем: syslog, logrotate и прочих, в отличие от того, чтобы быть независимыми от основной системы.

При этом мы в первый раз начали искать потенциальную замену Nginx.


Почему не Bandaid?


Мы часто говорим, что внутри инфраструктуры мы значительно полагаемся на Bandaid, прокси-сервер, написанный на Go. Он прекрасно внедрен в инфраструктуру Dropbox, поскольку основан на обширнейшей экосистеме внутренних библиотек Go: мониторинг, обнаружение сервисов, ограничение скорости и т.п. Мы рассматривали подобный переход с Nginx, однако есть несколько проблем, мешающих нам это сделать:


  • ПО на Golang требуется больше ресурсов, чем ПО на C++. Низкое потребление ресурсов особенно важно для нашего Edge, поскольку мы не можем легко "автоматически масштабировать" там наши сервисы.
    • дополнительное потребление процессорного времени в основном связано с сборщиком мусора (GC), парсером HTTP и TLS, причем последнее менее оптимизировано, чем BoringSSL, используемый Nginx\Envoy.
    • Модель "goroutine-per-request" и издержки GC значительно увеличивают требования к оперативной памяти в сервисах с большим числом соединений, как у нас.
  • Нет поддержки FIPS для Golang TLS
  • У Bandaid нет поддержки сообществом вне Dropbox, что означает, что мы сможем полагаться только на себя при разработке.

С учетом вышенаписанного мы решили начать перенос нашей транспортной инфраструктуры на Envoy.


Наша новая инфраструктура, основанная на Envoy


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


Производительность


Архитектура Nginx является событийной и многопроцессной. Она поддерживает SO_REUSEPORT, EPOLLEXCLUSIVE, а также привязку обработчиков к процессорным ядрам. Однако несмотря на то, что она событийная, она не полностью неблокирующая, что означает, что некоторые операции, например открытие файла или журналирование, потенциально может вызвать приостановку обслуживания (даже с aio, aio_write, включенными thread pools). Это приведет к увеличению задержек, которые могут достигать нескольких секунд на дисках с шпинделями.


у Envoy схожая событийная архитектура, но вместо процессов используются потоки. Также есть поддержка SO_REUSEPORT (с поддержкой фильтрации BPF) и зависимость от libevent для обработки цикла событий (другими словами — нету крутых фишек epoll(2), к примеру EPOLLEXCLUSIVE). В цикле событий Envoy нет каких-либо блокирующих операций ввода-вывода. Даже журналирование сделано неблокирующим, так что оно не может вызвать подвисание.


Похоже, что теоретически по производительности Nginx и Envoy будут близки. Надеяться — не в наших правилах, поэтому в первую очередь мы выполнили набор различных тестов на аналогично настроенных Nginx и Envoy. Если вы интересуетесь настройкой производительности, мы описали наши типовые руководства по настройке здесь, начиная с подбора оборудования и заканчивая оптимизацией операционной системы, выбором библиотек и настройками вебсервера.


Тесты показали схожую производительность на большинстве тестовых нагрузок: высокий RPS, высокая пропускная способность, и смешанное проксирование gRPC с низкими задержками и высокой пропускной способностью. Достаточно сложно сделать хороший тест производительности. У Nginx есть руководства, но они беспорядочные. У Envoy также есть руководство по нагрузочному тестированию, а также инструменты в проекте envoy-perf, но они, к сожалению, выглядят неподдерживаемыми. Мы стали использовать наш внутренний инструмент по имени "hulk", потому что он "ломать" наши сервисы.


Тем не менее были и заметные различия в результатах:


  • Nginx показал бОльшие задержки на долгоживущих соединениях. По большей части это связано с подвисанием цикла обработки событий при интенсивном вводе-выводе, особенно было заметно при работе с SO_REUSEPORT, поскольку в этом случае соединения могут приниматься от заблокированного в данный момент обработчика.
  • Производительность Nginx без сборщика статистики схожа с таковой для Envoy, активация сборщика на Lua замедлила Nginx в тесте с высоким RPS в три раза. Предсказуемо, с учетом зависимости от lua_shared_dict, который синхронизируется между обработчиками с помощью mutex. Мы понимаем, насколько неэффективным был наш сбор статистики. Мы попробовали сделать что-то подобное на counter(9) из FreeBSD, но только в пространстве пользователя: привязка к процессорному ядру, а также счетчики без блокировки на каждый обработчик вместе с функцией сбора данных, которая проходит по всем обработчикам и возвращает объединенную статистику. Но мы отказались от этой затеи, поскольку если бы мы завязались на внутренности Nginx (включая обработку ошибок, например), то нам пришлось бы поддерживать огроменнный патч, превращающий последующие обновления в настоящий ад.

У Envoy не было ни одной из этих проблем, а после перехода на него мы бы смогли освободить до 60% серверов, ранее занятых только под Nginx.


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


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


Некоммерческий Nginx имеет модуль "stub status", в котором есть семь характеристик:


Active connections: 291
server accepts handled requests
 16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106

Этого однозначно не достаточно для нас, так что мы добавили простой обработчик log_by_lua, который добавляет статистику по каждому запросу, основанному на заголовках и переменных, доступных в Lua: код статуса, размеры, попадание в кэш и т.п. Здесь пример простейшей функции, которая выдает характеристики:


function _M.cache_hit_stats(stat)
    if _var.upstream_cache_status then
        if _var.upstream_cache_status == "HIT" then
            stat:add("upstream_cache_hit")
        else
            stat:add("upstream_cache_miss")
        end
    end
end

В довесок к характеристикам, собираемым с каждого запроса, мы добавили очень хрупкий анализатор error.log, который отвечает классификацию ошибок upstream, http, Lua и TLS.


Поверх всего у нас работал отдельный обработчик для сбора внутреннего состояния Nginx: время с момента последней перезагрузки, число обработчиков, размеры RSS\VMS, сроки жизни сертификатов TLS и проч.


Типовая установка Envoy предоставляет нам тысячи отдельных метрик (в формате Prometheus), описывая как проксируемый трафик, так и внутреннее состояние сервера:


$ curl -s http://localhost:3990/stats/prometheus | wc -l
14819

Тут включено множество статистики с различной агрегацией:


  • Статистика по кластеру\по upstream\по виртуальному хосту, включая информацию о пуле соединений и различные временные ряды.
  • Статистика для каждого слушающего сокета: TCP\HTTP\TLS
  • Различная внутренняя статистика, начиная от версии и времени работы, и заканчивая статистикой выделения памяти и устаревшей функцией счетчиков использования.

Особый привет интерфейсу администратора Envoy. Он не только предоставляет отдельную статистику через /certs, /clusters и config_dump, но также имеет важнейшие для сопровождения особенности:


  • Способность менять на лету уровень журналирования через /logging, что позволило нам решать достаточно сложные проблемы за считанные минуты.
  • Точки входа /cpuprofiler, /heapprofiler, /contention, которые безусловно были весьма нужными при устранении проблем с производительностью
  • /runtime_modify, с которой можно изменять параметры конфигурации без применения новой конфигурации, что можно использовать при подборе функций и т.п.

В дополнение к статистике Envoy также поддерживает подключаемые механизмы отладки. Это используется не только нашей командой, ответственной за трафик и работающей с несколькими уровнями балансировки нагрузки, но и разработчиками приложений, желающими проверить сквозные задержки между Edge и серверами приложений. Технически у Nginx есть поддержка отладки через стороннюю программу OpenTracing, однако она вяло развивается.


Вишенкой на торте у Envoy идет способность отправлять журналы по gRPC, что позволило нашей команде по трафику убрать мосты syslog-to-hive (логично, т.к. журналы Envoy идут не через syslog прим. переводчика). Кроме прочего на боевых серверах гораздо удобнее (и безопаснее!) добавить еще один общий сервис gRPC, чем добавлять отдельный слушающий сокет TCP\UDP.


Настройка журналирования доступа в Envoy, как и прочие вещи, производится через сервис управления gRPС: Access Log Service (ALS). Сервисы управления являются стандартным способом интеграции Envoy data plane с различными сервисами, что подводит нас к следующей теме.


Интеграция


Способность Nginx к интеграции проще всего описать как "юниксовая". Конфигурация очень статичная. Есть сильная зависимость от файлов (собственно конфигурационный файл, сертификаты, белые\черные списки и т.п.) и широко известных производственных стандартов (журналирование в syslog, авторизация подзапросов через HTTP). Простота и обратная совместимость хорошая штука для небольших установок, потому что Nginx может быть достаточно просто автоматизирован парой shell-скриптов. Но по мере роста масштаба системы тестируемость и стандартизация становятся более важными.


Envoy гораздо более продвинут в том, как data plane трафика должен быть интегрирован с control plane, а, следовательно, и с остальной инфраструктурой. У него есть поддержка protobuf и gRPC, предоставляемая через стабильный API, называемый xDS. Envoy производит обнаружение своих динамических ресурсов запрашивая свои (одну или несколько) службы xDS. В настоящее время эти интерфейсы развиваются за пределы Envoy, перед UDPA (universal data plane interface) разработчики ставят амбициозную цель: стать стандартом "de facto" в мире балансировщиков L4\L7. Из нашего опыта — это работает. Мы уже используем ORCA (Open Request Cost Agregation) для внутреннего тестирования нагрузки и рассматриваем возможности UDPA для наших балансировщиков, не связанных с Envoy, например на основе Katran, балансировщика eBPF\XDP L4.


Это весьма хорошо для Dropbox, поскольку все сервисы взаимодействуют между собой через API на основе gRPC. Мы внедрили собственную xDS control plane, которая объединяет Envoy с нашей системой управления конфигурацией, обнаружением сервисов, управлением секретами и информацией о маршрутах. Если хотите знать больше о Dropbox RPC — можете почитать здесь, мы подробно описали интеграцию обнаружения сервисов, управление секретами, статистику, отладку и прочие вещи с gRPC.


Опишем несколько доступных сервисов xDS, их альтернативы для Nginx, а также примеры того, как мы их используем:


  • Access Log Service (ALS), как уже писали выше, позволяет нам динамически настраивать цели для журналов доступа, кодировки и форматы. Попробуйте представить себе динамическую версию log_format и access_log для Nginx
  • Endpoint discovery service (EDS), предоставляет информацию о членах кластера. Это аналог динамически обновляемого списка блоков upstream для секций server (например для Lua это будет balancer_by_lua_block) в конфигурационном файле Nginx. В нашем случае мы проксируем это на наш внутренний сервис обнаружения.
  • Secret discovery service (SDS), предоставляет различную информацию о TLS, которая в Nginx работает по директивам ssl_* (ну и ssl_*_by_lua_block соответственно). Мы взяли его в качестве нашего сервиса распостранения секретов.
  • Runtime discovery service (RTDS), предоставляет runtime флаги. Наша реализация этой функции на Nginx была довольно топорной, основанной на проверке существования различных файлов в Lua. С таким подходом сервера быстро стали несовместимыми по настройкам. В Envoy это также реализовано на файлах по-умолчанию, однако мы вместо этого подключили сервис с RTDS API к нашему распределенному хранилищу конфигурации, так что мы можем управлять целыми кластерами (инструментом, с интерфейсом, похожим на sysctl), а случайные несоотвествия между различными серверами исключены.
  • Route discovery service (RDS): выполняет сопоставление маршрутов с виртуальными хостами и позволяет выполнять дополнительную настройку заголовков и фильтров. В терминах Nginx это аналог динамического блока location c set_header```proxy_set_header\proxy_pass```. На нижних уровнях проксирования мы автоматически создаем их из наших конфигураионных файлов для определения сервисов.

В качестве примера интеграции Envoy в существующей боевой системе обычно приводят этот. Есть также и другие реализации control plane для Envoy, например Istio и менее сложная go-control-plane. Наша собственная платформа управления Envoy реализует все больше интерфейсов API xDS. Она развернута как обычный gRPC сервис и выступает в качестве промежуточного звена для наших инфраструктурных строительных блоков. Все это делается с помощью общих библиотек Golang для общения с внутренними сервисами и их предоставления для Envoy через интерфейсы API xDS. Сам процесс не включает вызовы файловой системы, подачу сигналов, обработку cron\logrotate\syslog\парсеров и т.п.


Настройка


У Nginx есть неоспоримое преимущество в виде простой и удобочитаемой конфигурации. Но это все становится неважным, как только конфигурационный файл становится сложным и начинает создаваться программными средствами. Как мы уже писали — наша конфигурация создается с помощью Python2, Jinja2 и YAML. Некоторые из вас возможно видели, или даже писали такое для erb, pug, Text::Template или даже m4 (А то! прим. переводчика):


{% for server in servers %}
server {
    {% for error_page in server.error_pages %}
    error_page {{ error_page.statuses|join(' ') }} {{ error_page.file }};
    {% endfor %}
    ...
    {% for route in service.routes %}
    {% if route.regex or route.prefix or route.exact_path %}
    location {% if route.regex %}~ {{route.regex}}{%
            elif route.exact_path %}= {{ route.exact_path }}{%
            else %}{{ route.prefix }}{% endif %} {
        {% if route.brotli_level %}
        brotli on;
        brotli_comp_level {{ route.brotli_level }};
        {% endif %}
        ...

С таким подходом создания конфигурации Nginx была огромная проблема: все используемые инструменты позволяли подстановку и\или логику. У YAML есть anchors, у Jinja2 — циклы, условия и макросы, ну а Python вообще обладает полнотой по Тьюрингу. Без чистой модели данных сложность быстро расползлась по всем трем инструментам. Это можно было бы решить, но дополнительно было еще:


  • Нет декларативного описания формата настроек. Если бы мы хотели программно создавать и проверять конфигурацию — нам пришлось бы его изобрести.
  • Синтаксически правильная конфигурация может все еще быть недействительной с точки зрения кода на C. Например, некоторые связанные с буфером переменные имеют ограничения по значениям, ограничения по выравниванию и взаимозависимости с другими переменными. Для проверки конфигурации нам нужно было бы запустить ее через nginx -t.

Envoy наоборот имеет унифицированную модель данных для настройки: всё определяется через Protocol Buffers. Это не только решает вопрос с моделированием, но также добавляет информацию о типе к значениям конфигурации. С учетом этого, а также того, что мы уже давно работаем с protobuf в других сервисах, а также общего способа описания\настройки сервисов — значительно упрощается интеграция.


Наш новый генератор конфигурации для Envoy основан на protobuf и Python3. Моделирование выполняется в файлах proto, а вся логика реализована на Python. Например:


from dropbox.proto.envoy.extensions.filters.http.gzip.v3.gzip_pb2 import Gzip
from dropbox.proto.envoy.extensions.filters.http.compressor.v3.compressor_pb2 import Compressor

def default_gzip_config(
    compression_level: Gzip.CompressionLevel.Enum = Gzip.CompressionLevel.DEFAULT,
    ) -> Gzip:
        return Gzip(
            # Envoy's default is 6 (Z_DEFAULT_COMPRESSION).
            compression_level=compression_level,
            # Envoy's default is 4k (12 bits). Nginx uses 32k (MAX_WBITS, 15 bits).
            window_bits=UInt32Value(value=12),
            # Envoy's default is 5. Nginx uses 8 (MAX_MEM_LEVEL - 1).
            memory_level=UInt32Value(value=5),
            compressor=Compressor(
                content_length=UInt32Value(value=1024),
                remove_accept_encoding_header=True,
                content_type=default_compressible_mime_types(),
            ),
        )

Обратите внимание на аннотации типов Python3 в этом кусочке кода! В сочетании с расширением mypy-protobuf обеспечивается сквозная типизация внутри генератора. Любая совместимая IDE сразу же выявит проблемы при несоотвествии. Все еще есть варианты, когда проверяемый тип в protobuf может быть неверным. В примере выше window_bits для Gzip может принимать значения от 9 до 15. Этот тип ограничений легко может быть задан с помощью расширения protoc-gen-validate:


google.protobuf.UInt32Value window_bits = 9 [(validate.rules).uint32 = {lte: 15 gte: 9}];

Ну и наконец неявное преимущество использования формально определенной модели конфигурации — она естественным способом сопоставляется с документацией, например:


// Value from 1 to 9 that controls the amount of internal memory used by zlib. Higher values.
// use more memory, but are faster and produce better compression results. The default value is 5.
google.protobuf.UInt32Value memory_level = 1 [(validate.rules).uint32 = {lte: 9 gte: 1}];

Тем, кто думает об использовании protobuf на боевых серверах, но беспокоится, что может не хватить представления без схемы, стоит ознакомиться с статьей от Harvey Tuch, ключевого разработчика Envoy.


Расширяемость


Расширение Nginx куда-либо дальше, чем разрешают настройки, обычно возможно путем написания модуля на C. Руководство разработчика предоставляет наиболее полное введение для доступных строительных блоков. Там же сказано, что это относительно тяжелый способ. На практике потребуется наличие серьезного сеньора для безопасного написания модуля Nginx. С точки зрения инфраструктуры, доступной для разработчиков модулей, есть наличие базовых контейнеров: hash-таблицы, очереди, красно-черные деревья, управление памятью (не RAII), а также перехват всех этапов обработки запроса HTTP. Есть и внешние библиотеки, pcre, zlib, openssl и конечно же libc.


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


Наиболее часто используемый способ расширения от сообщества основан на стороннем lua-nginx-module и различных библиотеках от OpenResty. Так можно подключиться на любом этапе обработки запроса. Мы использовали log_by_lua для сбора статистики и balancer_by_luaдля динамической перенастройки backend.


В теории Nginx предоставляет способность разработки модулей на C++, на практике же отсутствуют нужные интерфейсы и обертки над всеми примитивами, чтобы сделать это стоящим делом. Но тем не менее сообщество пытается что-то делать. Конечно же, они все еще не дошли до внедрения на боевых системах.


Основной механизм расширения для Envoy — написание расширений на C++. Процесс не так хорошо описан как в случае с Nginx, однако тут все проще. Это частично из-за того, что:


  • Чистые и хорошо прокомментированные интерфейсы. Классы — точки расширения и документирования.
  • Стандартная библиотека и язык C++14. Начиная от базовых языковых функций, например шаблонов и лямбда-функций, заканчивая типобезопасными контейнерами и алгоритмами. В целом писать на C++14 так же просто как на Golang или с небольшой натяжкой — Python. (провокационно! прим. переводчика)
  • Расширения C++14 и его стандартной библиотеки. Предоставляются библиотекой abseil, в которой собраны из более новых стандартов C++, например mutex с встроенным обнаружением взаимоблокировки, поддержка отладки, дополнительные\более эффективные контейнеры, и многое другое.

Мы смогли объединить Envoy вместе с Vortex2 (наш framework для мониторинга) написав всего 200 строчек кода для реализации интерфейса stats.


Envoy также поддерживает Lua через moonjit, форк LuaJIT c улучшенной поддержкой Lua 5.2. Однако по сравнению с сторонней интеграцией Lua в Nginx у нее гораздо меньше возможностей и преимуществ, что делает Lua в Envoy гораздо менее привлекательным из-за дополнительных сложностей в разработке, тестировании и отладке интерпретируемого кода. Компании, специализирующиеся на Lua, могут не согласиться, но в нашем случае проще было избежать Lua и использовать исключительно C++ для написания расширений для Envoy.


чем Envoy отличается от других вебсерверов, так это появившейся поддержкой WebAssembly (WASM) — быстрого, переносимого и безопасного механизма для расширений. WASM предназначен не для непосредственного использования. а в качестве цели компиляции любого языка программирования общего назначения. Envoy реализует спецификацию WebAssembly for Proxies (включая эталонные SDK для C++ и Rust), которая описывает границы между кодом WASM и универсальным прокси L4\L7. Такой способ разделения между прокси и кодом расширения обеспечивает безопасную изолированную среду, а низкоуровневый компактный бинарный формат WASM обеспечивает производительность практически близкую к оборудованию. Кроме того, расширения proxy-wasm уже интегрированы в xDS, что позволяет динамические обновления и даже потенциально A\B тестирование. В презентации с Kubecon'19 (вы таки помните, что были не виртуальные конференции?) есть хороший обзор WASM в Envoy и его потенциальных применениях. Также на ней было сказано об производительности порядка 60-70% от кода на C++.


Вместе с WASM поставщики услуг получают безопасный и эффективный способ выполнения кода клиентов на своей стороне. Клиенты получают переносимость, поскольку их расширения смогут работать в любом облаке, реализующем proxy-wasm ABI. Кроме прочего он позволяет использовать вашим пользователям любой язык, компилирующийся в WebAssembly. Это позволяет им безопасно и эффективно использовать большой набор библиотек, не связанных с C++.


Разработчики Istio также вкладывают кучу ресурсов в разработку WebAssembly, у них уже есть экспериментальная версия расширения телеметрии и сообщество WebAssemblyHub для обмена расширениями. Можно почитать об этом подробнее здесь.


Мы в Dropbox в настоящее время не используем WebAssembly, но все может поменяться, когда станет доступен proxy-wasm SDK для Go.


Сборка и тестирование


Для сборки Nginx применяется особая система конфигурации на основе shell-скриптов, а также система сборки на основе make. Просто и утонченно, но заняло слишком много времени для интеграции его в монорепо Bazel для получения преимуществ в виде инкрементных, распределенных, герметичных и воспроизводимых сборок. Google открыл исходный код своей версии Nginx для Bazel, которая состоит из Nginx, BoringSSL, PCRE, ZLIB и Brotli.


Что касается тестирования, то у Nginx есть набор интеграционных тестов в отдельном репозитории и нет unit-тестов.


С учетом интенсивного использования Lua и отсутствия встроенной инфраструктуры модульного тестирования мы перешли к тестированию на основе макетных настроек и простого тестового драйвера на Python:


class ProtocolCountersTest(NginxTestCase):
    @classmethod
    def setUpClass(cls):
        super(ProtocolCountersTest, cls).setUpClass()
        cls.nginx_a = cls.add_nginx(
            nginx_CONFIG_PATH, endpoint=["in"], upstream=["out"],
        )
        cls.start_nginxes()

    @assert_delta(lambda d: d == 0, get_stat("request_protocol_http2"))
    @assert_delta(lambda d: d == 1, get_stat("request_protocol_http1"))
    def test_http(self):
        r = requests.get(self.nginx_a.endpoint["in"].url("/"))
        assert r.status_code == requests.codes.ok

Также мы проверяем синтаксическую правильность всех созданных конфигурационных файлов с их предварительной обработкой (например меняем ip-адреса на 127.0.0.1/8, переключаем на самоподписанные сертификаты ну и т.п.) запуская nginx -c.


Если смотреть на Envoy, то его система сборки уже Bazel, так что интеграция в наш монорепо была простейшей: Bazel позволяет легко добавлять внешние зависимости. Мы также использовали скрипты copybara для синхронизации protobuf как для Envoy, так и для UDPA. Это удобо, если нужно сделать простые преобразования без необходимости поддержки большого набора патчей.


Для Envoy есть возможность использовать либо unit-тесты (на основе gtest\gmock) с предварительно написанными макетами, либо интегрированный фреймворк для тестирования, либо оба варианта сразу. Больше не нужно полагаться на медленные сквозные интеграционные тесты, запускаемые на каждое мелкое изменение.


При разработке Envoy с открытым исходным кодом требуется 100% покрытие unit-тестами. Тесты запускаются автоматически через конвейер CI в Azure при каждом запросе на слияние.


Кроме этого обычной практикой является обработка чувствительного к скорости работы кода с помощью google\benchmark:


$ bazel run --compilation_mode=opt test/common/upstream:load_balancer_benchmark -- --benchmark_filter=".*LeastRequestLoadBalancerChooseHost.*"
BM_LeastRequestLoadBalancerChooseHost/100/1/1000000          848 ms          449 ms            2 mean_hits=10k relative_stddev_hits=0.0102051 stddev_hits=102.051
...

После перехода на Envoy мы стали полагаться исключительно на unit-тесты при разработке наших внутренних модулей:


TEST_F(CourierClientIdFilterTest, IdentityParsing) {
  struct TestCase {
    std::vector<std::string> uris;
    Identity expected;
  };
  std::vector<TestCase> tests = {
    {{"spiffe://prod.dropbox.com/service/foo"}, {"spiffe://prod.dropbox.com/service/foo", "foo"}},
    {{"spiffe://prod.dropbox.com/user/boo"}, {"spiffe://prod.dropbox.com/user/boo", "user.boo"}},
    {{"spiffe://prod.dropbox.com/host/strange"}, {"spiffe://prod.dropbox.com/host/strange", "host.strange"}},
    {{"spiffe://corp.dropbox.com/user/bad-prefix"}, {"", ""}},
  };
  for (auto& test : tests) {
    EXPECT_CALL(*ssl_, uriSanPeerCertificate()).WillOnce(testing::Return(test.uris));
    EXPECT_EQ(GetIdentity(ssl_), test.expected);
  }
}

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


Bazel — одна из лучших вещей, которые когда-либо случались с нашими разработчиками. У него очень крутая кривая обучения и большие первоначальные вложения, но у него также очень высокая отдача: инкрементные сборки, удаленное кэширование, распределенные сборки / тесты и т.д.


Одним из менее обсуждаемых преимуществ Bazel является то, что он дает нам возможность запрашивать и даже расширять граф зависимостей. Программный интерфейс к графу зависимостей в сочетании с общей системой сборки для всех языков является очень мощной функцией. Его можно использовать в качестве основного строительного блока линтеров, генерации кода, отслеживания уязвимостей, системы развертывания и т.д.


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


Кодовая база Nginx весьма небольшая, с минимальными внешними зависимостями. Обычно можно увидеть только три внешних зависимости полученного бинарного файла: zlib (или более быстрый вариант), какая-либо библиотека TLS и PCRE. В Nginx реализованы все парсеры протоколов, библиотеки для работы с событиями. а также разработчики дошли до того, что повторно написали некоторые функции из libc.


Некоторое время Nginx считался настолько безопасным, что использовался в качестве вебсервера по умолчанию в OpenBSD. Однако после конфликта двух сообществ разработчики OpenBSD начали разработку httpd. Более подробно можно почитать в докладе с BSDCon.


Минимализм окупился на практике, у Nginx было всего 30 известных уязвимостей за последниее 11 лет.


У Envoy кода гораздо больше, особенно если учитывать, что код C++ более плотный, чем код на C, используемый в Nginx. Также сюда включены миллионы строк из внешних зависимостей. Все, начиная от уведомлений от событий, до парсеров протоколов выделяется в сторонние библиотеки. Это увеличивает плоскость атаки и раздувает результирующий бинарный файл.


Для противостояния Envoy в значительной степени опирается на современные методы обеспечения безопасности. Для этого используются AddressSanitizer, ThreadSanitizer и MemorySanitizer. Также разработчики пошли дальше и стали использовать fuzzing.


Любой проект с открытым исходным кодом, который имеет решающее значение для глобальной инфраструктуры IT, может быть принят в OSS-Fuzz, бесплатную платформу для автоматического fuzzing. Узнать больше можно здесь.


На практике, однако, все эти меры предосторожности не в полной мере успевают за ростом кодовой базы. Так за последние два года было найдено 22 уязвимости.


Для Envoy подробно описана политика безопасности для выпусков, также есть описание и для отдельных уязвимостей. Envoy является участником Google's Vulnerability Reward Program (VRP). Google по этой программе, открытой для всех исследователей, предоставляет вознаграждения за обнаруженные уязвимости и сообщает о них в соотвествии с их правилами.


Примером того, как некоторые уязвимости потенциально могут быть использованы, служит CVE-2019–18801


Для противодействия рискам уязвимостей мы применяем лучшие методы защиты от наших поставщиков операционных систем Ubuntu и Debian, а именно специальный hardened профиль для всех наших бинарных файлов, работающих на Edge. Он включает в себя ASLR, защиту стека и таблицы символов:


build:hardened --force_pic
build:hardened --copt=-fstack-clash-protection
build:hardened --copt=-fstack-protector-strong
build:hardened --linkopt=-Wl,-z,relro,-z,now

Вебсервера с использованием fork, такие как Nginx, в большинстве окружений имеют проблемы с защитой стека, поскольку основной и рабочие процессы разделяют одно и то же значение для переменной-канарейки в стеке, а поскольку при проверке этой переменной сбойный рабочий процесс убивается — значение этой переменной можно перебрать бит за битом примерно за 1000 попыток. У Envoy, который работает с потоками, не подвержен этой атаке.


Аналогично мы усиливаем сторонние зависимости при сборке, где это возможно. Так мы применяем BoringSSL в режиме FIPS, который включает в себя самопроверку при запуске и проверку целостности бинарного файла. Мы также рассматриваем возможность запуска бинарников с поддержкой ASAN на некоторых наших канареечных серверах на Edge.


Возможности


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


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


Со стороны прокси у Nginx отсутствуют функции, необходимые современной инфраструктуре. Например нету поддержки HTTP/2 для бэкэндов, проксирование gRPC есть, но без мультиплексирования соединений. Отсутствует поддержка транскодирования gRPC. Кроме того при использовании модели "открытое ядро" есть ограничения по возможностям, которые входят в версию с открытым исходным кодом. В результате некоторые из важных функций, например статистика, недоступны в версии, поддерживаемой сообществом.


Envoy наоборот развивался как ingress\egress прокси, чаще всего используемый для окружений с использованием gRPC и в хвост и в гриву. Его функции в качестве вебсервера рудиментарны: нету раздачи файлов, кэширование все еще не реализовано до конца, нету поддержки сжатия. Для подобных случаев мы все еще держим запасные Nginx, которые используются Envoy в качестве upstream кластера.


Когда кэширование в Envoy будет реализовано, мы сможем перенести на Envoy большинство сценариев раздачи статических файлов с использованием S3 вместо файловой системы в качестве хранилища. Можно почитать больше про eCache, кэш HTTP с несколькими бэкэндами для Envoy.


Также у Envoy уже есть встроенная поддержка многих возможностей, связанных с gRPC:


  • Проксирование gRPC, базовая способность, которая позволила нам использовать gRPC в наших приложениях (например клиент Dropbox для настольных компьютеров)
  • Поддержка HTTP/2 для бэкэндов, позволила нам значительно сократить число соединений TCP между уровнями трафика, уменьшая потребление памяти и поддерживающий трафик.
  • Мосты gRPC->HTTP (и обратно), с помощью которых мы можем публиковать старые приложения HTTP/1 с использованием современного стека gRPC.
  • gRPC->WEB, с помощью которого мы применяем сквозной gRPC там, где промежуточные узлы (firewall, IDS и прочие) еще не умеют работать с HTTP/2.
  • gRPC JSON transcoder, с которым мы смогли перекодировать весь входящий трафик, включая публичные API Dropbox, из REST в gRPC.

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


  • Egress прокси, поскольку Envoy поддерживает метод HTTP CONNECT — его можно использовать в качестве замены Squid прокси. Мы уже начали заменять наши сервера с Squid на Envoy, поскольку это не только значительно улучшает видимость, но также и сокращает трудоемкость за счет общего стека с data plane и мониторингом (не надо парсить журналы для статистики).
  • Обнаружение сторонних сервисов: в наших программах мы используем библиотеки Courier gRPC вместо использования Envoy в качестве service mesh. Но мы так используем Envoy в разовых случаях, когда надо подключить с минимальными затратами сервис с открытым исходным кодом к нашему обнаружению сервисов. К примеру мы применяем Envoy в качестве дополнительного модуля обнаружения сервисов в нашем стеке аналитики. Hadoop может динамически запрашивать свое имя и узлы для журналирования. Superset может запрашивать свои бэкэнды airflow, presto и hive. Grafanа может обнаруживать свою базу MySQL.

Сообщество


Разработка Nginx по большей части централизована, большинство разработок ведется за закрытыми дверями. Есть немного внешней активности в списке рассылок, а также иногда случаются дискуссии о дальнейшем развитии в официальном bug tracker. Есть канал #nginx в IRC сети FreeNode, можно свободно присоединиться для более интерактивной связи с сообществом.


Разработка Envoy открыта и децентрализована: ведется с использованием проблем\запросов на слияние на GitHub, через список рассылки и собрания сообщества (собираются в Zoom раз в две недели, прим. переводчика). Есть также достаточно активности и в Slack, приглашение можно получить здесь.


Трудно дать объективную оценку стилям разработки и сообществу, поэтому давайте посмотрим пример разработки HTTP/3.


Реализация QUIC и HTTP/3 для Nginx была недавно представлена F5. Код чистый, без внешних зависимостей. Сам процесс был довольно непрозрачный, а за полгода до этого Cloudflare разработала свою реализацию. В результате у сообщества есть две отдельных экспериментальных версии для Nginx (а как по мне — так это же круто, есть что сравнить! прим. переводчика)


В случае Envoy реализация все еще в процессе разработки, основывается на библиотеке quiche. Отслеживание статуса ведется в этой проблеме, документация по архитектуре была готова еще до готовности патчей, остальная работа, для которой требуется сообщество, помечена "Требуется помощь".


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


Текущее состояние перехода


Более полугода Nginx и Envoy работали вместе, мы постепенно переключали трафик с первого на второй с помощью DNS. На нынешний момент мы перенести на Envoy самые разные рабочие нагрузки:


  • Сервисы Ingress с высокой пропускной способностью. Все данные файлов из клиентов Dropbox для настольных компьютеров обрабатываются через сквозной gRPC с помощью Envoy. С переходом на Envoy мы также немного улучшили производительность для пользователей благодаря улучшенному повторному использованию соединений на Edge.
  • Сервисы Ingress с высоким RPS. Это клиентские метаданные, получили те же преимущества сквозного gRPC, а также из-за удаление пула соединений мы не ограничены одним запросом в одном соединении.
  • Сервисы уведомлений и телеметрии. Здесь ведется обработка всех уведомлений в режиме реального времени, поэтому на эти сервера приходят миллионы соединений по HTTP (одно на каждого активного клиента). Сервисы уведомлений теперь могут быть сделаны на потоках gRPC вместо затратного способа с long-polling.
  • Смешанные сервисы с высокой пропускной способностью и высоким RPC. Тут идет трафик API (метаданные и данные). Это позволило нам задуматься о доступных API gRPC. Мы даже можем переключиться к перекодированию наших сущетвующих API на основе REST прямо на Edge.
  • Сервисы Egress, высокопроизводительные прокси. В нашем случае — связь с AWS, в основном S3. В конечном итоге это позволит нам окончательно удалить все сервера с Squid из боевой сети, оставляя единственную L4\L7 data plane.

Одна из последних вещей, которые надо перенести, сам сайт www.dropbox.com. После переноса сайта мы сможем начать вывод из эксплуатации Edge серверов с Nginx. Эпоха закончится.


Проблемы, которые у нас возникли


Переход не был полностью гладким. Но это не привело к каким-либо заметным сбоям. Труднее всего было с нашими сервисами API. Различные устройства взаимодействуют с Dropbox через наш общедоступный API, от скриптов с curl\wget и встроенных устройств с особыми стеками HTTP/1.0 до всяких возможных библиотек HTTP. Nginx это испытанный промышленный стандарт "de facto", понятно, что большинство библиотек неявно зависят от его поведения. Наряду с некоторым числом несоответствий между поведением Nginx и Envoy, от которых зависят наши пользователи API, в Envoy и его библиотеках было несколько ошибок. Все они были быстро решены нами с помощью сообщества, а наши решения были приняты разработчиками.


Вот лишь несколько из необычных/ не по RFС поведений:


  • Слияние слешей в URL'ах. Слияние и нормализация — распостраненная функция для прокси, Nginx разрешает по умолчанию нормализацию и слияние, а вот Envoy не умел последнее. Мы предоставили патч, который добавляет такую функцию, и позволяет пользователям активизировать слияние с помощью параметра merge_slashes.
  • Порты в именах виртуальных хостов. Nginx может получать значение заголовка Host в обоих вариантах: example.com и example.com:port. У нас была часть пользователей, которые привыкли к такому поведению. Сначала мы работали над этим дублируя виртуальные хосты в нашей конфигурации (с портом и без него), а потом реализовали возможность игнорирования порта на стороне Envoy: strip_matching_host_port.
  • Чувствительность к регистру. Небольшая часть клиентов API по какой-то неизвестной причине использовала заголовок Transfer-Encoding: Chunked (обратите внимание на большую C). Технически это правильно, поскольку RFC7230 утверждает, что заголовки Transfer-Encoding/TE нечувствительны к регистру. Исправление было простейшим и было отправлено разработчикам.
  • Запрос, у которого есть и Content-Length и Transfer-Encoding: chunked. Эти запросы работали на Nginx, но были сломаны при переходе на Envoy. Согласно RFC7230 тут немного сложнее, но общая идея заключается в том, что вебсервера должны выдавать ошибку на такие запросы, поскольку запросы вероятно "нелегальные". С другой стороны следующее же предложение говорит о том, что прокси могут просто удалить заголовок Content-Length и переслать запрос. Мы расширили http-parse, чтобы пользователи библиотеки смогли работать с такими запросами, а сейчас работаем над поддержкой в самом Envoy.

Также наверное стоит упомянуть о некоторых распостраненных проблемах с настройкой, с которыми мы столкнулись:


  • Нерабочий circuit-breaking. По нашему опыту при использовании Envoy в качестве входящего прокси, особенно если смешать HTTP/1 и HTTP/2, неправильная настройка circuit breakers может привести к неожиданным простоям во время скачков трафика или отключении бэкэнда. Стоит отметить, что по умолчанию ограничения на отключение достаточно жесткие, поэтому стоит их ослабить если вы не используете Envoy в качестве mesh прокси.
  • Буферизация. Nginx разрешает буферизацию запросов на диск, что может быть полезным в окружениях, где есть устаревшие бэкэнды HTTP/1.0, которые не понимают передачу chunked. Nginx умеет преобразовывать такие запросы в запросы с Content-Length, сохраняя их на диске. У Envoy есть фильтр Buffer, но, без возможности хранения данных на диске, есть ограничения по оперативной памяти.

Если будете использовать Envoy в качестве пограничного прокси — будет полезно почитать эту статью. Существуют ограничения безопасности и ресурсов, которые хотелось бы иметь в наиболее открытой части вашей инфраструктуры.


Что далее?


  • Грядет HTTP/3. Поддержка уже добавлена в наиболее популярные браузеры. Его экспериментальная поддержка в Envoy уже доступна. После обновления ядра Linux для поддержки ускорения UDP мы будем проводить эксперименты на нашем Edge.
  • Внутренний балансировщик на основе xDS и обнаружение выбросов. В настоящее время мы смотрим комбинацию Load Reporting service (LRS) и Endpoint discovery service (EDS) в качестве строительных блоков для создания общего сбалансированного по нагрузке балансировщика как для Envoy, так и для gRPC.
  • Расширения для Envoy на основе WASM. Когда станет доступным Golang proxy-wasm SDK — мы сможем начать писать расширения Envoy на Go, который даст нам доступ к широкому набору внутренних библиотек Golang.
  • Замена Bandaid. Объединение всех уровней проксирования Dropbox под одним data plane — звучит весьма убедительно. Чтобы это случилось, нам надо перенести множество функций Bandaid (особенно насчет балансировки нагрузки) на Envoy. Долгий путь, но это наш нынешний план.
  • Envoy mobile. Наконец, мы хотим использовать Envoy в наших мобильных приложения. С точки зрения трафика очень важно поддерживать единый стек с общим мониторингом и современными возможностями (HTTP/3, gRPC, TLS 1.3 и т.п.) на всех мобильных платформах.

Благодарности


Переход прошел благодаря командному усилию. Возглавляли команды Traffic и Runtime, но другие сотрудники также внесли большой вклад: Agata Cieplik, Jeffrey Gensler, Konstantin Belyalov, Louis Opter, Naphat Sanguansin, Nikita V. Shirokov, Utsav Shah, Yi-Shu Tai, и конечно же потрясающее сообщество Envoy, которое помогало нам на протяжении всего этого путешествия.


Хотим также выразить признательность техническому руководителю команды Runtime Ruslan Nigmatullin, чьи действия в качестве евангелиста Envoy, автора Envoy MVP, а также основного разработчика позволили этому проекту осуществиться.

Southbridge
Обеспечиваем стабильную работу highload-проектов

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

    +21

    Я тоже перешел на nextcloud

      0

      Телеграм чат Envoy Proxy https://t.me/envoyproxy_ru

        +1

        Dropbox drop Nginx

          –2

          Интересно, что ДБ выбрали новый сервер вместо вложения сил в дописывание старого (nginx), для которого и кодовая база поменьше, и сам он очень знаком.


          Похоже, еще и вся эта история с Рамблером добавила аргументов?

            +7
            Там все аргументы в статье написаны. И про дописывание. Рамблер может и подкинул дополнительные аргументы на чашу, но по мне так мониторинг, который жрет в 2 раза больше, чем основной ворклоад — и так более чем весомый аргумент
              +5
              Так себе аргумент, потому что не надо сбор статистики писать на Lua.
                +2
                Не факт, что написание своего модуля для Nginx на сях будет проще, чем его смена на что-нибудь другое. Да и кто этот код потом поддерживать будет?
                  +3
                  Для веб-студии из 3 человек, конечно, это неразрешимый вопрос. Для компании масштаба Dropbox все перечисленные пункты не являются проблемой.
                    0

                    Написать и отдать в community. Если это хорошая и нужная вещь, то поддержка и миграция на новые версии не станет гигантской проблемой — энтузиасты найдутся.
                    Но, объективно, количество перечисленного в сумме более чем перевешивает и переход выглядит более чем оправданным. В конце концов это не Dropbox на nginx зарабатывает чтобы столько всего в него дописать.

                      +1
                      Уже есть VTS, кто про него знает? например sysadmin.pm/nginx-vhost-traffic-status
                      код сишный
                      github.com/vozlt/nginx-module-vts/tree/master/src

                      Мы собирали пару лет назад и потом есть ещё модуль (nginx-vts-exporter) чтобы prometheus штатно читал. Но оно никогда не появится в основном nginx, потому что идёт в разрез с их платной версией.
                        0

                        Код заброшен, есть ошибки. Посмотрите список Pull requests и Issues.

                    +1
                    Возникает закономерный вопрос, почему же она была написана на lua
                    +4

                    Производительность системы мониторинга — это довольно просто решаемый вопрос.


                    Гораздо важнее то, что envoy дает нам огромное количество средств инструментирования из коробки (статы, точные причины ошибок в access log'ах, трейсинг, и т.д.), а так же, что его гораздо проще интегрировать с существующей инфраструктурой за счет строгого разделения между data и control plane'ами.

                    +2

                    Вполне вероятно, что трудно уже там что-то дописывать, код зарос мхом и грибами. Часто так происходит со старой кодовой базой (тем более на Си). Очень хочется надеятся, что nginx тут исключение, конечно (тем более что альтернативное решение написано на C++, где тоже сложно надежно программировать, а не на, к примеру, дефолтно-секьюрном Расте).

                      +9

                      Если внимательно прочитать аргументы из статьи, то видно, что envoy выбрали в первую очередь за хорошую интегрируемость в "новый код" (который не использует модель обычного демона), и за протобуферы.

                      +1
                      Вебсервера с использованием fork, такие как Nginx, в большинстве окружений имеют проблемы с защитой стека, поскольку основной и рабочие процессы разделяют одно и то же значение для переменной-канарейки в стеке, а поскольку при проверке этой переменной сбойный рабочий процесс убивается — значение этой переменной можно перебрать бит за битом примерно за 1000 попыток.


                      Не уловил — а как атакующий получает доступ к «канарейке»-то?
                        0

                        я в код не смотрел, но предполагаю, что скорее всего канарейка инициализируется до fork

                          +1
                          Все равно не понимаю, как атакующий получает доступ к «канарейке».
                            +3

                            Имеется в виду Smash-Stack Protector (SSP) защита от переполнения стека, которая использует canary (канарейку) в качестве защитного слова между элементами стека.
                            При переполнении стека первым делом перетирается эта канарейка, что используется SSP для обнаружения факта совершения атаки.


                            Проблема в том, что значение канарейки определяется рандомным образом при старте процесса, и при форке у всех детей это значение наследуется без изменения.
                            Атакущий может слать запросы на тот же самый сервер и перебирать брутфорсом значение канарейки, пока рабочий процесс не перестанет падать.
                            Так как при смерти рабочего процесса главный просто форкнется еще раз с тем же значением канарейки — у атакующего есть бесконечное количество попыток.

                              +1
                              Это понятно. Непонятно, почему канарейка «перетирается». Видимо, подразумевается, что в атакуемой программе уже есть уязвимость, которая позволяет это делать?

                              Выходит, что канарейка будет работать какое-то время, а потом, если на крики канарейки (процесс-то падает с уведомлением, скорее всего) не обращать внимание, хакер сможет ее обойти — так?
                                +5

                                Все способы защиты стека предполагают, что в приложении уже есть уязвимости, и они нацелены на недопущении их эксплуатации (обычно путем краха приложения при обнаружении атаки, так как remote code execution считается гораздо опаснее, чем временное падение доступности сервиса).


                                И (пожалуйста поправьте меня, если я не прав) кажется, статья и вы имеете в виду разные канарейки. Канарейка в терминологии SSP — это особая переменная внутри процесса, она не имеет никакого отношения к процедуре деплоймента с помощью canary.

                                  +3
                                  к процедуре деплоймента с помощью canary


                                  Не думаю, что я имею это ввиду :)

                                  Но вопросов больше нет, благодарю за помощь!
                                0
                                и при форке у всех детей это значение наследуется без изменения.

                                А его не изменить при старте форка?
                                  +2

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

                                0

                                Всё относительно просто: меняешь один бит в канарейке, если приложение упало то не угадал, приложение перефокнулось и пытаешься снова. И того на каждый бит у тебя 50/50 шанс — 8 байт так перебрать можно очень быстро.

                                  0

                                  Поправка: меняешь байт (по битово перебирать нельзя ибо перезаписывать можно только по байтово.) Вероятность угадать байт 1/256 и того за (в среднем) 1024 попытки перебирается 8 байт.

                                    0

                                    (комментарий был удалён)

                            0
                            как Envoy поддерживает acept range, передачу видео и конечно самый главный вопрос как обстоят дела с перемоткой видео при отображение в вебе
                              +6
                              Подожди, вроде статья про то КАК они переходили, а не про остальные косвенные темы
                                0
                                ну так они точно это проверяли при переходе, т.к. понимаю их направление именно в мультимедию
                                +2
                                Мне кажется что это вопрос не к Envoy, поскольку сам он вообще не умеет раздавать статику. Это практически чистый прокси. А потому поддержка всего, что вы описали зависит от того сервиса, который стоит за Envoy.
                                +11

                                В кои-то веки получил технооргазм при прочтении статьи на Хабре. Даже покурить захотелось. К сожалению, все реже это получается.

                                  –22
                                  Envoy does not have any blocking IO operations in the event loop. Пилять в цикле обработки событиЯ а не событий
                                    +6

                                    Вообще-то event loop — это именно цикл обработки событий, во множественном числе.

                                      –17
                                      Тогда бы был events loop по аналогии с events queue.
                                        +2

                                        Причём тут вообще сигнатура функции в nginx? Это как бы широко распространенный термин, который на русском языке звучит именно как "цикл [обработки] событий".


                                        Почему на английском слово event пишется в единственном числе — спрашивайте носителей языка.

                                          +12

                                          Потому что синтаксически в этой фразе слово «event» играет роль прилагательного: событийный цикл.

                                      0
                                      Какого такого событиЯ? Если уж с английским плохо…
                                      +19
                                      Не помню, чтоб так подробно описывались сложности перехода на nginx, где без самого Игоря Сысоева переход бы не получился. Пока простой человек быстро не может разобраться в Envoy ни о какой популярности и стандарте de facto не может быть и речи. На любую проблему типа «ограничить полосу пропускания» и прочие, что в nginx задается одной строчкой, в Envoy нужно перелапатить кучу литературы, посетить несколько github веток с обсуждениями, перепробовать кучу примеров из разных обсуждений, понять нужен или не нужен в этой цепочке sidecar контейнер в поде и все-равно вернуться на nginx. Конкретно Dropbox'у это нужно было, сообразили и перешли с привлечением целой командой разработчиков Envoy. Среднестатистический проект столько усилий позволить себе не может. Пока Envoy не упростят для внедрения, не быть ему популярным
                                        +2
                                        В nginx тоже простой человек с разбегу не разберется (и в апаче и тд), все «простые человеки» настраивают его по статьям в интернете (иногда ужасным), методом копипаста конфигов и подставления своих переменных.
                                        Так что вопрос только в появлении этих самых статей.
                                        Ну и для nginx это вполне себе фидбэк что пора бы завести динамические конфигурации и прочее чего Дропбоксу не хватило (возможно часть этого уже давно в проекте).
                                          +7
                                          > Ну и для nginx это вполне себе фидбэк что пора бы завести динамические конфигурации и прочее чего Дропбоксу не хватило (возможно часть этого уже давно в проекте).

                                          Это было понятно много лет назад, поэтому в том числе появился Unit. Наращиваем функциональность постепенно.
                                            –1
                                            NGINX Unit is a lightweight dynamic open-source server for diverse web applications; to install it, see here.

                                            Built from scratch, Unit can run web apps in different language versions; fully configurable in runtime with zero interruption, it enables on-the-fly granular management for engineering and operations.

                                            Эти две строчки писал кто-то не очень хорошо владеющий англ. языком. Попросите кого-нибудь из ваших коллег-носителей переписать.

                                              +1

                                              Интересно узнать про лиценизию и отношению к этому продукту нового собственника nginx. Лицензия внезапно Apache License Version 2.0, January 2004.

                                                +2
                                                Интересно узнать про лиценизию
                                                Никакого большого замысла в этом нет. Проект соверешнно новый, написан практически с нуля. Посидели, подумали под какой лицензией его выкладывать: привычной нам «2-clause BSD-like» или что-то другое. Проконсультировались с юристами, посчитали, что Apache-2.0 будет не хуже и при этом теоретически лучше защитит пользователей, в том числе потому, что оговаривает отказ от патентных претензий.
                                                и отношению к этому продукту нового собственника nginx
                                                К тому, что об этом в своё время уже писал Игорь, мне особо добавить нечего:
                                                mailman.nginx.org/pipermail/nginx-ru/2019-March/061949.html — так оно есть и остается. С другой стороны в рамках крупной корпорации появилось больше возможностей для развития проекта, само собой больше ресурсов.
                                                0

                                                А поддержки Lua в "Supported App Languages" нет по каким-то веским причинам?
                                                Тот же Openresty КМК довольно популярен (в узких кругах :).

                                                  +1
                                                  Никаких веских причин, кроме отсутствия интереса со стороны сообщества веб-разработчиков.

                                                  Если бы Lua WSAPI был популярен, то давно бы уже сделали поддержку, но я ни одного запроса на это не припомню, как и других стандартизованных интерфейсов для веб-приложений на Lua. Да и популярных приложений и фреймворков, использующих Lua WSAPI — как-то не встречал. Тот же Orbit заброшен уже много лет. RestServer — также заброшен. Sputnik — заброшен. А что есть, может я не в курсе?

                                                  Openresty не является WSAPI сервером и поэтому его популярность тут мало релевантна. Это история исключительно про расширение nginx. Тот API, который предлагает Openresty для программирования веб-приложений на Lua — он крайне нишевый и сильно завязан на внутренности nginx, а у Unit от nginx нет практически ничего в плане кода и интерфейсов.

                                                  Openresty популярен не благодаря большой популярности Lua среди веб-программистов, а скорее благодаря тому, что не было другой вменяемой альтернативы расширять функциональность nginx. На тот момент nginx мог предложить только примитивный Perl-модуль, который к тому же не блистал производительностью и не рекомендовался в прод, либо модули на Си с крайне высоким входным порогом и большими трудозатратами на поддержку.

                                                  Unit — это история не про ещё один Node.js или Openresty (почему-то хочется поставить их рядом). Unit в плане поддержки языков программирования — это на данный момент история про запуск уже готовых популярных веб-приложений, существовавших задолго до Unit-а и использующих стандартизованные интерфейсы, общие для многих серверов: Python/WSGI, Perl/PSGI, PHP/SAPI, Ruby/Rack. На этом поле мы конкурируем с uWSGI, Gunicorn, Phusion Passenger, Unicorn, PHP-FPM и подобными.
                                                    0

                                                    Спасибо за развернутый ответ.


                                                    Тот API, который предлагает Openresty для программирования веб-приложений на Lua — он крайне нишевый и сильно завязан на внутренности nginx, а у Unit от nginx нет практически ничего в плане кода и интерфейсов.

                                                    Ясно, я несколько не верно представлял, что такое Unit. Думал, что это тот же Openresty, только "родной" и более правильный и под большее кол-во языков.

                                                  0
                                                  Ваш Unit это классная тема! Коммент из мира Python: а у вас есть поддержка ASGI? На сайте вижу только классический WSGI. Сейчас популярные такие асинхронные фреймворки как AIOHTTP, FastAPI, к ним обещает присоединиться Django3 (при этом, Django.Channels уже давно на ASGI). Технически должно быть не сложно, раз у вас уже есть поддержка Node.js и GoLang.
                                                    0
                                                    На данный момент поддерживается только классический WSGI.

                                                    Мы последние несколько месяцев занимались серьезной переделкой внутреннего механизма распределения запросов по процессам приложения, фактически поменяв концепцию с push на pull в этом месте. Предыдущее решение оказалось неудачным — из-за чего в Go и Node.js наблюдаются проблемы с производительностью. Эта переделка войдет в ближайшую новую версию, которая выйдет уже в этом месяце. Данное изменение как раз позволит в дальнейшем хорошо поддержать асинхронные интерфейсы, а также ввести возможность многопоточной работы. Думаю поддержка ASGI может появиться уже до конца года.
                                              +3

                                              Извините, но перевод очень слабый, даже Google Translate проще читается.


                                              Не только слабый, но ещё и с потерей смысла.


                                              We’ll compare Nginx to Envoy across many software engineering and operational dimensions.

                                              vs.


                                              Мы сравним Nginx и Envoy различными способами.
                                                +3
                                                Намасте всем! Есть мнение что Приянки, Ананды, Прадеепы и Випули из Дропбокса по древней восточной традиции на столько усложнили бэкенд, что nginx такое количество костылей не потянул даже с Lua модулем. Есть Cloudflare, Facebook и много еще чего очень большого и крупного, которые отлично себя чувствуют с nginx. По-больше им шанти, а мы пока продолжим использовать православный Nginx
                                                  +5

                                                  На тему пользователей:


                                                  1. Facebook использует свою L7 проксю на основе Proxygen в течение где-то последних 9 лет, с тех пор как существующие решения (такие как Nginx) перестали удовлетворять их нужды
                                                  2. Cloudflare использует сильно модифицированную версию Nginx в качестве edge сервера, например у них свои патчи для http/2 server prioritization, http/3 и многих других случаев. Этот подход требует отдельную команду разработки для развития и поддержки с призрачными шансами быть когда-либо влитыми в open source версию Nginx (в том числе, по причинами данным в публикации).

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

                                                    +3
                                                    принято
                                                    +3
                                                    По-больше им шанти, а мы пока продолжим использовать православный Nginx

                                                    Как уже написали, крупные простой nginx не используют как раз и либо пилят полностью свое, либо используют современную альтернативу (тот же envoy), либо обкладывают костылями nginx, потому что из коробки он ни на что не годится. Смысл держаться за nginx? Назло соседу отморожу уши? Сейчас на рынке есть пачка современных L7 балансеров, которые отлично интегрируются в современный стек. Нахер этот nginx не нужен в таких условиях с его устаревшим дизайном и отсутствием банально жизненно важных фич.
                                                      0
                                                      У меня вообще сложилось впечатление что переход делался просто ради перехода. Статистику ведь можно было и в Lua обсчитать. Стоило оно того или нет из статьи непонятно. Ресурсы типа памяти и CPU сейчас дешёвые, вместо танцев с бубнами как правило проще докупить железа. Хотя сама статья конечно интересная.
                                                        +2
                                                        Дык пишут что освободилось 60% серверов, т.е. больше половины, это большой прирост.
                                                        Не знаю какой у них там размер ДЦ, но даже если взять одну стойку с 48 серверами по 500 тыс руб каждый то появляется возможность сэкономить 14 млн руб.
                                                          0
                                                          Тут даже не об одном ДЦ речь. Это edge сеть c point of presence по всему миру, которых в статье от 2018 года было 20 штук. Число это только растет, и цепляться тут за технологию, которая плохо справляется с работой, себе дороже.
                                                            +2
                                                            Сейчас у нас порядка 30-ти точек присутствия в мире.
                                                            И да, освободившиеся ресурсы — это приятный бонус, но не цель самого проекта.
                                                              +1
                                                              Сейчас у нас порядка 30-ти точек присутствия.
                                                            +3
                                                            У меня вот сложилось впечатление, что люди статьи не читают. Вся статья посвящена причинам перехода. Железо не проще докупить, когда nginx это банально технический долг, который тащит за собой все остальное, а железа этого сотни и тысячи штук (в статье речь об их edge сети, которая по всему миру раскидана), где уже не имеет значения насколько что дешевое. Если бы переходили ради интереса, то этот переход не наблюдался бы настолько повсеместно. Простой nginx в нынешних условиях это скорее для мелких приложений сервер. Поставил, скопипастил из инета пример конфига и забыл.
                                                            +5

                                                            Самое забавное, что за переход отвечали Олег из Подмосковья и Руслан из Екатеринбурга, а рядом держал свечку Питерский наркоман Алексей =)

                                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                          Самое читаемое