Cerramos el módulo 9 con un diagnóstico incómodo: la plataforma Rutas Norte ya sabe escalar sola, resistir el puente de mayo y protegerse durante los mantenimientos, pero el directorio k8s/ se ha convertido en más de 120 ficheros YAML duplicados entre dev, pre y pro que alguien aplica a mano desde su portátil. El módulo 10 va precisamente de eso: de las herramientas que rodean a Kubernetes y que convierten un montón de manifiestos sueltos en un sistema operable.

Empezamos por el eslabón más cercano al equipo de desarrollo: el clúster local. Antes de plantillar nada con Helm (10-03), superponer nada con Kustomize (10-04) o reconciliar nada con Argo CD (10-05), hace falta un sitio barato y desechable donde probar los cambios. En el módulo 1 levantamos el clúster de prácticas con minikube start --profile=rutas-norte casi como un trámite, y dijimos que volveríamos a ello. Ha llegado el momento: en esta lección vamos a fondo con minikube, conocemos kind (la herramienta preferida en integración continua), comparamos las cinco alternativas más habituales y, sobre todo, aprendemos qué se puede validar en un clúster local y qué no, que es la raíz del clásico «en mi máquina funciona».

Contenido

  1. Por qué todo equipo necesita un clúster desechable
  2. Minikube a fondo: perfiles y controladores
  3. Dimensionado, versión de Kubernetes y clústeres multinodo
  4. El catálogo de addons
  5. Trabajar con imágenes locales sin registro
  6. Acceso al clúster: tunnel, service, dashboard, mount y ssh
  7. El ciclo de vida: stop, start, delete y logs
  8. kind a fondo: nodos como contenedores
  9. Tabla comparativa: minikube, kind, k3d, Docker Desktop y Rancher Desktop
  10. Las diferencias con producción que provocan «en mi máquina funciona»
  11. Receta reproducible: el entorno completo de Rutas Norte en local
  12. Errores comunes y consejos
  13. Ejercicios
  14. Conclusión

  1. Por qué todo equipo necesita un clúster desechable

En Rutas Norte S.L. hay tres entornos reales: rutas-norte-dev, rutas-norte-pre y rutas-norte-pro. Los tres viven en clústeres compartidos. Eso significa que cuando una desarrolladora quiere probar si su nuevo NetworkPolicy rompe la conexión entre api-reservas y postgres-reservas, tiene dos opciones malas:

  • Aplicarlo en rutas-norte-dev y arriesgarse a bloquear el trabajo de los otros seis compañeros que comparten ese namespace.
  • No probarlo y descubrirlo en pre, con el ciclo de espera que eso implica.

Un clúster local resuelve el dilema. Es suyo, gratis, rápido de recrear y, si lo rompe, minikube delete y vuelta a empezar en tres minutos.

Qué SÍ se puede validar en local

Aspecto ¿Validable en local? Comentario
Sintaxis y validez de los manifiestos Sí, perfectamente kubectl apply --dry-run=server ya valida contra el esquema real
Comportamiento de un Deployment, rollout y rollback El controlador es el mismo que en producción
Sondas de liveness/readiness/startup Misma lógica del kubelet
ConfigMaps, Secrets y variables de entorno Idéntico
RBAC: Roles, RoleBindings, ServiceAccounts La API es la misma
Ingress y enrutado por host/ruta Sí, con el addon o extraPortMappings Otro controlador, pero mismas reglas
CRDs y operadores Se instalan igual
HPA con métricas de CPU Sí, con metrics-server El comportamiento es realista
Jobs, CronJobs, StatefulSets Idéntico

Qué NO se puede validar en local

Aspecto Por qué falla Dónde se estudió
Balanceador de nube real (Service LoadBalancer) No hay proveedor de nube que asigne IP 04-02
Rendimiento y latencia reales del almacenamiento La StorageClass local es un directorio del nodo 05-04
NetworkPolicies con el CNI por defecto Muchos CNI locales las ignoran silenciosamente 04-06
Distribución topológica entre zonas Hay una sola "zona" ficticia 09-05
Cluster Autoscaler y adición de nodos No hay grupo de nodos elástico 09-03
Cuotas y presión real de recursos Tu portátil no es un nodo de 64 GB 03-04
Federación de identidad con la nube (IRSA, Workload Identity) No hay proveedor de identidad 10-06
Latencia entre nodos y particiones de red Todo corre en la misma máquina

La regla práctica: el clúster local valida la corrección de tus manifiestos y la lógica de tu aplicación; no valida el rendimiento, la topología ni la integración con la nube.

flowchart LR
    A[Portátil<br/>minikube / kind] -->|valida sintaxis,<br/>lógica, RBAC| B[rutas-norte-dev]
    B -->|valida integración,<br/>datos parecidos| C[rutas-norte-pre]
    C -->|valida carga,<br/>topología, coste| D[rutas-norte-pro]
    style A fill:#e8f4ff
    style D fill:#ffe8e8

  1. Minikube a fondo: perfiles y controladores

minikube es una herramienta que levanta un clúster de Kubernetes de un solo nodo (o de varios) dentro de una máquina virtual o un contenedor de tu equipo. Lo hemos usado desde el módulo 1; ahora vamos a entender qué hace por dentro.

Perfiles: varios clústeres a la vez

Un perfil es un clúster independiente con su propio nombre, su propia configuración y su propio contexto de kubectl. Es la razón por la que en el módulo 1 escribimos --profile=rutas-norte y no simplemente minikube start.

# Crear (o arrancar) el clúster del curso
minikube start --profile=rutas-norte

# Crear un segundo clúster para probar una versión antigua de Kubernetes
minikube start --profile=rutas-norte-viejo --kubernetes-version=v1.28.15

# Ver todos los perfiles y su estado
minikube profile list

Salida típica:

|--------------------|-----------|---------|--------------|------|---------|---------|-------|--------|
|      Profile       | VM Driver | Runtime |      IP      | Port | Version | Status  | Nodes | Active |
|--------------------|-----------|---------|--------------|------|---------|---------|-------|--------|
| rutas-norte        | docker    | docker  | 192.168.49.2 | 8443 | v1.30.4 | Running |     3 | *      |
| rutas-norte-viejo  | docker    | docker  | 192.168.58.2 | 8443 | v1.28.15| Stopped |     1 |        |
|--------------------|-----------|---------|--------------|------|---------|---------|-------|--------|

