Istio Circuit Breaker: desligando containers com falha

As festas acabaram e estamos de volta com nossa segunda postagem da série sobre o Istio Service Mesh.

Istio Circuit Breaker: desligando containers com falha

O tema de hoje é Circuit Breaker, que em português significa "disjuntor". No contexto do Istio, este disjuntor não desliga um circuito sobrecarregado, mas contêineres com falhas.

Como isso deve funcionar na teoria

Quando microserviços são gerenciados pelo Kubernetes, por exemplo, na plataforma OpenShift, eles são dimensionados automaticamente para cima ou para baixo dependendo da carga. Como os microserviços funcionam em pods, pode haver várias instâncias de um microserviço contêinerizado em um único ponto final, e o Kubernetes roteia as solicitações e balanceia a carga entre elas. E – na teoria – tudo isso deve funcionar perfeitamente.

Lembramos que microserviços são pequenos e efêmeros. A efemeridade, que aqui significa a facilidade de surgimento e desaparecimento, é frequentemente subestimada. O nascimento e a morte de uma instância de microserviço em um pod são coisas esperadas, OpenShift e Kubernetes lidam bem com isso, e tudo funciona bem – mas novamente, na teoria.

Como isso funciona na prática

Agora, imagine que uma instância específica de um microserviço, ou seja, um contêiner, se tornou inválida: ou não responde (erro 503), ou – o que é pior – responde, mas muito lentamente. Ou seja, está falhando ou não responde às solicitações, mas não é removido automaticamente do pool. O que fazer neste caso? Tentar novamente? Removê-lo do esquema de roteamento? E o que significa "muito lentamente" – quanto isso representa em números, e quem os define? Talvez seja melhor esperar e tentar novamente mais tarde? Se sim, quanto mais tarde?

O que é Pool Ejection no Istio

E aqui o Istio entra em ação com seus disjuntores, que removem temporariamente contêineres com falhas do pool de recursos de roteamento e balanceamento de carga, implementando o procedimento de Pool Ejection.

Utilizando a estratégia de detecção de outliers, o Istio detecta pods problemáticos que se destacam do grupo e os remove do pool de recursos por um tempo determinado, chamado de "janela de sono".

Para demonstrar como isso funciona no Kubernetes na plataforma OpenShift, começaremos com uma captura de tela de microsserviços funcionando normalmente do exemplo no repositório. Demonstrações de Desenvolvedores da Red Hat. Aqui 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 aplica por padrão o balanceamento de carga circular:

Istio Circuit Breaker: desligando containers com falha

Preparando para a falha

Antes de realizar a Ejeção do Pool, precisamos criar uma regra de roteamento do Istio. Suponha que queremos distribuir solicitações entre os pods na razão de 50/50. Além disso, aumentaremos o número de contêineres v2 de um para dois, assim:

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

Agora definimos a regra de roteamento para que o tráfego seja distribuído entre os pods na razão de 50/50.

Istio Circuit Breaker: desligando containers com falha
E aqui está como o resultado dessa regra se parece:

Istio Circuit Breaker: desligando containers com falha
Pode-se argumentar que nesta captura não é 50/50, mas sim 14:9, mas com o tempo a situação se ajustará.

Causando a falha

Agora vamos derrubar um dos dois contêineres v2, de modo que teremos um contêiner v1 funcionando, um contêiner v2 funcionando e um contêiner v2 com falha:

Istio Circuit Breaker: desligando containers com falha

Consertando a falha

Portanto, temos um contêiner com falha, e chegou a hora da Ejeção do Pool. Com uma configuração muito simples, excluiremos este contêiner com falha de quaisquer esquemas de roteamento por 15 segundos na expectativa de que ele se recupere (ou reinicie ou recupere o desempenho). Aqui está como essa configuração se parece e os resultados de sua execução:

Istio Circuit Breaker: desligando containers com falha
Istio Circuit Breaker: desligando containers com falha
Como pode ser visto, o contêiner com falha v2 não está mais sendo usado para roteamento de solicitações, pois foi removido do pool. Após 15 segundos, ele voltará automaticamente ao pool. Na verdade, acabamos de mostrar como funciona a Ejeção do Pool.

Começando a construir a arquitetura

A Ejeção do Pool em conjunto com as capacidades de monitoramento do Istio permite começar a construir uma estrutura de substituição automática de contêineres com falha, para reduzir ou até mesmo eliminar paradas e falhas.
 
