Capitulo 6: Services

Por: Artiko
k3skubernetesservicesnetworking

Capitulo 6: Services

< Volver al Indice del Tutorial

Por Que se Necesitan los Services

Los pods en Kubernetes son efimeros. Cuando un pod muere y se recrea, recibe una IP diferente. Si tienes 3 replicas de un backend, cada una tiene su propia IP y estas cambian constantemente.

Un Service resuelve este problema: proporciona una IP y un nombre DNS estable para acceder a un grupo de pods. Funciona como un balanceador de carga interno.

Cliente → Service (IP fija) → Pod 1
                             → Pod 2
                             → Pod 3

ClusterIP: Comunicacion Interna

Es el tipo por defecto. Crea una IP virtual accesible solo dentro del cluster.

apiVersion: v1
kind: Service
metadata:
  name: backend-svc
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
    - port: 80          # Puerto del Service
      targetPort: 3000  # Puerto del contenedor
      protocol: TCP
kubectl apply -f backend-svc.yaml
kubectl get svc backend-svc

DNS Automatico

Kubernetes asigna automaticamente un registro DNS a cada Service. El formato es:

nombre-servicio.namespace.svc.cluster.local

Por ejemplo, backend-svc en el namespace default se resuelve como:

backend-svc.default.svc.cluster.local

Desde cualquier pod dentro del mismo namespace, puedes usar simplemente el nombre:

curl http://backend-svc

Desde otro namespace:

curl http://backend-svc.default.svc.cluster.local

NodePort: Exponer en Puerto del Nodo

Expone el Service en un puerto estatico de cada nodo del cluster. Util para acceso externo sin un LoadBalancer.

apiVersion: v1
kind: Service
metadata:
  name: webapp-nodeport
spec:
  type: NodePort
  selector:
    app: webapp
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080  # Puerto en el nodo (30000-32767)
kubectl apply -f webapp-nodeport.yaml

Accede desde fuera del cluster:

curl http://IP-DEL-NODO:30080

El rango de puertos permitido es 30000-32767. Si no especificas nodePort, Kubernetes asigna uno aleatorio en ese rango.

Limitaciones de NodePort

LoadBalancer: ServiceLB de K3s

En K3s, el tipo LoadBalancer usa ServiceLB (anteriormente llamado Klipper). No necesitas un proveedor de nube; K3s crea un balanceador integrado.

apiVersion: v1
kind: Service
metadata:
  name: webapp-lb
spec:
  type: LoadBalancer
  selector:
    app: webapp
  ports:
    - port: 80
      targetPort: 80
kubectl apply -f webapp-lb.yaml
kubectl get svc webapp-lb

La columna EXTERNAL-IP muestra la IP asignada. En K3s con un solo nodo, esta IP es la misma del nodo.

ServiceLB funciona desplegando un DaemonSet que escucha en el puerto del host y redirige trafico al Service. Es simple y funcional para entornos pequenos.

Verificar ServiceLB

# Ver los pods de ServiceLB
kubectl get pods -n kube-system -l svccontroller.k3s.cattle.io=true

Endpoints: Descubrimiento via Selectors

Un Service usa el campo selector para encontrar los pods que debe incluir. Kubernetes mantiene un objeto Endpoints que lista las IPs de los pods que coinciden.

# Ver los endpoints de un Service
kubectl get endpoints backend-svc

Salida ejemplo:

NAME          ENDPOINTS                                AGE
backend-svc   10.42.0.15:3000,10.42.0.16:3000         5m

Si un pod no esta listo (falla el readiness probe), se elimina de los endpoints automaticamente. Esto evita enviar trafico a pods que no pueden responder.

Service sin selector

Puedes crear un Service sin selector para apuntar a un servicio externo:

apiVersion: v1
kind: Service
metadata:
  name: db-externa
spec:
  type: ClusterIP
  ports:
    - port: 5432
---
apiVersion: v1
kind: Endpoints
metadata:
  name: db-externa
subsets:
  - addresses:
      - ip: 192.168.1.100
    ports:
      - port: 5432

Esto permite que los pods internos usen db-externa como nombre DNS aunque el servicio real este fuera del cluster.

DNS Interno de K3s: CoreDNS

K3s incluye CoreDNS como servidor DNS del cluster. Se despliega automaticamente y resuelve nombres de Services a sus IPs.

# Verificar que CoreDNS esta corriendo
kubectl get pods -n kube-system -l k8s-app=kube-dns

Probar resolucion DNS

Lanza un pod temporal para probar:

kubectl run dns-test --image=busybox --rm -it --restart=Never -- nslookup backend-svc

Salida esperada:

Server:    10.43.0.10
Address 1: 10.43.0.10 kube-dns.kube-system.svc.cluster.local

Name:      backend-svc
Address 1: 10.43.X.X backend-svc.default.svc.cluster.local

Registros DNS que CoreDNS crea

TipoFormatoEjemplo
Servicesvc.namespace.svc.cluster.localbackend-svc.default.svc.cluster.local
Podpod-ip.namespace.pod.cluster.local10-42-0-15.default.pod.cluster.local

Ejemplo: Frontend Hablando con Backend

Despleguemos un frontend y un backend que se comunican via Services.

Backend

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: hashicorp/http-echo
          args:
            - "-text=Respuesta del backend"
            - "-listen=:5000"
          ports:
            - containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
  name: backend-svc
spec:
  selector:
    app: backend
  ports:
    - port: 5000
      targetPort: 5000

Frontend

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: frontend
          image: curlimages/curl
          command: ["sh", "-c"]
          args:
            - |
              while true; do
                echo "Consultando backend..."
                curl -s http://backend-svc:5000
                sleep 5
              done
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-svc
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080
kubectl apply -f backend.yaml
kubectl apply -f frontend.yaml

Verifica la comunicacion:

# Ver logs del frontend llamando al backend
kubectl logs -l app=frontend --tail=10

Salida esperada:

Consultando backend...
Respuesta del backend
Consultando backend...
Respuesta del backend

El frontend usa backend-svc:5000 como URL. CoreDNS resuelve ese nombre a la IP del ClusterIP del Service, que balancea entre las 2 replicas del backend.


Siguiente: Capitulo 7: ConfigMaps y Secrets —>