Volver a los microservicios con Istio. Parte 2



Nota perev. : La primera parte de esta serie se dedicó a presentar Istio y demostrarlo 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 finamente ajustado y la gestión del tráfico de red.

También le recordamos que el artículo utiliza configuraciones (manifiestos para Kubernetes e Istio) del repositorio de dominio de istio .

Gestión del tráfico


Con Istio, aparecen nuevas características en el clúster para proporcionar:

  • Enrutamiento de consultas dinámicas : despliegues canarios, pruebas A / B;
  • Equilibrio de carga : simple y consistente, basado en hashes;
  • Recuperación de caídas : tiempos de espera, reintentos, disyuntores;
  • Entrada de falla : demoras, interrupción de solicitudes, etc.

En la continuación del artículo, estas características se mostrarán como un ejemplo de la aplicación seleccionada y se introducirán nuevos conceptos en el camino. El primero de estos conceptos será DestinationRules (es decir, reglas sobre el destinatario del tráfico / solicitudes, aprox. Transl.) , Con las cuales activaremos las pruebas A / B.

Pruebas A / B: Reglas de destino en la práctica


Las pruebas A / B se usan en los casos en que hay dos versiones de la aplicación (generalmente difieren visualmente) y no estamos 100% seguros de cuál mejorará la interacción del usuario. Por lo tanto, lanzamos simultáneamente ambas versiones 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 para la "versión verde" difiere en dos lugares:

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

Dado que ambas implementaciones tienen la etiqueta app: sa-frontend , las solicitudes enrutadas por el sa-external-services virtual sa-external-services servicio sa-frontend se redirigirán a todas sus instancias y la carga se distribuirá utilizando el algoritmo round-robin , lo que conducirá a la siguiente situación:


Archivos solicitados no encontrados

Estos archivos no se encontraron debido al hecho de que se llaman de manera diferente 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 equilibrador de carga a pods que tienen una versión diferente, donde, por razones obvias, tales archivos no existen. Por lo tanto, para que la aplicación funcione, debemos poner una restricción: " la misma versión de la aplicación que proporcionó index.html también debe atender solicitudes posteriores ".

Alcanzaremos el objetivo con un equilibrio de carga consistente basado en hash ( Equilibrio de carga de hash consistente) . En este caso, las solicitudes de un cliente se envían a la misma instancia de back-end , para la que se utiliza una propiedad predefinida, por ejemplo, un encabezado HTTP. Implementado usando DestinationRules.

Reglas de destino


Después de que VirtualService haya enviado una solicitud al servicio deseado, utilizando DestinationRules podemos determinar las políticas que se aplicarán al tráfico destinado a las instancias de este servicio:


Istio Resource Traffic Management

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

Al usar las Reglas de destino, podemos configurar el equilibrio de carga para que se usen hashes consistentes y se garanticen las respuestas de la misma instancia de servicio al mismo usuario. La siguiente configuración permite esto ( destinationrule-sa-frontend.yaml ) para lograr esto:

 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 de la version HTTP.

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 comando a continuación y asegúrese de obtener los archivos que necesita al especificar el encabezado de la version :

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

Nota : Para agregar diferentes valores en el título y probar los resultados directamente en el navegador, puede usar esta extensión para Chrome (o esta para Firefox - Transl. Aprox.) .

En general, DestinationRules tiene más opciones en el campo del equilibrio de carga: consulte la documentación oficial para obtener más detalles.

Antes de seguir explorando VirtualService, eliminaremos la "versión verde" de la aplicación y la regla correspondiente en la dirección del tráfico 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 

Mirroring: servicios virtuales en la práctica


El sombreado ("blindaje") o el reflejo ("reflejo") se usa en aquellos casos en los que queremos probar un cambio en la producción sin afectar a los usuarios finales: para esto duplicamos las solicitudes ("espejo") para la segunda instancia, donde se realizan los cambios necesarios, y mira las consecuencias. En pocas palabras, esto es cuando su (a) colega selecciona el problema más crítico y hace una solicitud de extracción en forma de un trozo de suciedad tan grande que nadie puede realmente hacerle una revisión.

Para probar este escenario en acción, cree 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 ejecutamos el comando para asegurarnos de que todas las instancias con app=sa-logic tengan 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 

El servicio sa-logic dirige a los pods con la etiqueta app=sa-logic , por lo que todas las solicitudes se distribuirán entre todas las instancias:



... pero queremos que las solicitudes se dirijan a instancias con la versión v1 y se reflejen en instancias con la versión v2:



Lo lograremos a través del VirtualService en combinación con DestinationRule, donde las reglas determinarán los subconjuntos y rutas del VirtualService a un subconjunto particular.

