EndpointSlices

La API de EndpointSlice es el mecanismo que Kubernetes utiliza para permitir que tu Servicio escale para manejar un gran número de backends, y permite que el clúster actualice tu lista de backends saludables eficientemente.
FEATURE STATE: Kubernetes v1.21 [stable]

La API de EndpointSlice de Kubernetes proporciona una forma de rastrear los endpoints de red dentro de un clúster Kubernetes. EndpointSlices ofrece una alternativa más escalable y extensible a Endpoints.

EndpointSlice API

En Kubernetes, un EndpointSlice contiene referencias a un conjunto de endpoints de red. El plano de control crea automáticamente EndpointSlices para cualquier Servicio de Kubernetes que tenga especificado un selector. Estos EndpointSlices incluyen referencias a todos los Pods que coinciden con el selector de Servicio. Los EndpointSlices agrupan los endpoints de la red mediante combinaciones únicas de protocolo, número de puerto y nombre de Servicio.

El nombre de un objeto EndpointSlice debe ser un nombre de subdominio DNS válido.

A modo de ejemplo, a continuación se muestra un objeto EndpointSlice de ejemplo, propiedad del Servicio example de Kubernetes.

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-abc
  labels:
    kubernetes.io/service-name: example
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    nodeName: node-1
    zone: us-west2-a

Por defecto, el plano de control crea y gestiona EndpointSlices para que no tengan más de 100 endpoints cada una. Puedes configurar esto con la bandera de funcionalidad --max-endpoints-per-slice kube-controller-manager hasta un máximo de 1000.

EndpointSlices puede actuar como la fuente de verdad kube-proxy sobre cómo enrutar el tráfico interno.

Tipos de dirección

EndpointSlices admite tres tipos de direcciones:

  • IPv4
  • IPv6
  • FQDN (Fully Qualified Domain Name)

Cada objeto EndpointSlice representa un tipo de dirección IP específico. Si tienes un servicio disponible a través de IPv4 e IPv6, habrá al menos dos objetos EndpointSlice (uno para IPv4 y otro para IPv6).

Condiciones

La API EndpointSlice almacena condiciones sobre los endpoints que pueden ser útiles para los consumidores. Las tres condiciones son ready, serving y terminating.

Ready

ready es una condición que corresponde a la condición Ready de un Pod. Un Pod en ejecución con la condición Ready establecida a True debería tener esta condición EndpointSlice también establecida a true. Por razones de compatibilidad, ready NUNCA es true cuando un Pod está terminando. Los consumidores deben referirse a la condición serving para inspeccionar la disponibilidad de los Pods que están terminando. La única excepción a esta regla son los servicios con spec.publishNotReadyAddresses a true. Los endpoints de estos servicios siempre tendrán la condición ready a true.

Serving

FEATURE STATE: Kubernetes v1.26 [stable]

La condición serving es casi idéntica a la condición ready. La diferencia es que los consumidores de la API EndpointSlice deben comprobar la condición serving si se preocupan por la disponibilidad del pod mientras el pod también está terminando.

Terminating

FEATURE STATE: Kubernetes v1.22 [beta]

Terminating es una condición que indica si un endpoint está terminando. En el caso de los pods, se trata de cualquier pod que tenga establecida una marca de tiempo de borrado.

Información sobre topología

Cada endpoint dentro de un EndpointSlice puede contener información topológica relevante. La información de topología incluye la ubicación del endpoint e información sobre el Nodo y la zona correspondientes. Estos están disponibles en los siguientes campos por endpoint en EndpointSlices:

  • nodeName - El nombre del Nodo en el que se encuentra este endpoint.
  • zone - La zona en la que se encuentra este endpoint.

Administración

En la mayoría de los casos, el plano de control (concretamente, el endpoint slice controller) crea y gestiona objetos EndpointSlice. Existe una variedad de otros casos de uso para EndpointSlices, como implementaciones de servicios Mesh, que podrían dar lugar a que otras entidades o controladores gestionen conjuntos adicionales de EndpointSlices.

Para garantizar que varias entidades puedan gestionar EndpointSlices sin interferir unas con otras, Kubernetes define el parámetro label endpointslice.kubernetes.io/managed-by, que indica la entidad que gestiona un EndpointSlice. El controlador de endpoint slice establece endpointslice-controller.k8s.io como valor para esta etiqueta en todos los EndpointSlices que gestiona. Otras entidades que gestionen EndpointSlices también deben establecer un valor único para esta etiqueta.

Propiedad

En la mayoría de los casos de uso, los EndpointSlices son propiedad del Servicio para el que el objeto EndpointSlices rastree los endpoints. Esta propiedad se indica mediante una referencia de propietario en cada EndpointSlice, así como una etiqueta kubernetes.io/service-name que permite búsquedas sencillas de todos los EndpointSlices que pertenecen a un Servicio.

Replicación de EndpointSlice

En algunos casos, las aplicaciones crean recursos Endpoints personalizados. Para garantizar que estas aplicaciones no tengan que escribir simultáneamente en recursos Endpoints y EndpointSlice, el plano de control del clúster refleja la mayoría de los recursos Endpoints en los EndpointSlices correspondientes.

