Kubernetes Básico para Homelab: Guía desde Cero 2025

📋 TL;DR

Kubernetes es el siguiente paso natural después de Docker Compose. Si ya dominas Docker y quieres escalar tu homelab con auto-restart, escalado automático y rolling updates, esta guía te enseñará Kubernetes desde cero usando K3s (la versión ligera perfecta para homelabs).

Lo que conseguirás:

  • ✅ Instalar K3s en 2 minutos (vs horas con Kubernetes estándar)
  • ✅ Entender los objetos básicos de K8s comparándolos con Docker (Pod, Deployment, Service, Ingress, PV, PVC)
  • ✅ Desplegar aplicaciones reales (WordPress completo) con YAMLs funcionales
  • ✅ Troubleshooting de problemas comunes con soluciones prácticas

Tiempo de lectura: ~25 minutos | Nivel: Intermedio (requiere conocimientos de Docker)

⚠️ IMPORTANTE: Este artículo asume que conoces Docker. Si no sabes Docker, primero lee:

Kubernetes sin Docker es como intentar correr sin saber caminar. No es práctico.

📚 Tabla de Contenidos

  1. Introducción: ¿Por qué Kubernetes si Docker Compose funciona?
  2. Conceptos Fundamentales: Docker → Kubernetes

2.1 Contenedor → Pod

2.2 Docker Compose → Deployment

2.3 Volúmenes Docker → PV/PVC

2.4 Reverse Proxy → Ingress

2.5 Puertos Docker → Service

  1. Instalación K3s: Kubernetes Ligero para Homelab
  2. Primer Despliegue: Nginx Básico
  3. Despliegue con Volúmenes: Persistencia de Datos
  4. Exponer Servicios: Ingress con Traefik
  5. Caso Práctico Completo: WordPress en Kubernetes
  6. Comandos Esenciales kubectl
  7. Troubleshooting Común
  8. Mejores Prácticas para Homelab
  9. ¿Cuándo NO usar Kubernetes?
  10. Siguientes Pasos

> 📅 Última actualización: Noviembre 2025

> ✅ Verificado con: K3s v1.31.x, Kubernetes v1.31.x

> 🔄 Próxima revisión: Febrero 2026

Introducción: ¿Por qué Kubernetes si Docker Compose funciona?

Tienes tu homelab funcionando perfectamente con Docker Compose. Plex corriendo, n8n automatizando, Nextcloud guardando archivos… todo funciona. ¿Por qué complicarse con Kubernetes?

La respuesta corta: No necesitas Kubernetes si Docker Compose te funciona bien. Pero si quieres dar el siguiente paso, Kubernetes te da superpoderes que Docker Compose no tiene.

¿Cuándo usar Kubernetes vs Docker Compose?

Usa Docker Compose si:

  • ✅ Tienes 1-10 servicios en un único servidor
  • ✅ No necesitas escalado automático
  • ✅ Un solo servidor es suficiente
  • ✅ Quieres simplicidad máxima
  • ✅ Prototipo rápido o desarrollo

Usa Kubernetes si:

  • ✅ Tienes 10+ servicios que gestionar
  • ✅ Necesitas escalado automático (más pods cuando hay carga)
  • ✅ Quieres alta disponibilidad (múltiples nodos)
  • ✅ Necesitas rolling updates sin downtime
  • ✅ Quieres auto-restart robusto (si un pod falla, se recrea automáticamente)
  • ✅ Múltiples entornos (dev/staging/prod)

Regla de oro: Si tienes 3 o menos servicios en un único servidor, Docker Compose es suficiente. Si necesitas múltiples nodos, escalado automático, o alta disponibilidad, Kubernetes es tu camino.

K3s vs Kubernetes Completo: ¿Por qué K3s para Homelab?

K3s es Kubernetes completo pero optimizado para edge computing y homelabs. Consume ~512MB RAM vs 2GB+ de Kubernetes estándar.

Ventajas de K3s:

  • Tamaño: ~100MB vs 1GB+ de Kubernetes estándar
  • 🚀 Instalación: 2 minutos vs horas con kubeadm
  • 🎯 Optimizado: Para edge/IoT (perfecto para homelabs)
  • Compatible: 100% compatible con API de Kubernetes
  • 📦 Incluye: Traefik (Ingress), local-path-provisioner (Storage), CoreDNS (DNS)

K3s incluye por defecto:

  • Traefik como Ingress Controller (no necesitas instalarlo)
  • local-path-provisioner para almacenamiento local
  • CoreDNS para DNS interno
  • Flannel para networking

Kubernetes estándar requiere instalar cada componente manualmente, lo cual es complejo y consume más recursos.

Advertencia Crítica: Requisitos Previos

⚠️ Este artículo asume que conoces Docker.

Si no sabes Docker, primero aprende:

¿Por qué es importante?

  • Kubernetes usa contenedores Docker (o containerd) internamente
  • Los conceptos de Docker (imágenes, contenedores, volúmenes) son fundamentales
  • Sin entender Docker, Kubernetes será confuso y frustrante

Kubernetes sin Docker es como intentar correr sin saber caminar. No es práctico.

Conceptos Fundamentales: Docker → Kubernetes

La mejor forma de aprender Kubernetes es comparándolo con Docker. Si ya entiendes Docker, entenderás K8s rápidamente.

2.1 Contenedor → Pod

En Docker:

docker run -d --name nginx -p 8080:80 nginx:latest

Esto crea un contenedor Nginx que escucha en el puerto 80 del contenedor y lo mapea al puerto 8080 del host.

En Kubernetes (Pod):

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80

Analogía: Pod = contenedor con superpoderes

Diferencias clave:

  • Pod puede tener múltiples contenedores que comparten red y almacenamiento
  • Pod es efímero: Si un Pod falla, Kubernetes NO lo recrea automáticamente
  • Por eso usamos Deployments: Gestionan Pods y los recrean si fallan

Ejemplo de Pod con múltiples contenedores:

apiVersion: v1
kind: Pod
metadata:
  name: web-app
spec:
  containers:
  - name: nginx
    image: nginx:latest
  - name: log-collector
    image: fluentd:latest
  # Ambos contenedores comparten:
  # - Misma IP (localhost entre ellos)
  # - Mismo almacenamiento (volúmenes compartidos)

¿Cuándo usar Pods directamente?

Casi nunca en producción. Los Pods son efímeros y no se auto-recrean. Usa Deployments o StatefulSets.

2.2 Docker Compose → Deployment

En Docker Compose:

version: '3.8'
services:
  nginx:
    image: nginx:latest
    ports:
      - "8080:80"
    restart: always

Esto define un servicio Nginx que se reinicia automáticamente si falla.

En Kubernetes (Deployment):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

Analogía: Deployment = docker-compose + auto-restart + escalado automático + rolling updates + rollbacks

