Desplegando tu primer contenedor ML en Kubernetes

Video
25 min~10 min lectura

Reproductor de video

Desplegando tu primer contenedor ML en Kubernetes

Esta lección marca un hito crucial en tu viaje hacia el despliegue robusto de modelos de Machine Learning. Hasta ahora, has encapsulado tu lógica de inferencia en un contenedor Docker. El siguiente paso es hacer que ese contenedor sea escalable, resiliente y gestionable en un entorno de producción. Kubernetes (K8s) es la plataforma orquestadora de contenedores que lo hace posible. Aquí, pasarás de ejecutar un contenedor en tu máquina local a desplegarlo en un clúster de Kubernetes, entendiendo los objetos fundamentales que controlan su ciclo de vida y exposición al mundo exterior. Aprenderás a definir, lanzar, inspeccionar y escalar tu primera aplicación de ML en K8s.

Concepto Clave: Los Objetos Fundamentales de Kubernetes

Kubernetes no gestiona contenedores directamente. En su lugar, opera a través de objetos declarativos que describen el estado deseado de tu aplicación. Piensa en Kubernetes como el sistema de control de tráfico aéreo de un gran aeropuerto (el clúster). El Pod es la unidad más pequeña y desplegable, análoga a un avión individual. Un Pod puede contener uno o más contenedores (como el piloto y el copiloto) que comparten recursos de red y almacenamiento. Sin embargo, los Pods por sí mismos son efímeros; pueden despegar y aterrizar (ser creados y destruidos) en cualquier momento.

Para gestionar un conjunto de réplicas idénticas de Pods (una flota del mismo modelo de avión), utilizas un Deployment. El Deployment es el controlador que asegura que siempre haya un número especificado de "aviones" volando, reemplazando automáticamente cualquier unidad que falle. Finalmente, para permitir que el tráfico externo (los pasajeros o, en nuestro caso, las peticiones de inferencia) encuentre y se distribuya entre estos Pods, necesitas un Service. El Service actúa como la torre de control y el sistema de puertas de embarque, proporcionando una IP y un nombre de DNS estables que enrutan el tráfico a los Pods saludables, sin importar en qué nodo físico del clúster estén aterrizando. La combinación Deployment + Service es el patrón más común para desplegar aplicaciones web y APIs, como un servicio de inferencia de ML.

Tip Esencial: Memoriza esta analogía: Pod (Avión/Contenedor), Deployment (Controlador de la Flota), Service (Torre de Control/Puerta de Embarque). Todo despliegue básico en Kubernetes gira en torno a definir y conectar estos tres objetos.

Cómo funciona en la práctica: El Flujo de un Despliegue

El proceso comienza con la definición de tus objetos Kubernetes en uno o más archivos de manifiesto en formato YAML o JSON. Estos archivos son tu "receta" o "planos" para la aplicación. Para nuestro modelo de ML, el manifiesto del Deployment describirá cuántas réplicas del Pod queremos, y dentro de la especificación del Pod, definiremos el contenedor, señalando a la imagen Docker que construiste (ej: `mi-registry/modelo-sentimiento:v1.0`) y el puerto que expone (ej: 5000). El manifiesto del Service definirá cómo exponer ese Deployment, típicamente como un `ClusterIP` para acceso interno o `NodePort`/`LoadBalancer` para acceso externo.

Una vez definidos, usas la herramienta de línea de comandos `kubectl` para enviar estos manifiestos al API Server de Kubernetes. Este es el cerebro del clúster. El API Server registra el estado deseado y notifica a los controladores correspondientes. El controlador del Deployment observa que se necesita crear 3 réplicas de un Pod. A su vez, el Scheduler decide en qué Node (máquina trabajadora) del clúster colocar cada Pod, basándose en la disponibilidad de recursos. El kubelet (agente en cada Node) recibe la orden, descarga la imagen del registro de contenedores y finalmente ejecuta el contenedor. Paralelamente, el controlador del Service configura las reglas de red (a través de `kube-proxy`) para que la IP virtual del Service enrute el tráfico a los Pods recién creados. Todo este proceso orquestado sucede en segundos.

Código en acción: Manifiestos y Comandos para un Modelo de Clasificación

