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:
- Configurando o servidor NGINX, a estrutura de log e a funcionalidade Gzip. Isso é determinado globalmente em todos os casos.
- Configurando o NGINX para aceitar solicitações para o host one.example.com na porta 8080.
- 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
- Ouvindo cada ouvinte
- Aceitar novas conexões
- Criando um conjunto de filtros para uma conexão
- 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 }
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