Olá pessoal!
Hoje, queremos falar sobre como a equipe do serviço de reservas de hotéis
Ostrovok.ru resolveu o problema do crescimento de microsserviços, cuja tarefa é trocar informações com nossos fornecedores. Sobre sua experiência diz
imortal , DevOps Team Lead em Ostrovok.ru.
No início, o microsserviço era pequeno e executava as seguintes funções:
- aceitar uma solicitação de um serviço local;
- faça uma solicitação a um parceiro;
- normalizar a resposta;
- retorne o resultado ao serviço de consulta.
No entanto, com o tempo, o serviço cresceu junto com o número de parceiros e solicitações a eles.
À medida que o serviço cresceu, vários tipos de problemas começaram a surgir. Diferentes fornecedores apresentam suas próprias regras: alguém limita o número máximo de conexões, alguém restringe os clientes às listas brancas.
Como resultado, tivemos que resolver os seguintes problemas:
- é desejável ter vários endereços IP externos fixos para que você possa fornecê-los aos parceiros para adicioná-los às listas brancas,
- ter um único pool de conexões com todos os fornecedores para que, ao escalar nosso microsserviço, o número de conexões permaneça mínimo,
- encerre o SSL e mantenha a atividade
keepalive
em um só lugar, reduzindo assim a carga nos próprios parceiros.
Eles não pensaram por um longo tempo e imediatamente se perguntaram o que escolher: Nginx ou Haproxy.
No começo, o pêndulo girava em direção ao Nginx, pois resolvi a maioria dos problemas associados ao HTTP / HTTPS com sua ajuda e sempre fiquei satisfeito com o resultado.
O esquema era simples: uma solicitação foi feita ao nosso novo servidor proxy no Nginx com um domínio no formato
<partner_tag>.domain.local
, no Nginx havia um
map
que
<partner_tag>
correspondia ao endereço do parceiro. Um endereço foi retirado do
map
e
proxy_pass
foi feito para esse endereço.
Aqui está um exemplo de
map
com o qual analisamos o domínio e selecionamos o montante da lista:
E aqui está
snippet.d/upstreams_map
aparência de "
snippet.d/upstreams_map
":
“one” “http://one.domain.net”; “two” “https://two.domain.org”;
Aqui nós temos o
server{}
:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_address$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
Tudo é legal, tudo funciona. É possível terminar este artigo, se não for por uma nuance.
Ao usar proxy_pass, a solicitação vai diretamente para o endereço desejado, como regra, usando o protocolo HTTP / 1.0 sem
keepalive
e fecha imediatamente após a resposta ser concluída. Mesmo se
proxy_http_version 1.1
, nada será alterado sem o upstream (
proxy_http_version ).
O que fazer O primeiro pensamento é colocar todos os fornecedores no upstream, onde o servidor será o endereço do fornecedor de que precisamos e, no
map
mantenha
"tag" "upstream_name"
.
Adicione outro
map
para analisar o esquema:
E crie
upstreams
com nomes de tags:
upstream one { keepalive 64; server one.domain.com; } upstream two { keepalive 64; server two.domain.net; }
O servidor em si é ligeiramente modificado para levar em conta o esquema e usar o nome do upstream em vez do endereço:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_scheme$upstream_prefix$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
Ótimo. A solução funciona, adicione a diretiva
keepalive
em cada montante, defina
proxy_http_version 1.1
, - agora temos um pool de conexões e tudo funciona como deveria.
Desta vez, você pode definitivamente terminar o artigo e tomar um chá. Ou não?
De fato, enquanto tomamos chá, um dos fornecedores pode alterar o endereço IP ou o grupo de endereços no mesmo domínio (oi, Amazon), assim um dos fornecedores pode cair no auge da festa do chá.
Bem, o que fazer? O Nginx tem uma nuance interessante: durante a recarga, ele pode aumentar os servidores internos do
upstream
para novos endereços e colocar tráfego neles. Em geral, também uma solução. Jogue no
cron reload nginx
cada 5 minutos e continue tomando chá.
Mas ainda assim me pareceu uma decisão mais ou menos, então comecei a olhar desconfiado para Haproxy.
O Haproxy tem a capacidade de especificar
dns resolvers
e configurar o
dns cache
. Portanto, o Haproxy atualizará o
dns cache
do
dns cache
se as entradas nele expirarem e substituirá os endereços do upstream se eles tiverem sido alterados.
Ótimo! Agora cabe às configurações.
Aqui está um pequeno exemplo de configuração para o Haproxy:
frontend http bind *:80 http-request del-header X-Forwarded-For http-request del-header X-Forwarded-Port http-request del-header X-Forwarded-Proto capture request header Host len 32 capture request header Referer len 128 capture request header User-Agent len 128 acl host_present hdr(host) -m len gt 0 use_backend %[req.hdr(host),lower,field(1,'.')] if host_present default_backend default resolvers dns hold valid 1s timeout retry 100ms nameserver dns1 1.1.1.1:53 backend one http-request set-header Host one.domain.com server one--one.domain.com one.domain.com:80 resolvers dns check backend two http-request set-header Host two.domain.net server two--two.domain.net two.domain.net:443 resolvers dns check ssl verify none check-sni two.domain.net sni str(two.domain.net)
Tudo parece funcionar como deveria desta vez. A única coisa que eu não gosto no Haproxy é a complexidade da descrição da configuração. Você precisa criar muito texto para adicionar um trabalho a montante. Mas a preguiça é o motor do progresso: se você não quiser escrever a mesma coisa, escreva um gerador.
Eu já tinha um mapa do Nginx com o formato
"tag" "upstream"
, então decidi tomá-lo como base, analisar e gerar um back-end haproxy com base nesses valores.
Agora tudo o que precisamos é adicionar um novo host no nginx_map, iniciar o gerador e obter a configuração haproxy pronta.
Provavelmente é tudo por hoje. Este artigo se refere mais ao introdutório e foi dedicado ao problema de escolher uma solução e sua integração no ambiente atual.
No próximo artigo, mostrarei mais sobre as armadilhas que encontramos ao usar o Haproxy, quais métricas se mostraram úteis para monitorar e o que exatamente deve ser otimizado no sistema para obter o máximo desempenho dos servidores.
Obrigado a todos pela atenção, até mais!