Tilt: Desarrollo Local de Kubernetes con Live Updates

Tilt es una herramienta de desarrollo local para Kubernetes que ofrece actualizaciones en tiempo real, orquestación de múltiples servicios y una interfaz visual para monitorear el estado de tu entorno. Esta guía cubre la instalación de Tilt en Linux, la configuración del Tiltfile, live updates, desarrollo multi-servicio y flujos de depuración.

Requisitos Previos

  • Docker Desktop, kind, k3d o minikube para el clúster local
  • kubectl configurado
  • Sistema operativo Linux con kernel 4.15+
# Instalar kind para desarrollo local (recomendado con Tilt)
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x kind
sudo mv kind /usr/local/bin/

# Crear un clúster local
kind create cluster --name desarrollo

# Verificar el contexto de kubectl
kubectl config current-context

Instalación de Tilt

# Instalar Tilt en Linux
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash

# Verificar la instalación
tilt version

# Alternativa: instalar con Homebrew (si está disponible en tu sistema)
# brew install tilt

El Tiltfile Básico

El Tiltfile es el archivo de configuración de Tilt, escrito en Starlark (un dialecto de Python):

# Tiltfile - Configuración básica para una aplicación web
# Este archivo vive en la raíz del proyecto

# 1. Construir la imagen Docker
docker_build(
    'mi-aplicacion',      # Nombre de la imagen
    '.',                  # Contexto de construcción
    dockerfile='Dockerfile'
)

# 2. Aplicar los manifiestos de Kubernetes
k8s_yaml('k8s/deployment.yaml')
k8s_yaml('k8s/service.yaml')

# 3. Configurar la redirección de puertos para acceso local
k8s_resource(
    'mi-aplicacion',
    port_forwards='8080:8080'  # puerto_local:puerto_contenedor
)

Para múltiples archivos YAML:

# Cargar todos los manifiestos de un directorio
k8s_yaml(listdir('k8s/'))

# O usar Kustomize directamente
k8s_yaml(kustomize('k8s/overlays/desarrollo'))

# O usar Helm
k8s_yaml(helm(
    'helm/mi-app',
    name='mi-app',
    values=['helm/values-dev.yaml']
))

Live Updates

Los Live Updates son la característica más potente de Tilt: actualizar el código en el contenedor sin reconstruir la imagen:

# Tiltfile con live_update para una aplicación Python/Flask
docker_build(
    'mi-app-python',
    '.',
    dockerfile='Dockerfile',
    live_update=[
        # Sincronizar archivos Python directamente al contenedor
        sync('./src', '/app/src'),
        sync('./templates', '/app/templates'),
        # Ejecutar un comando después de sincronizar (reiniciar el servidor)
        run('cd /app && pip install -r requirements.txt',
            trigger='requirements.txt'),
    ]
)

k8s_yaml('k8s/')
k8s_resource('mi-app-python', port_forwards='5000:5000')

Live update para una aplicación Node.js:

# Tiltfile para Node.js con recarga automática
docker_build(
    'mi-app-node',
    '.',
    dockerfile='Dockerfile.dev',
    live_update=[
        # No sincronizar node_modules
        fall_back_on(['package.json', 'package-lock.json']),
        # Sincronizar el código fuente
        sync('./src', '/app/src'),
        sync('./public', '/app/public'),
    ]
)

Live update para Go (requiere recompilación en el contenedor):

# Live update para aplicaciones Go
docker_build(
    'mi-app-go',
    '.',
    live_update=[
        # Al cambiar cualquier .go, caer de vuelta a rebuild completo
        fall_back_on(['go.mod', 'go.sum']),
        sync('.', '/workspace'),
        # Recompilar dentro del contenedor (más rápido que reconstruir la imagen)
        run('cd /workspace && go build -o /app/servidor ./cmd/servidor/'),
        restart_container(),
    ]
)

Desarrollo Multi-Servicio

Tilt brilla especialmente cuando gestionas múltiples microservicios:

# Tiltfile para arquitectura de microservicios
# ----------------------------------------

# Servicio de base de datos (imagen preexistente, sin construcción)
k8s_yaml('k8s/postgres.yaml')
k8s_resource('postgres', port_forwards='5432:5432')

# Servicio Redis
k8s_yaml('k8s/redis.yaml')
k8s_resource('redis', port_forwards='6379:6379')

# Servicio de API backend
docker_build(
    'api-backend',
    'services/api',
    live_update=[
        sync('services/api/src', '/app/src'),
    ]
)
k8s_yaml('k8s/api.yaml')
k8s_resource(
    'api-backend',
    port_forwards='3000:3000',
    resource_deps=['postgres', 'redis']  # Esperar a que postgres y redis estén listos
)