El plano de control refleja los recursos de los Endpoints a menos que:

  • El recurso Endpoints tenga una etiqueta endpointslice.kubernetes.io/skip-mirror con el valor en true.
  • El recurso Endpoints tenga una anotación control-plane.alpha.kubernetes.io/leader.
  • El recurso Service correspondiente no exista.
  • El recurso Service correspondiente tiene un selector no nulo.

Los recursos Endpoints individuales pueden traducirse en múltiples EndpointSlices. Esto ocurrirá si un recurso Endpoints tiene múltiples subconjuntos o incluye endpoints con múltiples familias IP (IPv4 e IPv6). Se reflejará un máximo de 1000 direcciones por subconjunto en EndpointSlices.

Distribución de EndpointSlices

Cada EndpointSlice tiene un conjunto de puertos que se aplica a todos los endpoints dentro del recurso. Cuando se utilizan puertos con nombre para un Servicio, los Pods pueden terminar con diferentes números de puerto de destino para el mismo puerto con nombre, requiriendo diferentes EndpointSlices. Esto es similar a la lógica detrás de cómo se agrupan los subconjuntos con Endpoints.

El plano de control intenta llenar los EndpointSlices tanto como sea posible, pero no los reequilibra activamente. La lógica es bastante sencilla:

  1. Iterar a través de los EndpointSlices existentes, eliminar los endpoints que ya no se deseen y actualizar los endpoints coincidentes que hayan cambiado.
  2. Recorrer los EndpointSlices que han sido modificados en el primer paso y rellenarlos con los nuevos endpoints necesarios.
  3. Si aún quedan nuevos endpoints por añadir, intente encajarlos en un slice que no se haya modificado previamente y/o cree otros nuevos.

Es importante destacar que el tercer paso prioriza limitar las actualizaciones de EndpointSlice sobre una distribución perfectamente completa de EndpointSlices. Por ejemplo, si hay 10 nuevos endpoints que añadir y 2 EndpointSlices con espacio para 5 endpoints más cada uno, este enfoque creará un nuevo EndpointSlice en lugar de llenar los 2 EndpointSlices existentes. En otras palabras, es preferible una única creación de EndpointSlice que múltiples actualizaciones de EndpointSlice.

Con kube-proxy ejecutándose en cada Nodo y vigilando los EndpointSlices, cada cambio en un EndpointSlice se vuelve relativamente caro ya que será transmitido a cada Nodo del clúster. Este enfoque pretende limitar el número de cambios que necesitan ser enviados a cada Nodo, incluso si puede resultar con múltiples EndpointSlices que no están llenos.

En la práctica, esta distribución menos que ideal debería ser poco frecuente. La mayoría de los cambios procesados por el controlador EndpointSlice serán lo suficientemente pequeños como para caber en un EndpointSlice existente, y si no, es probable que pronto sea necesario un nuevo EndpointSlice de todos modos. Las actualizaciones continuas de los Deployments también proporcionan un reempaquetado natural de los EndpointSlices con todos los Pods y sus correspondientes endpoints siendo reemplazados.

Endpoints duplicados

Debido a la naturaleza de los cambios de EndpointSlice, los endpoints pueden estar representados en más de un EndpointSlice al mismo tiempo. Esto ocurre de forma natural, ya que los cambios en diferentes objetos EndpointSlice pueden llegar a la vigilancia / caché del cliente de Kubernetes en diferentes momentos.

Comparación con endpoints

La API Endpoints original proporcionaba una forma simple y directa de rastrear los endpoints de red en Kubernetes. A medida que los clústeres de Kubernetes y los Services crecían para manejar más tráfico y enviar más tráfico a más Pods backend, las limitaciones de la API original se hicieron más visibles. Más notablemente, estos incluyen desafíos con la ampliación a un mayor número de endpoints de red.

Dado que todos los endpoints de red para un Servicio se almacenaban en un único objeto Endpoint, esos objetos Endpoints podían llegar a ser bastante grandes. Para los Services que permanecían estables (el mismo conjunto de endpoints durante un largo período de tiempo), el impacto era menos notable; incluso entonces, algunos casos de uso de Kubernetes no estaban bien servidos.

Cuando un Service tenía muchos Endpoints de backend y la carga de trabajo se escalaba con frecuencia o se introducían nuevos cambios con frecuencia, cada actualización del objeto Endpoint para ese Service suponía mucho tráfico entre los componentes del clúster de Kubernetes (dentro del plano de control y también entre los nodos y el servidor de API). Este tráfico adicional también tenía un coste en términos de uso de la CPU.

Con EndpointSlices, la adición o eliminación de un único Pod desencadena el mismo número de actualizaciones a los clientes que están pendientes de los cambios, pero el tamaño de esos mensajes de actualización es mucho menor a gran escala.

EndpointSlices también ha permitido innovar en torno a nuevas funciones, como las redes de doble pila y el enrutamiento con conocimiento de la topología.

Siguientes pasos