Disjuntor Istio: desabilitando contêineres defeituosos

As férias acabaram e estamos de volta com nossa segunda publicação na série sobre Istio Service Mesh.

Disjuntor Istio: desabilitando contêineres defeituosos

O tema de hoje é Disjuntor, que em russo significa "interruptor automático" ou, coloquialmente, "disjuntor de circuito". No Istio, porém, esse disjuntor desliga contêineres defeituosos, e não circuitos em curto-circuito ou sobrecarga.

Como deveria funcionar idealmente

Quando os microsserviços são gerenciados pelo Kubernetes, por exemplo, na plataforma OpenShift, eles escalam automaticamente para cima e para baixo, dependendo da carga. Como os microsserviços são executados em pods, várias instâncias de um microsserviço conteinerizado podem estar em execução em um único endpoint, e o Kubernetes roteará as solicitações e balanceará a carga entre elas. E, idealmente, tudo isso deveria funcionar perfeitamente.

Lembremos que os microsserviços são pequenos e efêmeros. A efemeridade, que aqui se refere à facilidade de surgimento e desaparecimento, é frequentemente subestimada. O nascimento e a morte de mais uma instância de microsserviço em um pod são totalmente esperados; o OpenShift e o Kubernetes lidam bem com isso, e tudo funciona maravilhosamente bem — mas, novamente, isso é apenas na teoria.

Como funciona na prática

Agora imagine que uma instância de microsserviço específica, ou um contêiner, se tornou inutilizável: ou não está respondendo (erro 503) ou, pior, está respondendo, mas muito lentamente. Em outras palavras, está apresentando falhas ou não responde, mas não é removido automaticamente do pool. O que você deve fazer nesse caso? Tentar novamente? Removê-lo do roteamento? E o que significa "muito lento"? O que isso representa e quem determina isso? Talvez você deva simplesmente aguardar um pouco e tentar novamente mais tarde? Se sim, quanto tempo depois?

O que é a ejeção de pool no Istio?

É aí que o Istio entra em ação com seus mecanismos de proteção Circuit Breaker, que removem temporariamente os contêineres com defeito do pool de recursos de roteamento e balanceamento de carga, implementando o procedimento de Ejeção do Pool.

Utilizando uma estratégia de detecção de outliers, o Istio detecta pods que estão dessincronizados e os remove do pool de recursos por um período de tempo especificado, chamado de janela de repouso.

Para mostrar como isso funciona no Kubernetes na plataforma OpenShift, vamos começar com uma captura de tela de microsserviços em execução normal do repositório de exemplo. Demonstrações para desenvolvedores da Red HatAqui temos dois pods, v1 e v2, cada um executando um contêiner. Quando as regras de roteamento do Istio não são usadas, o Kubernetes usa por padrão o roteamento round-robin balanceado:

Disjuntor Istio: desabilitando contêineres defeituosos

Preparando-se para o fracasso

Antes de executar a ejeção do pool, precisamos criar uma regra de roteamento do Istio. Digamos que queremos distribuir as solicitações entre os pods em 50/50. Também aumentaremos o número de contêineres v2 de um para dois, assim:

oc scale deployment recommendation-v2 --replicas=2 -n tutorial

Agora, configuramos uma regra de roteamento para que o tráfego seja distribuído entre os pods em uma proporção de 50/50.

Disjuntor Istio: desabilitando contêineres defeituosos
Eis o resultado dessa regra:

Disjuntor Istio: desabilitando contêineres defeituosos
Pode-se criticar o fato de esta tela não ser 50/50, mas sim 14:9, porém a situação deverá melhorar com o tempo.

Estamos causando uma falha técnica.

Agora vamos desativar um dos dois contêineres v2 para que tenhamos um contêiner v1 íntegro, um contêiner v2 íntegro e um contêiner v2 com problemas:

Disjuntor Istio: desabilitando contêineres defeituosos

Estamos resolvendo o problema.

Então, temos um contêiner com falha e é hora de ejetar o contêiner do pool. Usando uma configuração bem simples, vamos excluir esse contêiner com falha de todo o roteamento por 15 segundos, na esperança de que ele se recupere sozinho (seja reiniciando ou restaurando o desempenho). Veja como ficou essa configuração e os resultados:

Disjuntor Istio: desabilitando contêineres defeituosos
Disjuntor Istio: desabilitando contêineres defeituosos
Como você pode ver, o contêiner v2 com defeito não é mais usado para roteamento de requisições porque foi removido do pool. No entanto, após 15 segundos, ele retornará automaticamente ao pool. Na verdade, acabamos de demonstrar como funciona a ejeção de pool.

Vamos começar a construir a arquitetura.

O recurso Pool Ejection, combinado com as funcionalidades de monitoramento do Istio, permite que você comece a construir uma estrutura para substituir automaticamente contêineres com falha, reduzindo, ou até mesmo eliminando, o tempo de inatividade e as falhas.