# Servicio frontend
docker_build(
    'frontend',
    'services/frontend',
    live_update=[
        sync('services/frontend/src', '/app/src'),
    ]
)
k8s_yaml('k8s/frontend.yaml')
k8s_resource(
    'frontend',
    port_forwards='8080:80',
    resource_deps=['api-backend']
)

# Worker de procesamiento en segundo plano
docker_build('worker', 'services/worker')
k8s_yaml('k8s/worker.yaml')
k8s_resource('worker', resource_deps=['redis'])

Dependencias entre Recursos

# Controlar el orden de inicio con dependencias
k8s_resource(
    'migraciones-db',
    resource_deps=['postgres']
)

k8s_resource(
    'api-backend',
    resource_deps=['migraciones-db']
)

# Ejecutar comandos locales como parte del flujo
local_resource(
    'generar-codigo',
    cmd='make generate',
    deps=['api/schema.proto'],
    labels=['herramientas']
)

# Recurso que corre tests automáticamente al cambiar el código
local_resource(
    'tests-unitarios',
    cmd='go test ./...',
    deps=['./src'],
    auto_init=False,  # No ejecutar al inicio, solo cuando cambien los deps
    trigger_mode=TRIGGER_MODE_MANUAL  # O automático: TRIGGER_MODE_AUTO
)

Depuración con Tilt

# Configurar Tilt para depuración remota con delve (Go)
docker_build(
    'mi-app-debug',
    '.',
    dockerfile='Dockerfile.debug',
    live_update=[
        sync('.', '/workspace'),
        run('cd /workspace && dlv debug --headless --listen=:2345 --api-version=2 ./cmd/main/'),
    ]
)

k8s_resource(
    'mi-app-debug',
    port_forwards=[
        '8080:8080',   # Puerto de la aplicación
        '2345:2345',   # Puerto del debugger Delve
    ]
)
# Acceder a la interfaz web de Tilt
# Tilt abre automáticamente http://localhost:10350

# Iniciar Tilt en modo headless (sin abrir el navegador)
tilt up --stream

# Ver los logs de un recurso específico
tilt logs mi-aplicacion

# Forzar la reconstrucción de un recurso
tilt trigger mi-aplicacion

# Ver el estado de todos los recursos
tilt get uiresources

Configuración Avanzada del Tiltfile

# Tiltfile avanzado con variables de configuración

# Cargar extensiones de la librería de Tilt
load('ext://restart_process', 'docker_build_with_restart')
load('ext://helm_resource', 'helm_resource', 'helm_repo')

# Configuración según el entorno
config.define_string_list('servicios', usage='Servicios a iniciar')
cfg = config.parse()
servicios_activos = cfg.get('servicios', ['frontend', 'api', 'worker'])

# Solo construir los servicios especificados
if 'api' in servicios_activos:
    docker_build('api', 'services/api')
    k8s_yaml('k8s/api.yaml')

if 'frontend' in servicios_activos:
    docker_build('frontend', 'services/frontend')
    k8s_yaml('k8s/frontend.yaml')

# Configurar labels para agrupar recursos en la UI
k8s_resource('postgres', labels=['infraestructura'])
k8s_resource('redis', labels=['infraestructura'])
k8s_resource('api', labels=['backend'])
k8s_resource('frontend', labels=['frontend'])
# Iniciar solo algunos servicios
tilt up -- --servicios=api,postgres

# Usar un Tiltfile diferente
tilt up --file Tiltfile.staging

Solución de Problemas

# Ver el estado detallado de Tilt
tilt get kubernetesapplys

# Verificar los logs de Tilt
tilt logs --follow

# Reiniciar un recurso desde la CLI
tilt trigger api-backend

# Limpiar todos los recursos creados por Tilt
tilt down

# Limpiar también las imágenes Docker
tilt down --delete-namespaces

# Depurar problemas de sincronización
tilt get filewatch

# Verificar que kind tiene acceso al registro local
# Para kind, es necesario cargar las imágenes al clúster
kind load docker-image mi-aplicacion --name desarrollo

Tilt no detecta cambios en archivos:

# Verificar el límite de inotify watches en Linux
cat /proc/sys/fs/inotify/max_user_watches

# Aumentar el límite si es necesario
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sysctl -p

Conclusión

Tilt transforma el desarrollo en Kubernetes en una experiencia fluida y reactiva, eliminando los tiempos de espera entre cambio de código y verificación del resultado. Su interfaz web centralizada, las live updates y la gestión declarativa de dependencias entre servicios lo convierten en la herramienta ideal para equipos que trabajan con arquitecturas de microservicios. La inversión en configurar un buen Tiltfile se amortiza rápidamente en productividad diaria.