Vuelta a los microservicios con Istio. Parte 2

Vuelta a los microservicios con Istio. Parte 2

Nota. traducir: La primera parte Esta serie estuvo dedicada a presentar las capacidades de Istio y demostrarlas en acción. Ahora hablaremos sobre aspectos más complejos de la configuración y el uso de esta malla de servicios y, en particular, sobre el enrutamiento y la gestión del tráfico de red.

También te recordamos que el artículo utiliza configuraciones (manifiestos para Kubernetes e Istio) del repositorio. istio-maestría.

la gestión del tráfico

Con Istio, aparecen nuevas capacidades en el clúster para proporcionar:

  • Enrutamiento de solicitudes dinámicas: implementaciones canary, pruebas A/B;
  • Balanceo de carga: simple y consistente, basado en hashes;
  • Recuperación después de caídas: tiempos de espera, reintentos, disyuntores;
  • Insertar fallos: retrasos, solicitudes abandonadas, etc.

A medida que avance el artículo, estas capacidades se ilustrarán utilizando la aplicación seleccionada como ejemplo y se introducirán nuevos conceptos a lo largo del camino. El primer concepto de este tipo será DestinationRules (es decir, reglas sobre el destinatario del tráfico/solicitudes - traducción aproximada), con cuya ayuda activamos las pruebas A/B.

Pruebas A/B: DestinationRules en la práctica

Las pruebas A/B se utilizan en los casos en los que existen dos versiones de una aplicación (normalmente son visualmente diferentes) y no estamos 100% seguros de cuál mejorará la experiencia del usuario. Por lo tanto, ejecutamos ambas versiones simultáneamente y recopilamos métricas.

Para implementar la segunda versión de la interfaz, necesaria para demostrar las pruebas A/B, ejecute el siguiente comando:

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

El manifiesto de implementación de la versión verde difiere en dos lugares:

  1. La imagen se basa en una etiqueta diferente: istio-green,
  2. Las vainas tienen una etiqueta. version: green.

Dado que ambas implementaciones tienen una etiqueta app: sa-frontend,solicitudes enrutadas por servicio virtual sa-external-services para servicio sa-frontend, será redirigido a todas sus instancias y la carga se distribuirá a través de algoritmo de turnos, lo que conducirá a la siguiente situación:

Vuelta a los microservicios con Istio. Parte 2
Los archivos solicitados no fueron encontrados.

Estos archivos no se encontraron porque tienen nombres diferentes en diferentes versiones de la aplicación. Asegurémonos de esto:

$ 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

Esto significa que index.html, que solicita una versión de archivos estáticos, puede ser enviado por el balanceador de carga a pods que tienen una versión diferente, donde, por razones obvias, dichos archivos no existen. Por lo tanto, para que la aplicación funcione, debemos establecer una restricción: “la misma versión de la aplicación que sirvió index.html debería atender solicitudes posteriores".

Lo lograremos con un equilibrio de carga consistente basado en hash (Equilibrio de carga de hash consistente). En este caso Las solicitudes del mismo cliente se envían a la misma instancia de backend., para el cual se utiliza una propiedad predefinida, por ejemplo, un encabezado HTTP. Implementado utilizando DestinationRules.

DestinoReglas

Después Servicio Virtual envió una solicitud al servicio deseado, usando DestinationRules podemos definir políticas que se aplicarán al tráfico destinado a instancias de este servicio:

Vuelta a los microservicios con Istio. Parte 2
Gestión del tráfico con recursos de Istio

Nota: El impacto de los recursos de Istio en el tráfico de la red se presenta aquí de una manera fácil de entender. Para ser precisos, la decisión sobre a qué instancia enviar la solicitud la toma el Envoy en el Ingress Gateway configurado en el CRD.