A NASA tem um lema famoso – Failure Is Not an Option, creditado ao chefe de voos Gene KranzEm português, isso pode ser traduzido como "Fracasso não é uma opção", e a ideia aqui é que tudo pode ser feito funcionar, desde que haja vontade suficiente. No entanto, na vida real, falhas não apenas ocorrem, elas são inevitáveis em todos os lugares e em tudo. E como lidar com elas no caso de microsserviços? Na nossa opinião, é melhor confiar nas capacidades dos contêineres, Kubernetes, Red Hat OpenShift, quanto Istio.

O Istio, como mencionado anteriormente, implementa a bem-sucedida concepção de disjuntores do mundo físico. Assim como um disjuntor elétrico desliga uma parte problemática do circuito, o Circuit Breaker do Istio interrompe a conexão entre o fluxo de solicitações e o contêiner problemático quando há algo errado com o ponto de extremidade, por exemplo, quando o servidor falhou ou começou a falhar.

No segundo caso, os problemas são ainda maiores, pois a lentidão de um contêiner não apenas causa um efeito cascata de atrasos nos serviços que dependem dele e, consequentemente, reduz o desempenho do sistema como um todo, mas também gera solicitações repetidas para um serviço que já está funcionando lentamente, o que apenas agrava a situação.

Circuit Breaker em teoria

Circuit Breaker é um proxy que controla o fluxo de solicitações para o ponto de extremidade. Quando esse ponto deixa de funcionar ou – dependendo das configurações definidas – começa a apresentar lentidão, o proxy corta a conexão com o contêiner. O tráfego é então redirecionado para outros contêineres, simplesmente por causa do balanceamento de carga. A conexão permanece aberta (open) durante um intervalo de descanso definido, digamos, dois minutos, e depois é considerada semi-aberta (half-open). A tentativa de enviar a próxima solicitação determina o estado futuro da conexão. Se o serviço estiver funcionando bem, a conexão retorna ao estado operacional e novamente se torna fechada (closed). Se houver algo errado com o serviço, a conexão é quebrada novamente e o intervalo de descanso é reativado. Aqui está um diagrama simplificado do ciclo de estados do Circuit Breaker:

Istio Circuit Breaker: desligando containers com falha
É importante notar que tudo isso acontece em um nível, por assim dizer, de arquitetura do sistema. Portanto, em algum momento, você precisará ensinar seus aplicativos a trabalhar com o Circuit Breaker, por exemplo, fornecendo um valor padrão em resposta ou, se possível, ignorando a existência do serviço. Para isso, utiliza-se o padrão bulkhead, mas isso está além do escopo deste artigo.

Circuit Breaker na prática

Como exemplo, vamos executar duas versões do nosso microsserviço de recomendações no OpenShift. A versão 1 funcionará normalmente, enquanto na v2 vamos incorporar um delay para simular lentidão no servidor. Para visualizar os resultados, utilizamos a ferramenta siege:

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

Istio Circuit Breaker: desligando containers com falha
Parece que tudo está funcionando, mas a que custo? À primeira vista, temos 100% de disponibilidade, mas observe – a duração máxima da transação chega a 12 segundos. Este é claramente um gargalo, e precisamos solucioná-lo.

Para isso, usaremos o Istio para evitar chamadas para contêineres lentos. Veja como é a configuração correspondente utilizando o Circuit Breaker:

Istio Circuit Breaker: desligando containers com falha
A última linha com o parâmetro httpMaxRequestsPerConnection sinaliza que a conexão deve ser interrompida ao tentar criar uma nova – segunda – conexão além da já existente. Como nosso contêiner simula um serviço lento, essas situações ocorrerão periodicamente, e então o Istio retornará um erro 503, enquanto isso, o siege mostrará:

Istio Circuit Breaker: desligando containers com falha

Ok, temos o Circuit Breaker, e agora?

Assim, implementamos a desconexão automática sem alterar o código-fonte dos serviços. 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 eles se normalizem e verificar seu estado em intervalos regulares – no nosso exemplo, a cada duas minutos (parâmetro sleepWindow).

Observe que a capacidade do aplicativo de reagir ao erro 503 ainda é definida no nível de seu código-fonte. Existem várias estratégias para trabalhar com o Circuit Breaker, que são aplicadas dependendo da situação.

No próximo post: vamos falar sobre rastreamento e monitoramento, que já estão integrados ou são facilmente adicionados ao Istio, além de como introduzir erros no sistema intencionalmente.

Fonte: habr.com

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