Sin --profile, minikube usa el perfil minikube. Puedes fijar el perfil activo para no repetirlo en cada orden:

minikube profile rutas-norte     # a partir de aquí, todas las órdenes van a este perfil
minikube status                  # ya no hace falta --profile

Consejo: fijar el perfil es cómodo pero peligroso, porque es un estado invisible. En los scripts del equipo Rutas Norte usamos siempre --profile=rutas-norte explícito.

Cada perfil crea también su contexto de kubectl con el mismo nombre:

kubectl config get-contexts
kubectl config use-context rutas-norte

Controladores: dónde vive el nodo

El controlador (driver) decide dónde se ejecuta el nodo de minikube. Es la decisión más importante al empezar.

Controlador Plataforma Aislamiento Velocidad de arranque Notas
docker Linux, macOS, Windows Contenedor Muy rápida (~40 s) El más usado; el nodo es un contenedor
podman Linux principalmente Contenedor Rápida Alternativa sin demonio; algo menos probado
kvm2 Linux Máquina virtual Media (~90 s) Aislamiento real de kernel; ideal si pruebas cosas de kernel
hyperkit macOS Intel Máquina virtual Media Sustituido en la práctica por docker o qemu en Apple Silicon
qemu macOS Apple Silicon, Linux Máquina virtual Lenta Útil cuando no hay Docker
virtualbox Multiplataforma Máquina virtual Lenta Compatible en todas partes, pero el más lento
none Linux Ninguno Instantánea Instala Kubernetes directamente en tu máquina; no lo uses en un portátil

Cómo elegir, en tres preguntas:

  1. ¿Tienes Docker o Podman funcionando? Usa --driver=docker (o podman). Es lo más rápido y lo que menos memoria consume, porque no hay una VM completa por medio.
  2. ¿Necesitas probar módulos del kernel, un CNI exigente o iptables de verdad? Usa kvm2 en Linux. Un contenedor comparte kernel con el anfitrión y algunas cosas no se pueden aislar.
  3. ¿Estás en Windows sin WSL2 o en un entorno corporativo restringido? hyperv o virtualbox son la salida.
# Elección explícita del controlador
minikube start --profile=rutas-norte --driver=docker

# Fijar el controlador por defecto para todos los perfiles futuros
minikube config set driver docker

Con el controlador docker, el nodo es literalmente un contenedor. Puedes verlo:

docker ps --filter "name=rutas-norte"
CONTAINER ID   IMAGE                                 NAMES
a3f19c2b7d41   gcr.io/k8s-minikube/kicbase:v0.0.45   rutas-norte
8b7e2211ff05   gcr.io/k8s-minikube/kicbase:v0.0.45   rutas-norte-m02

Ese kicbase es la imagen que contiene systemd, containerd, el kubelet y las herramientas necesarias para que un contenedor se comporte como un nodo.

  1. Dimensionado, versión de Kubernetes y clústeres multinodo

Dimensionado

Por defecto minikube pide 2 CPU y 2 GB de memoria. Para Rutas Norte eso se queda corto en cuanto instalamos Prometheus (07-03) y cert-manager (04-05):

minikube start \
  --profile=rutas-norte \
  --driver=docker \
  --cpus=4 \
  --memory=8192 \
  --disk-size=40g

Explicación de cada opción:

  • --cpus=4: número de CPU virtuales asignadas al nodo. Con menos de 4, el kube-apiserver y Prometheus compiten y el clúster se siente lento.
  • --memory=8192: memoria en MiB (8 GB). Puedes escribir también 8g. Ojo: esta memoria se reserva frente a tu sistema; si tu portátil tiene 16 GB, no le des 12.
  • --disk-size=40g: tamaño del disco del nodo. Las imágenes de contenedor se acumulan rápido; 20 GB se agotan en un par de semanas de trabajo real.

Estos valores se recuerdan por perfil. Si mañana ejecutas minikube start --profile=rutas-norte sin flags, respeta lo que ya configuraste. Para cambiarlos hay que borrar y recrear el perfil (o usar minikube config set antes).

Una advertencia importante: --cpus y --memory no se pueden cambiar en caliente con el controlador de VM. Si te quedas corto, minikube delete --profile=rutas-norte y vuelve a crearlo con los valores nuevos. Por eso conviene tener el script de creación versionado (apartado 11).

Fijar la versión de Kubernetes

Esta es una de las prácticas más rentables y más olvidadas. Si rutas-norte-pro corre Kubernetes 1.30 y tu minikube corre 1.32, puedes escribir manifiestos que funcionan en tu portátil y son rechazados en producción, o al revés: puedes no detectar una API obsoleta.

minikube start --profile=rutas-norte --kubernetes-version=v1.30.4
# Comprobar qué versiones concretas conoce tu minikube
minikube start --help | grep -A2 kubernetes-version
kubectl version

Salida:

Client Version: v1.30.4
Server Version: v1.30.4

Regla del equipo de plataforma de Rutas Norte: el fichero del script de arranque local declara la misma versión menor que producción. Cuando pro sube de versión, el script se actualiza en el mismo cambio.

Clústeres multinodo

Un solo nodo no permite probar nada relacionado con la planificación: afinidades (06-05), topologySpreadConstraints (09-05), DaemonSets con varios objetivos (06-02) o el drenaje de un nodo. minikube sabe crear varios:

minikube start \
  --profile=rutas-norte \
  --nodes=3 \
  --cpus=2 \
  --memory=4096 \
  --kubernetes-version=v1.30.4
kubectl get nodes -o wide
NAME             STATUS   ROLES           AGE   VERSION
rutas-norte      Ready    control-plane   3m    v1.30.4
rutas-norte-m02  Ready    <none>          2m    v1.30.4
rutas-norte-m03  Ready    <none>          2m    v1.30.4

Fíjate en que --cpus y --memory se aplican por nodo: tres nodos de 2 CPU y 4 GB consumen 6 CPU y 12 GB del anfitrión. Es fácil ahogar el portátil sin darse cuenta.

Puedes añadir o quitar nodos después:

