# Seagate Exos X CSI (ME5 dual‑site) — Guía de instalación y operación Este README documenta cómo he dejado **reproducible** la instalación del *Seagate Exos X CSI Driver* (soporta ME5) en un clúster Kubernetes con **dos cabinas / dos zonas** (site‑a y site‑b) usando iSCSI + multipath y *topología por zona*. > **Objetivo** > > * Un único despliegue del driver (Helm). > * **Dos StorageClass** (uno por sitio) con `allowedTopologies` y credenciales (Secret) separadas. > * *WaitForFirstConsumer* para que el volumen se cree en la **misma zona** del pod. > * Montajes iSCSI rápidos gracias a multipath bien configurado (modo `greedy`). --- ## 1) Prerrequisitos en los nodos 1. **Multipath** y **iSCSI** instalados/activos. 2. **/etc/multipath.conf** — opciones relevantes usadas: ```conf defaults { user_friendly_names "no" find_multipaths "greedy" no_path_retry "queue" } devices { device { vendor "DellEMC" product "ME5" path_grouping_policy "multibus" path_checker "tur" prio "alua" } } ``` > **Por qué `greedy`?** > > * `find_multipaths "greedy"` evita crear *maps* hasta que haya más de un camino **o** el dispositivo sea claramente multipath, reduciendo falsos positivos y estabilizando el *udev settle*. Mejora tiempos de descubrimiento y evita *flapping*. Reiniciar servicios y refrescar paths tras cambiar multipath: ```bash sudo systemctl restart multipathd sudo multipath -r ``` 3. **Propagación de montajes (rshared)** Asegurar que `/` y `/var/lib/kubelet` están en **rshared** para que los montajes hechos por el plugin dentro del pod del *node‑server* aparezcan en el host: ```bash sudo mount --make-rshared / # systemd drop‑in para kubelet sudo install -d /etc/systemd/system/kubelet.service.d cat <<'EOF' | sudo tee /etc/systemd/system/kubelet.service.d/10-mount-propagation.conf [Service] MountFlags= ExecStartPre=/bin/mkdir -p /var/lib/kubelet ExecStartPre=/bin/mount --bind /var/lib/kubelet /var/lib/kubelet ExecStartPre=/bin/mount --make-rshared /var/lib/kubelet EOF sudo systemctl daemon-reload sudo systemctl restart kubelet ``` Comprobar: ```bash sudo findmnt -o TARGET,PROPAGATION / sudo findmnt -o TARGET,PROPAGATION /var/lib/kubelet ``` 4. **Etiquetas de topología en nodos** Etiquetar cada nodo con su zona: ```bash kubectl label nodes topology.kubernetes.io/zone=site-a --overwrite kubectl label nodes topology.kubernetes.io/zone=site-b --overwrite ``` --- ## 2) Despliegue del Driver con Helm ### 2.1. Namespace y valores ```bash kubectl apply -f namespace.yaml # namespace: seagate ``` **values.yaml** (resumen de lo usado): * Imagen del driver: `ghcr.io/seagate/seagate-exos-x-csi:v1.10.0` * Sidecars: * `csi-provisioner v5.0.1` (timeout 60s) * `csi-attacher v4.6.1` * `csi-resizer v1.11.1` * `csi-snapshotter v8.0.1` * `csi-node-driver-registrar v2.9.0` * `controller.extraArgs: ["-v=2"]` * `node.extraArgs: ["-v=2"]` > **Nota:** no es necesario tocar `CSIDriver` para topología; la topología se maneja desde los `StorageClass` + etiquetas de nodo. ### 2.2. Instalación ```bash helm upgrade --install exos-x-csi \ -n seagate --create-namespace \ ./seagate-exos-x-csi \ -f ./values.yaml ``` #### Si hay residuos de una instalación anterior (RBAC) Si aparece un error de *invalid ownership metadata* con recursos tipo `ClusterRole/ClusterRoleBinding` de un release previo (p.ej. `exosx-csi`), eliminarlos: ```bash kubectl delete clusterrole external-provisioner-runner-systems kubectl delete clusterrolebinding csi-provisioner-role-systems # (si hubiera más, listarlos por label y borrarlos) # kubectl get clusterrole,clusterrolebinding -A -l app.kubernetes.io/instance= ``` Reintentar `helm upgrade --install`. --- ## 3) Secret por cabina (A y B) Un `Secret` por sitio en el *namespace* `seagate` con `apiAddress`, `username`, `password` en Base64. ```bash kubectl apply -f secret-me5-site-a.yaml kubectl apply -f secret-me5-site-b.yaml ``` > **Importante:** Los `StorageClass` deben usar las **claves estándar CSI** para que el provisioner pase el Secret al driver: > > * `csi.storage.k8s.io/provisioner-secret-name|namespace` > * `csi.storage.k8s.io/controller-publish-secret-name|namespace` > * `csi.storage.k8s.io/controller-expand-secret-name|namespace` > * `csi.storage.k8s.io/node-stage-secret-name|namespace` *(si aplica)* > * `csi.storage.k8s.io/node-publish-secret-name|namespace` *(si aplica)* El síntoma de no usar estos nombres es: `missing API credentials` en el evento del PVC. --- ## 4) StorageClass por zona (topología) Se definen **dos** `StorageClass` idénticos salvo: * Secret (A o B) * `pool` (p. ej., `dg01` para site‑a, `dg02` para site‑b) * `volPrefix` (ej. `sza` / `szb` para identificar site en el nombre de LUN) * `allowedTopologies` con la zona correspondiente * `volumeBindingMode: WaitForFirstConsumer` > Con WFFC, el PVC **no** se enlaza hasta que exista un Pod consumidor; el scheduler elige un nodo, y el provisioner crea el volumen en la **zona del nodo**. Aplicar ambos `StorageClass`: ```bash kubectl apply -f sc-me5-site-a.yaml kubectl apply -f sc-me5-site-b.yaml ``` --- ## 5) Prueba de extremo a extremo ### 5.1. PVC + Pod en site‑a * PVC: `pvc-a` con `storageClassName: sc-me5-site-a` * Pod: `pod-a` con `nodeSelector: topology.kubernetes.io/zone=site-a` ```bash kubectl apply -f pvc-pod-a.yaml kubectl apply -f pod-a.yaml kubectl get pvc,pod ``` Deberías ver el Pod en *Running* y el volumen creado/montado en la ME5 del site‑a. ### 5.2. Verificaciones útiles * **iSCSI nodes vistos:** ```bash sudo iscsiadm -m node | sort ``` * **Multipath:** ```bash sudo multipath -ll ``` * **Eventos del PVC:** ```bash kubectl describe pvc ``` * **Logs del controller:** (búsqueda de credenciales / errores de provisión) ```bash kubectl -n seagate logs deploy/seagate-exos-x-csi-controller-server \ -c seagate-exos-x-csi-controller | grep -i -E 'cred|secret|error' ``` --- ## 6) Medir el tiempo de *NodePublish* (montaje) Para medir cuánto tarda el montaje (fase *NodePublishVolume*) desde el *node‑server*: ```bash kubectl -n seagate logs -l name=seagate-exos-x-csi-node-server \ -c seagate-exos-x-csi-node --tail=10000 \ | grep "NodePublishVolume" \ | grep "ROUTINE END" \ | sed -E 's/.*NodePublishVolume.*<([^>]*)>.*/\1/' ``` * Valores \~**< 2 min** indican que el montaje completa dentro de la ventana de kubelet, evitando `DeadlineExceeded`. * Si ves \~**4m34s** constantes: el driver está esperando a que aparezcan *dm‑name* de portales inaccesibles. Revisa topologías, conectividad y que solo se prueben portales de la zona activa. > Para validar zona‑B, lanza un Pod/PVC análogo en `site-b` y repite el grep anterior en los logs. --- ## 7) Solución de problemas * **`missing API credentials` al provisionar** * Asegúrate de usar las **claves CSI** en `parameters:` del `StorageClass` (ver §3). * **Errores Helm de *invalid ownership metadata*** * Borra los `ClusterRole/ClusterRoleBinding` residuales del release antiguo (ver §2.2). * **`DeadlineExceeded` durante montaje** * Comprueba: * `find_multipaths "greedy"` y resto de multipath según §1.2. * Etiquetas de zona en el nodo donde programa el Pod. * Que el `StorageClass` correcto tenga `allowedTopologies` de esa zona. * **Ver puertos/portales iSCSI efectivos** * `sudo iscsiadm -m node | sort` para ver a qué destinos quedó configurado el nodo. Con topología bien aplicada, deben ser los del sitio correspondiente. --- ## 8) Limpieza y reintentos Para repetir la prueba desde cero (manteniendo el driver): ```bash kubectl delete -f pod-a.yaml kubectl delete -f pvc-pod-a.yaml ``` Si quisieras limpiar *todo el despliegue* del driver: ```bash helm uninstall exos-x-csi -n seagate # Si quedaron RBAC de releases previos: kubectl delete clusterrole external-provisioner-runner-systems || true kubectl delete clusterrolebinding csi-provisioner-role-systems || true ``` --- ## 9) Resumen de lo que quedó en repo (carpeta `seagate/`) * `namespace.yaml` — Namespace `seagate`. * `secret-me5-site-a.yaml` / `secret-me5-site-b.yaml` — Credenciales por sitio. * `values.yaml` — Valores de Helm usados para el driver v1.10.0. * `sc-me5-site-a.yaml` / `sc-me5-site-b.yaml` — StorageClass con `allowedTopologies`, `pool`, `volPrefix`, claves CSI de Secret y `WaitForFirstConsumer`. * `pvc-pod-a.yaml` + `pod-a.yaml` — Manifests de prueba en `site-a`. * *(Opcional)* `csi-exos-x-csidriver.yaml` — no es necesario modificarlo para topología en esta versión. --- ## 10) Anexos — Comandos útiles ejecutados * Reinicio multipath/kubelet y propagación de montajes. * Limpieza iSCSI/multipath (cuando se rehizo la prueba): ```bash sudo iscsiadm -m node -u || true sudo iscsiadm -m node -o delete || true sudo multipath -F || true sudo multipath -r ``` * Despliegue Helm + manejo de residuos RBAC (ver §2.2). * Aplicación secuencial de `namespace`, `secrets`, `StorageClass`, `PVC` y `Pod`. --- ### Resultado * **Reproducible**: con esta receta, el volumen se crea en la cabina de su zona y el Pod arranca. * **Tiempos de montaje**: bajan de \~4m34s a **≈1m30s** (observado), dentro del presupuesto de kubelet. * **Aislamiento por zona**: cada StorageClass limita portales iSCSI a su sitio gracias a `allowedTopologies` + etiquetas de nodo.