Skaffold es una herramienta de línea de comandos de Google que automatiza el ciclo de construir la imagen, publicarla y desplegarla en Kubernetes cada vez que cambias el código. En este tutorial instalarás Skaffold en Ubuntu 24.04, prepararás una pequeña aplicación Python, la desarrollarás con skaffold dev usando sincronización de archivos en caliente y crearás perfiles para desplegar la misma aplicación desde un pipeline de CI/CD.

Requisitos previos

  • Ubuntu 24.04 LTS en tu equipo de desarrollo o en un VPS de CubePath, con un usuario no root con sudo.
  • Docker Engine instalado y tu usuario en el grupo docker (docker run hello-world debe funcionar sin sudo).
  • kubectl instalado.
  • Un clúster de Kubernetes. Para desarrollo local se recomienda kind, que se instala en el paso 1.

Paso 1: Crear un clúster local con kind

Skaffold detecta los clústeres locales (kind, k3d, minikube) y carga en ellos las imágenes directamente, sin necesidad de un registro. Descarga kind desde su página de releases. 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. kind configura automáticamente el contexto kind-desarrollo en kubectl:

kind create cluster --name desarrollo
kubectl get nodes
NAME                       STATUS   ROLES           AGE   VERSION
desarrollo-control-plane   Ready    control-plane   40s   v1.32.2

Paso 2: Instalar Skaffold

Skaffold se distribuye como un binario estático. Descarga la última versión estable y colócala en /usr/local/bin:

curl -fsSLo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
sudo install -m 0755 skaffold /usr/local/bin/skaffold

En servidores ARM64 usa skaffold-linux-arm64 en lugar de skaffold-linux-amd64. Comprueba la instalación:

skaffold version
v2.16.0

Opcionalmente, activa el autocompletado en Bash:

echo 'source <(skaffold completion bash)' >> ~/.bashrc

Paso 3: Preparar una aplicación de ejemplo

Crea un directorio de proyecto con una aplicación Flask mínima:

mkdir -p ~/hola-skaffold/k8s
cd ~/hola-skaffold
nano app.py
from flask import Flask

app = Flask(__name__)


@app.route("/")
def index():
    return "Hola desde Skaffold\n"

Crea el Dockerfile. El servidor se arranca con --debug, que recarga la aplicación cuando un archivo .py cambia dentro del contenedor. Esto es lo que aprovechará la sincronización de Skaffold:

nano Dockerfile
FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir flask
COPY app.py .
CMD ["flask", "--app", "app", "run", "--host", "0.0.0.0", "--port", "8080", "--debug"]

Crea los manifiestos de Kubernetes. La imagen se llama hola-skaffold, sin etiqueta: Skaffold la sustituye por la etiqueta que genera en cada build.

nano k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hola-skaffold
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hola-skaffold
  template:
    metadata:
      labels:
        app: hola-skaffold
    spec:
      containers:
        - name: app
          image: hola-skaffold
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: hola-skaffold
spec:
  selector:
    app: hola-skaffold
  ports:
    - port: 80
      targetPort: 8080

Paso 4: Crear el archivo skaffold.yaml

skaffold init puede generar el archivo detectando el Dockerfile y los manifiestos, pero es mejor entender cada bloque. Crea skaffold.yaml en la raíz del proyecto:

nano skaffold.yaml
apiVersion: skaffold/v4beta11
kind: Config
metadata:
  name: hola-skaffold

build:
  artifacts:
    - image: hola-skaffold
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "*.py"
            dest: .
  local:
    push: false

manifests:
  rawYaml:
    - k8s/deployment.yaml

deploy:
  kubectl: {}

portForward:
  - resourceType: service
    resourceName: hola-skaffold
    port: 80
    localPort: 8080

Qué hace cada sección:

  • build.artifacts define la imagen y cómo se construye. sync.manual indica que los cambios en archivos .py se copian al contenedor en ejecución (en el WORKDIR, /app) en lugar de reconstruir la imagen.
  • build.local.push: false evita subir la imagen a un registro. Con kind, Skaffold la carga directamente en el clúster.
  • manifests y deploy separan qué se renderiza (YAML plano, Kustomize o Helm) de cómo se aplica (kubectl o helm).
  • portForward redirige localhost:8080 al Service cuando ejecutas Skaffold con --port-forward.

Valida la configuración renderizando los manifiestos sin desplegar nada:

skaffold render --digest-source=tag

La salida muestra el Deployment con la imagen ya etiquetada por Skaffold. Por defecto la etiqueta se basa en el commit de Git, así que conviene que el proyecto sea un repositorio (git init y un primer commit).