minikube node add --profile=rutas-norte
minikube node delete rutas-norte-m03 --profile=rutas-norte

Y etiquetar los nodos para simular zonas y probar la distribución topológica de 09-05:

kubectl label node rutas-norte-m02 topology.kubernetes.io/zone=zona-a
kubectl label node rutas-norte-m03 topology.kubernetes.io/zone=zona-b

Esto es una simulación útil para verificar que el selector funciona, pero recuerda: siguen estando en la misma máquina física, así que no prueba la tolerancia real a la caída de una zona.

  1. El catálogo de addons

Los addons son componentes preempaquetados que minikube sabe instalar y mantener por ti. Es lo que evita tener que aplicar a mano los manifiestos del controlador de Ingress o del metrics-server.

minikube addons list --profile=rutas-norte
|-----------------------------|--------------|--------------|
|         ADDON NAME          |    PROFILE   |    STATUS    |
|-----------------------------|--------------|--------------|
| csi-hostpath-driver         | rutas-norte  | disabled     |
| dashboard                   | rutas-norte  | disabled     |
| default-storageclass        | rutas-norte  | enabled ✅   |
| ingress                     | rutas-norte  | enabled ✅   |
| metrics-server              | rutas-norte  | enabled ✅   |
| registry                    | rutas-norte  | disabled     |
| storage-provisioner         | rutas-norte  | enabled ✅   |
| volumesnapshots             | rutas-norte  | disabled     |
|-----------------------------|--------------|--------------|

Los que usa este curso y por qué:

Addon Para qué lo usamos Lección
ingress Despliega ingress-nginx para las reglas de www.rutasnorte.example 04-04
metrics-server Da datos a kubectl top y al HPA 07-02, 09-01
storage-provisioner Aprovisiona PVs dinámicamente para postgres-reservas 05-05
default-storageclass Marca standard como StorageClass por defecto 05-04
csi-hostpath-driver + volumesnapshots Necesarios para practicar los snapshots CSI 05-05
dashboard Interfaz web de exploración esta lección
registry Registro de imágenes dentro del clúster esta lección
# Activar los del curso
minikube addons enable ingress --profile=rutas-norte
minikube addons enable metrics-server --profile=rutas-norte
minikube addons enable csi-hostpath-driver --profile=rutas-norte
minikube addons enable volumesnapshots --profile=rutas-norte

# Desactivar uno que consume recursos y no estás usando
minikube addons disable dashboard --profile=rutas-norte

Cada addon es, por dentro, un conjunto de manifiestos que minikube aplica en el namespace kube-system (o uno propio). Puedes verlos:

kubectl get pods -n ingress-nginx
kubectl get deploy metrics-server -n kube-system

Aviso importante: el addon ingress de minikube instala ingress-nginx con una configuración pensada para un solo nodo. En producción, tu equipo de plataforma probablemente lo instala con Helm y con valores muy distintos (réplicas, PDB, recursos). El comportamiento de las reglas es el mismo; el de la disponibilidad, no.

  1. Trabajar con imágenes locales sin registro

Este es, en la práctica diaria, el punto donde más tiempo se pierde. Has construido api-reservas:dev-local en tu portátil y el pod se queda en ImagePullBackOff, porque el nodo de minikube tiene su propio almacén de imágenes, separado del Docker de tu máquina.

Hay tres formas de resolverlo.

Opción A: minikube image load (la más simple y recomendable)

# 1. Construyes la imagen en tu máquina, como siempre
docker build -t registry.rutasnorte.example/api-reservas:dev-local ./api-reservas

# 2. La copias al almacén del nodo de minikube
minikube image load registry.rutasnorte.example/api-reservas:dev-local --profile=rutas-norte

# 3. Compruebas que está
minikube image ls --profile=rutas-norte | grep api-reservas

En el manifiesto, la clave es evitar que Kubernetes intente descargarla del registro:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-reservas
  namespace: rutas-norte-dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api-reservas
  template:
    metadata:
      labels:
        app: api-reservas
        app.kubernetes.io/part-of: rutas-norte
    spec:
      containers:
        - name: api
          image: registry.rutasnorte.example/api-reservas:dev-local
          # CLAVE: con IfNotPresent, el kubelet usa la imagen local
          # y no intenta contactar con el registro.
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080

Trampa clásica: si la etiqueta de la imagen es latest, Kubernetes aplica por defecto imagePullPolicy: Always y siempre intentará descargarla, ignorando la copia local. Nunca uses latest (ya lo vimos en 08-05); aquí además te rompe el flujo local.

Si en varios nodos, image load la copia a todos automáticamente.

Opción B: minikube docker-env (construir dentro del nodo)

Esta opción reapunta tu cliente Docker al demonio que corre dentro del nodo de minikube. Lo que construyas queda directamente disponible para el kubelet, sin paso de copia.

# Ver qué hace exactamente
minikube docker-env --profile=rutas-norte
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.49.2:2376"
export DOCKER_CERT_PATH="/home/usuario/.minikube/certs"
export MINIKUBE_ACTIVE_DOCKERD="rutas-norte"
# Aplicarlo a la sesión actual del terminal
eval $(minikube docker-env --profile=rutas-norte)

# A partir de aquí, docker build construye DENTRO del nodo
docker build -t registry.rutasnorte.example/api-reservas:dev-local ./api-reservas

# Deshacerlo cuando termines
eval $(minikube docker-env --unset --profile=rutas-norte)

Ventaja: es más rápido en el bucle editar-construir-probar, porque no hay copia. Desventaja: solo funciona con el runtime docker; si el perfil usa containerd, no aplica. Y es un estado del terminal fácil de olvidar (has construido dentro del nodo y luego no encuentras la imagen en tu máquina).

Opción C: el addon registry

minikube addons enable registry --profile=rutas-norte

Levanta un registro dentro del clúster. Es la opción más parecida a producción, pero también la que más pasos exige (hay que empujar la imagen, resolver el nombre desde el nodo). Para el trabajo diario, la opción A gana casi siempre.

Opción Velocidad del bucle Complejidad Cuándo usarla
image load Media (copia cada vez) Muy baja Uso general, multinodo
docker-env Alta Baja, pero con estado oculto Iteración intensiva en un solo nodo con runtime docker
Addon registry Baja Alta Cuando quieres reproducir el flujo real de registro

  1. Acceso al clúster: tunnel, service, dashboard, mount y ssh

