Benchmark de consumo de CPU para Istio y Linkerd

Benchmark de consumo de CPU para Istio y Linkerd

Introducción

En Shopify comenzamos a implementar Istio como un service mesh. En general, todo funciona bien, excepto por una cosa: es caro.

En en los benchmarks publicados para Istio se dice:

Con Istio 1.1, el proxy consume aproximadamente 0,6 vCPU (núcleos virtuales) por cada 1000 solicitudes por segundo.

Para la primera región en el service mesh (con 2 proxies en cada lado de la conexión), necesitaremos 1200 núcleos solo para los proxies, calculando un millón de solicitudes por segundo. Según la calculadora de costos de Google, esto suma aproximadamente $40/mes/núcleo para la configuración n1-standard-64, lo que significa que solo esta región nos costará más de 50 mil dólares al mes por 1 millón de solicitudes por segundo.

Aiven Sim (Ivan Sim) comparó visualmente las latencias del service mesh del año pasado y prometió lo mismo para memoria y CPU, pero no pudo hacerlo:

Parece que values-istio-test.yaml aumentará significativamente las solicitudes a la CPU. Si he hecho los cálculos correctamente, se necesitan alrededor de 24 núcleos de CPU para el panel de control y 0,5 CPU para cada proxy. No tengo suficientes. Repetiré las pruebas cuando me asignen más recursos.

Quería comprobar por mí mismo cuán similares son las métricas de Istio a las de otro service mesh de código abierto: Linkerd.

Instalación del service mesh

Primero, instalé en el clúster SuperGloo:

$ supergloo init
instalando supergloo versión 0.3.12
usando uri de gráfico https://storage.googleapis.com/supergloo-helm/charts/supergloo-0.3.12.tgz
configmap/sidecar-injection-resources creado
serviceaccount/supergloo creado
serviceaccount/discovery creado
serviceaccount/mesh-discovery creado
clusterrole.rbac.authorization.k8s.io/discovery creado
clusterrole.rbac.authorization.k8s.io/mesh-discovery creado
clusterrolebinding.rbac.authorization.k8s.io/supergloo-role-binding creado
clusterrolebinding.rbac.authorization.k8s.io/discovery-role-binding creado
clusterrolebinding.rbac.authorization.k8s.io/mesh-discovery-role-binding creado
deployment.extensions/supergloo creado
deployment.extensions/discovery creado
deployment.extensions/mesh-discovery creado
¡instalación exitosa!

Usé SuperGloo porque simplifica significativamente la carga inicial del service mesh. Prácticamente no tuve que hacer nada. En producción no usamos SuperGloo, pero para esta tarea es perfecto. Solo tuve que ejecutar un par de comandos para cada service mesh. Usé dos clústeres para aislacion — uno para Istio y otro para Linkerd.

El experimento se realizó en Google Kubernetes Engine. Usé Kubernetes 1.12.7-gke.7 y un grupo de nodos n1-standard-4 con escalado automático de nodos (mínimo 4, máximo 16).

Luego instalé ambos service meshes desde la línea de comandos.

Primero Linkerd:

$ supergloo install linkerd --name linkerd
+---------+--------------+---------+---------------------------+
| INSTALL |     TYPE     | STATUS  |          DETAILS          |
+---------+--------------+---------+---------------------------+
| linkerd | Linkerd Mesh | Pending | enabled: true             |
|         |              |         | version: stable-2.3.0     |
|         |              |         | namespace: linkerd        |
|         |              |         | mtls enabled: true        |
|         |              |         | auto inject enabled: true |
+---------+--------------+---------+---------------------------+

Luego Istio:

$ supergloo install istio --name istio --installation-namespace istio-system --mtls=true --auto-inject=true
+---------+------------+---------+---------------------------+
| INSTALL |    TYPE    | STATUS  |          DETAILS          |
+---------+------------+---------+---------------------------+
| istio   | Istio Mesh | Pending | enabled: true             |
|         |            |         | version: 1.0.6            |
|         |            |         | namespace: istio-system   |
|         |            |         | mtls enabled: true        |
|         |            |         | auto inject enabled: true |
|         |            |         | grafana enabled: true     |
|         |            |         | prometheus enabled: true  |
|         |            |         | jaeger enabled: true      |
+---------+------------+---------+---------------------------+

El crash-loop tomó unos minutos y luego los paneles de control se estabilizaron.

(Nota. SuperGloo solo soporta Istio 1.0.x por ahora. Repetí el experimento con Istio 1.1.3, pero no noté ninguna diferencia significativa.)

Configuración de la inyección automática de Istio

Para que Istio instale el sidecar Envoy, utilizamos un inyectador de sidecar — MutatingAdmissionWebhook. No hablaremos de él en este artículo. Solo diré que es un controlador que supervisa el acceso de todos los nuevos pods y agrega dinámicamente un sidecar y un initContainer que se encarga de las tareas iptables.

En Shopify, escribimos nuestro controlador de acceso para la inyección de sidecars, pero en este benchmark utilicé el controlador que viene con Istio. El controlador por defecto inyecta sidecars cuando hay una etiqueta en el espacio de nombres istio-injection: enabled:

$ kubectl label namespace irs-client-dev istio-injection=enabled
namespace/irs-client-dev labeled

$ kubectl label namespace irs-server-dev istio-injection=enabled
namespace/irs-server-dev labeled

Configuración de la inyección automática de Linkerd

Para configurar la inyección de sidecars de Linkerd, utilizamos anotaciones (las añadí manualmente a través de kubectl edit):

metadata:
  annotations:
    linkerd.io/inject: enabled

