Servicios

Como conectarnos a los recursos

Nos sirven para conectarse entre los pods como asi tambien para conectarse con el mundo exterior y asi poder ser accedidos.

services

Tipos de servicios

ClusterIP

Descripción: Este es el tipo de servicio predeterminado en Kubernetes. Se utiliza para exponer un servicio internamente dentro del clúster. No es accesible desde fuera del clúster. Acceso: Solo se puede acceder desde dentro del clúster, ya sea por otros pods o componentes del clúster. Uso: Ideal para servicios internos que no necesitan ser accesibles desde fuera, como microservicios o bases de datos internas.

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

NodePort

Descripción: Expone el servicio a través de un puerto específico en todos los nodos del clúster. Permite acceder a la aplicación desde fuera del clúster a través de la IP de cualquier nodo y el puerto asignado. Acceso: Externo, accesible desde fuera del clúster usando la dirección IP del nodo y el puerto que se asigna (generalmente en el rango de 30000-32767). Uso: Se usa cuando necesitas exponer temporalmente un servicio para pruebas o cuando no tienes un balanceador de carga disponible.

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

LoadBalancer

Descripción: Este tipo de servicio crea un balanceador de carga externo para exponer el servicio a través de una IP pública. Funciona en plataformas que soportan balanceadores de carga nativos, como GCP, AWS o Azure. Acceso: Externo, el servicio será accesible desde fuera del clúster a través de la IP asignada por el balanceador de carga. Uso: Ideal para aplicaciones que necesitan ser expuestas a Internet o a una red pública.

apiVersion: v1
kind: Service
metadata:
  name: mi-servicio
spec:
  selector:
    app: mi-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer
services-types

Como sabe un servicio que pods le corresponden?

El servicio tomara como referencia el label de los pods.

services-pods
apiVersion: v1
kind: Service
metadata:
  name: mi-servicio
spec:
  selector:
    app: mi-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-app-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mi-app
  template:
    metadata:
      labels:
        app: mi-app # este es el label que toma encuenta el service
    spec:
      containers:
        - name: mi-app-container
          image: mi-imagen:latest
          ports:
            - containerPort: 8080

Crear un servicio nodePort

kubectl create deployment apache --image=httpd
kubectl expose deploy apache --port=80 --type=NodePort 

Hacemos kubectl get svc

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
apacheNodePort10.106.109.82none80:30612/TCP64s
  • External IP esta en none porque no estamos en entorno cloud.
  • Vemos port que es 30612
  • Chekeamos con kubectl ip la ip a usar con el puerto
  • En el navegador hacemos ip:port Tendriamos que ver la web de apache.

Podemos ver los servicios de minikube con minikube service list

NAMESPACENAMETARGET PORTURL
defaultapache80http://192.168.49.2:30612
defaultkubernetesNo node port
kube-systemkube-dnsNo node port
------------------------------------------------------------------

Como sabe el servicio a que pods apuntar?

Si hacemos:

kubectl describe svc apache

Veremos :

Name:                     apache
Namespace:                default
Labels:                   app=apache
Annotations:              none
Selector:                 app=apache
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.106.109.82
IPs:                      10.106.109.82
Port:                     unset  80/TCP
TargetPort:               80/TCP
NodePort:                 unset  30612/TCP
Endpoints:                10.244.2.2:80
Session Affinity:         None
External Traffic Policy:  Cluster
Internal Traffic Policy:  Cluster
Events:                   none

Si prestamos atencion a

Endpoints:                10.244.2.2:80

Veremos que es la ip del pod. Revisemoslo.

kubectl get pods -o wide

NAME                     READY   STATUS    RESTARTS       AGE   IP           NODE                
apache-7d8d5c5d5-7g2bz   1/1     Running   1 (3m4s ago)   19h   10.244.2.2   mastercluster-m02   

Que pasa con los Endpoints del servicio si escalamos?

kubectl scale deploy apache --replicas=3

Veremos que automáticamente actualiza la lista de ips.

Name:                     apache
Namespace:                default
Labels:                   app=apache
Annotations:              none
Selector:                 app=apache
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.106.109.82
IPs:                      10.106.109.82
Port:                     unset  80/TCP
TargetPort:               80/TCP
NodePort:                 unset  30612/TCP
Endpoints:                10.244.2.2:80,10.244.0.3:80,10.244.3.2:80
Session Affinity:         None
External Traffic Policy:  Cluster
Internal Traffic Policy:  Cluster
Events:                   none