Ventajas sobre Docker Compose:

CaracterísticaDocker ComposeKubernetes Deployment
Auto-restartrestart: always✅ Automático (siempre)
EscaladoManual (docker compose up --scale)✅ Automático (replicas: 3)
Rolling updatesManual (stop, pull, start)✅ Automático sin downtime
RollbackManual (volver a versión anterior)✅ Automático (kubectl rollout undo)
Health checksBásico✅ Liveness + Readiness probes

Ejemplo de escalado:

# Docker Compose: Manual
docker compose up --scale nginx=3

# Kubernetes: Automático
kubectl scale deployment nginx-deployment --replicas=3
# O cambiar replicas: 3 en el YAML y aplicar

Rolling update automático:

# Docker Compose: Tienes que hacerlo manualmente
docker compose stop nginx
docker compose pull nginx
docker compose up -d nginx

# Kubernetes: Automático
kubectl set image deployment/nginx-deployment nginx=nginx:1.25
# Kubernetes actualiza pod por pod sin downtime

2.3 Volúmenes Docker → PV/PVC

En Docker:

services:
  app:
    volumes:
      - ./data:/app/data              # Bind mount (host → container)
      - myvolume:/app/storage         # Named volume

En Kubernetes (PV + PVC):

Kubernetes separa el almacenamiento en dos conceptos:

  1. PersistentVolume (PV): El disco físico (recurso del cluster)
  2. PersistentVolumeClaim (PVC): La solicitud de espacio (lo que pide tu aplicación)
# PersistentVolume (disco físico)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  hostPath:
    path: /mnt/data

---
# PersistentVolumeClaim (solicitud de espacio)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-path

---
# Montar en Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  template:
    spec:
      containers:
      - name: app
        image: app:latest
        volumeMounts:
        - name: storage
          mountPath: /app/data
      volumes:
      - name: storage
        persistentVolumeClaim:
          claimName: my-pvc

Analogía:

  • PV = disco físico (recurso del cluster, como un disco duro)
  • PVC = solicitud de espacio (lo que pide tu aplicación, como «necesito 10GB»)
  • StorageClass = tipo de almacenamiento (local-path, nfs, ceph, etc.)

¿Por qué esta separación?

  • Flexibilidad: Puedes cambiar el tipo de almacenamiento sin modificar tu aplicación
  • Abstracción: Tu aplicación no sabe dónde está el disco, solo pide espacio
  • Gestión: El administrador del cluster gestiona los PVs, los desarrolladores solo piden PVCs

AccessModes (modos de acceso):

  • ReadWriteOnce (RWO): Un solo nodo puede montar en read-write (como un disco local)
  • ReadOnlyMany (ROX): Múltiples nodos pueden leer (ideal para modelos inmutables)
  • ReadWriteMany (RWX): Múltiples nodos pueden leer/escribir (requiere NFS, CephFS)

K3s StorageClass por defecto: local-path (almacenamiento local)

Comparación con Docker:

# Docker: Directo
volumes:
  - ./data:/app/data

# Kubernetes: Abstracción
volumes:
  - name: storage
    persistentVolumeClaim:
      claimName: my-pvc

2.4 Reverse Proxy → Ingress

En Docker Compose:

services:
  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - "80:80"
      - "443:443"
  
  app1:
    image: app1:latest
  
  app2:
    image: app2:latest

Necesitas configurar Nginx manualmente para enrutar app1.tudominio.comapp1:80 y app2.tudominio.comapp2:80.

En Kubernetes (Ingress):

# Ingress Controller (Traefik viene con K3s)
# No necesitas instalarlo manualmente

---
# Ingress (ruteo HTTP/HTTPS)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - app1.tudominio.com
    - app2.tudominio.com
    secretName: tls-secret
  rules:
  - host: app1.tudominio.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app1-service
            port:
              number: 80
  - host: app2.tudominio.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app2-service
            port:
              number: 80

Analogía: Ingress = reverse proxy integrado en Kubernetes (no necesitas servicio separado)

Ventajas:

  • SSL automático: Cert-manager gestiona certificados Let’s Encrypt
  • Sin configuración manual: No editas nginx.conf
  • Declarativo: Todo en YAML, versionable en Git
  • Multi-servicio: Un solo Ingress para múltiples servicios

K3s: Traefik viene preinstalado como Ingress Controller. No necesitas instalarlo.

Comparación con Docker:

# Docker: Servicio separado + configuración manual
services:
  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf  # Configuración manual

# Kubernetes: Integrado + declarativo
apiVersion: networking.k8s.io/v1
kind: Ingress
# Todo en YAML, sin archivos de configuración separados

2.5 Puertos Docker → Service

En Docker:

services:
  nginx:
    ports:
      - "8080:80"  # host:container

Esto mapea el puerto 8080 del host al puerto 80 del contenedor.

En Kubernetes (Service):

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: ClusterIP  # Solo accesible dentro del cluster
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

Analogía: Service = mapeo de puertos + load balancing + DNS interno

Tipos de Service:

TipoDescripciónUso
ClusterIPSolo accesible dentro del cluster (por defecto)Comunicación interna entre pods
NodePortExpone en puerto del nodo (30000-32767)Acceso externo sin Ingress
LoadBalancerIP externa (requiere cloud provider)Producción en cloud (AWS, GCP, Azure)
ExternalNameAlias a servicio externoConectar a servicios fuera del cluster

DNS interno:

  • FQDN completo: nginx-service.default.svc.cluster.local
  • Corto (mismo namespace): nginx-service
  • Cross-namespace: nginx-service.production.svc.cluster.local

Ejemplo de uso:

# WordPress se conecta a MySQL usando DNS interno
env:
- name: WORDPRESS_DB_HOST
  value: "mysql-service"  # No necesitas IP, solo el nombre del Service

Load balancing automático:

Si tienes 3 réplicas de Nginx, el Service distribuye el tráfico automáticamente entre ellas.

Comparación con Docker:

# Docker: Mapeo directo
ports:
  - "8080:80"

# Kubernetes: Abstracción + load balancing
type: ClusterIP
selector:
  app: nginx  # Conecta a todos los pods con label app=nginx

Instalación K3s: Kubernetes Ligero para Homelab

Requisitos Hardware/Software

Mínimos:

  • RAM: 512MB (mínimo), 1GB recomendado
  • CPU: 1 core (mínimo), 2 cores recomendado
  • Disco: 10GB mínimo
  • OS: Linux (Ubuntu 20.04+, Debian 11+, etc.)

Recomendados para homelab:

  • RAM: 4GB+
  • CPU: 2+ cores
  • Disco: 50GB+ SSD

Software previo:

  • Docker instalado (o containerd)
  • Conocimientos básicos de línea de comandos Linux

Instalación Básica (2 minutos)

# Instalar K3s (incluye kubectl)
curl -sfL https://get.k3s.io | sh -