minikube tunnel: Services de tipo LoadBalancer

Cuando declaras un Service de tipo LoadBalancer (04-02), Kubernetes le pide a un controlador del proveedor de nube que aprovisione un balanceador. En tu portátil no hay proveedor de nube, así que el Service se queda para siempre en <pending>:

kubectl get svc tienda-web -n rutas-norte-dev
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
tienda-web   LoadBalancer   10.104.12.201   <pending>     80:31820/TCP   2m

minikube tunnel simula ese balanceador: crea rutas en tu máquina y asigna una IP externa.

# Ejecutar en un terminal APARTE y dejarlo abierto.
# Pide contraseña de administrador porque crea rutas de red.
minikube tunnel --profile=rutas-norte
Status:
	machine: rutas-norte
	pid: 48211
	route: 10.96.0.0/12 -> 192.168.49.2
	minikube: Running
	services: [tienda-web]

Ahora, en otro terminal:

kubectl get svc tienda-web -n rutas-norte-dev
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
tienda-web   LoadBalancer   10.104.12.201   10.104.12.201   80:31820/TCP   5m

Puntos que confunden a todo el mundo la primera vez:

  • El proceso debe seguir vivo. Si cierras el terminal, la IP vuelve a <pending>.
  • No es un balanceador real: no hay comprobaciones de salud del proveedor, ni distribución entre zonas, ni reglas de cortafuegos de nube. Solo enruta.
  • En macOS y Windows con el controlador docker, tunnel también es necesario para llegar al Ingress.

minikube service: abrir un Service en el navegador

Para Services de tipo NodePort (o incluso ClusterIP con --url), esta es la vía rápida:

# Abre el navegador apuntando al servicio
minikube service tienda-web -n rutas-norte-dev --profile=rutas-norte

# Solo imprimir la URL, sin abrir navegador (útil en scripts)
minikube service tienda-web -n rutas-norte-dev --url --profile=rutas-norte
http://192.168.49.2:31820

minikube dashboard: la interfaz web

minikube dashboard --profile=rutas-norte
minikube dashboard --url --profile=rutas-norte   # solo la URL

Activa el addon dashboard si no lo estaba, levanta un proxy y abre el navegador. Útil para explorar visualmente el clúster mientras aprendes; en el día a día profesional kubectl y k9s son más rápidos.

minikube mount: compartir un directorio del anfitrión

A veces quieres que el pod vea un directorio de tu portátil: un volcado SQL de datos de prueba para postgres-reservas, o los ficheros estáticos de tienda-web.

# Terminal aparte, se queda en primer plano
minikube mount ~/rutas-norte/datos-prueba:/datos-prueba --profile=rutas-norte

En el pod se usa un hostPath (05-01) apuntando a /datos-prueba, que ahora existe dentro del nodo:

      volumes:
        - name: datos
          hostPath:
            path: /datos-prueba
            type: Directory

Advertencia: hostPath está prohibido por las Pod Security Standards del nivel restricted (08-03). En local es aceptable para datos de prueba; en rutas-norte-pro sería rechazado, y con razón.

minikube ssh: entrar en el nodo

minikube ssh --profile=rutas-norte

Te deja dentro del nodo. Útil para:

# Ver el espacio en disco del nodo cuando hay problemas de eviction
df -h /var

# Ver las imágenes que tiene el runtime
sudo crictl images

# Ver los pods estáticos del plano de control (volveremos a esto en 10-02)
ls /etc/kubernetes/manifests

# Salir
exit

En un clúster multinodo, elige el nodo:

minikube ssh --node=rutas-norte-m02 --profile=rutas-norte

  1. El ciclo de vida: stop, start, delete y logs

# Apagar el clúster conservando todo (objetos, imágenes, datos de los PV)
minikube stop --profile=rutas-norte

# Volver a arrancarlo tal y como estaba
minikube start --profile=rutas-norte

# Estado actual
minikube status --profile=rutas-norte
rutas-norte
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
# Borrar el clúster (irreversible: se pierden objetos, PVs e imágenes)
minikube delete --profile=rutas-norte

# Borrar TODOS los perfiles de golpe (limpieza total)
minikube delete --all --purge

--purge borra además el directorio ~/.minikube con la configuración y la caché de imágenes de Kubernetes. Úsalo cuando minikube esté en un estado raro que no consigues explicar; es el equivalente a apagar y encender.

Diagnóstico con logs

Cuando minikube start falla o el clúster arranca a medias:

minikube logs --profile=rutas-norte

# Solo los problemas detectados, mucho más legible
minikube logs --problems --profile=rutas-norte

# Seguir en directo
minikube logs -f --profile=rutas-norte
==> Audit <==
==> kubelet <==
Sep 12 09:14:02 rutas-norte kubelet[2311]: E0912 09:14:02.118 Failed to
  create pod sandbox: rpc error: code = Unknown desc = failed to set up
  sandbox network: plugin type="bridge" failed

Ese tipo de mensaje casi siempre significa que el nodo se quedó sin recursos o que el estado de red del contenedor quedó corrupto tras un apagón brusco. minikube delete && minikube start lo resuelve, y es exactamente por eso que un clúster local debe ser desechable.

  1. kind a fondo: nodos como contenedores

kind (Kubernetes IN Docker) tiene un enfoque distinto y una filosofía más estrecha: cada nodo del clúster es un contenedor de Docker que ejecuta containerd y el kubelet. No hay addons, no hay tunnel, no hay dashboard. Solo Kubernetes, muy rápido y muy reproducible.

Precisamente por esa austeridad, es la herramienta que el propio proyecto Kubernetes usa para sus pruebas y la elección habitual en integración continua.

Un clúster mínimo

kind create cluster --name rutas-norte
Creating cluster "rutas-norte" ...
 ✓ Ensuring node image (kindest/node:v1.30.4) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-rutas-norte"

Tarda unos 25 segundos. El contexto se llama kind-rutas-norte (con el prefijo kind-).

Fichero de configuración: varios nodos, puertos y montajes

Aquí está la verdadera potencia de kind: el clúster se declara en un fichero versionable.