Imaginemos que tenemos un modelo de clasificación de texto (análisis de sentimiento) servido a través de una API Flask, ya contenerizada. La imagen se llama `ml-registry.local:5000/sentiment-api:v1`. Vamos a crear los archivos YAML para desplegarla en un clúster de Kubernetes local como Minikube o un clúster en la nube.

1. Manifiesto del Deployment (deployment-ml.yaml)

Este archivo describe la aplicación en sí: cuántas copias ejecutar y qué contenedor usar.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sentiment-model-deployment
  labels:
    app: sentiment-model
spec:
  replicas: 3  # Queremos tres instancias idénticas de nuestro Pod.
  selector:
    matchLabels:
      app: sentiment-model  # Este selector vincula el Deployment a los Pods.
  template:  # Esta es la plantilla para crear nuevos Pods.
    metadata:
      labels:
        app: sentiment-model  # Los Pods creados llevarán esta etiqueta.
    spec:
      containers:
      - name: sentiment-classifier
        image: ml-registry.local:5000/sentiment-api:v1  # TU imagen de Docker.
        ports:
        - containerPort: 5000  # El puerto que EXPONE el contenedor (el de Flask).
        env:
        - name: MODEL_PATH  # Ejemplo de variable de entorno para el contenedor.
          value: "/app/model.pkl"
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"  # 250 milicores.
          limits:
            memory: "1Gi"
            cpu: "500m"  # Límites para evitar que un Pod consuma todos los recursos.

2. Manifiesto del Service (service-ml.yaml)

Este archivo crea un punto de acceso de red estable para nuestro Deployment.

apiVersion: v1
kind: Service
metadata:
  name: sentiment-model-service
spec:
  selector:
    app: sentiment-model  # ¡CRUCIAL! Debe coincidir con la etiqueta de los Pods del Deployment.
  ports:
  - protocol: TCP
    port: 80        # El puerto en el que el Service estará disponible INTERNAMENTE en el clúster.
    targetPort: 5000 # El puerto del contenedor al que reenvía el tráfico.
  type: LoadBalancer  # Para entornos en la nube, crea un balanceador de carga externo.
  # type: NodePort   # Alternativa para desarrollo/entornos on-premise. Asigna un puerto alto en cada nodo.

3. Aplicando los Manifiestos con kubectl

Con los archivos listos, los aplicamos al clúster. Asegúrate de que tu contexto de `kubectl` apunte al clúster correcto.

# Aplicar el Deployment. Kubernetes creará los Pods.
kubectl apply -f deployment-ml.yaml
# Salida esperada: deployment.apps/sentiment-model-deployment created

# Aplicar el Service.
kubectl apply -f service-ml.yaml
# Salida esperada: service/sentiment-model-service created

# Verificar el estado del Deployment y los Pods.
kubectl get deployments
kubectl get pods

# Verificar el Service. Si usas LoadBalancer en la nube, espera a que la EXTERNAL-IP se asigne.
kubectl get services

# Si estás en Minikube con NodePort, puedes obtener la URL así:
minikube service sentiment-model-service --url

# Seguir los logs de uno de los Pods para ver la salida de tu aplicación Flask.
kubectl logs -f deployment/sentiment-model-deployment

# Una vez desplegado, puedes probar la inferencia (ajusta la IP/URL según tu entorno).
curl -X POST http://<EXTERNAL-IP>/predict -H "Content-Type: application/json" -d '{"text":"Este producto es increíble"}'

Errores comunes y cómo evitarlos

Al comenzar con Kubernetes, es fácil tropezar con ciertos errores que detienen el despliegue. Identificarlos rápidamente ahorra horas de depuración.

1. Error de pull de imagen: El mensaje `ErrImagePull` o `ImagePullBackOff` en `kubectl get pods` indica que Kubernetes no puede descargar tu imagen Docker. La causa más común es una ruta de imagen incorrecta en el manifiesto o problemas de autenticación con un registro privado. Solución: Verifica minuciosamente el nombre de la imagen y el tag. Para registros privados, debes crear un Secret de tipo `docker-registry` y referenciarlo en la especificación del Pod con `imagePullSecrets`.

