Información sobre los servicios de Kubernetes


En esta página se describen los diferentes tipos de servicios de Kubernetes y cómo usa Google Kubernetes Engine (GKE) los servicios para agrupar los endpoints de los pods.

Aprenderás a crear servicios mediante ejemplos de YAML. Cada tipo de servicio usa la dirección IP estable del servicio para reducir la complejidad de tareas específicas de redes y comunicaciones. Por ejemplo, aprenderás a usar varios puertos y endpoints, así como a configurar opciones de pila única o doble que admitan IPv4 e IPv6.

Para saber cómo crear un servicio, consulta Exponer aplicaciones mediante servicios.

Esta página está dirigida a operadores y desarrolladores que aprovisionan y configuran recursos en la nube, y despliegan aplicaciones y servicios. Para obtener más información sobre los roles habituales y las tareas de ejemplo que se mencionan en el contenido, consulta Roles y tareas habituales de los usuarios de GKE. Google Cloud

¿Qué es un servicio de Kubernetes?

La idea de un servicio es agrupar un conjunto de endpoints de pods en un único recurso. Puedes configurar varias formas de acceder a la agrupación. De forma predeterminada, obtienes una dirección IP de clúster estable que los clientes del clúster pueden usar para ponerse en contacto con los pods del servicio. Un cliente envía una solicitud a la dirección IP estable y la solicitud se dirige a uno de los pods del servicio.

Un servicio identifica sus pods miembros con un selector. Para que un pod sea miembro del servicio, debe tener todas las etiquetas especificadas en el selector. Una etiqueta es un par clave-valor arbitrario que se adjunta a un objeto.

El siguiente manifiesto de servicio tiene un selector que especifica dos etiquetas. El campo selector indica que cualquier pod que tenga tanto la etiqueta app: metrics como la etiqueta department:engineering es miembro de este servicio.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  # Use labels to select the member Pods of the Service.
  selector:
    app: metrics
    department: engineering
  ports:
  ...

¿Por qué usar un servicio de Kubernetes?

En un clúster de Kubernetes, cada pod tiene una dirección IP interna. Sin embargo, los pods de un deployment van y vienen, y sus direcciones IP cambian. Por lo tanto, no tiene sentido usar direcciones IP de pods directamente. Con un servicio, obtienes una dirección IP estable que dura toda la vida del servicio, aunque cambien las direcciones IP de los pods miembros.

Un servicio también proporciona balanceo de carga. Los clientes llaman a una única dirección IP estable y sus solicitudes se equilibran entre los pods que son miembros del servicio.

Tipos de servicios de Kubernetes

Hay cinco tipos de Servicios:

  • ClusterIP (predeterminado): los clientes internos envían solicitudes a una dirección IP interna estable.

  • NodePort: los clientes envían solicitudes a la dirección IP de un nodo en uno o varios valores de nodePort especificados por el servicio.

  • LoadBalancer: los clientes envían solicitudes a la dirección IP de un balanceador de carga de red.

  • ExternalName: los clientes internos usan el nombre de DNS de un servicio como alias de un nombre de DNS externo.

  • Sin interfaz gráfica: puedes usar un servicio sin interfaz gráfica cuando quieras agrupar pods, pero no necesites una dirección IP estable.

El tipo NodePort es una extensión del tipo ClusterIP. Por lo tanto, un servicio de tipo NodePort tiene una dirección IP de clúster.

El tipo LoadBalancer es una extensión del tipo NodePort. Por lo tanto, un servicio de tipo LoadBalancer tiene una dirección IP de clúster y uno o varios valores de nodePort.

Servicios de tipo ClusterIP

Cuando creas un servicio de tipo ClusterIP, Kubernetes crea una dirección IP estable a la que se puede acceder desde los nodos del clúster.

Este es un manifiesto de un servicio de tipo ClusterIP:

apiVersion: v1
kind: Service
metadata:
  name: my-cip-service
spec:
  selector:
    app: metrics
    department: sales
  # Create a ClusterIP Service. Kubernetes dynamically allocates an IP address
  # to the Service.
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

Puedes crear el servicio con kubectl apply -f [MANIFEST_FILE]. Una vez que hayas creado el servicio, puedes usar kubectl get service para ver la dirección IP estable:

NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
my-cip-service   ClusterIP   10.11.247.213   none          80/TCP

Los clientes del clúster llaman al servicio mediante la dirección IP del clúster y el puerto TCP especificado en el campo port del manifiesto del servicio. La solicitud se reenvía a uno de los pods miembros en el puerto TCP especificado en el campo targetPort. En el ejemplo anterior, un cliente llama al servicio en 10.11.247.213 en el puerto TCP 80. La solicitud se reenvía a uno de los pods miembros en el puerto TCP 8080. El Pod miembro debe tener un contenedor que esté escuchando en el puerto TCP 8080. Si no hay ningún contenedor escuchando en el puerto 8080, los clientes verán un mensaje como "No se ha podido conectar" o "No se puede acceder a este sitio".

