Capítulo 4. Añadir cargas de trabajo a la malla

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Conseguir que Linkerd funcione en tu clúster es un gran primer paso. Pero no tiene sentido ejecutar Linkerd sin nada más: para obtener un valor real de tu clúster Linkerd, necesitarás que las cargas de trabajo se ejecuten en tu malla de servicios. En este capítulo te mostraremos cómo hacerlo exactamente.

Cargas de trabajo frente a servicios

Hablaremos mucho de "cargas de trabajo" en este capítulo, pero a veces también hablaremos de "servicios" y a veces de "Servicios". Por desgracia, estas tres cosas tienen significados ligeramente distintos:

Servicio

Un recurso de Kubernetes muy utilizado para controlar cómo Kubernetes asigna nombres DNS y direcciones IP a los servicios (ver Figura 4-1).

Carga de trabajo

Una cosa que realmente hace trabajo en tu nombre. Una carga de trabajo recibe peticiones a través de la red y ejecuta código para realizar acciones. En Kubernetes, suele tratarse de uno o varios Pods (para proporcionar el cálculo), a menudo gestionados por un recurso Implementación o DaemonSet, más uno o varios Servicios (para gestionar los nombres y las direcciones IP), como se ilustra en la Figura 4-1.

servicio

Un término menos formal que podría referirse tanto a un Servicio como a una carga de trabajo, dependiendo del contexto. Esta falta de precisión muestra sólo uno de los muchos casos en los que la terminología de Kubernetes es mucho más confusa de lo que nos gustaría.

luar 0401
Figura 4-1. La carga de trabajo diferenciada del Servicio

Como desarrollador de aplicaciones, normalmente puedes decir simplemente "servicio" y confiar en que a la gente le parecerá bien la ambigüedad. Por desgracia, a menudo tenemos que ser más precisos cuando hablamos de mallas de servicios, de ahí que aquí hablemos de cargas de trabajo en lugar de servicios.

¿Qué significa añadir una carga de trabajo a la malla?

"Añadir una carga de trabajo a la malla" significa en realidad "añadir el sidecar Linkerd a cada uno de los Pods de tu carga de trabajo", como se muestra en la Figura 4-2.

En última instancia, esto significa cambiar la definición del Pod para incluir el contenedor sidecar. Aunque podrías hacerlo editando manualmente el YAML que define el Pod, es mucho más fácil y seguro dejar que Linkerd haga el trabajo pesado.

Linkerd incluye un controlador de admisión a Kubernetes llamadolinkerd-proxy-injector. Su trabajo, como es lógico, consiste en inyectar proxies Linkerd en los Pods de carga de trabajo. Por supuesto, no lo hace a ciegas, sino que busca anotaciones de Kubernetes que le indiquen qué Pods deben inyectarse, como se muestra en la Figura 4-3.

luar 0402
Figura 4-2. Añadir una carga de trabajo a la malla
luar 0403
Figura 4-3. El inyector proxy

Inyectar cargas de trabajo individuales

La forma más habitual de gestionar la inyección de es añadir la anotación linkerd.io/inject: enabled directamente al propio Pod, normalmente añadiendo la anotación a la plantilla del Pod en una Implementación, DaemonSet, etc. Cada vez quelinkerd-proxy-injector vea un nuevo Pod con esta anotación, inyectará el sidecar proxy en el Pod por ti.

Vale la pena señalar que el valor de la anotación es importante:enabled significa hacer una inyección sidecar normal. Veremos otros valores en breve.

Todas las cápsulas son iguales

No importa qué tipo de recurso se esté utilizando para crear el Pod. Implementaciones, DaemonSets, ReplicaSets hechos a mano, recursos Argo Rollouts: todos ellos crean sus Pods exactamente de la misma manera. Lo que nota el inyector Linkerd es que existe un nuevo Pod, no lo que provocó su creación.

Inyectar todas las cargas de trabajo en un espacio de nombres

Puedes añadir la anotación linkerd.io/inject a un Espacio de Nombres, en lugar de a un Pod. Una vez hecho esto, todo nuevo Pod creado en ese namespace será inyectado (y, de nuevo, no importa qué causa la creación del nuevo Pod).