# Verificar instalación
sudo k3s kubectl get nodes

# Debería mostrar:
# NAME     STATUS   ROLES                  AGE   VERSION
# server   Ready    control-plane,master   1m    v1.31.x+k3s1

¡Eso es todo! K3s está instalado y funcionando.

Configurar kubectl sin sudo

Por defecto, necesitas usar sudo k3s kubectl. Para usar kubectl directamente:

# Copiar kubeconfig
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

# Añadir a .bashrc/.zshrc (opcional pero recomendado)
export KUBECONFIG=~/.kube/config

# Verificar
kubectl get nodes

Ahora puedes usar kubectl directamente sin sudo.

Verificar Componentes Incluidos

K3s incluye varios componentes por defecto:

# Verificar Traefik (Ingress Controller)
kubectl get pods -n kube-system | grep traefik

# Verificar local-path-provisioner (Storage)
kubectl get pods -n kube-system | grep local-path

# Verificar CoreDNS (DNS interno)
kubectl get pods -n kube-system | grep coredns

# Verificar StorageClass
kubectl get storageclass
# Deberías ver: local-path (default)

Todo está listo para empezar a desplegar aplicaciones.

Primer Pod de Prueba

Vamos a crear un pod simple para verificar que todo funciona:

# Crear pod de prueba
kubectl run nginx-test --image=nginx:latest --port=80

# Verificar que está corriendo
kubectl get pods

# Ver logs
kubectl logs nginx-test

# Eliminar pod de prueba
kubectl delete pod nginx-test

Si ves el pod en estado «Running», K3s está funcionando correctamente.

Primer Despliegue: Nginx Básico

Vamos a desplegar Nginx usando Deployment + Service, comparándolo con Docker Compose.

Comparación: Docker Compose vs Kubernetes

Docker Compose:

version: '3.8'
services:
  nginx:
    image: nginx:latest
    ports:
      - "8080:80"
    restart: always

Kubernetes (Deployment + Service):

Crea nginx-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

Crea nginx-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

Desplegar

# Aplicar recursos
kubectl apply -f nginx-deployment.yaml
kubectl apply -f nginx-service.yaml

# Verificar
kubectl get pods
kubectl get svc

# Deberías ver:
# NAME                                READY   STATUS    RESTARTS   AGE
# nginx-deployment-7d4b8c9c6d-xxxxx   1/1     Running   0          30s
# nginx-deployment-7d4b8c9c6d-yyyyy   1/1     Running   0          30s
#
# NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# nginx-service   ClusterIP   10.43.xxx.xxx   <none>        80/TCP    30s

Acceder al Servicio

Opción 1: Port Forward (acceso local)

kubectl port-forward svc/nginx-service 8080:80

# Abrir http://localhost:8080 en el navegador

Opción 2: NodePort (acceso externo)

# Cambiar type: ClusterIP a type: NodePort
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080  # Puerto externo (30000-32767)
# Aplicar cambio
kubectl apply -f nginx-service.yaml

# Acceder desde fuera
curl http://<IP_DEL_NODO>:30080

Opción 3: Ingress (acceso con dominio)

Veremos esto en la sección de Ingress.

Escalar Manualmente

# Escalar a 5 réplicas
kubectl scale deployment nginx-deployment --replicas=5

# Verificar
kubectl get pods

# Deberías ver 5 pods corriendo

Comparación con Docker Compose:

# Docker Compose: Manual
docker compose up --scale nginx=5

# Kubernetes: Más simple
kubectl scale deployment nginx-deployment --replicas=5

Despliegue con Volúmenes: Persistencia de Datos

Vamos a desplegar una aplicación que guarda datos y necesita persistencia.

Comparación: Volúmenes Docker vs PV/PVC

Docker Compose:

services:
  app:
    volumes:
      - ./data:/app/data

Kubernetes (PV + PVC):

Crea pv.yaml:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nginx-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  hostPath:
    path: /mnt/data/nginx

Crea pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: local-path

Crea deployment.yaml (con volumen):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
      volumes:
      - name: html
        persistentVolumeClaim:
          claimName: nginx-pvc

Desplegar

# Aplicar en orden
kubectl apply -f pv.yaml
kubectl apply -f pvc.yaml
kubectl apply -f deployment.yaml

# Verificar
kubectl get pv
kubectl get pvc
kubectl get pods

# Deberías ver:
# NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   AGE
# nginx-pv  1Gi        RWO            Retain           Bound    default/nginx-pvc   local-path   30s
#
# NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
# nginx-pvc  Bound    nginx-pv  1Gi        RWO            local-path     30s

Verificar Persistencia

# Crear archivo en el pod
kubectl exec -it deployment/nginx-deployment -- sh -c "echo 'Hola desde K8s' > /usr/share/nginx/html/test.txt"

# Verificar archivo
kubectl exec -it deployment/nginx-deployment -- cat /usr/share/nginx/html/test.txt

# Reiniciar el pod (simular fallo)
kubectl delete pod -l app=nginx

# Esperar a que se recree
kubectl get pods -w

# Verificar que el archivo persiste
kubectl exec -it deployment/nginx-deployment -- cat /usr/share/nginx/html/test.txt
# Deberías ver: "Hola desde K8s"

Los datos persisten aunque reinicies el pod.

StorageClass: Automatización de PVs

En lugar de crear PVs manualmente, puedes usar StorageClass para que Kubernetes cree PVs automáticamente:

# PVC con StorageClass (K3s usa local-path por defecto)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: local-path  # K3s crea el PV automáticamente

Con StorageClass, no necesitas crear el PV manualmente. Kubernetes lo crea automáticamente cuando creas el PVC.

Exponer Servicios: Ingress con Traefik

Vamos a exponer Nginx usando Ingress, comparándolo con un reverse proxy en Docker.

Comparación: Reverse Proxy Docker vs Ingress

Docker Compose (Nginx reverse proxy):

services:
  nginx:
    image: nginx:latest
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - "80:80"
      - "443:443"
  
  app1:
    image: app1:latest
  
  app2:
    image: app2:latest

Necesitas configurar nginx.conf manualmente:

server {
    server_name app1.tudominio.com;
    location / {
        proxy_pass http://app1:80;
    }
}

server {
    server_name app2.tudominio.com;
    location / {
        proxy_pass http://app2:80;
    }
}

Kubernetes (Ingress):

# Ingress Controller (Traefik viene con K3s)
# No necesitas instalarlo manualmente

---
# Ingress (ruteo HTTP/HTTPS)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
spec:
  ingressClassName: traefik
  rules:
  - host: nginx.tudominio.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80

Ventajas:

  • Sin configuración manual: No editas nginx.conf
  • Declarativo: Todo en YAML, versionable en Git
  • SSL automático: Cert-manager gestiona certificados (opcional)

Verificar Traefik está Corriendo