Servicio de tipo NodePort

Cuando creas un servicio de tipo NodePort, Kubernetes te asigna un valor nodePort. Después, se podrá acceder al servicio mediante la dirección IP de cualquier nodo junto con el valor nodePort.

A continuación, se muestra un manifiesto de un servicio de tipo NodePort:

apiVersion: v1
kind: Service
metadata:
  name: my-np-service
spec:
  selector:
    app: products
    department: sales
  # Kubernetes allocates a nodePort between 30000 and 32767 for external traffic
  # that reaches the node IP address. Additionally, Kubernetes dynamically
  # allocates a clusterIP IP address for internal traffic.
  type: NodePort
  ports:
  - protocol: TCP
    port: 80 # Route internal traffic that reaches the clusterIP IP address on this port.
    targetPort: 8080

Después de crear el servicio, puedes usar kubectl get service -o yaml para ver su especificación y el valor de nodePort.

spec:
  clusterIP: 10.11.254.114
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 32675
    port: 80
    protocol: TCP
    targetPort: 8080

Los clientes externos llaman al servicio mediante la dirección IP externa de un nodo junto con el puerto TCP especificado por nodePort. La solicitud se reenvía a uno de los pods miembros en el puerto TCP especificado por el campo targetPort.

Por ejemplo, supongamos que la dirección IP externa de uno de los nodos del clúster es 203.0.113.2. En el ejemplo anterior, el cliente externo llama al servicio en 203.0.113.2 a través del puerto TCP 32675. La solicitud se reenvía a uno de los pods miembros en el puerto TCP 8080. El pod miembro debe tener un contenedor que escuche en el puerto TCP 8080.

El tipo de servicio NodePort es una extensión del tipo de servicio ClusterIP. Por lo tanto, los clientes internos tienen dos formas de llamar al servicio:

  • Usa clusterIP y port.
  • Usa la dirección IP de un nodo y nodePort.

En algunas configuraciones de clúster, el balanceador de carga de aplicaciones externo usa un servicio de tipo NodePort.

Un balanceador de carga de aplicaciones externo es un servidor proxy y es fundamentalmente diferente del balanceador de carga de red de paso a través externo que se describe en este tema en Servicio de tipo LoadBalancer.

Servicios de tipo LoadBalancer

Para obtener más información sobre los servicios de tipo LoadBalancer, consulta Conceptos de servicio LoadBalancer.

Servicio de tipo ExternalName

Un servicio de tipo ExternalName proporciona un alias interno para un nombre de DNS externo. Los clientes internos hacen solicitudes mediante el nombre de DNS interno y las solicitudes se redirigen al nombre externo.

A continuación, se muestra un manifiesto de un servicio de tipo ExternalName:

apiVersion: v1
kind: Service
metadata:
  name: my-xn-service
spec:
  type: ExternalName
  externalName: example.com

Cuando creas un servicio, Kubernetes crea un nombre de DNS que los clientes internos pueden usar para llamar al servicio. En el ejemplo anterior, el nombre de DNS es my-xn-service.default.svc.cluster.local. Cuando un cliente interno hace una solicitud a my-xn-service.default.svc.cluster.local, la solicitud se redirige a example.com.

El tipo de servicio ExternalName es fundamentalmente diferente de los demás tipos de servicio. De hecho, un servicio de tipo ExternalName no se ajusta a la definición de servicio que se ha dado al principio de este tema. Un servicio de tipo ExternalName no está asociado a un conjunto de pods y no tiene una dirección IP estable. En su lugar, un servicio de tipo ExternalName es una asignación de un nombre de DNS interno a un nombre de DNS externo.

Servicio sin interfaz gráfica

Un servicio sin encabezado es un tipo de servicio de Kubernetes que no asigna una dirección IP de clúster. En su lugar, un servicio sin encabezado usa DNS para exponer las direcciones IP de los pods asociados al servicio. De esta forma, puedes conectarte directamente a los pods en lugar de hacerlo a través de un proxy.

Los servicios sin interfaz gráfica son útiles en diversas situaciones, como las siguientes:

  • Descubrimiento de servicios: puedes usar un servicio sin encabezado para implementar el descubrimiento de servicios. Para implementar esto, crea un servicio con un nombre y un selector. El registro DNS del servicio sin encabezado contiene todas las IPs de los pods que están detrás del servicio y que coinciden con el selector. Los clientes pueden usar estos registros DNS para encontrar las direcciones IP de los pods asociados al servicio.

  • Acceso directo a los pods: los clientes pueden conectarse directamente a los pods asociados a un servicio sin interfaz, lo que puede ser útil para los servicios que requieren acceso directo a los pods subyacentes, como los balanceadores de carga y los servidores DNS.

  • Flexibilidad: los servicios sin encabezado se pueden usar para crear varias topologías diferentes, como balanceadores de carga, servidores DNS y bases de datos distribuidas.

