19 pontos por xguru 2020-08-10 | 3 comentários | Compartilhar no WhatsApp

Um texto que explica muito bem as vantagens do Envoy em relação ao Nginx na Dropbox, que lida com dezenas de milhões de conexões simultâneas, milhões de requisições por segundo e largura de banda na casa dos terabytes

Antes: nginx (versão open source) + python2 + Jinja2 + YAML

→ mesmo que só uma coisa mudasse, era preciso redeploy de tudo

→ as partes dinâmicas eram desenvolvidas em Lua

→ a lógica complexa era tratada no Bandaid, um proxy em Go

Isso funcionou bem por quase 10 anos, mas já não se encaixa bem no ambiente atual

→ as APIs internas e externas (privadas) estão migrando gradualmente de REST para gRPC, então precisam de recursos de transcoding no proxy

→ Protocol Buffers virou o padrão interno de definição de serviços

→ todos os softwares são compilados e testados com Bazel, independentemente da linguagem

→ os funcionários participam bastante das comunidades open source dos principais projetos de infraestrutura

O Nginx também era caro de manter do ponto de vista operacional

→ a lógica de geração de config era flexível demais e estava espalhada entre YAML, Jinja2 e Python

→ o monitoramento era uma mistura de Lua / parsing de logs / monitoramento baseado em sistema

→ a dependência crescente de módulos de terceiros afetava estabilidade/desempenho e gerava custo com upgrades frequentes

→ o deploy e o gerenciamento de processos do nginx são bem diferentes dos demais serviços. Dependem bastante de coisas fora do sistema padrão, como syslog e logrotate

Por isso, pela primeira vez em 10 anos, decidiram procurar um substituto para o Nginx

  • Por que não migrar para o Bandaid (proxy em Go desenvolvido pela Dropbox)? *

→ Go consome mais recursos do que C/C++.

→ a stack TLS do Go não suporta FIPS (Federal Information Processing Standards dos EUA)

→ por ser uma ferramenta interna, não é possível contar com suporte da comunidade externa

Agora: migração para uma infraestrutura de tráfego baseada em Envoy

----- Pontos em que o Envoy foi melhor que o Nginx ------

  • Desempenho *

A arquitetura do Nginx é event-driven / multiprocesso. Suporta SO_REUSEPORT & EPOLLEXCLUSIVE

Embora seja baseada em event loop, não é totalmente non-blocking. Ao abrir arquivos ou gravar logs, o event loop pode parar. (mesmo com aio, aio_write e threadpool ativados)

Isso gerava latência de cauda e, às vezes, atrasos de vários segundos

O Envoy também tem arquitetura event-driven, mas baseada em threads em vez de processos

Suporta SO_REUSEPORT (com suporte a filtro BPF) e event loop via libevent

Não há I/O bloqueante no event loop. O logging de eventos também é implementado de forma non-blocking.

Na teoria, parecia que os dois teriam características de desempenho parecidas, e na prática isso se confirmou na maioria dos testes de workload.

Mas o Nginx mostrou latência maior na long tail. O motivo era a interrupção do event loop quando havia muito I/O.

Sem coleta de estatísticas, o Nginx tinha desempenho parecido com o Envoy, mas a ferramenta interna de coleta de métricas em Lua deixava o Nginx 3x mais lento em testes de alto RPS. (isso por causa do lua_shared_dict, sincronizado por mutex). Havia problemas na forma como a Dropbox coletava estatísticas, mas eles desistiram de reimplementar isso de forma eficiente. (porque preveram que instrumentar o interior do Nginx dificultaria upgrades futuros)

De todo modo, como o Envoy não tinha esses problemas, depois da migração foi possível liberar até 60% dos servidores que antes eram usados pelo Nginx.

  • Observabilidade *

A versão gratuita do Nginx oferece apenas 7 estatísticas no módulo stub status

Como isso obviamente era insuficiente, eles usavam um handler log_by_lua para expor mais métricas.

Também havia um parser de error.log para exportar erros e um exporter separado para expor estados internos do nginx.

Uma configuração básica do Envoy já fornece milhares de métricas diferentes em formato Prometheus

Desde informações de tráfego do proxy até estado interno do servidor,

estatísticas por cluster / upstream / virtual host e métricas downstream TCP/HTTP/TLS por listener etc.

Junto com essas várias estatísticas, o Envoy também permite plugar Tracing Providers.

Isso é útil não só para o time de tráfego, mas também para desenvolvedores de aplicações.

Por fim, o Envoy consegue fazer streaming de access logs via gRPC.

Isso reduz a carga de manter a bridge syslog-to-hive do time de tráfego.

Rodar um serviço gRPC comum é bem mais fácil e seguro do que acoplar listeners TCP/UDP customizados.

  • Integração *

A integração do Nginx é muito “Unix-like”. A configuração é bem estática.

Depende de arquivos para config, certificados TLS, allowlist/blocklist etc.

Isso é simples e compatível com versões anteriores, então dá para automatizar com alguns scripts shell,

mas, conforme o sistema cresce, testabilidade e padronização vão ficando cada vez mais importantes.

O Envoy tem sua própria abordagem para esse tipo de integração.

Ele oferece uma API chamada xDS, incentivando o uso de protobuf e gRPC.