# Verificar Traefik (Ingress Controller)
kubectl get pods -n kube-system | grep traefik

# Deberías ver:
# NAME                       READY   STATUS    RESTARTS   AGE
# traefik-xxx                1/1     Running   0          5m

K3s incluye Traefik por defecto. No necesitas instalarlo.

Crear Ingress

Crea ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
spec:
  ingressClassName: traefik
  rules:
  - host: nginx.tudominio.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80
# Aplicar
kubectl apply -f ingress.yaml

# Verificar
kubectl get ingress

# Deberías ver:
# NAME            CLASS    HOSTS                  ADDRESS   PORTS   AGE
# nginx-ingress   traefik  nginx.tudominio.com             80      30s

Configurar DNS

Opción 1: /etc/hosts (testing local)

# Añadir en /etc/hosts
<IP_DEL_NODO> nginx.tudominio.com

Opción 2: DNS real (producción)

# Crear registro A en tu DNS
nginx.tudominio.com  A  <IP_DEL_NODO>

Acceder

# Acceder desde navegador
curl http://nginx.tudominio.com

# O abrir en navegador
# http://nginx.tudominio.com

SSL Automático con Cert-Manager (Opcional)

Para SSL automático, instala cert-manager:

# Instalar cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

# Esperar a que esté listo
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=cert-manager -n cert-manager --timeout=300s

Luego añade anotaciones al Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - nginx.tudominio.com
    secretName: nginx-tls
  rules:
  - host: nginx.tudominio.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80

Cert-manager gestionará automáticamente los certificados Let’s Encrypt.

Caso Práctico Completo: WordPress en Kubernetes

Vamos a desplegar WordPress completo (WordPress + MySQL) con persistencia e Ingress, comparándolo con Docker Compose.

Comparación: Docker Compose vs Kubernetes

Docker Compose:

version: '3.8'
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppassword
    volumes:
      - mysql_data:/var/lib/mysql
  
  wordpress:
    image: wordpress:latest
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppassword
    ports:
      - "8080:80"
    depends_on:
      - mysql

volumes:
  mysql_data:

Kubernetes (WordPress + MySQL completo):

Crea mysql-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-path

Crea mysql-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "rootpassword"
        - name: MYSQL_DATABASE
          value: "wordpress"
        - name: MYSQL_USER
          value: "wpuser"
        - name: MYSQL_PASSWORD
          value: "wppassword"
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-data
        persistentVolumeClaim:
          claimName: mysql-pvc

Crea mysql-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306

Crea wordpress-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress:latest
        env:
        - name: WORDPRESS_DB_HOST
          value: "mysql-service"
        - name: WORDPRESS_DB_NAME
          value: "wordpress"
        - name: WORDPRESS_DB_USER
          value: "wpuser"
        - name: WORDPRESS_DB_PASSWORD
          value: "wppassword"
        ports:
        - containerPort: 80

Crea wordpress-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: wordpress-service
spec:
  selector:
    app: wordpress
  ports:
  - port: 80
    targetPort: 80

Crea ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wordpress-ingress
spec:
  ingressClassName: traefik
  rules:
  - host: wordpress.tudominio.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wordpress-service
            port:
              number: 80

Desplegar

# Aplicar todos los recursos
kubectl apply -f mysql-pvc.yaml
kubectl apply -f mysql-deployment.yaml
kubectl apply -f mysql-service.yaml
kubectl apply -f wordpress-deployment.yaml
kubectl apply -f wordpress-service.yaml
kubectl apply -f ingress.yaml

# Verificar
kubectl get pods
kubectl get svc
kubectl get ingress

# Esperar a que MySQL esté listo
kubectl wait --for=condition=ready pod -l app=mysql --timeout=300s

# Verificar logs de WordPress
kubectl logs -l app=wordpress

Acceder

# Configurar DNS
# Añadir en /etc/hosts o DNS real:
<IP_DEL_NODO> wordpress.tudominio.com

# Acceder
curl http://wordpress.tudominio.com
# O abrir en navegador: http://wordpress.tudominio.com

Ventajas sobre Docker Compose

CaracterísticaDocker ComposeKubernetes
Escalado WordPressManual✅ Automático (réplicas: 2)
Rolling updatesManual✅ Automático sin downtime
Auto-restartrestart: always✅ Automático (siempre)
Load balancingNo nativo✅ Service distribuye tráfico
SSL automáticoManual (certbot)✅ Cert-manager (opcional)

Con Kubernetes, puedes escalar WordPress a 5 réplicas sin downtime:

kubectl scale deployment wordpress-deployment --replicas=5

Comandos Esenciales kubectl

kubectl es la herramienta de línea de comandos para interactuar con Kubernetes. Aquí están los comandos más importantes.

Ver Recursos

# Ver todos los pods
kubectl get pods

# Ver pods en namespace específico
kubectl get pods -n default

# Ver todos los recursos
kubectl get all

# Ver deployments
kubectl get deployments

# Ver services
kubectl get services

# Ver con más detalles
kubectl get pods -o wide

# Ver en tiempo real (watch)
kubectl get pods -w

Describir Recursos (Debugging)

# Describir pod (ver eventos, estado, etc.)
kubectl describe pod <pod-name>

# Describir deployment
kubectl describe deployment <deployment-name>

# Describir service
kubectl describe service <service-name>

# Describir nodo
kubectl describe node <node-name>

Ejemplo de salida útil:

kubectl describe pod nginx-deployment-xxx
# Muestra:
# - Estado del pod
# - Eventos (qué pasó, errores, etc.)
# - Configuración (imagen, recursos, etc.)
# - Volúmenes montados

Logs

# Ver logs de un pod
kubectl logs <pod-name>

# Ver logs de un deployment
kubectl logs deployment/<deployment-name>

# Seguir logs (tail -f)
kubectl logs -f <pod-name>

# Ver logs del contenedor anterior (si reinició)
kubectl logs <pod-name> --previous

# Ver logs de todos los pods con label
kubectl logs -l app=nginx

Ejecutar Comandos en Pods

# Entrar a un pod (bash)
kubectl exec -it <pod-name> -- /bin/bash

# Ejecutar comando
kubectl exec <pod-name> -- ls /app

# Ejecutar en deployment
kubectl exec -it deployment/<deployment-name> -- /bin/bash

Aplicar/Actualizar Recursos

# Aplicar YAML
kubectl apply -f deployment.yaml

# Aplicar todos los YAMLs en carpeta
kubectl apply -f ./manifests/

# Eliminar recurso
kubectl delete -f deployment.yaml

# Eliminar pod
kubectl delete pod <pod-name>

# Eliminar deployment (elimina todos los pods)
kubectl delete deployment <deployment-name>

kubectl apply vs kubectl create:

  • kubectl create: Crea recurso (falla si ya existe)
  • kubectl apply: Crea o actualiza recurso (idempotente, recomendado)