# k8s/local/kind-rutas-norte.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: rutas-norte

# Igualamos la red de pods a la de producción para que las
# NetworkPolicies con bloques CIDR se comporten igual.
networking:
  podSubnet: "10.244.0.0/16"
  serviceSubnet: "10.96.0.0/16"
  # Desactivamos el CNI por defecto para instalar Calico y así
  # PODER probar de verdad las NetworkPolicies de 04-06.
  disableDefaultCNI: false

nodes:
  # --- Plano de control ---
  - role: control-plane
    image: kindest/node:v1.30.4
    # Publicamos los puertos 80 y 443 del contenedor en el portátil,
    # para que el Ingress sea accesible en http://localhost sin tunnel.
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
    # Etiqueta que exige el controlador de ingress-nginx para kind.
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"

  # --- Nodos de trabajo, etiquetados como zonas distintas ---
  - role: worker
    image: kindest/node:v1.30.4
    labels:
      topology.kubernetes.io/zone: zona-a
    # Montamos un directorio del portátil dentro del nodo.
    extraMounts:
      - hostPath: /home/usuario/rutas-norte/datos-prueba
        containerPath: /datos-prueba
        readOnly: true

  - role: worker
    image: kindest/node:v1.30.4
    labels:
      topology.kubernetes.io/zone: zona-b
kind create cluster --config k8s/local/kind-rutas-norte.yaml
kubectl get nodes
NAME                        STATUS   ROLES           AGE   VERSION
rutas-norte-control-plane   Ready    control-plane   47s   v1.30.4
rutas-norte-worker          Ready    <none>          32s   v1.30.4
rutas-norte-worker2         Ready    <none>          32s   v1.30.4

Repasemos los campos clave:

  • extraPortMappings: publica un puerto del contenedor-nodo en tu máquina. Es la solución de kind al problema del acceso externo. Con hostPort: 80, una vez instalado ingress-nginx, curl -H "Host: www.rutasnorte.example" http://localhost llega a tienda-web. Solo se puede definir al crear el clúster: si lo olvidas, hay que recrearlo.
  • extraMounts: el equivalente a minikube mount, pero declarativo y sin proceso en primer plano. Mucho más cómodo.
  • kubeadmConfigPatches: kind usa kubeadm por dentro (lo veremos en 10-02). Este campo te deja modificar su configuración; aquí lo usamos para poner la etiqueta ingress-ready=true que el manifiesto de ingress-nginx para kind espera en su nodeSelector.
  • image: fija la versión de Kubernetes. Igual que con minikube, iguálala a producción.
  • labels: etiquetas de nodo aplicadas desde el principio, sin kubectl label posterior.

Instalar el Ingress en kind

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=120s

Ahora sí:

curl -H "Host: www.rutasnorte.example" http://localhost/

Cargar imágenes locales

docker build -t registry.rutasnorte.example/api-reservas:dev-local ./api-reservas
kind load docker-image registry.rutasnorte.example/api-reservas:dev-local --name rutas-norte

Igual que en minikube, la imagen se copia a todos los nodos. Y también aquí hace falta imagePullPolicy: IfNotPresent y evitar la etiqueta latest.

También puedes cargar un fichero de imagen exportado, práctico en canalizaciones donde la construcción y la carga están en pasos distintos:

docker save registry.rutasnorte.example/api-reservas:dev-local -o api.tar
kind load image-archive api.tar --name rutas-norte

Por qué kind manda en integración continua

Motivo Explicación
Velocidad 25-40 s frente a 60-120 s de minikube; en una canalización que corre 40 veces al día, eso son horas
Sin virtualización anidada Al ser contenedores, funciona en corredores de CI que no permiten KVM
Configuración en un fichero El clúster es reproducible bit a bit desde el repositorio
Multinodo barato Añadir un nodo es añadir un contenedor
Limpieza total kind delete cluster no deja rastro
Sin estado oculto No hay perfiles persistentes ni configuración recordada entre ejecuciones

Un paso típico en la canalización ci-rutasnorte se reduce a: kind create cluster --config kind-ci.yaml --wait 120s, construir y cargar la imagen, kubectl apply -k k8s/entornos/dev (eso es Kustomize, lo veremos en 10-04), esperar el rollout, ejecutar las pruebas de humo y kind delete cluster al final pase lo que pase.

  1. Tabla comparativa: minikube, kind, k3d, Docker Desktop y Rancher Desktop

Criterio minikube kind k3d Docker Desktop Rancher Desktop
Qué es Clúster local con addons Nodos como contenedores k3s en contenedores Kubernetes integrado en la app App de escritorio con k3s
Arranque (1 nodo) 60-120 s 25-40 s 15-25 s ~90 s (al activarlo) ~60 s
Multinodo Sí (--nodes) Sí (fichero) Sí (--agents) No No
Consumo en reposo Medio-alto Medio Bajo Alto Medio
Addons integrados Muchos (ingress, metrics, CSI, registro) Ninguno Traefik y servidor local incluidos Ninguno Traefik incluido
Configuración en fichero Parcial (flags + config set) Completa y versionable Completa y versionable No Parcial
Idoneidad para CI Regular (lento, requiere driver) Excelente Excelente Nula Baja
Fidelidad con Kubernetes estándar Alta Muy alta (kubeadm real) Media (k3s recorta componentes) Alta Media (k3s)
Registro local integrado Addon registry No (se monta aparte) Sí (k3d registry) Comparte el de Docker Comparte el suyo
Licencia comercial Libre Libre Libre De pago en empresas grandes Libre
Mejor para Aprender y trabajo diario con extras CI y pruebas reproducibles Bucles ultrarrápidos, equipos pequeños Quien ya lo tiene y no quiere más herramientas Sustituto libre de Docker Desktop