Paso 5: Desarrollar con skaffold dev

skaffold dev construye la imagen, la despliega, muestra los logs de los pods y vigila el directorio. Cuando detecta cambios, sincroniza o reconstruye según corresponda. Al pulsar Ctrl+C elimina lo que ha desplegado.

skaffold dev --port-forward
Generating tags...
 - hola-skaffold -> hola-skaffold:3f2c1ab
Building [hola-skaffold]...
Loading images into kind cluster nodes...
Starting deploy...
 - deployment.apps/hola-skaffold created
 - service/hola-skaffold created
Waiting for deployments to stabilize...
 - deployment/hola-skaffold is ready.
Port forwarding service/hola-skaffold in namespace default, remote port 80 -> http://127.0.0.1:8080
Watching for changes...

En otra terminal, comprueba la respuesta:

curl http://127.0.0.1:8080
Hola desde Skaffold

Ahora edita app.py y cambia el texto a Hola de nuevo. En la terminal de Skaffold verás Syncing 1 files for hola-skaffold en lugar de un build completo, y Flask recargará la aplicación en uno o dos segundos:

curl http://127.0.0.1:8080
Hola de nuevo

Si en cambio modificas el Dockerfile, Skaffold reconstruye la imagen y vuelve a desplegar, porque ese archivo no está cubierto por la regla de sincronización.

Paso 6: Añadir perfiles para otros entornos

Los perfiles modifican partes de skaffold.yaml según el entorno. Añade al final del archivo un perfil ci que publica la imagen en un registro y comprueba el estado del despliegue con un límite de tiempo:

nano skaffold.yaml
profiles:
  - name: ci
    build:
      artifacts:
        - image: hola-skaffold
          docker:
            dockerfile: Dockerfile
      local:
        push: true
    deploy:
      kubectl: {}
      statusCheckDeadlineSeconds: 180

Las listas de un perfil sustituyen por completo a las de la configuración principal, por eso el perfil vuelve a declarar el artefacto, esta vez sin sync, que no tiene sentido en CI. Comprueba que el perfil se resuelve correctamente:

skaffold diagnose -p ci

Un perfil también puede activarse solo con un contexto o un comando concretos mediante activation, por ejemplo para usar siempre el perfil ci al apuntar al clúster de staging:

    activation:
      - kubeContext: staging

Paso 7: Desplegar desde CI/CD

En un pipeline no quieres el modo de vigilancia, sino un build y un deploy únicos. Inicia sesión en tu registro y usa skaffold run con el perfil ci. La opción --default-repo antepone el registro al nombre de la imagen y --tag fija la etiqueta al commit:

docker login registry.example.com
skaffold run -p ci \
  --default-repo registry.example.com/your_team \
  --tag "$(git rev-parse --short HEAD)"

Sustituye registry.example.com/your_team por la ruta de tu registro. El clúster de destino es el contexto actual de kubectl, así que el job de CI debe tener un kubeconfig con permisos sobre el namespace.

Si prefieres separar el build del despliegue (por ejemplo, construir una vez y promocionar la misma imagen entre entornos), Skaffold puede guardar el resultado del build en un archivo y reutilizarlo:

skaffold build -p ci --default-repo registry.example.com/your_team --file-output=build.json
skaffold deploy -p ci --build-artifacts=build.json

Para borrar lo desplegado por skaffold run:

skaffold delete -p ci

Solución de problemas

ErrImagePull o ImagePullBackOff en un clúster remoto: con push: false la imagen solo existe en tu Docker local. En clústeres que no son kind, k3d ni minikube necesitas push: true, --default-repo y, si el registro es privado, un imagePullSecret.

Los cambios no se sincronizan: ejecuta skaffold dev -v debug y busca líneas con sync. El patrón de src es relativo al contexto del artefacto y dest es relativo al WORKDIR del contenedor.

Build lento o que reutiliza una capa antigua: desactiva la caché de artefactos de Skaffold con skaffold dev --cache-artifacts=false.

Ver la configuración efectiva tras aplicar perfiles: skaffold diagnose --yaml-only -p ci imprime el skaffold.yaml resultante.

Conclusión

Has instalado Skaffold, creado un flujo de desarrollo en el que los cambios de código llegan al pod en segundos sin reconstruir la imagen, y un perfil para construir, publicar y desplegar desde CI/CD. Como siguientes pasos, puedes sustituir rawYaml por kustomize o helm para gestionar varios entornos, usar skaffold debug para conectar un depurador a los pods, o comparar este flujo con Tilt si trabajas con muchos microservicios a la vez.