Port Forward (Acceso Local)

# Port forward a un pod
kubectl port-forward <pod-name> 8080:80

# Port forward a un service
kubectl port-forward svc/<service-name> 8080:80

# Port forward a un deployment
kubectl port-forward deployment/<deployment-name> 8080:80

Útil para: Testing local, debugging, acceso temporal sin Ingress.

Escalar

# Escalar deployment
kubectl scale deployment nginx-deployment --replicas=5

# Escalar con archivo
kubectl scale --replicas=5 -f deployment.yaml

Rolling Updates

# Actualizar imagen
kubectl set image deployment/nginx-deployment nginx=nginx:1.25

# Ver estado del rollout
kubectl rollout status deployment/nginx-deployment

# Ver historial de rollouts
kubectl rollout history deployment/nginx-deployment

# Rollback a versión anterior
kubectl rollout undo deployment/nginx-deployment

# Rollback a versión específica
kubectl rollout undo deployment/nginx-deployment --to-revision=2

Troubleshooting Común

1. Pod en estado Pending

Síntoma:

kubectl get pods
# NAME          READY   STATUS    RESTARTS   AGE
# nginx-xxx     0/1     Pending   0          5m

Diagnóstico:

kubectl describe pod <pod-name>
# Events:
#   Warning  FailedScheduling  5m  default-scheduler  0/1 nodes are available: 1 Insufficient memory.

Causas comunes:

  • Recursos insuficientes (RAM/CPU)
  • No hay nodos disponibles
  • Taints/Tolerations no coinciden
  • PVC no puede montarse

Solución:

# Ver recursos del nodo
kubectl describe node

# Ver eventos del pod
kubectl describe pod <pod-name>

# Reducir requests/limits en Deployment

Ejemplo de ajuste:

resources:
  requests:
    memory: "128Mi"  # Reducir de 512Mi
    cpu: "100m"      # Reducir de 500m
  limits:
    memory: "256Mi"
    cpu: "200m"

2. Pod en estado CrashLoopBackOff

Síntoma:

kubectl get pods
# NAME          READY   STATUS             RESTARTS   AGE
# app-xxx       0/1     CrashLoopBackOff   5          3m

Diagnóstico:

# Ver logs del pod
kubectl logs <pod-name>

# Ver logs del contenedor anterior
kubectl logs <pod-name> --previous

# Describir pod
kubectl describe pod <pod-name>

Causas comunes:

  • Error en la aplicación (código)
  • Comando mal configurado
  • Dependencias faltantes
  • Healthcheck demasiado agresivo
  • Permisos incorrectos

Solución:

# Ajustar probes
livenessProbe:
  httpGet:
    path: /health
    port: 80
  initialDelaySeconds: 30  # Dar tiempo a iniciar
  periodSeconds: 10
  failureThreshold: 3

Ejemplo de debugging:

# Ver logs para identificar el error
kubectl logs app-xxx --previous

# Si el error es "permission denied", ajustar permisos
kubectl exec -it app-xxx -- chown -R 1000:1000 /app/data

3. Service no conecta

Síntoma: Pods no pueden comunicarse entre sí

Diagnóstico:

# Verificar service existe
kubectl get svc

# Verificar selectors del service coinciden con labels del pod
kubectl describe service <service-name>
kubectl get pods --show-labels

# Probar DNS
kubectl exec -it <pod-name> -- nslookup <service-name>

Causas comunes:

  • Selectors del Service no coinciden con labels del Pod
  • Pod no está en estado Running
  • NetworkPolicy bloquea tráfico

Solución:

# Verificar selectors coinciden
apiVersion: v1
kind: Service
spec:
  selector:
    app: nginx  # Debe coincidir con labels del Pod

Ejemplo de debugging:

# Ver labels del pod
kubectl get pods --show-labels
# NAME                    LABELS
# nginx-xxx               app=nginx,version=1.0

# Ver selectors del service
kubectl describe service nginx-service
# Selector: app=nginx  # ✅ Coincide

4. PVC no se monta (Pending)

Síntoma:

kubectl get pvc
# NAME            STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
# my-pvc          Pending                                      local-path     5m

Diagnóstico:

kubectl describe pvc <pvc-name>
# Events:
#   Warning  ProvisioningFailed  5m  persistentvolume-controller  storageclass.storage.k8s.io "local-path" not found

Causas comunes:

  • StorageClass no existe
  • No hay PersistentVolumes disponibles
  • StorageClass no tiene provisioner

Solución:

# Verificar StorageClass existe
kubectl get storageclass

# K3s incluye local-path-provisioner por defecto
# Si no existe, instalar:
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.30/deploy/local-path-storage.yaml

5. Ingress no funciona

Síntoma: No se puede acceder a la aplicación desde fuera

Diagnóstico:

# Verificar Ingress existe
kubectl get ingress

# Verificar Ingress Controller está corriendo
kubectl get pods -n kube-system | grep traefik

# Describir Ingress
kubectl describe ingress <ingress-name>

Causas comunes:

  • Ingress Controller no está instalado (K3s lo incluye por defecto)
  • Anotaciones incorrectas
  • Service no existe o no coincide
  • DNS no apunta al nodo

Solución:

# Verificar Traefik está corriendo (K3s)
kubectl get pods -n kube-system | grep traefik

# Verificar Service existe
kubectl get svc

# Verificar DNS apunta al nodo
dig app.tudominio.com

Mejores Prácticas para Homelab

1. Namespaces: Organizar Recursos

Los namespaces permiten organizar recursos en grupos lógicos.

# Crear namespace
kubectl create namespace production
kubectl create namespace development

# Desplegar en namespace específico
kubectl apply -f deployment.yaml -n production

# Ver recursos en namespace
kubectl get pods -n production

Ejemplo de organización:

# production/wordpress-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: production  # Especificar namespace
spec:
  # ...

Ventajas:

  • ✅ Organización clara (production, development, staging)
  • ✅ Aislamiento de recursos
  • ✅ Quotas de recursos por namespace

2. Resource Limits: Evitar que un Pod Consuma Todo

Siempre define requests y limits para evitar que un pod consuma todos los recursos.

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

Explicación:

  • requests: Recursos garantizados (el pod siempre tendrá esto)
  • limits: Recursos máximos (el pod no puede exceder esto)

Sin limits:

# ❌ MAL: Sin limits
resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
# El pod puede consumir toda la RAM del nodo

Con limits:

# ✅ BIEN: Con limits
resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"
# El pod está limitado a 512Mi RAM

3. Health Checks: Liveness y Readiness Probes

Los probes permiten a Kubernetes verificar que tu aplicación está funcionando correctamente.

livenessProbe:
  httpGet:
    path: /health
    port: 80
  initialDelaySeconds: 30
  periodSeconds: 10
  failureThreshold: 3