Esto puede ser muy útil para situaciones en las que la automatización está creando Pods, pero es difícil o propenso a errores modificar las anotaciones en los propios Pods. Por ejemplo, algunos controladores de entrada volverán a crear Implementaciones cada vez que cambies un recurso; en lugar de perder el tiempo modificando laboriosamente la plantilla Pod utilizada por el controlador de entrada (si es que es posible), puedes simplemente anotar el Espacio de Nombres en el que se crearán las Implementaciones.

linkerd.io/inject Valores

El valor de la anotación linkerd.io/inject sí importa: no se trata sólo de tener una cadena no vacía. Hay tres valores específicos que son significativos:

linkerd.io/inject: enabled

El caso más común:linkerd-proxy-injector añadirá un contenedor proxy al Pod y le dirá al proxy que se ejecute en su modo "normal".

linkerd.io/inject: ingress

linkerd-proxy-injector añadirá un contenedor proxy al Pod, pero el proxy se ejecutará en modo "entrada" (del que hablaremos en el Capítulo 5).

linkerd.io/inject: disabled

Esto indica explícitamente a linkerd-proxy-injector queno añada el sidecar proxy, aunque haya una anotación Namespace que, de otro modo, diría que se añada el sidecar.

Hablaremos más sobre el modo de entrada en el Capítulo 5: es una solución para los controladores de entrada que sólo saben enrutar solicitudes directamente a los puntos finales de carga de trabajo. En la mayoría de los casos, debes utilizar linkerd.io/inject: enabled para obtener el modo "normal".

¿Por qué podrías decidir no añadir una carga de trabajoa la malla?

En general:

  • Siempre querrás añadir las cargas de trabajo de tu aplicación a la malla.

  • Nunca querrás añadir infraestructura de clúster a la malla.

Así, por ejemplo, las cosas en el espacio de nombres kube-system nunca se inyectan. Todos estos Pods están diseñados para protegerse a sí mismos independientemente de lo que ocurra, y algunos de ellos necesitan estar seguros de que no hay nada entre ellos y la capa de red, por lo que no deberías inyectarlos.