Notas para elegir:

  • k3s (el motor de k3d y Rancher Desktop) es una distribución ligera y conforme, pero sustituye algunas piezas: usa Traefik como Ingress, SQLite en vez de etcd por defecto y desactiva componentes de nube. Es perfecto para desarrollar; es engañoso si quieres comprobar un comportamiento fino del plano de control.
  • Docker Desktop con Kubernetes activado es lo más cómodo si ya lo tienes, pero es un solo nodo, no configurable, y su licencia es de pago para empresas por encima de cierto tamaño. Rutas Norte S.L. es pequeña, así que no le afecta, pero es un factor real en decisiones de equipo.
  • La recomendación de este curso: minikube para el trabajo diario (por los addons, que ahorran mucha instalación manual) y kind en la canalización ci-rutasnorte (por velocidad y reproducibilidad). Que sean dos herramientas distintas no es un problema si el manifiesto que despliegas es el mismo.

  1. Las diferencias con producción que provocan «en mi máquina funciona»

Este apartado es el más importante de la lección. Cada punto es un incidente real esperando a ocurrir.

10.1 No hay balanceador de nube

En local, type: LoadBalancer no hace nada sin minikube tunnel. En rutas-norte-pro, ese mismo Service aprovisiona un balanceador de verdad, con su IP pública, su coste mensual y su tiempo de aprovisionamiento de varios minutos.

Consecuencias que solo ves en la nube: anotaciones específicas del proveedor (tipo de balanceador, certificados, listas de acceso), comprobaciones de salud propias, y el hecho de que borrar el Service borra el balanceador. Lo veremos en 10-06.

Mitigación: en local, usa Ingress (04-04) y no LoadBalancer en los manifiestos de la aplicación. Deja el LoadBalancer solo para el controlador de Ingress, que en local se publica por NodePort o extraPortMappings.

10.2 La clase de almacenamiento es otra

En minikube, la StorageClass standard es un directorio del nodo servido por storage-provisioner. En rutas-norte-pro, postgres-reservas usa una StorageClass de disco de red con IOPS provisionadas (05-04).

Diferencia Local Producción
Modo de acceso ReadWriteOnce, pero con un nodo todo "funciona" ReadWriteOnce de verdad: dos pods en nodos distintos fallan
Rendimiento El SSD de tu portátil Disco de red, con latencia y límites
Expansión y snapshots Puede no estar soportada Sí, nativos del proveedor
Vinculación Inmediata Frecuentemente WaitForFirstConsumer

Ese último punto es traicionero: en producción el PV no se crea hasta que el pod se planifica, y eso cambia el orden de los eventos. Un StatefulSet que arranca bien en local puede quedarse en Pending en la nube por una afinidad de zona incompatible.

10.3 El CNI por defecto puede ignorar las NetworkPolicies

Este es el más peligroso porque falla en silencio. Escribes una NetworkPolicy que aísla postgres-reservas para que solo api-reservas pueda hablar con él (04-06), la aplicas en tu minikube, el objeto se crea sin error... y no hace absolutamente nada, porque el CNI por defecto no la implementa.

kubectl apply -f k8s/base/netpol-postgres.yaml
networkpolicy.networking.k8s.io/postgres-solo-api created

Parece que funciona. Pero:

# Desde un pod que NO debería poder conectar
kubectl run curiosa --rm -it --image=busybox:1.36 -n rutas-norte-dev -- \
  nc -zv postgres-reservas 5432
postgres-reservas (10.104.55.12:5432) open

Conecta. La política se ignoró.

Cómo detectarlo: kubectl get pods -n kube-system -o wide | grep -Ei 'calico|cilium|flannel|kindnet'. kindnet (el de kind por defecto) y el bridge básico no implementan NetworkPolicies; calico y cilium sí.

Mitigación: en minikube, --cni=calico. En kind, disableDefaultCNI: true con podSubnet: "192.168.0.0/16" y aplicar después el manifiesto de Calico.

Y la regla de oro: una NetworkPolicy no está probada hasta que has verificado que algo que antes conectaba, ahora no conecta. Probar solo que lo permitido sigue funcionando no demuestra nada.

10.4 No hay varias zonas ni escalado de nodos

topologySpreadConstraints (09-05) con topologyKey: topology.kubernetes.io/zone en un clúster de un nodo se satisface trivialmente. Etiquetar los nodos de un clúster multinodo simula zonas y verifica que el selector y las etiquetas son correctos, pero no prueba la tolerancia a fallos.

Igual con el Cluster Autoscaler (09-03): en local no hay grupo de nodos elástico, así que un pod Pending por falta de recursos se queda Pending para siempre. En producción aparecería un nodo nuevo en dos minutos. El síntoma en local es idéntico a un fallo grave en producción, lo que despista.

10.5 Resumen de mitigaciones

Riesgo Mitigación en local Prueba definitiva
Balanceador ausente Usa Ingress; minikube tunnel si hace falta rutas-norte-pre
StorageClass distinta Nombra la clase en el manifiesto, no confíes en la de por defecto rutas-norte-pre
NetworkPolicies ignoradas Instala Calico o Cilium en local rutas-norte-pre con prueba negativa
Topología y zonas Etiqueta nodos para validar selectores rutas-norte-pro
Escalado de nodos No simulable rutas-norte-pre
Recursos y límites Ajusta límites bajos a propósito para ver qué se estrangula Pruebas de carga con k6 (09-06)

  1. Receta reproducible: el entorno completo de Rutas Norte en local

Lo peor de un entorno local es que cada persona del equipo lo tenga distinto. La solución es un script versionado en el repositorio, junto a los manifiestos.

#!/usr/bin/env bash
# k8s/local/levantar-local.sh
# Levanta el entorno local completo de Rutas Norte sobre minikube.
# Uso:  ./levantar-local.sh          (crea y configura)
#       ./levantar-local.sh borrar   (destruye el perfil)

set -euo pipefail

PERFIL="rutas-norte"
# Debe coincidir con la versión menor de rutas-norte-pro.
VERSION_K8S="v1.30.4"
NODOS=3
CPUS=2
MEMORIA=4096
DISCO="30g"

if [[ "${1:-}" == "borrar" ]]; then
  echo "==> Borrando el perfil ${PERFIL}"
  minikube delete --profile="${PERFIL}"
  exit 0
fi

echo "==> 1/6 Creando el clúster ${PERFIL} (Kubernetes ${VERSION_K8S})"
minikube start \
  --profile="${PERFIL}" \
  --driver=docker \
  --kubernetes-version="${VERSION_K8S}" \
  --nodes="${NODOS}" \
  --cpus="${CPUS}" \
  --memory="${MEMORIA}" \
  --disk-size="${DISCO}" \
  --cni=calico          # imprescindible para que las NetworkPolicies se apliquen

