En el Episode 2 dejé el cluster andando: un control plane, dos workers, kubectl get nodes en Ready, y cada worker con un segundo disco de 100 GB esperando.
Un cluster sin storage replicado es un playground. Cualquier cosa con estado —una base de datos, un registry, Grafana con sus dashboards— necesita que los datos sobrevivan a que se caiga el nodo completo, no sólo a que el pod se reinicie. Este episodio es eso: Longhorn, que convierte el disco extra de cada worker en una StorageClass que sobrevive a perder un nodo entero.
Y en Talos hay un detalle que me gusta: el “disco extra” ya estaba declarado en la config del nodo del episodio anterior, no formateado con mkfs ni a mano. Este episodio sólo lo consume.
1. El disco
El patch del episodio anterior (UserVolumeConfig con !system_disk) ya dejó el volumen u-longhorn montado en /var/mnt/longhorn en ambos workers. Verifico:
talosctl -n $WORKER1_IP,$WORKER2_IP get volumestatus u-longhorn
talosctl -n $WORKER1_IP,$WORKER2_IP get mountstatus | grep longhorn
Si la config se hubiera generado sin el patch (por ejemplo, un cluster que ya existía antes de leer el episodio 2), aplicarlo tarde no requiere reinstalar nada:
talosctl -n $WORKER1_IP,$WORKER2_IP patch machineconfig --patch @talos/longhorn-user-disk.yaml
Este es el payoff de declararlo como UserVolumeConfig y no de crear el directorio a mano: Talos propaga el montaje al mount namespace del kubelet. Un directorio suelto dentro de /var/mnt no recibe ese trato — es la diferencia entre que Longhorn vea el disco y que se cuelgue en timeouts sin un error claro.
2. Namespace con pod security privilegiado
Longhorn necesita kernels de iSCSI y controladores: corre privilegiado. Como el cluster tiene enforce: baseline global, el label hay que ponerlo antes de instalar, no después:
kubectl create namespace longhorn-system
kubectl label namespace longhorn-system pod-security.kubernetes.io/{enforce,audit,warn}=privileged
Si lo ponés después, los pods ya fueron rechazados y tenés que recrearlos. Es más rápido acordarse.
3. Instalar (chart 1.12.1)
helm repo add longhorn https://charts.longhorn.io && helm repo update
helm install longhorn longhorn/longhorn -n longhorn-system --version 1.12.1 \
--set defaultSettings.defaultDataPath=/var/mnt/longhorn \
--set defaultSettings.defaultReplicaCount=2 \
--set persistence.defaultClassReplicaCount=2
Dos valores hacen todo el trabajo:
defaultDataPath=/var/mnt/longhorn: los datos van al disco dedicado, no al disco del sistema. El sistema en 30 GB, los datos en sus 100 GB propios — sin competir por espacio ni por IOPS.- 2 réplicas: con 2 workers es el máximo útil. Cada volumen vive en ambos nodos; un nodo menos es un “RAID” degradado que sigue sirviendo, no una pérdida.
La versión del chart va pineada, como siempre: --version 1.12.1, no latest.
4. Verificar
Primero que el driver desplegó:
kubectl -n longhorn-system rollout status deploy/longhorn-driver-deployer
kubectl get nodes.longhorn.io -n longhorn-system # 2 workers, SCHEDULABLE=true
Después, de punta a punta: un PVC que llega a Bound y un pod que llega a Running escribiendo en él. Longhorn se instala como StorageClass default, así que el PVC no necesita declarar nada:
kubectl create ns storage-test
kubectl -n storage-test apply -f - <<'YAML'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
restartPolicy: Never
containers:
- name: test
image: busybox
command: ["sh", "-c", "echo ok > /data/f && cat /data/f && sleep 300"]
volumeMounts:
- name: d
mountPath: /data
volumes:
- name: d
persistentVolumeClaim:
claimName: test
YAML
kubectl -n storage-test get pvc,pod
kubectl get volume -n longhorn-system # el PVC con 2 réplicas
kubectl delete ns storage-test
Y la prueba que de verdad importa, cuando el cluster esté maduro para ella: apagar un worker entero (qm stop 211) y ver el pod reprogramarse en el otro con sus datos intactos. Ese es el momento Longhorn.
La UI
El dashboard de Longhorn no está expuesta al LAN, y así va a seguir. Cuando hace falta, port-forward:
kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80
# http://localhost:8080
Lo que me llevo de este episodio
- El disco se declara, no se formatea. Todo el “preparar el disco” de Longhorn en Talos fue un YAML del episodio anterior; acá sólo lo verifiqué.
defaultDataPathapunta al disco dedicado. Sistema y datos en discos separados: sin competir por espacio ni por IOPS.- 2 réplicas con 2 workers es el máximo útil. Un nodo menos es un RAID degradado que sigue sirviendo.
- El label de pod security va antes de instalar. Después, es recrear pods.
- Verificar de punta a punta. StorageClass en el listado no prueba nada; PVC en
Boundcon un pod escribiendo, sí.
Próximo episodio
PVCs replicados listos: el cluster ya guarda cosas que sobreviven a que se caiga un nodo completo. Pero nada de esto sirve si no hay forma limpia de exponer servicios al LAN — type: LoadBalancer en un lab casero queda en <pending> para siempre. Ahí entra MetalLB.
Ahora cada byte mío vive dos veces, en dos nodos distintos.
Romper el cluster se volvió mucho más difícil — y mucho más interesante. 😎