Saltar al contenido principal

Servicios en Kubernetes

Los servicios en Kubernetes son un recurso fundamental para exponer aplicaciones, agrupar pods y gestionar el acceso a los recursos dentro y fuera del clúster. Dado que los pods son efímeros y sus IPs pueden cambiar constantemente, los servicios proporcionan una IP estable (y DNS) y balancean las peticiones entre los pods asociados.

🎥 Vídeo completo, utilicemos los capítulos de youtube para saltar a la sección que nos interese: https://youtu.be/LIramFKvf3E

Servicios en Kubernetes

Resumen: El Service actúa como una puerta de entrada estable, y los endpoints son la lista dinámica de pods a los que realmente se dirige el tráfico.

Comparativa rápida de tipos de servicios en Kubernetes​

TipoAccesible desdeExposición externaUso típico
ClusterIPDentro del clústerNoComunicación interna entre pods
NodePortDentro y fuera (IP nodo)Sí (puerto del nodo)Acceso externo sencillo, pruebas
LoadBalancerDentro y fueraSí (IP pública)Exposición pública en cloud
ExternalNameDentro del clústerNo (redirige DNS)Redirigir a servicios externos por DNS

Diagrama visual de un servicio trabajando en conjunto con un deployment​

Tipos de servicios en Kubernetes​

ClusterIP (por defecto)​

  • Es el tipo de servicio por defecto.
  • Crea una IP interna accesible solo dentro del clúster.
  • Balancea el tráfico entre los pods seleccionados mediante endpoints.
  • No es accesible desde fuera del clúster.

Ejemplo YAML:

apiVersion: v1
kind: Service
metadata:
name: mi-servicio-clusterip
spec:
selector:
app: mi-app
ports:
- port: 8080
targetPort: 80
type: ClusterIP

Truco de examen: podemos crear un servicio para un deployment existente con un solo comando: kubectl expose deployment mi-app --port=8080 --target-port=80. Añadiendo --type=NodePort o --type=LoadBalancer cambiamos el tipo.

Ya hemos visto lo sencillo que es definir estos objetos y exponer nuestras aplicaciones. Pero, ¿cómo sabe el service a qué pods debe redirigir el tráfico?

Esto es posible gracias a los selectores y los endpoints. Los selectores simplemente definen qué pods deben ser parte del servicio, basándose en etiquetas (labels) que se asignan a los pods. Recordad el apartado de conceptos básicos y la importancia de las etiquetas en Kubernetes.

Los endpoints, por otra parte, son la lista actualizada de las IPs de los pods que cumplen con el selector del servicio. Así, el Service puede redirigir el tráfico a los pods correctos, incluso si estos cambian de IP o se reemplazan. Recordemos que cada pod tiene una IP única asignada por el clúster, y los pods pueden ser efímeros, es decir, pueden aparecer y desaparecer con frecuencia.

EndpointSlices​

Esa lista se guarda en objetos EndpointSlice (estables desde Kubernetes 1.21): cada Service tiene uno o varios "trozos" (slices) con las IPs y puertos de sus pods, y el clúster los crea y actualiza automáticamente. Normalmente no es necesario interactuar directamente con ellos.

Aun así, por fines educativos, podemos ver cómo funcionan. Cada EndpointSlice lleva la label kubernetes.io/service-name con el nombre de su servicio:

kubectl get endpointslices
kubectl get endpointslices -l kubernetes.io/service-name=<nombre-servicio>
kubectl describe endpointslice <nombre-endpointslice>

Esto nos mostrará los endpoints asociados a cada servicio, es decir, las IPs de los pods que están siendo balanceados por el servicio (y si están listos para recibir tráfico).

Nota: el objeto antiguo Endpoints (kubectl get endpoints o kubectl get ep) está deprecado desde Kubernetes 1.33. Sigue existiendo por compatibilidad, pero no soporta dual-stack y se trunca a partir de 1000 endpoints, así que lo correcto es usar EndpointSlices. Lo veremos aún en muchos tutoriales y respuestas antiguas.

