Migrando do Nginx para o Envoy Proxy

Olá Habr! Trago a sua atenção a tradução do post: Migração do Nginx para o Envoy Proxy .


O Envoy é um servidor proxy distribuído de alto desempenho (escrito em C ++) projetado para serviços e aplicativos individuais; também é um barramento de comunicação e um "plano de dados universal" projetado para grandes arquiteturas de "service mesh" de microsserviço. Quando ele foi criado, as soluções para problemas surgidos durante o desenvolvimento de servidores como NGINX, HAProxy, balanceadores de carga de hardware e balanceadores de nuvem foram levadas em consideração. O Enviado trabalha com todos os aplicativos e abstrai a rede, fornecendo funções comuns, independentemente da plataforma. Quando todo o tráfego do escritório na infraestrutura passa pela grade do Envoy, fica fácil visualizar as áreas problemáticas com observabilidade consistente, ajustando o desempenho geral e adicionando funções básicas em um local específico.


As possibilidades


  • Arquitetura fora de processo: o enviado é um servidor autônomo de alto desempenho que consome uma pequena quantidade de RAM. Funciona em conjunto com qualquer linguagem ou estrutura de aplicativo.
  • Suporte para http / 2 e grpc: o enviado possui suporte de primeira classe para http / 2 e grpc para conexões de entrada e saída. Este é um proxy transparente de http / 1.1 para http / 2.
  • Balanceamento de carga aprimorado: o enviado suporta recursos avançados de balanceamento de carga, incluindo novas tentativas automáticas, circuito aberto, limite de velocidade global, solicitações de sombreamento, balanceamento de carga na zona local, etc.
  • API de gerenciamento de configuração: o enviado fornece uma API robusta para gerenciar dinamicamente sua configuração.
  • Observabilidade: observabilidade profunda do tráfego L7, suporte interno para rastreamento distribuído e observabilidade do mongodb, dynamodb e muitas outras aplicações.

Etapa 1 - Exemplo de configuração do NGINX


Este script usa um arquivo nginx.conf especialmente criado, com base em um exemplo completo do Wiki do NGINX . Você pode visualizar a configuração no editor abrindo nginx.conf


Configuração nginx de origem