readinessProbe:
  httpGet:
    path: /ready
    port: 80
  initialDelaySeconds: 5
  periodSeconds: 5
  failureThreshold: 3

Diferencia:

  • Liveness probe: Si falla, Kubernetes reinicia el pod
  • Readiness probe: Si falla, Kubernetes quita el pod del Service (no recibe tráfico)

Ejemplo práctico:

# WordPress tarda en iniciar (conexión a MySQL)
readinessProbe:
  httpGet:
    path: /wp-admin/install.php
    port: 80
  initialDelaySeconds: 30  # Dar tiempo a conectar a MySQL
  periodSeconds: 10

4. Secrets: No Hardcodear Contraseñas

Nunca hardcodees contraseñas en YAMLs. Usa Secrets.

# Crear secret
kubectl create secret generic mysql-secret \
  --from-literal=password=mi-password-segura

# Usar en Deployment
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: mysql
        env:
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: password

Ventajas:

  • ✅ Contraseñas no están en YAMLs (no se versionan en Git)
  • ✅ Fácil rotación (cambiar secret, reiniciar pods)
  • ✅ Separación de configuración y secretos

5. ConfigMaps: Configuración Externa

Usa ConfigMaps para configuración que puede cambiar.

# Crear ConfigMap
kubectl create configmap app-config \
  --from-literal=database_host=mysql-service \
  --from-literal=log_level=info
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: app
        envFrom:
        - configMapRef:
            name: app-config

Ventajas:

  • ✅ Cambiar configuración sin modificar YAMLs
  • ✅ Reutilizar configuración entre deployments
  • ✅ Separación de código y configuración

¿Cuándo NO usar Kubernetes?

Kubernetes no es la solución para todo. Hay casos donde Docker Compose es mejor.

❌ NO uses Kubernetes si:

1. Homelab simple (1-2 servicios)

  • Docker Compose es más simple y suficiente
  • Kubernetes añade complejidad innecesaria
  • Overhead de recursos no justificado

2. Sin experiencia Docker

  • Aprende Docker primero
  • Kubernetes sin Docker es confuso
  • Curva de aprendizaje muy alta

3. Recursos limitados (<4GB RAM)

  • K3s mínimo requiere 512MB, pero recomendado 4GB+
  • Docker Compose consume menos recursos
  • Mejor usar Docker Compose si tienes recursos limitados

4. Prototipo rápido

  • Docker Compose es más rápido para prototipar
  • Kubernetes requiere más configuración inicial
  • Para pruebas rápidas, Docker Compose es mejor

5. Solo 1 servidor sin necesidad de escalado

  • Si no necesitas múltiples nodos, Docker Compose es suficiente
  • Kubernetes brilla con múltiples nodos y escalado automático
  • Para un solo servidor, Docker Compose es más simple

✅ Usa Kubernetes si:

  • Tienes 10+ servicios que gestionar
  • Necesitas escalado automático
  • Requieres alta disponibilidad (múltiples nodos)
  • Quieres rolling updates sin downtime
  • Tienes equipo con experiencia DevOps/K8s

Regla de oro: Si Docker Compose te funciona bien, no necesitas Kubernetes. Kubernetes es para cuando Docker Compose se queda corto.

Siguientes Pasos

1. Helm: Gestor de Paquetes para Kubernetes

Helm es como «apt» o «yum» para Kubernetes. Permite instalar aplicaciones complejas con un solo comando.

# Instalar Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# Instalar aplicación (ejemplo: WordPress)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install wordpress bitnami/wordpress

Ventajas:

  • ✅ Instalación rápida de aplicaciones complejas
  • ✅ Configuración parametrizable
  • ✅ Actualizaciones y rollbacks fáciles

Artículo relacionado: Kubernetes para IA: Cómo Desplegar Modelos ML en Producción (cubre Helm para aplicaciones IA)

2. Monitoring: Prometheus + Grafana

Monitorear tu cluster Kubernetes es esencial para producción.

# Instalar Prometheus + Grafana con Helm
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack

Métricas importantes:

  • CPU y RAM por pod
  • Uptime de servicios
  • Latencia de requests
  • Errores y restarts

Artículo relacionado: Prometheus + Grafana: Monitoreo Profesional para Homelab 2025 (próximamente)

3. CI/CD: GitLab CI, GitHub Actions

Automatiza el despliegue de aplicaciones en Kubernetes.

GitLab CI ejemplo:

deploy:
  script:
    - kubectl apply -f deployment.yaml
    - kubectl rollout status deployment/my-app

GitHub Actions ejemplo:

- name: Deploy to Kubernetes
  run: |
    kubectl apply -f deployment.yaml
    kubectl rollout status deployment/my-app

4. Artículos Relacionados

Para profundizar:

Requisitos previos:

📥 Descargar Ejemplos

Todos los ejemplos de este tutorial están disponibles en GitHub:

🔗 GitHub: kubernetes-basico-homelab-guia-desde-cero

Incluye:

  • 01-nginx-basico/ – Deployment + Service
  • 02-nginx-con-volumen/ – PV + PVC
  • 03-nginx-con-ingress/ – Ingress con Traefik
  • 04-wordpress-completo/ – WordPress + MySQL completo
  • docker-compose-equivalente/ – Comparación con Docker Compose

Clonar todo:

git clone https://github.com/ziruelen/learningaiagents.git
cd learningaiagents/kubernetes/kubernetes-basico-homelab-guia-desde-cero/

🔗 Artículos Relacionados

Continúa aprendiendo con estas guías complementarias:

🔗 Artículos Relacionados

Continúa aprendiendo con estas guías complementarias:

Uptime Kuma: Monitoreo Self-Hosted Gratis para tu Homelab (Guía Completa 2025)

Guía completa para instalar y configurar Uptime Kuma, la mejor alternativa open-source y self-hosted…

DGX Spark vs Servidor IA Casero: Comparativa Real 2025

NVIDIA lanza el DGX Spark por $4,000-$5,000. ¿Merece la pena o puedes montar un servidor IA casero m…

Caddy Reverse Proxy: SSL Automático y Configuración Simple para Homelab 2025

Guía completa de Caddy: reverse proxy con SSL automático para homelab. Instalación Docker, configura…

Dockge vs Portainer: ¿Cuál Elegir para Gestionar Docker en tu Homelab? (Guía Completa 2025)

Comparativa completa entre Dockge y Portainer: características, consumo de recursos, casos de uso, i…

Ollama vs LM Studio: ¿Cuál Elegir para Ejecutar LLMs Locales en tu Homelab? (Guía 2025)

Comparativa completa entre Ollama y LM Studio para ejecutar modelos de lenguaje locales. Benchmarks …

Los 10 Blogs Imprescindibles sobre AI Agents para 2025