O Envoy encontra recursos dinâmicos consultando esse xDS.

  • O xDS já está evoluindo para além do Envoy como a Universal Data Place API (UDPA), tentando virar o padrão de facto para load balancers L4/L7, e, pela experiência deles, isso está caminhando bem. Eles também estão tentando usar UDPA no Katran, o load balancer L4 eBPF/XDP, não só no Envoy.

Como a Dropbox já integra seus serviços internamente via gRPC, isso se encaixa muito melhor.

  • Configuração *

O Nginx tem como grande vantagem arquivos de configuração fáceis para humanos lerem.

Mas essa vantagem vai se perdendo à medida que a configuração fica mais complexa e passa a ser gerada automaticamente.

Na Dropbox, como era gerada por Python2, Jinja2 e YAML, o modelo de dados acabou ficando confuso e complexo.

O Envoy tem um modelo de dados unificado para configuração. Todos os valores são definidos em Protocol Buffers. Isso resolve problemas de modelagem de dados e adiciona informação de tipo às configs.

Como protobuf já é muito usado internamente na Dropbox, a integração fica fácil

  • Extensibilidade *

Para expandir o Nginx, é preciso escrever módulos em C. Para criar módulos seguros, são necessários desenvolvedores sêniores. Ele até oferece interfaces em Perl / JS para módulos mais leves, mas elas são muito limitadas. Por isso, o caminho mais comum acaba sendo via lua-nginx-module.

O principal mecanismo de extensão do Envoy é por plugins em C++, e embora a documentação não seja tão boa quanto a do nginx, isso é bem simples. Isso se deve a interfaces limpas e bem comentadas, além de C++14 e da biblioteca padrão

O grande diferencial do Envoy em relação a outros web servers é o suporte a WebAssembly (WASM).

Isso permite desenvolver extensões em várias linguagens, como Rust.

A Dropbox ainda não usa WASM, mas isso pode mudar no futuro se houver suporte ao Go SDK para proxy-wasm

  • Build e testes *

O Nginx usa por padrão configuração customizada baseada em shell e build baseado em make. É simples e ótimo, mas integrar isso a um monorepo compilado com Bazel exige bastante esforço

O Nginx tem testes de integração em Perl, mas não tem testes unitários.

O Envoy já usa um sistema de build baseado em Bazel e foi integrado facilmente ao monorepo deles.

Também oferece testes unitários com gtest/gmock e framework de testes de integração

  • Segurança *

O código do Nginx é bem pequeno e tem poucas dependências externas, então não apresenta muitas vulnerabilidades de segurança.

O Envoy tem muito mais código, então naturalmente parece ter mais superfície de ataque. Para compensar isso, o Envoy depende fortemente de práticas modernas de segurança, usando AddressSanitizer, ThreadSanitizer, MemorySanitizer etc.

  • Recursos *

Essa parte tem bastante opinião subjetiva, então vale considerar isso ao ler

O Nginx começou como um web server para servir arquivos estáticos com pouquíssimos recursos.

Ou seja, suas funções principais são static serving, caching e range caching

Do ponto de vista de proxy, o Nginx carece bastante de recursos que a infraestrutura atual exige.

Não faz conexão HTTP/2 com backends, não funciona como proxy gRPC multiplexado, não faz gRPC transcoding etc.

Como usa um modelo de licença open core, alguns recursos importantes ficam fora da “versão comunitária”

O Envoy, por outro lado, já nasceu como proxy de ingress/egress e é muito usado em ambientes com alta carga de gRPC.

Os recursos de web service ainda são bem básicos. Ele não serve arquivos, o cache ainda está em desenvolvimento e ainda não suporta brotli etc.

Para esse tipo de ambiente, eles ainda usam setups com Nginx tendo o Envoy como upstream cluster

A expectativa é que, quando o Envoy passar a suportar cache HTTP, esses ambientes de serving estático também possam migrar

O Envoy oferece muitos recursos ligados a gRPC

  • proxying de gRPC

  • HTTP/2 para backends

  • bridge gRPC → HTTP (+ reverso)

  • gRPC-WEB

  • transcoder JSON para gRPC

Além disso, o Envoy também pode ser usado como proxy de saída

  • Egress Proxy

  • service discovery de software de terceiros com a biblioteca Courier gRPC

  • Comunidade *

O desenvolvimento do Nginx é centralizado e, em grande parte, fechado.

O desenvolvimento do Envoy é aberto e descentralizado. Acontece via issues/PRs no GitHub, além de bastante atividade em mailing lists e Slack

----- Estado atual da migração na Dropbox -----

Eles estão operando Nginx e Envoy juntos há meio ano e migrando o tráfego gradualmente via DNS

A migração não foi totalmente sem problemas: houve pequenos incidentes, mas nenhuma falha grave.

Também organizaram soluções para problemas causados por comportamentos “unusual” ou “non-RFC” (veja o texto original para os detalhes)

** Próximos passos **

  • HTTP/3: o Envoy também começou a oferecer suporte experimental. Eles pretendem testar isso após atualizar o kernel Linux para acelerar UDP

  • load balancer interno baseado em xDS e Outlier Detection

  • extensões Envoy baseadas em WASM

  • substituir o Bandaid (proxy em Go) por Envoy

  • aplicar Envoy também em apps mobile com Envoy Mobile

3 comentários

 
baeba 2020-08-13

Obrigado por resumir um conteúdo tão bom

de forma simples.

Agradeço.

 
before30 2020-08-11

Obrigado. :)

 
loslch 2020-08-11

A organização detalhada e até os comentários gentis ajudaram muito na compreensão. Obrigado :)