Tilt es una herramienta de desarrollo local para Kubernetes: lee un archivo Tiltfile, construye tus imágenes, las despliega en el clúster y mantiene todo actualizado mientras editas el código. Su punto fuerte es live update, que copia los archivos modificados al contenedor en ejecución sin reconstruir la imagen, y una interfaz web que muestra el estado y los logs de todos los servicios. En este tutorial instalarás Tilt en Ubuntu 24.04, lo usarás con un clúster kind y montarás un entorno con una API y una base de datos Redis.
Requisitos previos
- Ubuntu 24.04 LTS en tu equipo o en un VPS de CubePath, con un usuario no root con
sudo. - Docker Engine instalado y tu usuario en el grupo
docker. kubectlinstalado.- Al menos 4 GB de RAM libres para el clúster local.
Paso 1: Crear un clúster local con kind
Tilt funciona con cualquier clúster, pero por seguridad solo despliega sin preguntar en contextos locales conocidos (kind, k3d, minikube, Docker Desktop). Instala kind; consulta la última versión en https://github.com/kubernetes-sigs/kind/releases y ajusta la variable si hay una más reciente:
KIND_VERSION=v0.27.0
curl -fsSLo kind "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64"
sudo install -m 0755 kind /usr/local/bin/kind
Crea el clúster y comprueba que kubectl apunta a él:
kind create cluster --name desarrollo
kubectl config current-context
kind-desarrollo
Paso 2: Instalar Tilt
Tilt publica un script de instalación que detecta tu arquitectura, descarga el binario de la última versión y lo coloca en tu PATH. Descárgalo, revísalo y ejecútalo:
curl -fsSLo install-tilt.sh https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh
less install-tilt.sh
bash install-tilt.sh
Comprueba la instalación:
tilt version
v0.35.0
Paso 3: Preparar una aplicación de ejemplo
Crea un proyecto con una API Flask que cuenta visitas en Redis:
mkdir -p ~/hola-tilt/api ~/hola-tilt/k8s
cd ~/hola-tilt
nano api/app.py
import os
import redis
from flask import Flask
app = Flask(__name__)
r = redis.Redis(host=os.environ.get("REDIS_HOST", "redis"), port=6379)
@app.route("/")
def index():
visitas = r.incr("visitas")
return f"Hola desde Tilt. Visitas: {visitas}\n"
nano api/requirements.txt
flask
redis
El Dockerfile instala las dependencias en una capa propia y arranca Flask con --debug, que recarga el proceso cuando cambia un archivo .py:
nano api/Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["flask", "--app", "app", "run", "--host", "0.0.0.0", "--port", "8080", "--debug"]
Crea los manifiestos. Redis usa una imagen pública; la API usa la imagen api que construirá Tilt:
nano k8s/redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
selector:
app: redis
ports:
- port: 6379
nano k8s/api.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: api
ports:
- containerPort: 8080
env:
- name: REDIS_HOST
value: redis
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
Paso 4: Escribir el Tiltfile
El Tiltfile se escribe en Starlark, un dialecto de Python. Crea el archivo en la raíz del proyecto:
nano Tiltfile
# Manifiestos de Kubernetes que Tilt aplicará
k8s_yaml(['k8s/redis.yaml', 'k8s/api.yaml'])
# Imagen de la API con live update
docker_build(
'api',
'api',
live_update=[
fall_back_on(['api/Dockerfile']),
sync('api/app.py', '/app/app.py'),
sync('api/requirements.txt', '/app/requirements.txt'),
run('pip install --no-cache-dir -r /app/requirements.txt',
trigger=['api/requirements.txt']),
],
)
# Recursos: nombre del workload, puertos y dependencias
k8s_resource('redis', labels=['infraestructura'])
k8s_resource(
'api',
port_forwards='8080:8080',
resource_deps=['redis'],
labels=['backend'],
)
Cómo funciona:
k8s_yamlcarga los manifiestos. Tilt agrupa cada Deployment con su Service en un recurso con el nombre del workload (redis,api).docker_build('api', 'api', ...)construye la imagenapicon el directorioapi/como contexto y la sustituye en el manifiesto que la usa. Con kind, Tilt carga la imagen en el clúster sin necesidad de registro.live_updatedefine los pasos que se ejecutan cuando cambian archivos:fall_back_onfuerza un rebuild completo si cambia elDockerfile(debe ir primero),synccopia archivos al contenedor yrunejecuta un comando solo cuando cambia un archivo detrigger.resource_deps=['redis']hace que la API no se despliegue hasta que Redis esté listo.labelsagrupa los recursos en la interfaz web.
Paso 5: Arrancar el entorno con tilt up
Inicia Tilt desde el directorio del proyecto:
tilt up
Tilt started on http://localhost:10350/
(space) to open the browser
(s) to stream logs (--stream=true)
(t) to open legacy terminal mode (--legacy=true)
(ctrl-c) to exit
Abre http://localhost:10350 para ver la interfaz web: cada recurso aparece con su estado de build, de despliegue y sus logs. Si trabajas en un VPS remoto, abre un túnel SSH desde tu equipo y usa la misma URL en tu navegador local:
ssh -L 10350:localhost:10350 -L 8080:localhost:8080 your_user@your_server_ip
Cuando api aparezca en verde, prueba el servicio:
curl http://localhost:8080
Hola desde Tilt. Visitas: 1
Paso 6: Comprobar el live update
Edita api/app.py y cambia el texto a Hola de nuevo. En la interfaz web, el recurso api mostrará una actualización de tipo live update que tarda uno o dos segundos, sin reconstruir la imagen. Flask detecta el cambio y recarga:
curl http://localhost:8080
Hola de nuevo. Visitas: 2
El contador sigue subiendo porque Redis no se ha reiniciado. Si modificas requirements.txt, Tilt sincroniza el archivo y ejecuta pip install dentro del contenedor. Si modificas el Dockerfile, hace un build completo y vuelve a desplegar el pod.
Notalos cambios de live update solo existen en el contenedor en ejecución. Si el pod se reinicia, Kubernetes arranca de nuevo la última imagen construida y Tilt vuelve a sincronizar en la siguiente actualización.
Paso 7: Añadir tareas locales
local_resource ejecuta comandos en tu máquina como parte del entorno, por ejemplo tests que se lanzan al cambiar el código. Añade al final del Tiltfile:
local_resource(
'tests-api',
cmd=['python3', '-c', "import ast; ast.parse(open('api/app.py').read())"],
deps=['api/app.py'],
labels=['tests'],
)
El comando comprueba la sintaxis de app.py sin escribir archivos en api/ (un py_compile generaría __pycache__ dentro del contexto de build y forzaría un rebuild). Tilt recarga el Tiltfile automáticamente al guardarlo. El recurso tests-api aparece en la interfaz y se ejecuta cada vez que cambia api/app.py; si el archivo tiene un error de sintaxis, el recurso se marca en rojo con la salida del comando. En un proyecto real sustituye el comando por tu suite de tests (pytest, go test ./..., npm test).
Para tareas costosas que no quieras lanzar en cada cambio, añade trigger_mode=TRIGGER_MODE_MANUAL y auto_init=False, y ejecútalas desde el botón de la interfaz o con tilt trigger tests-api.
Paso 8: Usar la CLI y detener el entorno
Con Tilt en marcha, puedes consultar su estado desde otra terminal:
tilt get uiresources
tilt logs api
tilt trigger api
tilt trigger fuerza una actualización de un recurso. Para CI existe tilt ci, que levanta el entorno, espera a que todos los recursos estén sanos y termina con código de error si alguno falla.
Para detener Tilt pulsa Ctrl+C en la terminal de tilt up. Los recursos siguen en el clúster hasta que los eliminas:
tilt down
Solución de problemas
Stop! staging might be production: Tilt se niega a desplegar en contextos que no reconoce como locales. Si el clúster es realmente de desarrollo, autorízalo al principio del Tiltfile con allow_k8s_contexts('nombre-del-contexto').
Tilt no detecta cambios en archivos: en proyectos grandes puedes agotar los watches de inotify. Aumenta el límite de forma persistente:
echo 'fs.inotify.max_user_watches=524288' | sudo tee /etc/sysctl.d/60-inotify.conf
sudo sysctl --system
El pod queda en ErrImagePull: el nombre de imagen de docker_build debe coincidir exactamente con el image: del manifiesto. Revisa también que el contexto de kubectl sea el de kind y no uno remoto sin registro configurado.
Live update falla y Tilt hace rebuild completo: la ruta de destino de sync debe existir en el contenedor y el usuario del contenedor debe poder escribir en ella.
Conclusión
Has instalado Tilt, creado un clúster kind y configurado un entorno con dos servicios en el que los cambios de código llegan al pod en segundos gracias a live update, con dependencias entre recursos y tests locales integrados. Como siguientes pasos, puedes cargar manifiestos con kustomize() o helm() desde el Tiltfile, usar las extensiones de load('ext://...') para tareas habituales, o configurar un registro local con ctlptl para acelerar los builds en proyectos grandes.