Top 10 Blogs de Noticias sobre AI Agents y Agentic AI (Actualización 2025) Tiempo estimado…

Portainer: Gestiona Docker con Interfaz Web (Guía Completa 2025)

Instala Portainer en 30 segundos y gestiona Docker con interfaz web. Controla múltiples servidores r…

Homelab Completo 2025: La Guía Definitiva desde Cero

Si alguna vez has soñado con tener tu propio servidor en casa, ejecutar servicios 24/7,…

Preguntas Frecuentes

¿Necesito saber Docker antes de aprender Kubernetes?

Sí, absolutamente. Kubernetes usa contenedores Docker (o containerd) internamente. Sin entender Docker (imágenes, contenedores, volúmenes), Kubernetes será confuso y frustrante.

Recomendación: Aprende Docker primero con Docker para Novatos: La Guía Definitiva 2025 y Docker Compose: De 0 a Homelab Completo 2025.

Kubernetes sin Docker es como intentar correr sin saber caminar. No es práctico.

¿K3s es lo mismo que Kubernetes?

Sí y no. K3s es Kubernetes completo (100% compatible con la API), pero optimizado para edge computing y homelabs.

Diferencias:

  • Tamaño: K3s ~100MB vs Kubernetes estándar 1GB+
  • Instalación: K3s 2 minutos vs Kubernetes estándar horas
  • Incluye: K3s incluye Traefik, local-path-provisioner, CoreDNS por defecto
  • Compatibilidad: 100% compatible con API de Kubernetes

Para homelab, K3s es la mejor opción. Es más ligero, más fácil de instalar, y tiene todo lo que necesitas.

¿Cuánta RAM necesito para K3s?

Mínimos:

  • 512MB RAM (mínimo absoluto)
  • 1GB RAM (recomendado mínimo)

Recomendados para homelab:

  • 4GB+ RAM (para desplegar aplicaciones reales)
  • 2+ cores CPU
  • 50GB+ disco SSD

K3s consume:

  • Base: ~512MB RAM (solo K3s)
  • Con aplicaciones: Depende de tus aplicaciones

Si tienes menos de 4GB RAM, considera Docker Compose en lugar de Kubernetes.

¿Puedo usar Kubernetes en un solo servidor?

Sí, absolutamente. K3s funciona perfectamente en un solo servidor. No necesitas múltiples nodos para empezar.

Ventajas de un solo servidor:

  • ✅ Más simple (no necesitas configurar networking entre nodos)
  • ✅ Menos recursos (no necesitas múltiples máquinas)
  • ✅ Perfecto para aprender y homelabs

Cuándo necesitas múltiples nodos:

  • Alta disponibilidad (si un nodo falla, otros siguen funcionando)
  • Escalado horizontal (más capacidad)
  • Producción enterprise

Para homelab, un solo servidor con K3s es perfecto.

¿Qué es la diferencia entre Pod y Deployment?

Pod: Es la unidad básica de Kubernetes. Un Pod puede tener uno o más contenedores que comparten red y almacenamiento.

Deployment: Gestiona Pods. Un Deployment crea y mantiene Pods, los recrea si fallan, y permite escalado y rolling updates.

Analogía:

  • Pod = contenedor (unidad básica)
  • Deployment = docker-compose + auto-restart + escalado (gestor de pods)

¿Cuándo usar cada uno?

  • Pods directamente: Casi nunca en producción (son efímeros, no se auto-recrean)
  • Deployments: Siempre en producción (gestionan pods automáticamente)

Ejemplo:

# Pod: Si falla, no se recrea
apiVersion: v1
kind: Pod
# ...

# Deployment: Si falla, se recrea automáticamente
apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 3  # Mantiene 3 pods siempre

¿Cómo expongo un servicio al exterior en K8s?

Tres opciones:

1. Port Forward (testing local):

kubectl port-forward svc/nginx-service 8080:80
# Acceder: http://localhost:8080

2. NodePort (acceso externo sin dominio):

apiVersion: v1
kind: Service
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 30080  # Puerto externo (30000-32767)
# Acceder: http://<IP_DEL_NODO>:30080

3. Ingress (acceso con dominio, recomendado):

apiVersion: networking.k8s.io/v1
kind: Ingress
spec:
  rules:
  - host: app.tudominio.com
    http:
      paths:
      - path: /
        backend:
          service:
            name: nginx-service
            port:
              number: 80
# Acceder: http://app.tudominio.com

Para producción, usa Ingress. Es más limpio, permite SSL automático, y es el estándar.

¿Los datos persisten si reinicio un pod?

Depende. Si usas volúmenes normales (emptyDir), los datos se pierden. Si usas PersistentVolume (PV), los datos persisten.

Sin persistencia (datos se pierden):

volumes:
- name: temp
  emptyDir: {}  # Volumen temporal, se pierde al reiniciar pod

Con persistencia (datos persisten):

volumes:
- name: data
  persistentVolumeClaim:
    claimName: my-pvc  # Volumen persistente, datos persisten

Ejemplo práctico:

# Crear archivo en pod con PVC
kubectl exec -it deployment/my-app -- sh -c "echo 'test' > /data/test.txt"

# Reiniciar pod
kubectl delete pod -l app=my-app

# Verificar que archivo persiste
kubectl exec -it deployment/my-app -- cat /data/test.txt
# Deberías ver: "test"

Para bases de datos y aplicaciones con estado, SIEMPRE usa PersistentVolume.

¿Kubernetes es más difícil que Docker Compose?

Sí, Kubernetes es más complejo que Docker Compose. Tiene más conceptos, más configuración, y curva de aprendizaje más alta.

Comparación:

AspectoDocker ComposeKubernetes
ComplejidadBaja (YAML simple)Alta (múltiples conceptos)
Curva aprendizaje1-2 días1-2 semanas
Configuración1 archivo (docker-compose.yml)Múltiples archivos (Deployment, Service, Ingress, etc.)
Conceptos5-10 conceptos básicos20+ conceptos (Pod, Deployment, Service, Ingress, PV, PVC, Namespace, etc.)

Pero: Si ya dominas Docker, Kubernetes no es tan difícil. Los conceptos son similares, solo más abstractos.

Recomendación: Aprende Docker Compose primero. Cuando lo domines, Kubernetes será más fácil de entender.

¿Cuándo debo usar Kubernetes vs Docker Compose?

Usa Docker Compose si:

  • ✅ Tienes 1-10 servicios en un único servidor
  • ✅ No necesitas escalado automático
  • ✅ Un solo servidor es suficiente
  • ✅ Quieres simplicidad máxima
  • ✅ Prototipo rápido o desarrollo

Usa Kubernetes si:

  • ✅ Tienes 10+ servicios que gestionar
  • ✅ Necesitas escalado automático
  • ✅ Requieres alta disponibilidad (múltiples nodos)
  • ✅ Quieres rolling updates sin downtime
  • ✅ Tienes equipo con experiencia DevOps/K8s