$ k edit ns irs-server-dev 
namespace/irs-server-dev edited

$ k get ns irs-server-dev -o yaml
apiVersion: v1
kind: Namespace
metadata:
  annotations:
    linkerd.io/inject: enabled
  name: irs-server-dev
spec:
  finalizers:
  - kubernetes
status:
  phase: Active

Simulador de tolerancia a fallos de Istio

Hemos creado un simulador de resistencia a fallos de Istio para experimentar con el tráfico exclusivo de Shopify. Necesitábamos una herramienta para crear una topología arbitraria que representara una parte específica del grafo de nuestro servicio con una configuración dinámica para modelar cargas de trabajo concretas.

La infraestructura de Shopify enfrenta una gran carga durante las ventas flash. En este contexto, Shopify recomienda a los vendedores llevar a cabo tales ventas más a menudo. Algunos clientes grandes a veces avisan sobre una venta flash planificada. Otros las realizan de forma inesperada para nosotros en cualquier momento del día y de la noche.

Queríamos que nuestro simulador de resistencia a fallos modelara flujos de trabajo que correspondieran a las topologías y cargas de trabajo que habían causado la sobrecarga de la infraestructura de Shopify en el pasado. El objetivo principal de usar un service mesh es que necesitamos fiabilidad y resistencia a fallos a nivel de red, y es importante para nosotros que el service mesh maneje eficazmente las cargas que anteriormente interrumpían el funcionamiento de los servicios.

En la base del simulador de resistencia a fallos se encuentra un nodo de trabajo que actúa como nodo de service mesh. El nodo de trabajo puede configurarse de manera estática al iniciar o dinámicamente a través de la API REST. Usamos la configuración dinámica de nodos de trabajo para crear flujos de trabajo en forma de pruebas regresivas.

Aquí hay un ejemplo de tal proceso:

  • Lanzamos 10 servidores como bar un servicio que retorna una respuesta 200/OK después de 100 ms.
  • Lanzamos 10 clientes, cada uno enviando 100 solicitudes por segundo a bar.
  • Cada 10 segundos, retiramos 1 servidor y monitoreamos errores 5xx en el cliente.

Al final del flujo de trabajo, revisamos los registros y métricas y verificamos si la prueba fue superada. Así, aprendemos sobre el rendimiento de nuestro service mesh y realizamos una prueba regresiva para validar nuestras hipótesis sobre la resistencia a fallos.

(Nota: estamos considerando abrir el código fuente del simulador de resistencia a fallos de Istio, pero aún no estamos listos para ello.)

Simulador de resistencia a fallos de Istio para el benchmark del service mesh

Configuramos varios nodos de trabajo del simulador:

  • irs-client-loadgen: 3 réplicas que envían 100 solicitudes por segundo a irs-client.
  • irs-client: 3 réplicas que reciben la solicitud, esperan 100 ms y redirigen la solicitud a irs-server.
  • irs-server: 3 réplicas que devuelven 200/OK después de 100 ms.

Con esta configuración, podemos medir un flujo de tráfico estable entre 9 puntos finales. Los sidecars en irs-client-loadgen y irs-server reciben 100 solicitudes por segundo, y irs-client — 200 (entrantes y salientes).

Estamos monitoreando el uso de recursos a través de DataDog, ya que no tenemos un clúster de Prometheus.

Resultados

Paneles de control

Primero, estudiamos el consumo de CPU.

Benchmark de consumo de CPU para Istio y Linkerd
Panel de control de Linkerd ~22 mil millones

Benchmark de consumo de CPU para Istio y Linkerd
Panel de control de Istio: ~750 mil millones

El panel de control de Istio utiliza aproximadamente 35 veces más recursos de CPU, que Linkerd. Por supuesto, todo está configurado por defecto, y muchos recursos de CPU son consumidos aquí por istio-telemetry (se puede desactivar renunciando a algunas funciones). Si se elimina este componente, aún se obtienen más de 100 mil millones, es decir, 4 veces más, que Linkerd.

Proxy sidecar

Luego revisamos el uso de proxies. Aquí debería haber una dependencia lineal del número de solicitudes, pero para cada sidecar hay algunos gastos generales que afectan la curva.

Benchmark de consumo de CPU para Istio y Linkerd
Linkerd: ~100 mil millones para irs-client, ~50 mil millones para irs-client-loadgen

Los resultados parecen lógicos, ya que el proxy client recibe el doble de tráfico que el proxy loadgen: por cada solicitud saliente de loadgen, hay una solicitud entrante y una saliente en el client.

Benchmark de consumo de CPU para Istio y Linkerd
Istio/Envoy: ~155 mil millones para irs-client, ~75 mil millones para irs-client-loadgen

Vemos resultados similares para los sidecars de Istio.

Pero en general, los proxies de Istio/Envoy consumen aproximadamente un 50% más de recursos de CPU, que Linkerd.

Vemos el mismo patrón del lado del servidor:

Benchmark de consumo de CPU para Istio y Linkerd
Linkerd: ~50 mil millones para irs-server

Benchmark de consumo de CPU para Istio y Linkerd
Istio/Envoy: ~80 mil millones para irs-server

Del lado del servidor, el sidecar de Istio/Envoy consume aproximadamente un 60% más de recursos de CPU, que Linkerd.

Conclusión

El proxy de Istio Envoy consume más del 50% de CPU que Linkerd en nuestra carga de trabajo simulada. El panel de control de Linkerd consume muchos menos recursos que Istio, especialmente en lo que respecta a los componentes principales.

Todavía estamos pensando en cómo reducir estos costos. ¡Si tienes ideas, compártelas!

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster