Plataforma Kubernetes
Clúster RKE2 de 3 nodos sobre KVM: una mini plataforma de empresa (app web + PostgreSQL, Ingress, monitorización, logs y Rancher) construida por fases. Longhorn entra cuando ya dominas PV/PVC.
▸ 10 fases
▸ en construcción
▸ homelab · KVM/libvirt
Laboratorio para aprender Kubernetes desde la óptica de un administrador de plataformas (no de desarrollador). Se construye una pequeña plataforma de empresa que crece por fases, y cada fase reutiliza la anterior. La app es sencilla a propósito — lo importante es la infraestructura.
HARDWARE
Host Arch Linux · KVM/libvirt · red NAT · SSH
VMs: rke2-server · rke2-worker1 · rke2-worker2
VMs: rke2-server · rke2-worker1 · rke2-worker2
FILOSOFÍA
No copiar YAML: entender por qué existe cada componente, qué problema resuelve, cómo se relaciona con el resto y cómo se diagnostica.
# flujo objetivo
Internet → Ingress → Service → App Pod 1 / App Pod 2 → PostgreSQL → PVC / PVobservabilidad: Prometheus · Grafana · Alertmanager | logs: Loki · Promtail
gestión: Rancher | almacenamiento distribuido (al final): Longhorn
▸ FASE 0 Preparación del clúster RKE2
OBJETIVO
Levantar un clúster RKE2 de 3 nodos (1 server + 2 workers) sobre las VMs KVM y dejarlo en estado Ready.
CONOCIMIENTOS PREVIOS
Linux, SSH, red NAT, systemd, firewall (firewalld/nftables).
COMPONENTES NUEVOS
RKE2containerdkubeletkubectlkubeconfigcontrol-planeworker
COMANDOS CLAVE
$ systemctl status rke2-server
$ kubectl get nodes -o wide
$ kubectl describe node rke2-worker1
$ kubectl get pods -A
EJERCICIOS
Instalar el server, unir los 2 workers con el token, exportar KUBECONFIG y comprobar que los 3 nodos están Ready.
AVERÍAS
✕Nodo NotReady
✕Token o URL de unión incorrectos
✕Puertos 6443/9345 bloqueados por firewall
✕Reloj desincronizado (falta NTP)
BUENAS PRÁCTICAS
Server dedicado · NTP (chrony) en todos los nodos · backup de /etc/rancher/rke2 y del token · nunca exponer 6443 a internet.
▸ FASE 1 Primer despliegue: la app en su namespace
OBJETIVO
Desplegar una app web sencilla y entender la cadena Deployment → ReplicaSet → Pod.
CONOCIMIENTOS PREVIOS
Fase 0 completada.
COMPONENTES NUEVOS
NamespacePodReplicaSetDeploymentService (ClusterIP)
DOCKER (solo imágenes)
Escribir un Dockerfile de la app,
docker build, etiquetar y subir a un registry. Docker aquí solo sirve para construir/entender la imagen; el despliegue es 100% Kubernetes.COMANDOS CLAVE
$ kubectl create namespace plataforma
$ kubectl apply -f app/ -n plataforma
$ kubectl get deploy,rs,pods -n plataforma
$ kubectl logs deploy/web -n plataforma
$ kubectl describe pod <pod> -n plataforma
EJERCICIOS
Escalar a 3 réplicas y observar el ReplicaSet; borrar un pod y ver cómo se recrea solo.
AVERÍAS
✕ImagePullBackOff (tag o registry mal)
✕CrashLoopBackOff (la app peta al arrancar)
✕ErrImagePull (credenciales del registry)
BUENAS PRÁCTICAS
Un namespace por app · labels/selectors claros · imágenes con tag de versión (nunca
:latest).▸ FASE 2 Configuración: ConfigMaps y Secrets
OBJETIVO
Sacar la configuración y las credenciales fuera de la imagen.
COMPONENTES NUEVOS
ConfigMapSecretenv / envFromvolumen de config
COMANDOS CLAVE
$ kubectl create configmap app-config --from-file=app.conf
$ kubectl create secret generic db-cred --from-literal=pass=...
$ kubectl get cm,secret -n plataforma
EJERCICIOS
Pasar parámetros por ConfigMap y las credenciales de BD por Secret; montar un fichero de config como volumen.
AVERÍAS
✕CreateContainerConfigError por ConfigMap/Secret inexistente
✕Variable de entorno mal referenciada
✕Secret con base64 incorrecto
BUENAS PRÁCTICAS
Nada de credenciales en la imagen ni en git · Secrets separados del código · ConfigMaps versionados.
▸ FASE 3 Salud y ciclo de vida
OBJETIVO
Que la plataforma se actualice sin cortes y se recupere sola.
COMPONENTES NUEVOS
Liveness probeReadiness proberequests / limitsRolling updateRollback
COMANDOS CLAVE
$ kubectl set image deploy/web web=web:v2 -n plataforma
$ kubectl rollout status deploy/web -n plataforma
$ kubectl rollout history deploy/web -n plataforma
$ kubectl rollout undo deploy/web -n plataforma
EJERCICIOS
Actualizar a v2 con rolling update; forzar un fallo y hacer rollback; ajustar requests/limits y observar el efecto.
AVERÍAS
✕Readiness que nunca pasa → Service sin endpoints (503)
✕OOMKilled por límite de memoria demasiado bajo
✕Liveness agresiva reiniciando el pod en bucle
BUENAS PRÁCTICAS
SIEMPRE readiness + liveness y requests/limits · maxUnavailable/maxSurge para actualizaciones seguras.
▸ FASE 4 Estado persistente: PostgreSQL
OBJETIVO
Añadir PostgreSQL con datos que sobrevivan a reinicios del pod.
COMPONENTES NUEVOS
PersistentVolumePersistentVolumeClaimStorageClass (local-path)StatefulSet
COMANDOS CLAVE
$ kubectl get sc,pv,pvc -n plataforma
$ kubectl describe pvc data-postgres-0 -n plataforma
$ kubectl exec -it postgres-0 -n plataforma -- psql -U app
EJERCICIOS
Conectar la app a Postgres por su Service interno; borrar el pod de Postgres y comprobar que los datos siguen ahí.
AVERÍAS
✕PVC en Pending (sin StorageClass o sin PV disponible)
✕Datos que desaparecen (se guardaron en el contenedor, no en el PVC)
✕Permisos del volumen
BUENAS PRÁCTICAS
Datos SIEMPRE en el PVC · backups lógicos (pg_dump) · no montar la BD sin almacenamiento.
⚠️ Aquí usas el StorageClass local del nodo. Todavía NO Longhorn — el porqué está en la Fase 9.
▸ FASE 5 Exposición: Ingress
OBJETIVO
Publicar la app por un nombre de host, no por IP:puerto.
COMPONENTES NUEVOS
Ingress Controller (ingress-nginx)Ingressreglas host/pathTLS
COMANDOS CLAVE
$ kubectl get ingress -n plataforma
$ kubectl describe ingress web -n plataforma
$ kubectl get endpoints -n plataforma
EJERCICIOS
Exponer la app en
plataforma.lab con una entrada en /etc/hosts; añadir un segundo path/servicio.AVERÍAS
✕404 del Ingress (host/path mal)
✕503 (Service sin endpoints)
✕DNS / /etc/hosts mal
✕Certificado TLS no válido
BUENAS PRÁCTICAS
Un Ingress por app · TLS incluso en el lab · nombres coherentes.
▸ FASE 6 Observabilidad: Prometheus + Grafana + Alertmanager
OBJETIVO
Ver el estado del clúster y de la app, y recibir alertas.
COMPONENTES NUEVOS
kube-prometheus-stackPrometheusGrafanaAlertmanagerServiceMonitor
COMANDOS CLAVE
$ helm install monitoring prometheus-community/kube-prometheus-stack -n monitoring --create-namespace
$ kubectl get pods -n monitoring
$ kubectl port-forward svc/grafana 3000:80 -n monitoring
EJERCICIOS
Dashboard de CPU/memoria y estado de pods; alerta de «pod caído» y «nodo NotReady»; medir el consumo de la app.
AVERÍAS
✕Target Down en Prometheus
✕Sin métricas (ServiceMonitor mal etiquetado)
✕Grafana sin datasource
BUENAS PRÁCTICAS
Alertas accionables (que exijan actuar) · retención razonable · un dashboard por servicio.
▸ FASE 7 Logs: Loki + Promtail
OBJETIVO
Centralizar y buscar los logs de todos los pods.
COMPONENTES NUEVOS
LokiPromtailLogQL
COMANDOS CLAVE
$ helm install loki grafana/loki-stack -n logging --create-namespace
$ kubectl logs -f deploy/web -n plataforma
$ {namespace="plataforma"} |= "error" # LogQL en Grafana
EJERCICIOS
Buscar el log del pod que provoca un CrashLoop; correlacionar el pico de una métrica con el log del error.
AVERÍAS
✕Diagnosticar un fallo real (config mal, conexión a BD…) usando solo los logs centralizados
BUENAS PRÁCTICAS
Etiquetas útiles (namespace, app) · nada de datos sensibles en logs · retención acotada.
▸ FASE 8 Administración con Rancher + RBAC
OBJETIVO
Gestionar el clúster desde una interfaz y controlar los accesos.
COMPONENTES NUEVOS
Rancherimport clúster RKE2ProjectsRBAC (Role, RoleBinding)
COMANDOS CLAVE
$ kubectl get clusterroles,roles -A
$ kubectl auth can-i create pods -n plataforma --as=usuario
EJERCICIOS
Crear un usuario con permisos solo de lectura en un namespace; gestionar deployments/pods/almacenamiento desde Rancher; ver logs y monitorización en la UI.
AVERÍAS
✕Acceso denegado por RBAC (rol insuficiente)
✕Usuario viendo namespaces que no debería
✕Rancher sin conectar al clúster
BUENAS PRÁCTICAS
RBAC de mínimo privilegio · no usar admin para el día a día · un Project por entorno.
▸ FASE 9 Almacenamiento distribuido: Longhorn ⚠️
CUÁNDO: solo cuando domines PV, PVC y StorageClass (Fase 4) y hayas sentido su límite.
POR QUÉ AQUÍ
El StorageClass local ata cada volumen a un nodo — si ese nodo cae, la BD deja de estar disponible. Ya sabes qué es un PVC y qué problema tiene; ahora Longhorn lo resuelve replicando el volumen entre los 3 nodos, con snapshots y backups. Meterlo antes sería magia sin entender qué reemplaza.
COMPONENTES NUEVOS
LonghornStorageClass longhornréplicassnapshotsbackups
COMANDOS CLAVE
$ kubectl get pods -n longhorn-system
$ kubectl get sc
$ kubectl get volumes.longhorn.io -n longhorn-system
EJERCICIOS
Migrar el PVC de Postgres a la StorageClass
longhorn; apagar un nodo y comprobar que la BD sigue viva; hacer un snapshot y restaurarlo.AVERÍAS
✕Réplicas degradadas (volume degraded)
✕Sin espacio en disco
✕Volumen que no monta al mover el pod de nodo
BUENAS PRÁCTICAS
3 réplicas · backups a destino externo (NFS/S3) · vigilar el espacio de cada nodo.
CIERRE · hacia SUSE Edge
Al terminar tienes una plataforma «de empresa» en miniatura: RKE2 + app + PostgreSQL + Ingress + observabilidad + logs + Rancher + almacenamiento distribuido. Es la base natural para el siguiente salto — Harvester, K3s, Edge Image Builder, Metal3, Turtles y SUSE Edge — porque ya dominas el clúster, el almacenamiento y la operación.