Comunicacion entre servicios de tipo ClusterIP

El ClusterIP se utiliza solo para la Comunicacion interna entre los objetos de un cluster.

A continuacion crearemos dos deploy y probaremos la comunicacion entre ellos.

  1. kubectl create deployment redis-master --image=redis
  2. kubectl create deployment redis-slave --image=redis
  3. kubectl get deployment
  4. kubectl expose deployment redis-master --port=6379 (no ponemos el type porque por default es ClusterIP)
  5. kubectl get services
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
redis-master   ClusterIP   10.105.11.137   <none>        6379/TCP       7s

El NAME lo podemos usar como DNS desde otros pods.

  1. kubectl get pods
  2. Copiamos el NAME del pod de redis-slave
  3. kubectl exec redis-slave-77464dc784-nlwbt -it -- sh
  4. redis-cli -h redis-master

redis-cli es la app de redis , no nuestro pod. este comando
nos permite conectarnos a otras db redis. -h es host y podemos el DNS del servicio, en este caso redis-master.

Practica, correr nuestr app con deployment + service.

El ejercicio se hara con nextjs14+ para eso tenemos que tener nuestra app de nextjs.

  1. npx create-next-app@latest
  2. Creamos Dockerfile con lo siguiente.
# Etapa 1: Construcción
# Usa la imagen oficial de Node.js como base
FROM node:18-alpine AS builder

# Establece el directorio de trabajo dentro del contenedor
WORKDIR /app

# Copia los archivos package.json y package-lock.json o yarn.lock
COPY package*.json ./
# Si usas Yarn, reemplaza el comando anterior por: COPY yarn.lock ./

# Instala las dependencias de producción
RUN npm install --frozen-lockfile

# Copia el resto de los archivos de la aplicación
COPY . .

# Establece la variable de entorno para la producción
ENV NODE_ENV=production

# Construye la aplicación Next.js en modo standalone
RUN npm run build

# Elimina las dependencias de desarrollo
RUN npm prune --production

# Etapa 2: Ejecución
# Usa una imagen base mínima para ejecutar el contenedor
FROM node:18-alpine

# Establece el directorio de trabajo dentro del contenedor
WORKDIR /app

# Copia los archivos generados de la fase de construcción
COPY --from=builder /app ./

# Expone el puerto por defecto en el que Next.js se ejecuta (3000)
EXPOSE 3000

# Comando por defecto para iniciar la aplicación
CMD ["npm", "start"]
  1. Generamos la imagen: docker build -t k8s-next .
  2. Creamos el yml:
#############
# DEPLOYMENT  
#############
apiVersion: apps/v1 
kind: Deployment
metadata:
  name: web-d
spec:
  selector:   #permite seleccionar un conjunto de objetos que cumplan las condicione
    matchLabels:
      app: k8s-next
  replicas: 2 # indica al controlador que ejecute 2 pods
  template:   # Plantilla que define los containers
    metadata:
      labels:
        app: k8s-next # Con esto el servicio sabe a que pod apuntar!
    spec:
      containers:
      - name: k8s-next
        image: k8s-next:latest
        imagePullPolicy: Never # Como nuestra imagen es local tenemos que setear esto.
        ports:
        - containerPort: 3000 # IP por delfault por el que escucha nuestra app
---

#############
# SERVICIO  
#############
apiVersion: v1
kind: Service
metadata:
  name: web-svc
  labels:
     app: k8s-next
spec:
  type: NodePort
  ports:
  - port: 3000
    nodePort: 30002
    protocol: TCP
  selector: 
     app: k8s-next # este es el que apunta a los pods del __deployment__ es importante!
  1. Cargamos la imagen en minikube minikube image load k8s-next:latest
  2. Ejecutamos kubectl apply -f completo.yml
  3. Vamos al navegador ponemos IP:3002 donde la ip la obtenemos de minikube ip

Obtener informacion adicional de Endpoits:

kubectl get endpoints web-svc -o yaml
kubectl describe endpoint web-svc
kubectl get endpoints

Ejercicio recomendado

El siguiente ejercicio muestra como tener un servidor con redis donde tendremos 1-master(redis) y 2-slave(redis).

Tene en cuenta que lo que determina que redis es maestro y esclavo se determina al momento de crear la imagen de redis.En este caso ya te las da el ejemplo. En la vida real tenes que setear vos hacia donde apuntan los slave.

Lets’go Baby