echo "==> 2/6 Activando addons"
for addon in ingress metrics-server csi-hostpath-driver volumesnapshots; do
  minikube addons enable "${addon}" --profile="${PERFIL}"
done

echo "==> 3/6 Etiquetando nodos como zonas simuladas"
kubectl label node "${PERFIL}-m02" topology.kubernetes.io/zone=zona-a --overwrite
kubectl label node "${PERFIL}-m03" topology.kubernetes.io/zone=zona-b --overwrite

echo "==> 4/6 Creando el namespace de desarrollo"
kubectl create namespace rutas-norte-dev --dry-run=client -o yaml | kubectl apply -f -
kubectl label namespace rutas-norte-dev \
  app.kubernetes.io/part-of=rutas-norte entorno=dev --overwrite

echo "==> 5/6 Construyendo y cargando las imágenes locales"
for comp in tienda-web api-reservas worker-notificaciones; do
  docker build -t "registry.rutasnorte.example/${comp}:dev-local" "./${comp}"
  minikube image load "registry.rutasnorte.example/${comp}:dev-local" --profile="${PERFIL}"
done

echo "==> 6/6 Esperando a que el controlador de Ingress esté listo"
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=180s

echo
echo "Clúster listo. Para llegar al Ingress:"
echo "  echo \"\$(minikube ip --profile=${PERFIL}) www.rutasnorte.example api.rutasnorte.example\" | sudo tee -a /etc/hosts"
echo "Despliega la aplicación con:  kubectl apply -k k8s/entornos/dev"

Explicación de las decisiones del script:

  • set -euo pipefail: aborta a la primera orden que falle, en vez de seguir dejando un clúster a medias.
  • Versión de Kubernetes en una variable arriba: cuando pro sube de versión, se cambia una línea.
  • --cni=calico: la diferencia entre probar de verdad las NetworkPolicies y engañarse.
  • --dry-run=client -o yaml | kubectl apply -f -: el patrón idempotente de 01-06; crea el namespace si no existe y no falla si ya existe.
  • kubectl wait: el script no termina hasta que el clúster es realmente usable. Sin esto, quien lo ejecuta prueba a desplegar y falla porque el Ingress aún no está.
  • El aviso final sobre /etc/hosts: los dominios www.rutasnorte.example y api.rutasnorte.example no existen en ningún DNS; hay que resolverlos a mano contra la IP del nodo.

El equivalente con kind es aún más corto, porque toda la configuración del clúster ya está en kind-rutas-norte.yaml: basta kind create cluster --config ... --wait 180s, aplicar Calico e ingress-nginx, esperar al controlador y cargar las imágenes con kind load docker-image. Y no hace falta tocar /etc/hosts: gracias a extraPortMappings, el Ingress responde en http://localhost con la cabecera Host adecuada.

Errores Comunes y Consejos

1. Dar a minikube casi toda la memoria del portátil. Con 16 GB físicos, --memory=12288 deja al sistema operativo sin margen y todo va a tirones. Regla: no más de la mitad de la RAM física, y recuerda que en multinodo el valor se multiplica por el número de nodos.

2. Olvidar que --cpus y --memory se quedan grabados en el perfil. Ejecutas minikube start --profile=rutas-norte --memory=8192 un día, y al día siguiente minikube start --profile=rutas-norte sin flags: sigue usando 8192. Si crees que cambiaste el valor y no cambió nada, es esto. Para cambiarlo hay que recrear el perfil.

3. ImagePullBackOff con una imagen que "sí has construido". Casi siempre son dos causas: no hiciste minikube image load / kind load docker-image, o la etiqueta es latest y el imagePullPolicy implícito es Always. Usa etiquetas explícitas (dev-local) e imagePullPolicy: IfNotPresent.

4. Creer que una NetworkPolicy aplicada está funcionando. Comprueba siempre el CNI y haz la prueba negativa: intenta la conexión que debe estar bloqueada y verifica que da timeout. Sin esa prueba, no has probado nada.

5. Cerrar el terminal de minikube tunnel o minikube mount. Ambos son procesos en primer plano. Si desaparece la IP externa o el directorio montado, mira si el proceso sigue vivo antes de buscar causas exóticas.

6. Descubrir tarde que faltaba un extraPortMapping en kind. No se pueden añadir a un clúster ya creado. Ten el fichero de configuración con los puertos 80, 443 y quizá alguno más desde el principio.

7. Usar latest de la imagen del nodo. Tanto kindest/node como la versión de minikube deben estar fijadas. Un compañero con v1.31 y tú con v1.29 producís resultados distintos sobre los mismos manifiestos.

8. Acumular perfiles olvidados. Cada perfil detenido sigue ocupando disco. minikube profile list de vez en cuando y minikube delete --profile=X de lo que no uses.

9. Tratar el clúster local como si fuera valioso. Si dudas si algo está en un estado raro, bórralo y recréalo. El script del apartado 11 hace que eso cueste tres minutos; investigar un estado corrupto cuesta una tarde.

10. Probar el rendimiento en local. Un benchmark de postgres-reservas en tu SSD no dice nada del disco de red de producción. Para eso están rutas-norte-pre y k6 (09-06).

Ejercicios

Ejercicio 1: clúster multinodo con zonas simuladas

Crea un perfil de minikube llamado rutas-norte-topologia con 3 nodos, Kubernetes v1.30.4 y Calico como CNI. Etiqueta los dos nodos de trabajo como zona-a y zona-b. Despliega api-reservas con 4 réplicas y un topologySpreadConstraint que reparta entre zonas con maxSkew: 1, y comprueba con kubectl get pods -o wide que el reparto es 2 y 2.

Ejercicio 2: demostrar que el CNI importa

Crea dos clústeres de kind: uno con el CNI por defecto (kindnet) y otro con disableDefaultCNI: true y Calico instalado. En ambos, aplica una NetworkPolicy de denegación total de entrada en el namespace rutas-norte-dev y comprueba, con un pod busybox intentando conectar a un Service, cuál de los dos la respeta de verdad.

Ejercicio 3: bucle de desarrollo con imagen local