Si tienes requisitos de red especiales para tus cargas de trabajo que no se pueden resolver con servicios sin encabezado con selectores, también puedes usar servicios sin encabezado sin selectores. Los servicios sin encabezado son una herramienta útil para acceder a servicios que no se encuentran en el propio clúster de Kubernetes, ya que el plano de control no crea objetos EndpointSlice. Puedes consultar más información sobre este tema en Servicio sin selectores.

El siguiente ejemplo es un manifiesto de un servicio sin interfaz gráfica:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  # Prevent Kubernetes from allocating an IP address to the Service.
  clusterIP: None
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    targetPort: 80

Una vez que hayas creado un servicio sin encabezado, puedes encontrar las direcciones IP de los pods asociados al servicio consultando el DNS. Por ejemplo, el siguiente comando muestra las direcciones IP de los pods asociados al servicio nginx:

dig +short nginx.default.svc.cluster.local

Otro ejemplo que usa la expansión de consultas de Kubernetes::

dig +short +search nginx

Puedes crear un servicio sin interfaz gráfica con un solo comando, y estos servicios son fáciles de actualizar y escalar.

kubectl create service clusterip my-svc --clusterip="None" --dry-run=client -o yaml > [file.yaml]

Abstracción de servicios

Un servicio es una abstracción en el sentido de que no es un proceso que escucha en alguna interfaz de red. Parte de la abstracción se implementa en las reglas iptables de los nodos del clúster. En función del tipo de servicio, otras partes de la abstracción se implementan mediante un balanceador de carga de red de paso a través externo o un balanceador de carga de aplicaciones externo.

Puertos de servicio arbitrarios

El valor del campo port de un manifiesto de servicio es arbitrario. Sin embargo, el valor de targetPort no es arbitrario. Cada Pod miembro debe tener un contenedor que escuche en targetPort.

Aquí tienes un servicio de tipo LoadBalancer que tiene un valor port de 50.000:

apiVersion: v1
kind: Service
metadata:
  name: my-ap-service
spec:
  # Set your own IP address for internal traffic instead of letting Kubernetes
  # select one for you.
  clusterIP: 10.11.241.93
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 30641 # Set a port for external traffic that reaches the node IP address.
    port: 50000 # Set a port for internal traffic that reaches the clusterIP IP address.
    protocol: TCP
    targetPort: 8080
  selector:
    app: parts
    department: engineering
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 203.0.113.200

Un cliente llama al Servicio al número 203.0.113.200 a través del puerto TCP 50000. La solicitud se reenvía a uno de los pods miembros en el puerto TCP 8080.

Varios puertos

El campo ports de un servicio es una matriz de objetos ServicePort. El objeto ServicePort tiene estos campos:

  • name
  • protocol
  • port
  • targetPort
  • nodePort

Si tiene más de un ServicePort, cada uno de ellos debe tener un nombre único.

A continuación, se muestra un servicio de tipo LoadBalancer que tiene dos objetos ServicePort:

apiVersion: v1
kind: Service
metadata:
  name: my-tp-service
spec:
  clusterIP: 10.11.242.196
  externalTrafficPolicy: Cluster
  ports:
  # Forward traffic that reaches port 31233 on the node IP address, or port
  # 60000 on the clusterIP IP address, to port 50000 on a member Pod.
  - name: my-first-service-port
    nodePort: 31233
    port: 60000
    protocol: TCP
    targetPort: 50000
  # Forward traffic that reaches port 31081 on the node IP address, or port
  # 60001 on the clusterIP IP address, to port 8080 on a member Pod.
  - name: my-second-service-port
    nodePort: 31081
    port: 60001
    protocol: TCP
    targetPort: 8080
  selector:
    app: tests
    department: engineering
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 203.0.113.201

En el ejemplo anterior, si un cliente llama al servicio en 203.0.113.201 en el puerto 60000 de TCP, la solicitud se reenvía a un pod miembro en el puerto 50000 de TCP. Sin embargo, si un cliente llama al servicio en 203.0.113.201 en el puerto TCP 60001, la solicitud se reenvía a un pod miembro en el puerto TCP 8080.

Cada pod miembro debe tener un contenedor que escuche en el puerto TCP 50000 y otro que escuche en el puerto TCP 8080. Puede ser un solo contenedor con dos hilos o dos contenedores que se ejecuten en el mismo pod.

Puntos finales de servicio

Cuando creas un servicio, Kubernetes crea un objeto Endpoints que tiene el mismo nombre que el servicio. Kubernetes usa el objeto Endpoints para registrar qué pods son miembros del servicio.

Servicios de pila única y de pila dual

Puedes crear un servicio IPv6 de tipo ClusterIP o NodePort.

En cada uno de estos tipos de servicio, puedes definir los campos ipFamilies y ipFamilyPolicy como IPv4, IPv6 o un servicio de pila dual.

Siguientes pasos