Definición de subconjuntos en reglas de destino


Los subconjuntos se definen mediante 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. El host determina que esta regla se aplica solo a los casos en que la ruta va hacia el servicio sa-logic ;
  2. Los nombres de los subconjuntos se utilizan al enrutar a instancias del subconjunto;
  3. Una etiqueta define los pares clave-valor que las instancias deben coincidir para formar parte de un 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, puede continuar y configurar VirtualService para aplicar las reglas a las solicitudes de sa-logic para que:

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

El siguiente manifiesto le permite lograr su plan ( 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 requiere explicación aquí, así que solo eche un vistazo a la acción:

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

Agregue la carga llamando a este 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 podemos ver que la versión con buggy bloquea para ~ 60% de las solicitudes, pero ninguno de estos accidentes afecta a los usuarios finales porque tienen un servicio que funciona.


Éxito de las respuestas de diferentes versiones del servicio sa-logic

Aquí vimos por primera vez cómo se aplica VirtualService a los Enviados de nuestros servicios: cuando la aplicación sa-web-app realiza una solicitud a sa-logic , pasa por el Enviado lateral, que, a través de VirtualService, está configurado para enrutar la solicitud al subconjunto v1 y espejo una solicitud a un subconjunto de v2 del sa-logic .

Lo sé: ya tuvo tiempo de pensar que los servicios virtuales son simples. En la siguiente sección, ampliamos esta vista por el hecho de que también son realmente magníficos.

Panecillos canarios


Canary Deployment es el proceso de implementar una nueva versión de una aplicación para un pequeño número de usuarios. Se utiliza para asegurarse de que no haya problemas en el lanzamiento y solo después de eso, ya confiando en su calidad (lanzamiento) suficiente, para extenderse a un público más amplio.

Para demostrar los lanzamientos de canarios, continuaremos trabajando con un subconjunto de buggy en sa-logic .

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

Actualice la configuración anterior de VirtualService para sa-logic siguiente comando:

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

... e inmediatamente veo que parte de las solicitudes falla:

 $ 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 activan los despliegues canarios: en este caso, redujimos las posibles consecuencias de los problemas al 20% de la base de usuarios. Genial Ahora, en cada caso, cuando no estamos seguros de nuestro código (en otras palabras, siempre ...), podemos usar la duplicación y los lanzamientos de canarios.

Tiempos de espera y reintentos


Pero no siempre hay errores en el código. En la lista de " 8 errores en la informática distribuida ", en primer lugar, aparece la opinión errónea de que "la red es confiable". En realidad, la red no es confiable, y por esta razón necesitamos tiempos de espera y reintentos .

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

Deje que nuestro servicio con errores tenga una probabilidad de 1/3 de una respuesta que es demasiado larga, 1/3 para completar con un Error interno del servidor y 1/3 para una devolución de página exitosa.

Para mitigar las consecuencias de tales problemas y mejorar la vida de los usuarios, podemos:

  1. agregue un tiempo de espera si el servicio responde durante más de 8 segundos,
  2. vuelva a intentar 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 para la solicitud se establece en 8 segundos;
  2. Los intentos de solicitud repetidos se realizan 3 veces;
  3. Y cada intento se considera infructuoso si el tiempo de respuesta supera los 3 segundos.

Por lo tanto, hemos logrado la optimización, porque el usuario no tiene que esperar más de 8 segundos y haremos tres nuevos intentos para obtener una respuesta en caso de fallas, aumentando la posibilidad 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 terminado:


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 no habrá más experimentos en esta práctica, aprox. Transl.) , Elimine 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 

Disyuntores y patrones de mamparo


Estamos hablando de dos patrones importantes en la arquitectura de microservicios que le permiten lograr servicios de autocuración .

El disyuntor ("disyuntor") se usa para detener las solicitudes que llegan a una instancia de un servicio que no se considera saludable y restaurarlo mientras las solicitudes del cliente se redirigen a instancias saludables de este servicio (lo que aumenta el porcentaje de respuestas exitosas). (Nota: Aquí se puede encontrar una descripción más detallada del patrón, por ejemplo).

Bulkhead ("partición") aísla las fallas de servicio de la derrota de todo el sistema. Por ejemplo, el servicio B está roto y otro servicio (el cliente del servicio B) realiza una solicitud al servicio B, como resultado de lo cual utilizará su grupo de subprocesos y no podrá atender otras solicitudes (incluso si no están relacionadas con el servicio B). (Nota: Aquí se puede encontrar una descripción más detallada del patrón, por ejemplo).

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

PD del traductor


Lea también en nuestro blog:

Source: https://habr.com/ru/post/440378/


All Articles