Del mismo modo, un webhook de conversión de Kubernetes (como se muestra en el espacio de nombres application-codede la Figura 4-3) generalmente no debería estar en la malla. El propio mecanismo de webhook ya hace demandas específicas en torno a TLS, y la malla no ayudará con eso. (Otro buen ejemplo son las implementaciones CNI: necesitan acceso directo a la capa de red y no deberían inyectarse.

Por otro lado, las cargas de trabajo que forman parte de tu aplicación que se ejecuta en el clúster deben inyectarse siempre en la malla. Todas estas directrices se muestran en la Figura 4-4.

luar 0404
Figura 4-4. Inyecta la aplicación, no la infraestructura

Otras opciones de configuración del proxy

Aunque la anotación básica linkerd.io/inject es la única opción de configuración del proxy que debes proporcionar, en realidad hay bastantes otras cosas que puedes configurar sobre el proxy. Puedes encontrar la lista completa enla documentación sobre la configuración del proxy Linkerd, pero hay dos áreas que merece mucho la pena conocer desde el principio: la detección de protocolos ylos límites de recursos de Kubernetes.

Detección de Protocolos

Como comentamos en el Capítulo 1, Linkerd pone mucho empeño en la simplicidad operativa; siempre que es posible, Linkerd intenta asegurarse de que las cosas simplemente funcionen cuando introduces tu aplicación en la malla. La detección de protocolos es una parte crítica de esto, porque Linkerd tiene que conocer el protocolo que se utiliza en una conexión para gestionar correctamente la conexión, como se muestra en la Figura 4-5.

luar 0405
Figura 4-5. Detección de protocolo

Hay varias razones por las que Linkerd (o cualquier otra malla) necesita conocer el protocolo que se utiliza a través del cable. Mencionaremos sólo algunas de ellas:

Observabilidad

Linkerd no puede proporcionar métricas adecuadas sin comprender el flujo del protocolo. Identificar el principio y el final de una solicitud es crucial para medir la tasa de solicitudes y la latencia. Leer el estado de una solicitud es fundamental para medir la tasa de éxito.

Fiabilidad

Cualquier función de fiabilidad más allá de la más básica requiere conocer el protocolo en vuelo. Considera el equilibrio de carga, por ejemplo: si Linkerd no conoce el protocolo, sólo puede hacer un equilibrio de carga basado en la conexión, en el que una conexión TCP entrante se asigna a un Pod de carga de trabajo específico.

Sin embargo, el equilibrio de carga basado en conexiones no funciona muy bien para protocolos como HTTP/2 y gRPC. En estos protocolos, una única conexión de larga duración puede transportar muchas solicitudes, con múltiples solicitudes activas al mismo tiempo. Linkerd puede mejorar drásticamente la fiabilidad y el rendimiento asignando solicitudes individuales a Pods de carga de trabajo, en lugar de fijar una conexión entera a un Pod. (Es un hecho divertido de Linkerd que haga esto automáticamente, sin configuración alguna; sólo tienes que instalar Linkerd, y obtendrás esto gratis).

Seguridad

Si una carga de trabajo establece una conexión TLS con otra carga de trabajo, Linkerd no debería intentar volver a cifrarla. Tampoco debería intentar hacer nada extravagante con el equilibrio de carga, ya que no podrá ver nada dentro de la conexión. (Esto implica que obtendrás los mejores resultados con Linkerd haciendo que tus cargas de trabajo no utilicen TLS cuando se conecten entre sí: ¡deja que Linkerd haga mTLS por ti!)

Cuando la detección de protocolos va mal

La detección automática de protocolos tiene una limitación importante: sólo puede funcionar para protocolos en los que la entidad que establece la conexión es también la primera en enviar datos(protocolos en los que el cliente habla primero ). Fracasará en los protocolos en los que la entidad que recibe la conexión sea la primera en enviar datos( protocolos en losque primero habla el servidor ).

La razón de esta limitación es que hasta que Linkerd no conozca el protocolo, no puede tomar decisiones razonables sobre cómo hacer el equilibrio de carga, por lo que no puede decidir a qué servidor conectarse, ¡por lo que no puede averiguar qué dirá el servidor! Todos los proxy tienen este frustrante problema circular.

En el mundo nativo de la nube, muchos -¿quizás la mayoría?- de los protocolos habituales de son, afortunadamente, protocolos en los que el cliente habla primero; por ejemplo, HTTP, gRPC y el propio TLS son todos protocolos en los que el cliente habla primero. Por desgracia, hay algunos protocolos importantes en los que el servidor habla primero: SMTP, MySQL y Redis son algunos ejemplos.

Si Linkerd no puede detectar el protocolo, asumirá que se trata de una conexión TCP sin procesar, simplemente porque ése es el mínimo común denominador que siempre funcionará. El problema es que, en el caso de los protocolos "server-speaks-first", Linkerd esperará 10 segundos antes de asumir que no podrá detectar el protocolo, y ese retraso de 10 segundos obviamente no es lo que quieres. Para evitarlo, tienes que decirle a Linkerd que debe o bien saltarse la conexión por completo o tratarla como opaca.

Puertos opacos frente a puertos omitidos

Cuando le dices a Linkerd que omita una conexión, le estás diciendo que no tenga absolutamente nada que ver con esa conexión. De hecho, los proxies Linkerd no tocan la conexión en absoluto: los paquetes fluyen directamente de carga de trabajo a carga de trabajo.

Esto significa que Linkerd no puede hacer mTLS, equilibrio de carga, recopilación de métricas ninada parecido. De hecho, la conexión se produce totalmente fuera de la malla.

En cambio, una conexión opaca sí pasa por los proxies de Linkerd, , lo que significa que se transmite por mTLS. Sigue estando encriptada y Linkerd sigue aplicando cualquier política que se haya configurado, pero sólo obtendrás métricas por conexión y equilibrio de carga (porque Linkerd sabe que no puede ver en el flujo para examinar las solicitudes individuales).

Esta distinción se muestra en la Figura 4-6.

luar 0406
Figura 4-6. Puertos opacos frente a puertos de salto

Todo esto implica que si necesitas que utilice protocolos de "habla primero el servidor", es mejor marcarlos como opacos, en lugar de omitirlos por completo. Omitirlas sólo debería ser necesario cuando el destino del tráfico no forme parte de tu malla. Como las conexiones opacas siguen dependiendo de un proxy Linkerd para hacer mTLS, ¡no pueden funcionar si no hay un proxy para recibir la conexión!

Configurar la detección de protocolos

Hay dos formas de informar a Linkerd sobre los protocolos. Puedes utilizar un recurso Servidor, del que hablaremos cuando hablemos de políticas en el Capítulo 8, o puedes utilizar las siguientes anotaciones para marcar puertos concretos como opacos u omitidos:

config.linkerd.io/opaque-ports

Las conexiones hacia o desde estos puertos siempre se tratarán como opacas.

config.linkerd.io/skip-inbound-ports

Las conexiones que lleguen a esta carga de trabajo por estos puertos siempre se omitirán.

config.linkerd.io/skip-outbound-ports

Las conexiones que abandonen esta carga de trabajo en estos puertos se omitirán siempre.

Todos ellos aceptan listas separadas por comas de números de puerto o rangos de puertos, por lo que todos los siguientes son legales:

config.linkerd.io/opaque-ports: 25

Esto tratará sólo el puerto 25 como opaco.

config.linkerd.io/skip-inbound-ports: 3300,9900

Esto omitirá las conexiones que entren por el puerto 3300 o 9900.

config.linkerd.io/skip-inbound-ports: 8000-9000

Esto omitirá las conexiones que entren por cualquier puerto entre 8000 y 9000, ambos inclusive.

config.linkerd.io/skip-outbound-ports: 25,587,8000-9000

Esto omitirá las conexiones que salgan por el puerto 25, el puerto 587 o cualquier puerto entre 8000 y 9000, ambos inclusive.

También existe la opción config.linkerd.io/skip-subnets, que omite cualquier conexión hacia o desde cualquier subred de la lista. Su argumento es una lista separada por comas de rangos CIDR (Classless Inter-Domain Routing), por ejemplo, config.linkerd.io/skip-subnets: 10.0.0.0/8,192.168.1.0/24.

Puertos opacos por defecto

A partir de Linkerd 2.12, varios puertos están marcados como opacos por defecto (para más detalles, consulta la lista en "Puertos opacos por defecto" ).

Los puertos por defecto están pensados para permitir que varios protocolos server-speaks-first, como MySQL y SMTP, funcionen sin problemas con Linkerd. Si utilizas estos puertos para protocolos cliente-speaks-first, tendrás que utilizar un recurso Servidor para anular el puerto por defecto (o -mejor- ¡elige un puerto diferente para tu protocolo cliente-speaks-first!).

Límites de recursos de Kubernetes

En comparación con la detección de protocolos, Los límites de recursos de Kubernetes son mucho más sencillos. Hay un sencillo conjunto de anotaciones a establecer que te permitirán especificar solicitudes y límites de recursos, como se muestra en la Tabla 4-1.

Tabla 4-1. Anotaciones de Linkerd para solicitudes y límites de recursos
Anotación Efecto

config.linkerd.io/proxy-cpu-limit

Cantidad máxima de unidades CPU que puede utilizar el proxy sidecar

config.linkerd.io/proxy-cpu-request

Cantidad de unidades CPU que solicita el proxy sidecar

config.linkerd.io/proxy-ephemeral-storage-limit

Sirve para anular la configuración de limitEphemeralStorage

config.linkerd.io/proxy-ephemeral-storage-request

Sirve para anular la configuración de requestEphemeralStorage

config.linkerd.io/proxy-memory-limit

Cantidad máxima de memoria que puede utilizar el proxy sidecar

config.linkerd.io/proxy-memory-request

Cantidad de memoria que solicita el proxy sidecar

Resumen

Ahí lo tienes: la guía de principio a fin para conseguir que tus cargas de trabajo sean una parte eficaz de la malla Linkerd. Esperemos que ahora tengas una buena comprensión de cómo hacer que todo funcione, y de los problemas que surgen por el camino (como los protocolos server-speaks-first). Lo siguiente es conseguir que Linkerd y los controladores de entrada funcionen bien juntos.

Get Linkerd: En marcha now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.