De volta aos microsserviços com o Istio. Parte 2

De volta aos microsserviços com o Istio. Parte 2

Observação. trad.: A primeira parte Esta série foi dedicada a apresentar os recursos do Istio e demonstrá-los em ação. Agora falaremos sobre aspectos mais complexos da configuração e uso dessa malha de serviço e, em particular, sobre roteamento ajustado e gerenciamento de tráfego de rede.

Lembramos também que o artigo utiliza configurações (manifestos para Kubernetes e Istio) do repositório domínio do istio.

Gestão de tráfego

Com o Istio, novos recursos aparecem no cluster para fornecer:

  • Roteamento dinâmico de solicitações: lançamentos canário, testes A/B;
  • Balanceamento de carga: simples e consistente, baseado em hashes;
  • Recuperação após quedas: timeouts, novas tentativas, disjuntores;
  • Inserindo falhas: atrasos, solicitações descartadas, etc.

À medida que o artigo continua, esses recursos serão ilustrados usando o aplicativo selecionado como exemplo e novos conceitos serão introduzidos ao longo do caminho. O primeiro desses conceitos será DestinationRules (ou seja, regras sobre o destinatário do tráfego/solicitações - tradução aproximada), com a ajuda do qual ativamos o teste A/B.

Teste A/B: DestinationRules na prática

O teste A/B é usado nos casos em que existem duas versões de um aplicativo (geralmente são visualmente diferentes) e não temos 100% de certeza de qual delas melhorará a experiência do usuário. Portanto, executamos as duas versões simultaneamente e coletamos métricas.

Para implantar a segunda versão do frontend, necessária para demonstrar o teste A/B, execute o seguinte comando:

$ kubectl apply -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions/sa-frontend-green created

O manifesto de implantação da versão verde difere em dois locais:

  1. A imagem é baseada em uma tag diferente - istio-green,
  2. Os pods têm um rótulo version: green.

Como ambas as implantações têm um rótulo app: sa-frontend,solicitações roteadas por serviço virtual sa-external-services a serviço sa-frontend, será redirecionado para todas as suas instâncias e a carga será distribuída através algoritmo round-robin, o que levará à seguinte situação:

De volta aos microsserviços com o Istio. Parte 2
Os arquivos solicitados não foram encontrados

Esses arquivos não foram encontrados porque têm nomes diferentes em diferentes versões do aplicativo. Vamos ter certeza disso:

$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.c7071b22.css
/static/js/main.059f8e9c.js
$ curl --silent http://$EXTERNAL_IP/ | tr '"' 'n' | grep main
/static/css/main.f87cd8c9.css
/static/js/main.f7659dbb.js

Isto significa que, index.html, solicitando uma versão de arquivos estáticos, pode ser enviado pelo balanceador de carga para pods que possuem uma versão diferente, onde, por motivos óbvios, tais arquivos não existem. Portanto, para que o aplicativo funcione, precisamos definir uma restrição: “a mesma versão do aplicativo que atendeu index.html deve atender às solicitações subsequentes".

Chegaremos lá com balanceamento de carga consistente baseado em hash (Balanceamento de carga de hash consistente). Neste caso solicitações do mesmo cliente são enviadas para a mesma instância de back-end, para o qual uma propriedade predefinida é usada – por exemplo, um cabeçalho HTTP. Implementado usando DestinationRules.

Regras de Destino

Depois Serviço Virtual enviado uma requisição para o serviço desejado, utilizando DestinationRules podemos definir políticas que serão aplicadas ao tráfego destinado às instâncias deste serviço:

De volta aos microsserviços com o Istio. Parte 2
Gerenciamento de tráfego com recursos do Istio

Nota: o impacto dos recursos do Istio no tráfego de rede é apresentado aqui de uma forma fácil de entender. Para ser mais preciso, a decisão sobre para qual instância enviar a solicitação é feita pelo Envoy no Ingress Gateway configurado no CRD.

Com as Regras de Destino, podemos configurar o balanceamento de carga para usar hashes consistentes e garantir que a mesma instância de serviço responda ao mesmo usuário. A configuração a seguir permite que você consiga isso (destinationrule-sa-frontend.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sa-frontend
spec:
  host: sa-frontend
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpHeaderName: version   # 1

1 – o hash será gerado com base no conteúdo do cabeçalho HTTP version.

Aplique a configuração com o seguinte comando:

$ kubectl apply -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io/sa-frontend created

Agora execute o comando abaixo e certifique-se de obter os arquivos corretos ao especificar o cabeçalho version:

$ curl --silent -H "version: yogo" http://$EXTERNAL_IP/ | tr '"' 'n' | grep main

Nota: Para adicionar valores diferentes no cabeçalho e testar os resultados diretamente no navegador, você pode usar esta extensão para o Chrome (ou isso para Firefox - aprox. trad.).

Em geral, DestinationRules possui mais recursos na área de balanceamento de carga – verifique detalhes em documentação oficial.

Antes de estudarmos mais o VirtualService, vamos excluir a “versão verde” da aplicação e a regra de direção de tráfego correspondente executando os seguintes comandos:

$ kubectl delete -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions “sa-frontend-green” deleted
$ kubectl delete -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io “sa-frontend” deleted

Espelhamento: Serviços Virtuais na Prática

Sombreamento (“blindagem”) ou espelhamento (“espelhamento”) usado nos casos em que queremos testar uma alteração na produção sem afetar os usuários finais: para isso, duplicamos (“espelhamos”) as solicitações para uma segunda instância onde as alterações desejadas foram feitas e observamos as consequências. Simplificando, é quando seu colega escolhe o problema mais crítico e faz uma solicitação pull na forma de um pedaço de sujeira tão grande que ninguém consegue revisá-lo.

Para testar este cenário em ação, vamos criar uma segunda instância do SA-Logic com bugs (buggy) executando o seguinte comando:

$ kubectl apply -f resource-manifests/kube/shadowing/sa-logic-service-buggy.yaml
deployment.extensions/sa-logic-buggy created

E agora vamos executar o comando para garantir que todas as instâncias com app=sa-logic Eles também possuem etiquetas com as versões correspondentes:

$ kubectl get pods -l app=sa-logic --show-labels
NAME                              READY   LABELS
sa-logic-568498cb4d-2sjwj         2/2     app=sa-logic,version=v1
sa-logic-568498cb4d-p4f8c         2/2     app=sa-logic,version=v1
sa-logic-buggy-76dff55847-2fl66   2/2     app=sa-logic,version=v2
sa-logic-buggy-76dff55847-kx8zz   2/2     app=sa-logic,version=v2

Serviço sa-logic direciona pods com um rótulo app=sa-logic, então todas as solicitações serão distribuídas entre todas as instâncias:

De volta aos microsserviços com o Istio. Parte 2

... mas queremos que as solicitações sejam enviadas para instâncias v1 e espelhadas para instâncias v2:

De volta aos microsserviços com o Istio. Parte 2

Conseguiremos isso através do VirtualService em combinação com DestinationRule, onde as regras determinarão os subconjuntos e rotas do VirtualService para um subconjunto específico.

Definição de subconjuntos em regras de destino

Subconjuntos (subconjuntos) são determinados pela seguinte configuração (sa-logic-subsets-destinationrule.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sa-logic
spec:
  host: sa-logic    # 1
  subsets:
  - name: v1        # 2
    labels:
      version: v1   # 3
  - name: v2
    labels:
      version: v2

  1. Hospedar (host) define que esta regra se aplica apenas aos casos em que a rota segue em direção ao serviço sa-logic;
  2. Títulos (name) subconjuntos são usados ​​ao rotear para instâncias de subconjunto;
  3. Rótulo (label) define os pares de valores-chave que as instâncias devem corresponder para se tornarem parte do subconjunto.

Aplique a configuração com o seguinte comando:

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-destinationrule.yaml
destinationrule.networking.istio.io/sa-logic created

Agora que os subconjuntos estão definidos, podemos seguir em frente e configurar o VirtualService para aplicar regras às solicitações ao sa-logic para que:

  1. Roteado para um subconjunto v1,
  2. Espelhado para um subconjunto v2.

O manifesto a seguir permite que você concretize seus planos (sa-logic-subsets-shadowing-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic          
  http:
  - route:
    - destination:
        host: sa-logic  
        subset: v1      
    mirror:             
      host: sa-logic     
      subset: v2

Nenhuma explicação é necessária aqui, então vamos ver isso em ação:

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-shadowing-vs.yaml
virtualservice.networking.istio.io/sa-logic created

Vamos adicionar a carga chamando o seguinte comando:

$ while true; do curl -v http://$EXTERNAL_IP/sentiment 
    -H "Content-type: application/json" 
    -d '{"sentence": "I love yogobella"}'; 
    sleep .8; done

Vejamos os resultados no Grafana, onde você pode ver que a versão com bugs (buggy) resulta em falha em aproximadamente 60% das solicitações, mas nenhuma dessas falhas afeta os usuários finais, pois elas são respondidas por um serviço em execução.

De volta aos microsserviços com o Istio. Parte 2
Respostas bem-sucedidas de diferentes versões do serviço sa-logic

Aqui vimos pela primeira vez como o VirtualService é aplicado aos Envoys dos nossos serviços: quando sa-web-app faz um pedido para sa-logic, ele passa pelo sidecar Envoy, que - via VirtualService - é configurado para rotear a solicitação para o subconjunto v1 e espelhar a solicitação para o subconjunto v2 do serviço sa-logic.

Eu sei, você já deve pensar que os Serviços Virtuais são simples. Na próxima seção, expandiremos isso dizendo que eles também são realmente ótimos.

Lançamentos canário

Canary Deployment é o processo de implementação de uma nova versão de um aplicativo para um pequeno número de usuários. É utilizado para garantir que não haja problemas no lançamento e só depois disso, já confiante na qualidade do seu lançamento, distribuí-lo para outros usuários.оpúblico maior.

Para demonstrar implementações canário, continuaremos trabalhando com um subconjunto buggy у sa-logic.

Não vamos perder tempo com ninharias e enviar imediatamente 20% dos usuários para a versão com bugs (isso representará nosso lançamento canário) e os 80% restantes para o serviço normal. Para fazer isso, use o seguinte VirtualService (sa-logic-subsets-canary-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic    
  http:
  - route: 
    - destination: 
        host: sa-logic
        subset: v1
      weight: 80         # 1
    - destination: 
        host: sa-logic
        subset: v2
      weight: 20 # 1

1 é o peso (weight), que especifica a porcentagem de solicitações que serão direcionadas a um destinatário ou a um subconjunto do destinatário.

Vamos atualizar a configuração anterior do VirtualService para sa-logic com o seguinte comando:

$ kubectl apply -f resource-manifests/istio/canary/sa-logic-subsets-canary-vs.yaml
virtualservice.networking.istio.io/sa-logic configured

... e veremos imediatamente que algumas solicitações levam a falhas:

$ while true; do 
   curl -i http://$EXTERNAL_IP/sentiment 
   -H "Content-type: application/json" 
   -d '{"sentence": "I love yogobella"}' 
   --silent -w "Time: %{time_total}s t Status: %{http_code}n" 
   -o /dev/null; sleep .1; done
Time: 0.153075s Status: 200
Time: 0.137581s Status: 200
Time: 0.139345s Status: 200
Time: 30.291806s Status: 500

Os VirtualServices permitem implementações canário: neste caso, reduzimos o impacto potencial dos problemas para 20% da base de usuários. Maravilhoso! Agora, em todos os casos em que não temos certeza do nosso código (em outras palavras - sempre...), podemos usar espelhamento e implementações canário.

Tempos limite e novas tentativas

Mas os bugs nem sempre acabam no código. Na lista de "8 Equívocos sobre Computação Distribuída“Em primeiro lugar está a crença errônea de que “a rede é confiável”. Na realidade a rede não confiável e por esse motivo precisamos de tempos limite (tempo limite) e tenta novamente (tentativas).

Para demonstração, continuaremos a usar a mesma versão do problema sa-logic (buggy) e simularemos a falta de confiabilidade da rede com falhas aleatórias.

Deixe nosso serviço com bugs ter 1/3 de chance de demorar muito para responder, 1/3 de chance de terminar com um erro interno do servidor e 1/3 de chance de retornar a página com sucesso.

Para mitigar o impacto de tais problemas e melhorar a vida dos usuários, podemos:

  1. adicione um tempo limite se o serviço demorar mais de 8 segundos para responder,
  2. tente novamente se a solicitação falhar.

Para implementação, usaremos a seguinte definição de recurso (sa-logic-retries-timeouts-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic
  http:
  - route: 
    - destination: 
        host: sa-logic
        subset: v1
      weight: 50
    - destination: 
        host: sa-logic
        subset: v2
      weight: 50
    timeout: 8s           # 1
    retries:
      attempts: 3         # 2
      perTryTimeout: 3s # 3

  1. O tempo limite da solicitação é definido como 8 segundos;
  2. As solicitações são repetidas 3 vezes;
  3. E cada tentativa é considerada malsucedida se o tempo de resposta exceder 3 segundos.

Trata-se de uma otimização porque o usuário não terá que esperar mais de 8 segundos e faremos três novas tentativas para obter resposta em caso de falhas, aumentando a chance de uma resposta bem-sucedida.

Aplique a configuração atualizada com o seguinte comando:

$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts-vs.yaml
virtualservice.networking.istio.io/sa-logic configured

E verifique nos gráficos do Grafana que o número de respostas bem-sucedidas aumentou acima:

De volta aos microsserviços com o Istio. Parte 2
Melhorias nas estatísticas de respostas bem-sucedidas após adicionar tempos limite e novas tentativas

Antes de passar para a próxima seção (ou melhor, para a próxima parte do artigo, porque não haverá mais experimentos práticos - aprox. trad.), excluir sa-logic-buggy e VirtualService executando os seguintes comandos:

$ kubectl delete deployment sa-logic-buggy
deployment.extensions “sa-logic-buggy” deleted
$ kubectl delete virtualservice sa-logic
virtualservice.networking.istio.io “sa-logic” deleted

Padrões de disjuntores e anteparos

Estamos falando de dois padrões importantes na arquitetura de microsserviços que permitem alcançar a autorrecuperação (autocura) Serviços.

Circuit Breaker ("disjuntor") usado para encerrar solicitações que chegam a uma instância de um serviço considerado não íntegro e restaurá-lo enquanto as solicitações do cliente são redirecionadas para instâncias íntegras desse serviço (o que aumenta a porcentagem de respostas bem-sucedidas). (Nota: Uma descrição mais detalhada do padrão pode ser encontrada, por exemplo, aqui.)

Anteparo ("partição") isola falhas de serviço de afetar todo o sistema. Por exemplo, o Serviço B está quebrado e outro serviço (cliente do Serviço B) faz uma solicitação ao Serviço B, fazendo com que ele esgote seu pool de threads e não consiga atender outras solicitações (mesmo que não sejam do Serviço B). (Nota: Uma descrição mais detalhada do padrão pode ser encontrada, por exemplo, aqui.)

Omitirei os detalhes de implementação desses padrões porque eles são fáceis de encontrar em documentação oficial, e também quero mostrar autenticação e autorização, que serão discutidas na próxima parte do artigo.

PS do tradutor

Leia também em nosso blog:

Fonte: habr.com

Adicionar um comentário