user www www; pid /var/run/nginx.pid; worker_processes 2; events { worker_connections 2000; } http { gzip on; gzip_min_length 1100; gzip_buffers 4 8k; gzip_types text/plain; log_format main '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$gzip_ratio"'; log_format download '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$http_range" "$sent_http_content_range"'; upstream targetCluster { 172.18.0.3:80; 172.18.0.4:80; } server { listen 8080; server_name one.example.com www.one.example.com; access_log /var/log/nginx.access_log main; error_log /var/log/nginx.error_log info; location / { proxy_pass http://targetCluster/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } } 

As configurações do NGINX geralmente têm três elementos principais:


  1. Configurando o servidor NGINX, a estrutura de log e a funcionalidade Gzip. Isso é determinado globalmente em todos os casos.
  2. Configurando o NGINX para aceitar solicitações para o host one.example.com na porta 8080.
  3. Definindo o local de destino, como lidar com o tráfego para diferentes partes do URL.

Nem toda a configuração será aplicada ao Envoy Proxy, e você não precisa definir algumas configurações. O Envoy Proxy possui quatro tipos principais que suportam a infraestrutura subjacente oferecida pelo NGINX. O núcleo é:


  • Ouvintes: Eles determinam como o Envoy Proxy aceita solicitações de entrada. Atualmente, o Envoy Proxy suporta apenas ouvintes baseados em TCP. Depois que a conexão é estabelecida, ela é transferida para um conjunto de filtros para processamento.
  • Filtros: fazem parte de uma arquitetura em pipeline que pode processar dados de entrada e saída. Essa funcionalidade inclui filtros, como o Gzip, que comprime os dados antes de enviá-los ao cliente.
  • Roteadores: eles redirecionam o tráfego para o destino desejado, definido como um cluster.
  • Clusters: Eles definem o terminal para o tráfego e as definições de configuração.

Usaremos esses quatro componentes para criar a configuração do Proxy Envoy para corresponder à configuração específica do NGINX. O objetivo do Enviado é trabalhar com a API e a configuração dinâmica. Nesse caso, a configuração básica usará parâmetros estáticos e codificados no NGINX.


Etapa 2 - Configurar o NGINX


A primeira parte do nginx.conf define alguns dos componentes internos do NGINX que precisam ser configurados.


Conexões do trabalhador


A configuração abaixo determina o número de processos e conexões de trabalho. Isso indica como o NGINX será dimensionado para atender à demanda.


 worker_processes 2; events { worker_connections 2000; } 

O Envoy Proxy gerencia fluxos de trabalho e conexões de maneira diferente.


O enviado cria um fluxo de trabalho para cada thread de hardware no sistema. Cada encadeamento de trabalhador executa um loop de eventos sem bloqueio, responsável por


  1. Ouvindo cada ouvinte
  2. Aceitar novas conexões
  3. Criando um conjunto de filtros para uma conexão
  4. Manipulando todas as operações de E / S durante a vida útil de uma conexão.

Todo o processamento de conexão adicional é totalmente processado no fluxo de trabalho, incluindo qualquer comportamento de encaminhamento.


Para cada fluxo de trabalho no Envoy, há uma conexão no pool. Portanto, os conjuntos de conexões HTTP / 2 estabelecem apenas uma conexão para cada host externo por vez; se houver quatro threads de trabalho, haverá quatro conexões HTTP / 2 para cada host externo em um estado estável. Ao armazenar tudo em um fluxo de trabalho, quase todo o código pode ser gravado sem o bloqueio, como se fosse de thread único. Se mais de fluxos de trabalho necessários forem alocados, isso poderá levar ao uso não racional da memória, à criação de um grande número de conexões inativas e a uma diminuição no número de conexões retornadas ao pool.


Para mais informações, visite o blog Envoy Proxy .


Configuração HTTP


O seguinte bloco de configuração do NGINX define configurações HTTP, como:


  • Quais tipos MIME são suportados
  • Tempos limite padrão
  • Configuração Gzip

Você pode configurar esses aspectos usando filtros no Envoy Proxy, que discutiremos mais adiante.


Etapa 3 - Configuração do servidor


No bloco de configuração HTTP, a configuração do NGINX instrui você a ouvir na porta 8080 e responder a solicitações de entrada para os domínios one.example.com e www.one.example.com .


  server { listen 8080; server_name one.example.com www.one.example.com; 

Dentro do Enviado, os Ouvintes controlam.


Ouvintes enviados


O aspecto mais importante da introdução ao Envoy Proxy é identificar ouvintes. Você precisa criar um arquivo de configuração que descreva como deseja executar uma instância do Envoy.


O trecho abaixo criará um novo ouvinte e o associará à porta 8080. A configuração informa ao Proxy Envoy a quais portas ele deve estar vinculado para solicitações de entrada.


O Envoy Proxy usa a notação YAML para sua configuração. Para se familiarizar com essa notação, consulte o link aqui.


 Copy to Editorstatic_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 8080 } 

Não há necessidade de definir server_name , pois os filtros Envoy Proxy podem lidar com isso.


Etapa 4 - Configuração da Localização


Quando uma solicitação chega ao NGINX, o bloco de localização determina como processar e para onde direcionar o tráfego. No fragmento a seguir, todo o tráfego para o site é transmitido para um cluster upstream (nota do tradutor: upstream geralmente é um servidor de aplicativos) chamado targetCluster . O cluster upstream define os nós que devem processar a solicitação. Discutiremos isso na próxima etapa.


 location / { proxy_pass http://targetCluster/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } 

Na Envoy, a Filters faz isso.


Filtros enviados


Para uma configuração estática, os filtros determinam como lidar com solicitações recebidas. Nesse caso, definimos filtros que correspondem a server_names na etapa anterior. Quando chegam solicitações que correspondem a domínios e rotas específicos, o tráfego é roteado para o cluster. Isso é equivalente à configuração upstream do NGINX.


 Copy to Editor filter_chains: - filters: - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "one.example.com" - "www.one.example.com" routes: - match: prefix: "/" route: cluster: targetCluster http_filters: - name: envoy.router 

O nome envoy.http_connection_manager é um filtro interno no Envoy Proxy. Outros filtros incluem Redis , Mongo , TCP . Você pode encontrar a lista completa na documentação .


Para obter mais informações sobre outras políticas de balanceamento de carga, visite a documentação do Enviado .


Etapa 5 - Proxy e configuração upstream


No NGINX, a configuração upstream define o conjunto de servidores de destino que manipularão o tráfego. Nesse caso, dois clusters foram atribuídos.


  upstream targetCluster { 172.18.0.3:80; 172.18.0.4:80; } 

No Envoy, ele é gerenciado por cluster.


Clusters enviados


O equivalente a montante é definido como clusters. Nesse caso, os hosts que atenderão ao tráfego foram identificados. Um método para acessar hosts, como tempos limite, é definido como uma configuração de cluster. Isso permite que você controle com mais precisão a granularidade de aspectos como latência e balanceamento de carga.


 Copy to Editor clusters: - name: targetCluster connect_timeout: 0.25s type: STRICT_DNS dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN hosts: [ { socket_address: { address: 172.18.0.3, port_value: 80 }}, { socket_address: { address: 172.18.0.4, port_value: 80 }} ] 

Ao usar a descoberta de serviço STRICT_DNS, o Envoy resolverá contínua e assincronamente os destinos DNS especificados. Cada endereço IP retornado como resultado do DNS será considerado um host explícito no cluster upstream. Isso significa que, se a solicitação retornar dois endereços IP, o Envoy assumirá que existem dois hosts no cluster e ambos deverão ter balanceamento de carga. Se o host for removido do resultado, o Envoy assumirá que ele não existe mais e selecionará o tráfego de qualquer pool de conexões existente.


Para obter mais informações, consulte a documentação do Proxy Envoy .


Etapa 6 - Log de acesso e erros


A configuração final é registro. Em vez de transferir logs de erro para o disco, o Envoy Proxy usa uma abordagem baseada na nuvem. Todos os logs do aplicativo são exibidos em stdout e stderr .


Quando os usuários fazem uma solicitação, os logs de acesso são opcionais e são desativados por padrão. Para habilitar logs de acesso para solicitações HTTP, ative a configuração access_log para o HTTP Connection Manager. O caminho pode ser um dispositivo, como stdout , ou um arquivo em disco, dependendo dos seus requisitos.


A configuração a seguir redirecionará todos os logs de acesso para o stdout (nota do tradutor - o stdout é necessário para usar o enviado dentro da janela de encaixe. Se você usar sem a janela de encaixe, substitua / dev / stdout pelo caminho para o arquivo de log normal). Copie o trecho para a seção de configuração do gerenciador de conexões:


 Copy to Clipboardaccess_log: - name: envoy.file_access_log config: path: "/dev/stdout" 

Os resultados devem ficar assim:


  - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http access_log: - name: envoy.file_access_log config: path: "/dev/stdout" route_config: 

Por padrão, o Envoy possui uma sequência de formatos que inclui os detalhes da solicitação HTTP:


 [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n 

O resultado dessa sequência de formato:


 [2018-11-23T04:51:00.281Z] "GET / HTTP/1.1" 200 - 0 58 4 1 "-" "curl/7.47.0" "f21ebd42-6770-4aa5-88d4-e56118165a7d" "one.example.com" "172.18.0.4:80" 

O conteúdo da saída pode ser personalizado, definindo o campo de formato. Por exemplo:


 access_log: - name: envoy.file_access_log config: path: "/dev/stdout" format: "[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n" 

A cadeia de log também pode ser impressa no formato JSON, configurando o campo json_format . Por exemplo:


 access_log: - name: envoy.file_access_log config: path: "/dev/stdout" json_format: {"protocol": "%PROTOCOL%", "duration": "%DURATION%", "request_method": "%REQ(:METHOD)%"} 

Para mais informações sobre técnicas de registro de enviado, visite


https://www.envoyproxy.io/docs/envoy/latest/configuration/access_log#config-access-log-format-dictionaries


O registro não é a única maneira de ter uma idéia de trabalhar com o Envoy Proxy. Possui recursos avançados integrados para rastreamento e métricas. Você pode descobrir mais na documentação de rastreamento ou através do Script de rastreamento interativo .


Etapa 7 - Iniciar


Agora você transferiu a configuração do NGINX para o Envoy Proxy. A etapa final é executar uma instância do Envoy Proxy para testá-lo.


Executar do usuário


Na parte superior da configuração do NGINX, o usuário da linha www www; indica que o NGINX foi iniciado como um usuário com baixos privilégios para aprimorar a segurança.


O Envoy Proxy adota uma abordagem baseada em nuvem para gerenciar quem é o proprietário do processo. Quando executamos o Envoy Proxy através do contêiner, podemos especificar um usuário com um baixo nível de privilégio.


Iniciar o Proxy Envoy


O comando abaixo iniciará o Envoy Proxy através do contêiner do Docker no host. Este comando fornece ao Envoy a capacidade de escutar solicitações de entrada pela porta 80. No entanto, conforme indicado na configuração do ouvinte, o Proxy Envoy escuta o tráfego de entrada pela porta 8080. Isso permite que o processo seja executado como um usuário com privilégios baixos.


 docker run --name proxy1 -p 80:8080 --user 1000:1000 -v /root/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy 

Teste


Com os proxies em execução, agora os testes podem ser feitos e processados. O comando cURL a seguir emite uma solicitação com o cabeçalho do host definido na configuração do proxy.


 curl -H "Host: one.example.com" localhost -i 

Uma solicitação HTTP resultará no erro 503 . Isso se deve ao fato de as conexões upstream não funcionarem e não estarem disponíveis. Portanto, o Envoy Proxy não possui destinos de destino disponíveis para a solicitação. O comando a seguir iniciará uma série de serviços HTTP que correspondem à configuração definida para o Envoy.


 docker run -d katacoda/docker-http-server; docker run -d katacoda/docker-http-server; 

Com os serviços disponíveis, o Envoy pode proxy com êxito o tráfego para o seu destino.


 curl -H "Host: one.example.com" localhost -i 

Você deverá ver uma resposta indicando qual contêiner do Docker processou a solicitação. Nos logs do Proxy Envoy, você também deve ver a sequência de acesso exibida.


Cabeçalhos de resposta HTTP adicionais


Você verá cabeçalhos HTTP adicionais nos cabeçalhos de resposta da solicitação real. O cabeçalho exibe o tempo que o host upstream passou processando a solicitação. É expresso em milissegundos. Isso é útil se o cliente deseja determinar o tempo de serviço comparado à latência da rede.


 x-envoy-upstream-service-time: 0 server: envoy 

Configuração final


 static_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 8080 } filter_chains: - filters: - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "one.example.com" - "www.one.example.com" routes: - match: prefix: "/" route: cluster: targetCluster http_filters: - name: envoy.router clusters: - name: targetCluster connect_timeout: 0.25s type: STRICT_DNS dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN hosts: [ { socket_address: { address: 172.18.0.3, port_value: 80 }}, { socket_address: { address: 172.18.0.4, port_value: 80 }} ] admin: access_log_path: /tmp/admin_access.log address: socket_address: { address: 0.0.0.0, port_value: 9090 } 

Informações adicionais do tradutor


As instruções de instalação do Envoy Proxy podem ser encontradas em https://www.getenvoy.io/


Por padrão, no rpm, não há nenhuma configuração do serviço systemd.


Adicione o serviço systemd config /etc/systemd/system/envoy.service:


 [Unit] Description=Envoy Proxy Documentation=https://www.envoyproxy.io/ After=network-online.target Requires=envoy-auth-server.service Wants=nginx.service [Service] User=root Restart=on-failure ExecStart=/usr/bin/envoy --config-path /etc/envoy/config.yaml [Install] WantedBy=multi-user.target 

Você precisa criar o diretório / etc / envoy / e colocar o config.yaml config lá.


Por procurador enviado, há um bate-papo por telegrama: https://t.me/envoyproxy_ru


O Proxy Envoy não suporta distribuição de conteúdo estático. Então, quem pode votar no recurso: https://github.com/envoyproxy/envoy/issues/378

Source: https://habr.com/ru/post/pt469455/


All Articles