Con las reglas de destino, podemos configurar el equilibrio de carga para utilizar hashes coherentes y garantizar que la misma instancia de servicio responda al mismo usuario. La siguiente configuración le permite lograr esto (regla de destino-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: el hash se generará en función del contenido del encabezado HTTP version.

Aplique la configuración con el siguiente comando:

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

Ahora ejecute el siguiente comando y asegúrese de obtener los archivos correctos cuando especifique el encabezado version:

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

Nota: Para agregar diferentes valores en el encabezado y probar los resultados directamente en el navegador, puede usar esta extensión a Chrome (o con este para Firefox: aprox. trad.).

En general, DestinationRules tiene más capacidades en el área de equilibrio de carga; consulte los detalles en documentación oficial.

Antes de seguir estudiando VirtualService, eliminemos la "versión verde" de la aplicación y la regla de dirección del tráfico correspondiente ejecutando los siguientes 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

Duplicación: servicios virtuales en la práctica

Sombra (“blindaje”) o Duplicación ("reflejo") Se utiliza en los casos en los que queremos probar un cambio en la producción sin afectar a los usuarios finales: para hacer esto, duplicamos (“reflejamos”) las solicitudes en una segunda instancia donde se han realizado los cambios deseados y observamos las consecuencias. En pocas palabras, esto es cuando su colega elige el problema más crítico y realiza una solicitud de extracción en forma de un montón de basura tan grande que nadie puede revisarlo.

Para probar este escenario en acción, creemos una segunda instancia de SA-Logic con errores (buggy) ejecutando el siguiente comando:

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

Y ahora ejecutemos el comando para asegurarnos de que todas las instancias con app=sa-logic También disponen de etiquetas con las versiones correspondientes:

$ 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

Servicio sa-logic apunta a vainas con una etiqueta app=sa-logic, por lo que todas las solicitudes se distribuirán entre todas las instancias:

Vuelta a los microservicios con Istio. Parte 2

... pero queremos que las solicitudes se envíen a instancias v1 y se reflejen en instancias v2:

Vuelta a los microservicios con Istio. Parte 2

Esto lo lograremos a través de VirtualService en combinación con DestinationRule, donde las reglas determinarán los subconjuntos y rutas del VirtualService a un subconjunto específico.

Definición de subconjuntos en reglas de destino

Subconjuntos (subconjuntos) están determinados por la siguiente configuración (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. Anfitrión (host) define que esta regla se aplica solo en los casos en que la ruta se dirige hacia el servicio sa-logic;
  2. Títulos (name) los subconjuntos se utilizan cuando se enruta a instancias de subconjunto;
  3. Etiqueta (label) define los pares clave-valor que las instancias deben coincidir para formar parte del subconjunto.

Aplique la configuración con el siguiente comando:

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

Ahora que los subconjuntos están definidos, podemos continuar y configurar VirtualService para aplicar reglas a las solicitudes de sa-logic para que:

  1. Enrutado a un subconjunto v1,
  2. Reflejado en un subconjunto v2.

El siguiente manifiesto le permite realizar sus planes (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

No se necesita explicación aquí, así que veámoslo en acción:

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

Agreguemos la carga llamando al siguiente comando:

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

Veamos los resultados en Grafana, donde puedes ver que la versión con errores (buggy) genera fallas en aproximadamente el 60 % de las solicitudes, pero ninguna de estas fallas afecta a los usuarios finales, ya que son respondidas por un servicio en ejecución.

Vuelta a los microservicios con Istio. Parte 2
Respuestas exitosas de diferentes versiones del servicio sa-logic

Aquí vimos por primera vez cómo se aplica VirtualService a los Envoys de nuestros servicios: cuando sa-web-app hace una solicitud a sa-logic, pasa por el sidecar Envoy, que, a través de VirtualService, está configurado para enrutar la solicitud al subconjunto v1 y reflejar la solicitud en el subconjunto v2 del servicio. sa-logic.

Lo sé, quizás ya pienses que los Servicios Virtuales son simples. En la siguiente sección, ampliaremos esto diciendo que también son realmente geniales.

Lanzamientos canarios

Canary Deployment es el proceso de implementar una nueva versión de una aplicación para una pequeña cantidad de usuarios. Se utiliza para asegurarse de que no haya problemas en la versión y solo después de eso, ya confiando en su calidad (de la versión), distribuirlo a otros usuarios.оaudiencia más grande.

Para demostrar las implementaciones canary, continuaremos trabajando con un subconjunto buggy у sa-logic.

No perdamos el tiempo en nimiedades y enviemos inmediatamente al 20% de los usuarios a la versión con errores (esto representará nuestro lanzamiento canario) y al 80% restante al servicio normal. Para hacer esto, use el siguiente servicio virtual (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 es el peso (weight), que especifica el porcentaje de solicitudes que se dirigirán a un destinatario o a un subconjunto del destinatario.

Actualicemos la configuración anterior de VirtualService para sa-logic con el siguiente comando:

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

... e inmediatamente veremos que algunas solicitudes conducen a fallas:

$ 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

Los servicios virtuales permiten implementaciones canary: en este caso, hemos reducido el impacto potencial de los problemas al 20 % de la base de usuarios. ¡Maravilloso! Ahora, en todos los casos en los que no estemos seguros de nuestro código (en otras palabras, siempre...), podemos usar mirroring y canary rollouts.

Tiempos de espera y reintentos

Pero los errores no siempre terminan en el código. En la lista de "8 conceptos erróneos sobre la informática distribuida“En primer lugar está la creencia errónea de que “la red es confiable”. En realidad la red no confiable, y por esta razón necesitamos tiempos de espera (tiempos de espera) y reintentos (reintentos).

Para demostración continuaremos usando la misma versión del problema. sa-logic (buggy), y simularemos la falta de confiabilidad de la red con fallas aleatorias.

Deje que nuestro servicio con errores tenga 1/3 de posibilidades de tardar demasiado en responder, 1/3 de posibilidades de finalizar con un error interno del servidor y 1/3 de posibilidades de devolver la página con éxito.

Para mitigar el impacto de tales problemas y mejorar la vida de los usuarios, podemos:

  1. agregue un tiempo de espera si el servicio tarda más de 8 segundos en responder,
  2. Vuelva a intentarlo si la solicitud falla.

Para la implementación, utilizaremos la siguiente definición 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. El tiempo de espera de la solicitud se establece en 8 segundos;
  2. Las solicitudes se reintentan 3 veces;
  3. Y cada intento se considera fallido si el tiempo de respuesta supera los 3 segundos.

Esto es una optimización porque el usuario no tendrá que esperar más de 8 segundos y realizaremos tres nuevos intentos para obtener una respuesta en caso de fallas, aumentando las posibilidades de una respuesta exitosa.

Aplique la configuración actualizada con el siguiente comando:

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

Y compruebe en los gráficos de Grafana que el número de respuestas exitosas ha aumentado arriba:

Vuelta a los microservicios con Istio. Parte 2
Mejoras en las estadísticas de respuestas exitosas después de agregar tiempos de espera y reintentos

Antes de pasar a la siguiente sección (o mejor dicho, a la siguiente parte del artículo, porque en esto no habrá más experimentos prácticos - aprox. traducción.), borrar sa-logic-buggy y VirtualService ejecutando los siguientes 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

Patrones de disyuntores y mamparas

Estamos hablando de dos patrones importantes en la arquitectura de microservicios que le permiten lograr la autorrecuperación. (autosanación) servicios.

Cortacircuitos ("cortacircuitos") se utiliza para finalizar las solicitudes que llegan a una instancia de un servicio que se considera en mal estado y restaurarlo mientras las solicitudes de los clientes se redirigen a instancias en buen estado de ese servicio (lo que aumenta el porcentaje de respuestas exitosas). (Nota: Puede encontrar una descripción más detallada del patrón, por ejemplo, aquí.)

Mamparo ("dividir") Aísla las fallas del servicio para que no afecten a todo el sistema. Por ejemplo, el Servicio B está roto y otro servicio (el cliente del Servicio B) realiza una solicitud al Servicio B, lo que hace que agote su grupo de subprocesos y no pueda atender otras solicitudes (incluso si no son del Servicio B). (Nota: Puede encontrar una descripción más detallada del patrón, por ejemplo, aquí.)

Omitiré los detalles de implementación de estos patrones porque son fáciles de encontrar en documentación oficialY también quiero mostrar autenticación y autorización, que se analizarán en la siguiente parte del artículo.

PD del traductor

Lea también en nuestro blog:

Fuente: habr.com

Añadir un comentario