Regla de oro: Si tienes 3 o menos servicios en un único servidor, Docker Compose es suficiente. Si necesitas múltiples nodos, escalado automático, o alta disponibilidad, Kubernetes es tu camino.

Para homelab típico: Empieza con Docker Compose. Si creces y necesitas más, migra a Kubernetes.

¿Cómo actualizo una aplicación en K8s sin downtime?

Rolling update automático: Kubernetes actualiza pod por pod sin downtime.

# Actualizar imagen
kubectl set image deployment/nginx-deployment nginx=nginx:1.25

# Kubernetes automáticamente:
# 1. Crea nuevo pod con nueva imagen
# 2. Espera a que esté listo (readiness probe)
# 3. Quita tráfico del pod viejo
# 4. Elimina pod viejo
# 5. Repite para cada pod

Ver estado del rollout:

kubectl rollout status deployment/nginx-deployment

Rollback si algo falla:

kubectl rollout undo deployment/nginx-deployment

Comparación con Docker Compose:

# Docker Compose: Manual (hay downtime)
docker compose stop nginx
docker compose pull nginx
docker compose up -d nginx

# Kubernetes: Automático (sin downtime)
kubectl set image deployment/nginx-deployment nginx=nginx:1.25

¿Qué es un Ingress y para qué sirve?

Ingress es el objeto de Kubernetes que gestiona el acceso HTTP/HTTPS externo a tus servicios.

Analogía: Ingress = reverse proxy integrado en Kubernetes (como Nginx o Traefik, pero nativo de K8s)

Ventajas:

  • SSL automático: Cert-manager gestiona certificados Let’s Encrypt
  • Sin configuración manual: No editas nginx.conf
  • Declarativo: Todo en YAML, versionable en Git
  • Multi-servicio: Un solo Ingress para múltiples servicios

Ejemplo:

apiVersion: networking.k8s.io/v1
kind: Ingress
spec:
  rules:
  - host: app1.tudominio.com
    http:
      paths:
      - path: /
        backend:
          service:
            name: app1-service
  - host: app2.tudominio.com
    http:
      paths:
      - path: /
        backend:
          service:
            name: app2-service

K3s incluye Traefik como Ingress Controller por defecto. No necesitas instalarlo.

¿Cómo veo los logs de mis aplicaciones en K8s?

Comandos básicos:

# Ver logs de un pod
kubectl logs <pod-name>

# Ver logs de un deployment
kubectl logs deployment/<deployment-name>

# Seguir logs (tail -f)
kubectl logs -f <pod-name>

# Ver logs del contenedor anterior (si reinició)
kubectl logs <pod-name> --previous

# Ver logs de todos los pods con label
kubectl logs -l app=nginx

Comparación con Docker:

# Docker
docker logs <container-name>

# Kubernetes
kubectl logs <pod-name>

Para múltiples pods (deployment):

# Kubernetes muestra logs de todos los pods del deployment
kubectl logs deployment/nginx-deployment

# Docker Compose muestra logs de todos los servicios
docker compose logs

¿Puedo usar docker-compose.yml en Kubernetes?

No directamente. Kubernetes no entiende docker-compose.yml. Necesitas convertir los servicios a objetos de Kubernetes (Deployments, Services, etc.).

Herramientas de conversión:

  • kompose: Convierte docker-compose.yml a YAMLs de Kubernetes
  • Manual: Crear Deployments, Services, etc. manualmente (recomendado para aprender)

Ejemplo con kompose:

# Instalar kompose
curl -L https://github.com/kubernetes/kompose/releases/download/v1.28.0/kompose-linux-amd64 -o kompose
chmod +x kompose
sudo mv kompose /usr/local/bin/

# Convertir
kompose convert

# Genera: deployment.yaml, service.yaml, etc.

Recomendación: Aprende a crear YAMLs de Kubernetes manualmente. Es mejor para entender los conceptos.

¿Kubernetes consume muchos recursos?

K3s consume:

  • Base: ~512MB RAM (solo K3s, sin aplicaciones)
  • Con Traefik, CoreDNS, etc.: ~700MB RAM

Kubernetes estándar consume:

  • Base: ~2GB RAM (solo Kubernetes, sin aplicaciones)

Comparación con Docker Compose:

  • Docker Compose: ~100MB RAM (solo Docker daemon)

Recomendación:

  • <4GB RAM: Usa Docker Compose
  • 4GB+ RAM: K3s es viable
  • 8GB+ RAM: K3s cómodo, puedes desplegar muchas aplicaciones

Para homelab típico (4-8GB RAM), K3s es perfecto.

¿Qué es kubectl y cómo se usa?

kubectl es la herramienta de línea de comandos para interactuar con Kubernetes. Es como docker para Docker, pero para Kubernetes.

Comandos básicos:

# Ver recursos
kubectl get pods
kubectl get services
kubectl get deployments

# Aplicar YAMLs
kubectl apply -f deployment.yaml

# Ver logs
kubectl logs <pod-name>

# Ejecutar comandos en pods
kubectl exec -it <pod-name> -- /bin/bash

# Eliminar recursos
kubectl delete pod <pod-name>

Instalación:

  • K3s incluye kubectl: No necesitas instalarlo por separado
  • Kubernetes estándar: Instalar kubectl manualmente

Comparación con Docker:

# Docker
docker ps
docker logs <container>
docker exec -it <container> bash

# Kubernetes
kubectl get pods
kubectl logs <pod>
kubectl exec -it <pod> -- bash

Conclusión

Resumen de Puntos Clave

En este tutorial has aprendido:

K3s es Kubernetes ligero – Perfecto para homelabs, instalación en 2 minutos

Comparaciones Docker→K8s – Pod, Deployment, Service, Ingress, PV/PVC explicados con analogías

Despliegue completo – WordPress + MySQL con persistencia e Ingress

Comandos kubectl esenciales – get, describe, logs, exec, apply, scale

Troubleshooting práctico – 5 problemas comunes con soluciones

Mejores prácticas – Namespaces, resource limits, health checks, secrets

Próximos Pasos Recomendados

Para comenzar HOY:

  1. Instala K3scurl -sfL https://get.k3s.io | sh -
  2. Despliega Nginx – Usa los ejemplos de GitHub
  3. Experimenta – Escala, actualiza, reinicia pods

Para profundizar:

Recursos Adicionales:

  • Documentación Oficial Kubernetes: https://kubernetes.io/docs/
  • Documentación K3s: https://k3s.io/
  • kubectl Reference: https://kubernetes.io/docs/reference/kubectl/
  • Ejemplos de Código: https://github.com/ziruelen/learningaiagents/tree/main/kubernetes/kubernetes-basico-homelab-guia-desde-cero

Por ziru

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
El Diario IA
Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.