Esta guía documenta los pasos necesarios para configurar Apache Airflow 3.1.7 en un entorno de desarrollo local utilizando Helm v4+ y Kubernetes (idealmente con Docker Desktop).
Antes de instalar el Chart de Airflow, es necesario contar con las siguientes herramientas instaladas. Si utilizas Docker Desktop, puedes habilitar Kubernetes directamente desde su configuración, lo cual incluye kubectl.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client --output=yamlcurl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-4
chmod 700 get_helm.sh
./get_helm.shReferencias: Guía oficial de instalación de Helm
Para el desarrollo local de DAGs (autocompletado, linting y pruebas) y la correcta resolución de dependencias en Python, crea y configura un entorno virtual:
python3 -m venv .venv
source .venv/bin/activate
AIRFLOW_VERSION=3.1.7
PYTHON_VERSION="$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
CONSTRAINT_URL="https://raw.githubusercontent.com/apache/airflow/constraints-${AIRFLOW_VERSION}/constraints-${PYTHON_VERSION}.txt"
pip install "apache-airflow[postgres,fab,otel]==${AIRFLOW_VERSION}" --constraint "${CONSTRAINT_URL}"Antes de instalar Airflow, es necesario preparar el entorno, lo cual incluye la creación de una imagen Docker personalizada con las dependencias del proyecto, descargar la configuración base y preparar las credenciales para sincronizar tus DAGs (GitSync).
1. Instalar charts
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm upgrade --install cnpg \
--namespace cnpg-system \
--create-namespace \
cnpg/cloudnative-pg
helm upgrade --install database \
--namespace database \
--create-namespace \
cnpg/cluster2. Revisar instalación
Comandos provistos por la misma instalación de cnpg/cluster
# Run Helm Tests
helm test --namespace database database
# Get a list of all base backups
kubectl --namespace database get backups --selector cnpg.io/cluster=database-cluster
# Connect to the cluster's primary instance
kubectl --namespace database exec --stdin --tty services/database-cluster-rw -- bash3. Crear base de datos para MLFlow
# Conectar a la instancia primaria del cluster
kubectl --namespace database exec --stdin --tty services/database-cluster-rw -- bash
psqlEn la consola de PostgreSQL:
-- Crear base de datos para MLFlow
CREATE DATABASE mlflow_db;
-- Para listar las bases de datos
\l
-- Para conectar a la base de datos MLFlow
\c mlflow_db
-- Para listar tablas y otros (vacío si se creó recién la BD)
\dtReferencia
4. Obtener servicio PostgreSQL
El servicio -rw es el que usaremos para poder leer y escribir. Dado que MLFlow se despliega en un namespace distinto (airflow) al de la base de datos (database), debemos utilizar su Fully Qualified Domain Name (FQDN) al configurarlo en el --backend-store-uri del archivo mlflow-deployment. Es decir, usaremos database-cluster-rw.database.svc.cluster.local.
kubectl get svc -n database
# Retornará algo como
#NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
#database-cluster-r ClusterIP 10.102.73.18 <none> 5432/TCP 53m
#database-cluster-ro ClusterIP 10.96.243.211 <none> 5432/TCP 53m
#database-cluster-rw ClusterIP 10.100.145.95 <none> 5432/TCP 53mDado que este proyecto requiere librerías específicas (como pandas, scikit-learn o mlflow) que no vienen preinstaladas en la imagen oficial del Helm Chart de Apache Airflow, es necesario construir una imagen propia.
1. Archivo de configuración (Dockerfile)
El proyecto incluye un archivo Dockerfile en el directorio raíz en el que se especifican las dependencias adicionales a instalar sobre la versión base de Airflow:
FROM apache/airflow:3.1.8
RUN pip install --no-cache-dir \
"apache-airflow[google]==3.1.8" \
--constraint "https://raw.githubusercontent.com/apache/airflow/constraints-3.1.8/constraints-3.12.txt"
RUN pip install --no-cache-dir \
scikit-learn==1.8.0 \
"mlflow[mlserver]==3.10.1" \
great_expectations==1.14.0 \
google-cloud-storage==3.9.02. Construcción de la imagen
Ejecuta el siguiente comando para construir la imagen del contenedor localmente. La etiqueta (airflow-custom:0.0.1) asignada aquí será la que utilicemos posteriormente en la configuración de Helm:
docker build --pull --tag airflow-custom:0.0.1 .Añade el repositorio oficial del Chart de Apache Airflow y actualiza el índice de paquetes:
helm repo add apache-airflow https://airflow.apache.org
helm repo updateCrea el namespace dedicado donde residirán todos los componentes de Apache Airflow:
kubectl create namespace airflowExtrae los valores por defecto del Chart hacia un archivo local (airflow-values.yaml). Este archivo servirá como plantilla base para personalizar tu despliegue de forma estructurada e incluir nuestra imagen Docker:
helm show values apache-airflow/airflow > airflow-values.yamlPara que Airflow sincronice tus DAGs automáticamente desde tu repositorio, debes configurar un secreto en Kubernetes que contenga las credenciales de GitHub.
Prerrequisitos:
- Crear un Personal Access Token (PAT) en GitHub (se recomienda la opción "Fine-grained").
- Asignar el permiso de sólo lectura para código (
Contents: Read).
# Eliminar el secreto en caso de que ya exista para evitar conflictos
kubectl delete secret git-credentials -n airflow --ignore-not-found
# Generar el secreto en Kubernetes
kubectl create secret generic git-credentials \
--from-literal=GITSYNC_USERNAME='<usuario_github>' \
--from-literal=GITSYNC_PASSWORD='<PAT_github>' \
--from-literal=GIT_SYNC_USERNAME='<usuario_github>' \
--from-literal=GIT_SYNC_PASSWORD='<PAT_github>' \
-n airflowPara utilizar Google Cloud Storage como Artifact Store de MLflow, es necesario crear un bucket y una cuenta de servicio (Service Account) con los permisos adecuados.
1. Configurar proyecto y autenticación
export PROJECT_ID="k8s-mlflow"
gcloud auth application-default set-quota-project $PROJECT_ID
gcloud config set project $PROJECT_ID
gcloud auth login2. Crear el bucket
REGION=us-central1
# Para contener el resultado de las ejecuciones
# Crear directorios .../models/iris
gcloud storage buckets create gs://k8s-mlflow-mlruns \
--default-storage-class=STANDARD \
--location=$REGION \
--uniform-bucket-level-access \
--public-access-prevention
# Ruta para servir el resultado productivo
# Crear directorio .../iris
gcloud storage buckets create gs://mlflow-serving \
--default-storage-class=STANDARD \
--location=$REGION \
--uniform-bucket-level-access \
--public-access-prevention
3. Crear y configurar la Cuenta de Servicio
# Crear cuenta de servicio
gcloud iam service-accounts create k8s-mlflow-sa
# Agregar permisos de administrador de almacenamiento para el bucket
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:k8s-mlflow-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/storage.admin"4. Generar y descargar la llave JSON
Genera una clave de acceso (JSON) para la cuenta de servicio y guárdala localmente, ya que se utilizará para que los pods se autentiquen:
- Desde la Consola de Google Cloud, dirígete a IAM y administración > Cuentas de servicio.
- Selecciona
k8s-mlflow-sa, ve a la pestaña Claves, haz clic en Agregar clave > Crear clave nueva, elige JSON y descárgala. - Mueve y renombra el archivo descargado a la ruta local donde centralizas tus credenciales, por ejemplo,
/home/eric/.config/gcloud/k8s-mlflow-key.json
Tanto MLflow como Airflow necesitan acceso a credenciales para la base de datos y Google Cloud.
1. Copiar el secreto de base de datos al namespace airflow
La configuración de MLflow referencia las credenciales a través de un secreto. Como el cluster de PostgreSQL se encuentra en el namespace database y MLflow en airflow, debemos clonarlo:
kubectl create secret generic database-cluster-superuser \
--from-literal=username=$(kubectl get secret database-cluster-superuser -n database -o jsonpath='{.data.username}' | base64 -d) \
--from-literal=password=$(kubectl get secret database-cluster-superuser -n database -o jsonpath='{.data.password}' | base64 -d) \
-n airflow2. Crear el secreto de GCP para el namespace de Airflow
Esto permitirá a Airflow y MLflow autenticarse contra Google Cloud Storage:
kubectl create secret generic gcp-mlflow-key \
--from-file=key.json=/home/eric/.config/gcloud/k8s-mlflow-key.json -n airflow(Nota: Más adelante se creará otro secreto con el formato específico para KServe en su propio namespace).
La configuración de MLFlow se encuentra en los archivos MLFlow Deployment y MLFlow Service.
El deployment referencia el secret database-cluster-superuser (generado automáticamente por CloudNativePG y copiado en el paso anterior) y el secreto gcp-mlflow-key.
1. Montar el secreto de GCP en MLflow
En tu archivo mlflow-deployment.yaml, en la especificación de contenedores (spec.containers), asegúrate de montar el secreto y definir la variable de entorno para GCP:
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
volumeMounts:
- name: gcp-key
mountPath: /var/secrets/google
readOnly: true
volumes:
- name: gcp-key
secret:
secretName: gcp-mlflow-key2. Aplicar archivos de configuración
kubectl apply -f mlflow-deployment.yaml
kubectl apply -f mlflow-service.yaml
# Si es una actualización de secretos y MLflow ya estaba desplegado, reinícialo:
kubectl rollout restart deployment mlflow -n airflow3. Redireccionar el puerto a la interfaz de MLFlow
# Se usa el puerto 30500 ya que el puerto 5000
# en windows está usado por otra aplicación (al menos en mi equipo)
kubectl port-forward svc/mlflow 30500:5000 -n airflow4. Crear el Experimento en MLFlow
Una vez que el servidor de MLflow esté accesible, accede a la interfaz http://localhost:30500 y crea el experimento que usarán los DAGs de Airflow.
| Campo | Valor |
|---|---|
| Experiment name | airflow-mlflow-iris-gcp |
| Artifact location | gs://k8s-mlflow-mlruns/models/iris |
Nota: El nombre del experimento debe coincidir exactamente con el valor de la variable
MLFlow_Experiment_Irisque se configurará en Airflow (ver sección 4.2.1). La Artifact location debe apuntar al bucket GCS donde se guardarán los artefactos del entrenamiento.
Abre el archivo airflow-values.yaml descargado en el paso 3.5. Debes añadir la sección gitSync, configurar nuestra imagen Docker personalizada y montar las credenciales de Google Cloud (gcp-mlflow-key) para que Airflow interactúe con el almacenamiento de artefactos y no falle al subir sus propios logs/resultados.
# Configurar la imagen personalizada de Airflow
images:
airflow:
repository: airflow-custom
tag: 0.0.1
# Sincronizar DAGs desde Git
dags:
gitSync:
enabled: true
repo: https://github.com/ericmartinezr/airflow-helm.git
branch: dev
rev: HEAD
ref: dev
subPath: 'src/dags'
credentialsSecret: git-credentials
# Configurable globalmente (si lo soporta) o por componente
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
# Logging
logs:
persistence:
enabled: true
# Configurar volúmenes para componentes (tmp y GCP key)
workers:
extraVolumes:
- name: tmp-files
persistentVolumeClaim:
claimName: airflow-tmp-files-pvc
- name: gcp-key
secret:
secretName: gcp-mlflow-key
extraVolumeMounts:
- name: tmp-files
mountPath: '{{ .Values.airflowHome }}/tmp'
- name: gcp-key
mountPath: '/var/secrets/google'
readOnly: true
scheduler:
extraVolumes:
- name: tmp-files
persistentVolumeClaim:
claimName: airflow-tmp-files-pvc
- name: gcp-key
secret:
secretName: gcp-mlflow-key
extraVolumeMounts:
- name: tmp-files
mountPath: '{{ .Values.airflowHome }}/tmp'
- name: gcp-key
mountPath: '/var/secrets/google'
readOnly: true
apiServer:
extraVolumes:
- name: tmp-files
persistentVolumeClaim:
claimName: airflow-tmp-files-pvc
extraVolumeMounts:
- name: tmp-files
mountPath: '{{ .Values.airflowHome }}/tmp'
webserver:
extraVolumes:
- name: tmp-files
persistentVolumeClaim:
claimName: airflow-tmp-files-pvc
extraVolumeMounts:
- name: tmp-files
mountPath: '{{ .Values.airflowHome }}/tmp'Con la configuración y secretos listos, procede a instalar Airflow aplicando tu archivo airflow-values.yaml personalizado:
helm install airflow apache-airflow/airflow --namespace airflow --create-namespace -f airflow-values.yamlNota: La descarga de imágenes y creación de contenedores puede tomar unos minutos la primera vez.
Referencias: Helm Chart for Apache Airflow
Verifica que los pods se estén iniciando correctamente:
kubectl get pods -n airflowTambién puedes comprobar el estado general del chart instalado en Helm:
helm list -n airflowUna vez que Airflow esté corriendo y accesible en http://localhost:8080, es necesario crear manualmente los siguientes objetos de configuración desde la interfaz web (Admin > Variables / Connections / Pools) antes de ejecutar los DAGs.
Variables
| Variable | Valor | Descripción |
|---|---|---|
MLFlow_Experiment_Iris |
airflow-mlflow-iris-gcp |
Nombre del experimento en MLflow (debe coincidir con el creado en la sección 3.9) |
MLFlow_Tracking_URL |
http://mlflow:5000 |
URL interna del servidor de tracking de MLflow dentro del clúster |
Conexiones
| Conexión | Connection Type | Extra / Configuración |
|---|---|---|
google_cloud_default |
Google Cloud | Keyfile JSON: contenido del archivo JSON de la cuenta de servicio GCP |
temp_files |
File (path) | Path: /opt/airflow/tmp (debe coincidir con el mountPath definido en airflow-values.yaml) |
Pools
| Pool | Slots | Descripción |
|---|---|---|
ml_pool |
1 |
Pool dedicado para las tareas de ML, permite controlar la concurrencia |
Grafana es una plataforma de observabilidad y visualización de métricas. A continuación se documenta su despliegue en Kubernetes mediante el Helm Chart oficial.
Agrega el repositorio oficial de Grafana y actualiza el índice de paquetes local:
# Agregar el repositorio oficial de Grafana
helm repo add grafana-community https://grafana-community.github.io/helm-charts
# Actualizar el índice local de repositorios
helm repo update
# (Opcional) Verificar que el repositorio fue registrado correctamente
helm repo list
# (Opcional) Buscar el chart de Grafana disponible en el repositorio
helm search repo grafana-community/grafanaCrea el namespace dedicado e instala el chart de Grafana:
# Crear el namespace dedicado para Grafana
kubectl create namespace grafana-ns
# Instalar Grafana en el namespace
helm install grafana grafana-community/grafana \
--namespace grafana-nsVerifica que todos los recursos hayan sido creados y que los pods estén en estado Running:
# Verificar el estado del release en Helm
helm list -n grafana-ns
# Ver el estado de todos los objetos en el namespace (pods, servicios, etc.)
kubectl get all -n grafana-nsSi los pods no levantan de inmediato, espera unos segundos y vuelve a ejecutar el comando. La descarga de la imagen puede tardar la primera vez.
El Chart de Grafana no expone el servicio externamente por defecto. Para acceder desde el navegador local, utiliza port-forward.
1. Consultar las instrucciones post-instalación del chart:
helm get notes grafana -n grafana-ns
# Nombre DNS del servicio
# grafana.grafana-ns.svc.cluster.local2. Obtener la contraseña del usuario admin:
La contraseña es generada automáticamente y almacenada en un Secret de Kubernetes:
kubectl get secret --namespace grafana-ns grafana \
-o jsonpath="{.data.admin-password}" | base64 --decode; echo3. Redirigir el puerto y acceder desde el navegador:
# Obtener el nombre del pod de Grafana
export POD_NAME=$(kubectl get pods \
--namespace grafana-ns \
-l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=grafana" \
-o jsonpath="{.items[0].metadata.name}")
# Redirigir el puerto local 3100 al puerto 3000 del pod
kubectl --namespace grafana-ns port-forward $POD_NAME 3100:3000Accede desde el navegador a: http://localhost:3100 con el usuario admin y la contraseña obtenida en el paso anterior.
Referencia: Grafana on Helm Charts
Como parte de la estrategia de monitoreo y observabilidad, configuraremos Prometheus como origen de métricas para Grafana. Se instalará en el namespace grafana-ns para facilitar su integración conjunta con Grafana.
1. Añadir el Repositorio de Prometheus
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update2. Instalación Base
Como el namespace grafana-ns ya fue creado en la sección anterior (4.3.2), puedes proceder directamente con la instalación:
helm install prometheus prometheus-community/kube-prometheus-stack -n grafana-ns3. Extraer y Actualizar Configuración (prometheus-values.yaml)
Para asegurar un correcto funcionamiento en un entorno de desarrollo local y evitar errores con node-exporter (como caídas o reinicios inesperados al intentar montar volúmenes raíz del host), se deben modificar sus valores base.
Primero, extrae los valores por defecto:
helm show values prometheus-community/kube-prometheus-stack > prometheus-values.yamlA continuación, abre el archivo generado prometheus-values.yaml, busca el bloque de configuración prometheus-node-exporter y asegúrate de configurar el parámetro hostRootFsMount de la siguiente manera:
prometheus-node-exporter:
hostRootFsMount:
enabled: falseTambién agrega la siguiente configuración de scraping para capturar las métricas de Airflow
additionalScrapeConfigs:
- job_name: airflow-statsd
scrape_interval: 10s
metrics_path: /metrics
static_configs:
- targets:
- airflow-statsd.airflow.svc.cluster.local:9102Luego en grafana genera un nuevo dashboard con la configuración descrita en el archivo grafana-airflow-dashboard.json (Nota: el dashboard no pretende ser productivo, sino demostrativo del funcionamiento)
4. Aplicar los Nuevos Valores
Aplica la actualización utilizando el nuevo archivo de configuración en el clúster:
helm upgrade prometheus prometheus-community/kube-prometheus-stack -n grafana-ns -f prometheus-values.yaml5. Configurar Datasource y Dashboard en Grafana
Accede a la interfaz de Grafana (ver sección 4.3.4) y realiza lo siguiente para enlazar Prometheus con Grafana:
- Crear Datasource de Prometheus: Dirígete a la configuración de conexiones (Data sources), añade uno nuevo de tipo Prometheus e ingresa la siguiente URL interna del clúster:
http://prometheus-kube-prometheus-prometheus.grafana-ns.svc.cluster.local:9090 - Importar Dashboard: Dirígete al panel de Dashboards, entra en la opción Import y arrastra o selecciona el archivo local
grafana-kubernetes-dashboard.json.
Referencia
https://medium.com/@gayatripawar401/deploy-prometheus-and-grafana-on-kubernetes-using-helm-5aa9d4fbae66
https://github.com/prometheus-community/helm-charts/pkgs/container/charts%2Fkube-prometheus-stack
KServe es el componente responsable del despliegue en Kubernetes (model serving) de los modelos entrenados por MLflow.
KServe requiere Cert Manager para el manejo de certificados webhooks, e Istio como controlador de red (Ingress Gateway) para exponer los servicios de inferencia.
# Crear namespace de destino
kubectl create namespace mlflow-kserve
# Instalar Cert Manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.0/cert-manager.yaml
# Agregar chart de Istio al repositorio
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update
# Instalar Istio
helm install istio-base istio/base -n istio-system --set defaultRevision=default --create-namespace
helm install istiod istio/istiod -n istio-system --wait
# Instalar ingress gateway de Istio.
# La etiqueta `istio=ingressgateway` es OBLIGATORIA: el controlador de Istio la usa
# para identificar qué proxies Envoy deben procesar las rutas de objetos Ingress.
kubectl create namespace istio-ingress
helm install istio-ingress istio/gateway -n istio-ingress --set labels.istio=ingressgateway --waitValidar instalación de Istio
helm ls -n istio-system
kubectl get deployments -n istio-system --output wideRegistrar Istio como IngressClass
KServe en modo Estándar crea objetos Ingress de Kubernetes con la clase istio. Es necesario registrar dicha clase en el clúster para que el controlador de Istio los detecte y programe las rutas en Envoy:
# El archivo kserve-ingress.yaml define el IngressClass de tipo `istio.io/ingress-controller`
kubectl apply -f kserve-ingress.yamlUna vez instaladas las dependencias, despliega KServe en modo Estándar, configurado explícitamente para usar Istio (y no el Gateway API de Kubernetes):
# Instalar KServe CRDs
helm install kserve-crd oci://ghcr.io/kserve/charts/kserve-crd --version v0.17.0
# Instalar recursos de KServe
helm install kserve-resources oci://ghcr.io/kserve/charts/kserve-resources --version v0.17.0 \
--namespace mlflow-kserve \
--set kserve.controller.deploymentMode=Standard \
--set kserve.controller.gateway.ingressGateway.enableGatewayApi=false \
--set kserve.controller.gateway.ingressGateway.kserveGateway=mlflow-kserve/kserve-ingress-gatewayImportante: El flag
enableGatewayApi=falsees necesario para que KServe genere objetosIngressestándar (en lugar deHTTPRoute), que son los que Istio traduce automáticamente en reglas de Envoy.
Para que los contenedores de inferencia puedan descargar el modelo alojado en el bucket GCS:
# Crear el secreto con el formato y nombre de archivo que espera KServe
kubectl create secret generic gcp-mlflow-key \
--from-file=gcloud-application-credentials.json=/home/eric/.config/gcloud/k8s-mlflow-key.json \
-n mlflow-kserve
# Vincular el secreto a la Service Account principal en el namespace
kubectl patch serviceaccount default \
-n mlflow-kserve \
-p '{"secrets": [{"name": "gcp-mlflow-key"}]}'Aplica los manifiestos para configurar el Gateway de Istio y el servicio de inferencia:
# Gateway de Istio para KServe.
# Define un recurso `networking.istio.io/v1beta1 Gateway` que selecciona el proxy
# Envoy del namespace `istio-ingress` para enrutar el tráfico entrante al modelo.
kubectl apply -f kserve-gateway.yaml
# InferenceService del modelo Iris
kubectl apply -f kserve-service.yaml
# Verificar estado (puede tardar unos minutos en la primera inicialización)
kubectl get inferenceservice mlflow-iris-classifier -n mlflow-kserveEl servicio estará listo cuando la columna READY muestre True.
Con el servicio activo, envía una petición HTTP al Ingress Gateway de Istio para obtener una predicción del modelo.
1. Preparar los datos de entrada (test-input.json)
El modelo fue entrenado con un DataFrame de Pandas, por lo que cada campo de entrada debe coincidir exactamente con el nombre de columna registrado en MLflow (incluyendo variables derivadas como sepal_ratio). El archivo test-input.json ya contiene la estructura correcta:
{
"inputs": [
{
"name": "sepal length (cm)",
"shape": [1],
"datatype": "FP64",
"data": [5.1]
},
{
"name": "sepal width (cm)",
"shape": [1],
"datatype": "FP64",
"data": [3.5]
},
{
"name": "petal length (cm)",
"shape": [1],
"datatype": "FP64",
"data": [1.4]
},
{
"name": "petal width (cm)",
"shape": [1],
"datatype": "FP64",
"data": [0.2]
},
{
"name": "sepal_ratio",
"shape": [1],
"datatype": "FP64",
"data": [1.4571]
}
]
}Nota:
sepal_ratioes la variable derivadasepal_length / sepal_widthcalculada e incluida como feature durante el entrenamiento del modelo en el DAG de MLflow.
2. Redirigir el puerto del Istio Ingress Gateway
INGRESS_GATEWAY_SERVICE=$(kubectl get svc -n istio-ingress --selector="app=istio-ingress" -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward -n istio-ingress svc/${INGRESS_GATEWAY_SERVICE} 8888:80 &3. Ejecutar la inferencia
# Obtener el hostname asignado por KServe al servicio
SERVICE_HOSTNAME=$(kubectl get inferenceservice mlflow-iris-classifier -n mlflow-kserve -o jsonpath='{.status.url}' | cut -d "/" -f 3)
# Enviar la petición al endpoint V2 de inferencia
curl -H "Host: ${SERVICE_HOSTNAME}" \
-H "Content-Type: application/json" \
http://localhost:8888/v2/models/mlflow-iris-classifier/infer \
-d @./test-input.jsonUna respuesta exitosa incluirá la clase predicha por el modelo (por ejemplo, 0 = Iris setosa):
{
"model_name": "mlflow-iris-classifier",
"outputs": [
{
"name": "output-1",
"shape": [1, 1],
"datatype": "INT64",
"data": [0]
}
]
}Referencias:
Si posteriormente realizas cambios en tu archivo airflow-values.yaml (ej. cambiar configuraciones, habilitar nuevos servicios), aplica los cambios con el comando de actualización:
helm upgrade airflow apache-airflow/airflow -n airflow -f airflow-values.yaml --timeout 15m --waitEn caso de requerir una instalación desde cero o si ocurren errores irrecuperables, puedes limpiar todo el entorno:
helm uninstall airflow -n airflow
kubectl delete all --all -n airflowPara acceder a la consola web de Airflow de manera local, redirecciona el puerto 8080 de tu equipo al servicio correspondiente de la API/UI en Kubernetes.
kubectl port-forward svc/airflow-api-server 8080:8080 --namespace airflowAccede desde el navegador a: http://localhost:8080
Si algo no está funcionando como se espera o algún DAG no aparece (como problemas con GitSync), puedes revisar los registros (logs) de los pods:
# 1. Obtener la lista de los pods activos
kubectl get pods -n airflow
# 2. Ver registros de un pod específico
kubectl logs pod/<nombre-del-pod-api-server> -n airflow
kubectl logs pod/<nombre-del-pod-dag-processor> -n airflow
kubectl logs pod/<nombre-del-pod-scheduler> -n airflow
kubectl logs pod/<nombre-del-pod-triggerer> -n airflow
# 3. Si un pod no inicia, inspeccionar los eventos de Kubernetes
kubectl describe pod <nombre-del-pod> -n airflow
kubectl get events -n airflowMuy útil para validar si un DAG en particular se ha sincronizado y existe dentro del sistema de archivos del contenedor:
kubectl exec -it -n airflow <nombre-del-pod-scheduler> -- sh(Nota: Dentro del pod, los DAGs típicamente se sincronizan en /opt/airflow/dags)
Recursos recomendados para ampliar información sobre la herramienta y el Helm Chart:
- Airflow Helm Chart Quick start for Beginners (Apuntes Notion)
- Video explicativo original - YouTube
- https://docs.greatexpectations.io/docs/core/connect_to_data/dataframes/
- https://grafana.com/docs/grafana/latest/setup-grafana/installation/helm/
- https://mlflow.org/docs/latest/ml/deployment/deploy-model-to-kubernetes/tutorial/
- https://kserve.github.io/website/docs/admin-guide/kubernetes-deployment
- https://cert-manager.io/docs/installation/
- https://mlflow.org/docs/latest/self-hosting/architecture/artifact-store/#google-cloud-storage
- https://istio.io/latest/docs/setup/install/helm/
Lista consolidada de todos los comandos necesarios para redirigir los puertos y acceder a las interfaces web de las aplicaciones desplegadas:
# Apache Airflow (UI / API Server)
# Acceso: http://localhost:8080
kubectl port-forward svc/airflow-api-server 8080:8080 --namespace airflow &
# MLflow (Tracking Server)
# Acceso: http://localhost:30500
# Nota: se usa el puerto 30500 porque el 5000 puede estar ocupado en Windows
kubectl port-forward svc/mlflow 30500:5000 -n airflow &
# Grafana (Dashboard de Métricas)
# Acceso: http://localhost:3100
# Nota: exportar la variable con el nombre del pod antes de ejecutar el port-forward
export POD_NAME=$(kubectl get pods \
--namespace grafana-ns \
-l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=grafana" \
-o jsonpath="{.items[0].metadata.name}")
kubectl --namespace grafana-ns port-forward $POD_NAME 3100:3000 &
# KServe / Istio Ingress Gateway (Inferencia de Modelos)
# Puerto local 8888 → puerto 80 del servicio istio-ingress
INGRESS_GATEWAY_SERVICE=$(kubectl get svc -n istio-ingress --selector="app=istio-ingress" -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward -n istio-ingress svc/${INGRESS_GATEWAY_SERVICE} 8888:80 &Comandos para detener los procesos
kill -9 $(lsof -t -i:8080)
kill -9 $(lsof -t -i:30500)
kill -9 $(lsof -t -i:3100)
kill -9 $(lsof -t -i:8888)Nota: Los archivos de documentación correspondiente fueron generados o asistidos mediante el uso de inteligencia artificial.