NodePort​

  • Expone el servicio fuera del clúster a través de un puerto del nodo (rango 30000-32767).
  • Permite acceder al servicio usando la IP del nodo y el puerto asignado.
  • Internamente sigue balanceando entre los endpoints de los pods.
  • Útil para pruebas o acceso sencillo desde fuera del clúster.

Ejemplo YAML:

apiVersion: v1
kind: Service
metadata:
name: mi-servicio-nodeport
spec:
selector:
app: mi-app
ports:
- port: 8080
targetPort: 80
nodePort: 30080 # Opcional, si no se indica se asigna automáticamente
type: NodePort

LoadBalancer​

  • Crea un balanceador de carga externo (en cloud providers compatibles) y asigna una IP pública.
  • El balanceador dirige el tráfico al NodePort, que a su vez lo distribuye entre los endpoints de los pods.
  • Ideal para exponer servicios a Internet de forma sencilla.

Ejemplo YAML:

apiVersion: v1
kind: Service
metadata:
name: mi-servicio-loadbalancer
spec:
selector:
app: mi-app
ports:
- port: 80
targetPort: 80
type: LoadBalancer

ExternalName​

  • No crea un proxy ni IP propia.
  • Mapea el nombre del servicio a un DNS externo.
  • Útil para redirigir tráfico a servicios fuera del clúster (por ejemplo, una base de datos gestionada).

Ejemplo YAML:

apiVersion: v1
kind: Service
metadata:
name: mi-servicio-externalname
spec:
type: ExternalName
externalName: ejemplo.com

Nota: Ingress no es un tipo de Service, sino un recurso que permite gestionar el acceso externo (HTTP/HTTPS) a los servicios del clúster mediante reglas de enrutamiento. Suele trabajar junto a servicios de tipo ClusterIP o NodePort.

Casos de uso recomendados​

  • ClusterIP: Comunicación interna entre microservicios.
  • NodePort: Pruebas locales, acceso sencillo desde fuera del clúster sin balanceador externo.
  • LoadBalancer: Exposición pública de aplicaciones en cloud.
  • ExternalName: Integración con servicios externos vía DNS.

Caso práctico exponiendo una aplicación web​

Empezaremos exponiendo el servicio con un despliege tipo ClusterIP, que es el tipo por defecto. Creamos un Deployment de Nginx con 2 réplicas y un Service que balancea el tráfico entre los pods.

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx # el Deployment gestiona los pods con esta etiqueta
template:
metadata:
labels:
app: nginx # la etiqueta que llevan los pods
spec:
containers:
- name: nginx
image: nginx:1.28-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: ClusterIP
selector:
app: nginx # el Service envía tráfico a los pods con esta etiqueta
ports:
- port: 80 # el puerto del Service
targetPort: 80 # el puerto del contenedor

Podemos ver los endpoints del servicio con:

kubectl get endpoints nginx

Esto nos abtrae la IP de los pods que están recibiendo el tráfico del servicio. Si hacemos un kubectl get pods -o wide, veremos las IPs de los pods y podremos comprobar que coinciden con los endpoints del servicio.

Le podemos hacer una petición a la IP del cluster (ClusterIP, la podemos obtener con kubectl get svc nginx) del servicio desde otro pod dentro de cualquier nodo del clúster, por ejemplo:

curl http://<CLUSTER_IP_DEL_SERVICIO>

Para concluir esta sección, vamos a exponer una aplicación web sencilla utilizando un Service de tipo NodePort.

apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web-app
ports:
- port: 8080
targetPort: 80
nodePort: 30080 # Puerto accesible desde fuera del clúster
type: NodePort

Ahora podremos acceder a la aplicación web desde cualquier nodo del clúster usando la IP del nodo y el puerto 30080. Podemos probarlo con:

curl http://<IP_DEL_NODO>:30080

Conclusiones y relación con Ingress​

Los servicios son esenciales para exponer aplicaciones y repartir el tráfico entre los pods.

En un caso real, estos servicios se combinarían con un Ingress Controller para gestionar el acceso externo de forma más avanzada, permitiendo enrutar tráfico HTTP/HTTPS a diferentes servicios según dominios o rutas.

Esto lo veremos en la siguiente sección, Ingress Controller.


Volver al índice