2. Selectores que no coinciden: Si tu Service muestra `<pending>` o `No endpoints`, y `kubectl describe service <nombre>` muestra "Endpoints: <none>", el selector (`spec.selector`) del Service no encuentra ningún Pod con las etiquetas que especifica. Solución: Asegúrate de que las etiquetas en `spec.template.metadata.labels` del Deployment sean IDÉNTICAS a las que busca el selector del Service. Usa `kubectl get pods --show-labels` para verificar las etiquetas de los Pods en ejecución.

3. Falta de recursos (CPU/Memoria): Los Pods se quedan en estado `Pending`. `kubectl describe pod <nombre-pod>` revelará un mensaje como `Insufficient cpu` o `Insufficient memory`. Solución: Define correctamente las peticiones (`requests`) en tu manifiesto. Son las garantías mínimas que el Pod necesita para ser programado. Ajusta los valores según los recursos de tu clúster y los requisitos reales de tu modelo (p.ej., modelos grandes de Deep Learning necesitan más memoria).

4. Puerto incorrecto o contenedor no escuchando: El Pod está en estado `Running`, pero las peticiones al Service fallan con timeouts o errores de conexión. Solución: Verifica que el `containerPort` en el manifiesto del Pod coincida exactamente con el puerto en el que tu aplicación (Flask, FastAPI, etc.) está realmente escuchando dentro del contenedor. Además, confirma que el `targetPort` del Service apunte a ese mismo `containerPort`. Usa `kubectl logs` para ver si la aplicación se inició correctamente dentro del contenedor.

5. Olvidar la persistencia para el modelo o los datos: Si tu contenedor descarga el modelo al iniciar o necesita almacenar datos, estos se perderán cuando el Pod se reinicie o se mueva a otro nodo. Solución: Para modelos grandes, considera almacenarlos en un PersistentVolume (PV) y montarlo en el Pod mediante un PersistentVolumeClaim (PVC). Para datos de configuración o pequeños, usa ConfigMaps o Secrets. Nunca confíes en el sistema de archivos efímero del contenedor para datos críticos.

Checklist de dominio

Antes de considerar esta lección completa, asegúrate de poder realizar y verificar cada uno de los siguientes puntos:

  • Puedo escribir un manifiesto YAML básico para un Deployment que despliegue 2 o más réplicas de mi imagen de contenedor de ML, definiendo correctamente las etiquetas, el contenedor y sus puertos.
  • Puedo escribir un manifiesto YAML para un Service de tipo `ClusterIP` o `LoadBalancer` que seleccione y exponga correctamente los Pods creados por mi Deployment.
  • Sé usar los comandos `kubectl apply`, `kubectl get` (pods, deployments, services), `kubectl describe`, y `kubectl logs` para desplegar y diagnosticar mi aplicación.
  • Puedo explicar la diferencia entre `port`, `targetPort`, y `containerPort` en la configuración de un Service y un Pod.
  • Entiendo el propósito de las secciones `spec.selector` en el Deployment y el Service, y por qué sus etiquetas deben coincidir.
  • Sé cómo escalar manualmente un Deployment usando el comando `kubectl scale deployment/<nombre> --replicas=5`.
  • Puedo identificar y solucionar al menos tres de los errores comunes listados anteriormente (Error de imagen, selectores, recursos).
  • Comprendo que los Pods son efímeros y por qué el Service es necesario para proporcionar un endpoint de red estable para las peticiones de inferencia.

Has llegado al final de la lección fundamental. Has pasado de tener un contenedor estático a tener un servicio de inferencia de ML gestionado, que se puede escalar, reiniciar automáticamente y al que se puede acceder de manera estable. Este es el núcleo del despliegue de ML en Kubernetes. En las próximas lecciones, construiremos sobre esto, añadiendo configuraciones más avanzadas, manejo de secretos, actualizaciones graduales (rolling updates) y monitorización. El poder de Kubernetes para ML comienza con el dominio sólido de estos conceptos básicos.

De lección a portfolio

Convertí esta lección en una habilidad visible para entrevistas.

Guardá el curso, completá los ejercicios y conectá esta habilidad con una ruta de empleo, data, IA, programación o marketing.

Newsletter Cursalo

Recibí rutas y cursos nuevos

Sumate para recibir recursos orientados a empleo y portfolio.

  • Rutas de empleo
  • Cursos prácticos
  • Portfolio y entrevistas

Sin spam. También podés entrar con tu cuenta para guardar progreso. Iniciá sesión