A NASA tem um lema impactante: "Falhar não é uma opção", cujo autor é considerado o diretor de voo. Gene KranzA frase pode ser traduzida para o russo como "Falhar não é uma opção", e a ideia é que tudo pode funcionar com força de vontade suficiente. No entanto, na vida real, as falhas não acontecem por acaso; são inevitáveis, em todos os lugares e em tudo. Então, como lidar com elas no caso de microsserviços? Na nossa opinião, é melhor confiar não na força de vontade, mas nas capacidades dos contêineres. Kubernetes, Red Hat OpenShiftE Istio.

Como já mencionamos, o Istio implementa o conceito bem estabelecido de disjuntores no mundo físico. Assim como um disjuntor elétrico desconecta uma seção problemática de um circuito, o Circuit Breaker baseado em software do Istio desconecta a conexão entre o fluxo de requisições e o contêiner problemático quando algo dá errado com o endpoint, como uma falha no servidor ou lentidão.

Além disso, no segundo caso, os problemas só aumentam, uma vez que a lentidão de um contêiner não apenas causa uma cascata de atrasos nos serviços que o acessam e, consequentemente, reduz o desempenho do sistema como um todo, mas também gera solicitações repetidas a um serviço já lento, o que só piora a situação.

Disjuntor na teoria

O Circuit Breaker é um proxy que controla o fluxo de requisições para um endpoint. Quando esse endpoint para de funcionar ou, dependendo das configurações, começa a ficar lento, o proxy desconecta a conexão com o contêiner. O tráfego é então redirecionado para outros contêineres, simplesmente para fins de balanceamento de carga. A conexão permanece aberta por um período de espera especificado, digamos, dois minutos, e então é considerada semiaberta. Uma tentativa de enviar a próxima requisição determina o estado subsequente da conexão. Se o serviço estiver OK, a conexão retorna ao estado de funcionamento e é fechada novamente. Se o serviço ainda falhar, a conexão é desconectada e o período de espera é reativado. Aqui está um diagrama simplificado da transição de estado do Circuit Breaker:

Disjuntor Istio: desabilitando contêineres defeituosos
É importante notar que tudo isso acontece no nível da arquitetura do sistema, por assim dizer. Portanto, em algum momento, você precisará ensinar seus aplicativos a trabalhar com o Circuit Breaker, por exemplo, fornecendo um valor padrão na resposta ou, se possível, ignorando a existência do serviço. Isso é feito usando o padrão Bulkhead, mas está além do escopo deste artigo.

Disjuntor na prática

Neste exemplo, executaremos duas versões do nosso microsserviço de recomendação no OpenShift. A versão 1 será executada normalmente, mas na versão 2 adicionaremos um atraso para simular a latência do servidor. Para visualizar os resultados, utilize a ferramenta. cerco:

siege -r 2 -c 20 -v customer-tutorial.$(minishift ip).nip.io

Disjuntor Istio: desabilitando contêineres defeituosos
Tudo parece estar funcionando, mas a que custo? À primeira vista, temos 100% de disponibilidade, mas, analisando mais de perto, vemos que a duração máxima da transação é de impressionantes 12 segundos. Isso é claramente um gargalo e precisa ser resolvido.

Para isso, usaremos o Istio para impedir chamadas a contêineres lentos. Veja como fica a configuração correspondente usando o Circuit Breaker:

Disjuntor Istio: desabilitando contêineres defeituosos
A última linha com o parâmetro `httpMaxRequestsPerConnection` indica que a conexão deve ser fechada ao tentar criar uma segunda conexão além da existente. Como nosso contêiner está simulando um serviço lento, essas situações ocorrerão periodicamente e, então, o Istio retornará um erro 503. Veja o que o Siege exibirá:

Disjuntor Istio: desabilitando contêineres defeituosos

Certo, temos o Circuit Breaker, qual é o próximo passo?

Implementamos, portanto, o desligamento automático sem alterar o código-fonte dos serviços em si. Usando o Circuit Breaker e o procedimento de ejeção de pool descrito acima, podemos remover contêineres lentos do pool de recursos até que voltem ao normal e verificar seu status em um intervalo especificado — em nosso exemplo, a cada dois minutos (o parâmetro sleepWindow).

Observe que a capacidade de um aplicativo responder a um erro 503 ainda é definida no nível do código-fonte. Existem diversas estratégias para lidar com o Circuit Breaker, dependendo da situação.

Na próxima publicação: Vamos abordar o rastreamento e o monitoramento, que já estão integrados ao Istio ou podem ser facilmente adicionados, bem como a forma de introduzir erros intencionalmente no sistema.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção DDoS, servidores VPS VDS 🔥 Compre hospedagem de sites confiável com proteção contra DDoS, servidores VPS/VDS | ProHoster