Con el perfil rutas-norte en marcha, construye una imagen registry.rutasnorte.example/tienda-web:dev-local, cárgala en minikube y despliégala con un Ingress para www.rutasnorte.example. Después, cambia el contenido de la página, reconstruye con la misma etiqueta, recarga la imagen y consigue que el pod sirva el contenido nuevo. Explica por qué un simple kubectl apply no basta.

Soluciones

Solución 1

minikube start --profile=rutas-norte-topologia \
  --driver=docker --kubernetes-version=v1.30.4 \
  --nodes=3 --cpus=2 --memory=3072 --cni=calico

kubectl label node rutas-norte-topologia-m02 topology.kubernetes.io/zone=zona-a
kubectl label node rutas-norte-topologia-m03 topology.kubernetes.io/zone=zona-b
kubectl create namespace rutas-norte-dev
# api-reservas-topologia.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-reservas
  namespace: rutas-norte-dev
spec:
  replicas: 4
  selector:
    matchLabels: { app: api-reservas }
  template:
    metadata:
      labels:
        app: api-reservas
        app.kubernetes.io/part-of: rutas-norte
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels: { app: api-reservas }
      containers:
        - name: api
          image: nginx:1.27-alpine
          resources:
            requests: { cpu: 50m, memory: 64Mi }
kubectl apply -f api-reservas-topologia.yaml
kubectl get pods -n rutas-norte-dev -o wide

Deben salir 2 pods en -m02 y 2 en -m03. El nodo del plano de control no recibe pods porque tiene el taint correspondiente (06-05).

Solución 2

# kind-sin-netpol.yaml -> nodes: [{role: control-plane}], sin networking
# kind-con-netpol.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: con-netpol
networking:
  disableDefaultCNI: true
  podSubnet: "192.168.0.0/16"
nodes: [{role: control-plane}]
---
# denegar-todo.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: denegar-entrada, namespace: rutas-norte-dev }
spec:
  podSelector: {}
  policyTypes: [Ingress]
for c in sin-netpol con-netpol; do
  kind create cluster --config kind-${c}.yaml
  [ "$c" = "con-netpol" ] && kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml && sleep 60
  kubectl create ns rutas-norte-dev
  kubectl -n rutas-norte-dev create deploy web --image=nginx:1.27-alpine
  kubectl -n rutas-norte-dev expose deploy web --port=80
  kubectl apply -f denegar-todo.yaml
  echo "--- $c ---"
  kubectl -n rutas-norte-dev run p --rm -i --restart=Never --image=busybox:1.36 -- \
    timeout 5 wget -qO- http://web || echo "BLOQUEADO (correcto)"
done

En sin-netpol devuelve el HTML de nginx: la política se ignoró. En con-netpol da timeout: Calico la aplica.

Solución 3

docker build -t registry.rutasnorte.example/tienda-web:dev-local ./tienda-web
minikube image load registry.rutasnorte.example/tienda-web:dev-local --profile=rutas-norte
kubectl apply -f k8s/base/tienda-web.yaml   # con imagePullPolicy: IfNotPresent

# --- tras editar el contenido ---
docker build -t registry.rutasnorte.example/tienda-web:dev-local ./tienda-web
minikube image load registry.rutasnorte.example/tienda-web:dev-local --profile=rutas-norte
kubectl rollout restart deploy/tienda-web -n rutas-norte-dev

Por qué kubectl apply no basta: el manifiesto no ha cambiado (misma etiqueta de imagen), así que el objeto en la API es idéntico y el Deployment no genera un nuevo ReplicaSet. rollout restart añade una anotación con la marca de tiempo a la plantilla del pod, lo que sí provoca el despliegue. Esta es exactamente la razón por la que en producción se usan etiquetas inmutables con digest (08-05): la imagen nueva tiene un identificador nuevo y el cambio se propaga solo.

Conclusión

Ya sabes montar y destruir clústeres locales con criterio. Los puntos que conviene llevarse:

  • minikube brilla por sus addons: ingress, metrics-server, storage-provisioner y csi-hostpath-driver te ahorran instalar a mano media plataforma. Los perfiles permiten tener varios clústeres a la vez, --kubernetes-version iguala tu entorno a producción, y --nodes abre la puerta a probar planificación y topología.
  • kind gana en velocidad y reproducibilidad: los nodos son contenedores, todo el clúster se declara en un fichero versionado con extraPortMappings y extraMounts, y por eso es la elección natural de la canalización ci-rutasnorte.
  • El bucle de trabajo con imágenes locales se resuelve con minikube image load o kind load docker-image, siempre con imagePullPolicy: IfNotPresent y etiquetas que no sean latest.
  • Y lo más importante: un clúster local valida corrección, no realismo. No hay balanceador de nube, la StorageClass es otra, el CNI por defecto puede ignorar tus NetworkPolicies en silencio, y no hay zonas ni escalado de nodos. Saber exactamente qué no estás probando es lo que evita el «en mi máquina funciona».

Nos queda una pregunta pendiente: los clústeres locales son juguetes controlados, pero ¿cómo se monta un clúster de verdad, en máquinas propias, sin que lo haga un proveedor de nube por ti? En la siguiente lección, Kubeadm, veremos la herramienta oficial para arrancar un clúster conforme: preparar las máquinas, instalar containerd, ejecutar kubeadm init con un fichero de configuración, unir nodos, montar un plano de control en alta disponibilidad, renovar certificados y actualizar de versión. Y descubriremos por qué kind, por dentro, no es más que kubeadm dentro de un contenedor.

Curso de Kubernetes

Módulo 1: Introducción a Kubernetes

Módulo 2: Componentes Principales de Kubernetes

Módulo 3: Gestión de Configuración y Secretos

Módulo 4: Redes en Kubernetes

Módulo 5: Almacenamiento en Kubernetes

Módulo 6: Conceptos Avanzados de Kubernetes

Módulo 7: Monitoreo y Registro

Módulo 8: Seguridad en Kubernetes

Módulo 9: Escalado y Rendimiento

Módulo 10: Ecosistema y Herramientas de Kubernetes

Módulo 11: Estudios de Caso y Aplicaciones del Mundo Real

Módulo 12: Preparación para la Certificación de Kubernetes

© Copyright 2026. Todos los derechos reservados