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
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
| Tipo | Accesible desde | Exposición externa | Uso típico |
|---|---|---|---|
| ClusterIP | Dentro del clúster | No | Comunicación interna entre pods |
| NodePort | Dentro y fuera (IP nodo) | Sí (puerto del nodo) | Acceso externo sencillo, pruebas |
| LoadBalancer | Dentro y fuera | Sí (IP pública) | Exposición pública en cloud |
| ExternalName | Dentro del clúster | No (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=NodePorto--type=LoadBalancercambiamos 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 endpointsokubectl 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 usarEndpointSlices. 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.
- Lista de vídeos en Youtube: Curso Kubernetes
