Files
kubernetes/rook/readme.md
2025-08-24 21:08:20 +00:00

448 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Despliegue de RookCeph en clúster **Kubernetes** (SUSE) con discos locales (Bluestore)
> Guía actualizada para un clúster **Kubernetes** (no K3s) en SUSE, con 4 nodos iniciales y **futura ampliación a stretch** con un quinto nodo **árbitro**. Discos locales (RAID/HBA), red de almacenamiento dedicada **VLAN 30 192.168.3.0/24**, y exposición del dashboard **vía Ingress NGINX** con TLS.
---
## 1) Requisitos previos
* 4 nodos Kubernetes operativos: `srvfkvm01`, `srvfkvm02`, `srvfkvm03`, `srvfkvm04` (control-plane o mixtos)
* Cada nodo con **6 discos** dedicados (\~894GB) para Ceph
* Acceso a Internet desde los nodos
* Red de almacenamiento dedicada **VLAN 30 192.168.3.0/24** (Ceph public/cluster)
* `kubectl` configurado y permisos de admin
> **Nota de versiones**: ejemplos probados con Rook 1.17.x y Ceph v19.x (Squid) o v18.x (Reef). En los manifiestos se usa una imagen estable.
---
## 2) Preparar discos en SUSE (solo discos de datos)
Instala utilidades necesarias en **cada nodo**:
```bash
sudo zypper -n install gdisk util-linux
```
Limpieza segura **solo** de `sdb…sdg` (ajusta si difiere):
```bash
set -euo pipefail
DISKS=(sdb sdc sdd sde sdf sdg)
for d in "${DISKS[@]}"; do
echo ">>> /dev/$d"
sudo sgdisk --zap-all /dev/$d || true # limpia GPT/MBR
sudo wipefs -a /dev/$d || true # borra firmas FS/LVM
sudo blkdiscard -f /dev/$d || \ # TRIM (si soporta)
sudo dd if=/dev/zero of=/dev/$d bs=1M count=10 oflag=direct,dsync
done
```
Obtén las rutas **persistentes** *byid* para cada disco (en cada nodo):
```bash
for d in sdb sdc sdd sde sdf sdg; do
echo "=== $HOSTNAME -> $d ==="
ls -l /dev/disk/by-id/ | awk -v d="$d" '$NF ~ ("/" d "$") {print "/dev/disk/by-id/"$9}'
done
```
> **Usa siempre** `/dev/disk/by-id/...` en los manifiestos (campo `fullpath:`) para evitar cambios de letra.
---
## 3) Etiquetado de nodos por **site**
Vamos a distribuir por zonas lógicas desde el inicio (A/B). El árbitro llegará después.
```bash
# SITE A
kubectl label node srvfkvm01 topology.kubernetes.io/zone=site-a --overwrite
kubectl label node srvfkvm02 topology.kubernetes.io/zone=site-a --overwrite
# SITE B
kubectl label node srvfkvm03 topology.kubernetes.io/zone=site-b --overwrite
kubectl label node srvfkvm04 topology.kubernetes.io/zone=site-b --overwrite
```
> Cuando exista el nodo **árbitro**, se etiquetará como `topology.kubernetes.io/zone=arbiter`.
---
## 4) Instalar Rook (CRDs, comunes y operador)
```bash
kubectl create namespace rook-ceph || true
# Clonar repo oficial (opcional para tener toolbox/ejemplos)
git clone https://github.com/rook/rook.git
cd rook/deploy/examples
kubectl apply -f crds.yaml -f common.yaml -f operator.yaml
```
Comprueba el operador:
```bash
kubectl -n rook-ceph get pods | grep operator
```
---
## 5) CephCluster 4 nodos, discos *byid*, red de storage (VLAN 30)
Archivo `cluster/ceph-cluster.yaml`:
```yaml
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
cephVersion:
image: quay.io/ceph/ceph:v19.2.3 # estable (puedes usar v18.2.x si prefieres)
dataDirHostPath: /var/lib/rook
# Red: usamos hostNetworking y restringimos a VLAN de storage
network:
provider: host
addressRanges:
public:
- "192.168.3.0/24"
cluster:
- "192.168.3.0/24"
mon:
count: 3
allowMultiplePerNode: false
dashboard:
enabled: true
# No queremos OSDs en el futuro nodo árbitro
placement:
osd:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: ["site-a", "site-b"]
storage:
useAllNodes: false
useAllDevices: false
nodes:
- name: srvfkvm01
devices:
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d94b003012d5bb177a1716
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d94b003012d5dc196bd3a7
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d94b003012d5f81b10f7ef
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d94b003012d6151cca8afd
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d94b003012d62f1e5e9699
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d94b003012d64f204b2405
- name: srvfkvm02
devices:
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9430030127eef88828273
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9430030127f879197de32
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9430030128081a076ba0c
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9430030128114a93e33b9
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d94300301281a7b1fc151a
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9430030128235ba79d801
- name: srvfkvm03
devices:
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9510030128aef3bb4e0ae
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9510030128b0e3d8bc1dc
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9510030128b2b3f446dd7
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9510030128b4440c2d027
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9510030128b5e42510c2a
- fullpath: /dev/disk/by-id/wwn-0x64cd98f036d9510030128b7d442e592c
- name: srvfkvm04
devices:
- fullpath: /dev/disk/by-id/wwn-0x6ec2a72037894c003012887ebfca6752
- fullpath: /dev/disk/by-id/wwn-0x6ec2a72037894c0030128896e360075f
- fullpath: /dev/disk/by-id/wwn-0x6ec2a72037894c00301288ac038600d4
- fullpath: /dev/disk/by-id/wwn-0x6ec2a72037894c00301288c62acb6efc
- fullpath: /dev/disk/by-id/wwn-0x6ec2a72037894c00301288e456c6d441
- fullpath: /dev/disk/by-id/wwn-0x6ec2a72037894c00301288f976534b4f
```
Aplicar y verificar:
```bash
kubectl apply -f cluster/ceph-cluster.yaml
kubectl -n rook-ceph get pods
```
> Instala el **toolbox** para diagnósticos: `kubectl -n rook-ceph apply -f rook/deploy/examples/toolbox.yaml`
---
## 6) Pool RBD inicial (replica **4** sobre **hosts**) + StorageClass
> Con 2 sites (A/B) y **sin** árbitro, **no** uses `failureDomain: zone` con `size: 4` o las PGs quedarán *undersized*. Empezamos con **`host`** y, cuando activemos **stretch**, pasaremos a `zone`.
`pools/ceph-blockpool-rbd.yaml`:
```yaml
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: rbd-2x2-sites
namespace: rook-ceph
spec:
failureDomain: host
replicated:
size: 4
```
`storageclasses/rbd.yaml`:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ceph-rbd
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
clusterID: rook-ceph
pool: rbd-2x2-sites
imageFormat: "2"
imageFeatures: layering
csi.storage.k8s.io/fstype: ext4
csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions: ["discard"]
```
Aplicar:
```bash
kubectl apply -f pools/ceph-blockpool-rbd.yaml
kubectl apply -f storageclasses/rbd.yaml
kubectl get sc
```
> Si creaste el pool inicialmente con `failureDomain: zone` y ves `active+undersized`, crea y asigna una **CRUSH rule** a host:
>
> ```bash
> kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- bash -lc '
> set -e
> ceph osd crush rule create-replicated rbd-4x-host default host || true
> ceph osd pool set rbd-2x2-sites crush_rule rbd-4x-host
> ceph osd pool get rbd-2x2-sites crush_rule
> '
> ```
---
## 7) Marcar OSDs como **SSD** (si Ceph los detecta como HDD por el HBA)
```bash
# Desde el toolbox
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- bash -lc '
for id in $(ceph osd ls); do ceph osd crush rm-device-class osd.$id || true; done
for id in $(ceph osd ls); do ceph osd crush set-device-class ssd osd.$id; done
ceph osd tree | egrep "zone|host|osd."
'
```
> Si más adelante creas un pool **soloSSD**, añade `spec.deviceClass: ssd` al `CephBlockPool`.
---
## 8) Dashboard por **Ingress** (NGINX) en `ceph.c2et.net`
> El dashboard del MGR escucha por defecto en **HTTP 7000**. Hacemos **TLS en el Ingress** (certmanager) y **HTTP** hacia el backend.
`ingress/dashboard.yaml`:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ceph-dashboard
namespace: rook-ceph
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
ingressClassName: nginx
tls:
- hosts: ["ceph.c2et.net"]
secretName: ceph-dashboard-tls
rules:
- host: ceph.c2et.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rook-ceph-mgr-dashboard
port:
number: 7000
```
Credenciales:
```bash
# Usuario por defecto
admin
# Contraseña generada
kubectl -n rook-ceph get secret rook-ceph-dashboard-password -o jsonpath="{.data.password}" | base64 -d; echo
# Cambiar contraseña (ejemplo)
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- bash -lc \
'echo -n "MiNuevaPass" | ceph dashboard ac-user-set-password admin -i -'
```
> Si prefieres **HTTPS 8443** también hacia el backend, habilita TLS en el dashboard de Ceph y cambia el Ingress a `backend-protocol: "HTTPS"` y puerto `8443` (y opcionalmente `proxy-ssl-verify: "off"`).
---
## 9) Prueba rápida de PVC
`tests/pvc-test.yaml`:
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-rbd
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
storageClassName: ceph-rbd
```
`tests/pod-test.yaml`:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: rbd-tester
spec:
containers:
- name: app
image: busybox
command: ["sh","-c","sleep 36000"]
volumeMounts:
- mountPath: /data
name: vol
volumes:
- name: vol
persistentVolumeClaim:
claimName: test-rbd
```
Aplicar y verificar:
```bash
kubectl apply -f tests/pvc-test.yaml
kubectl apply -f tests/pod-test.yaml
kubectl exec -it rbd-tester -- sh -c 'df -h /data && dd if=/dev/zero of=/data/test.bin bs=1M count=100 && ls -lh /data'
```
---
## 10) **Ampliación futura**: modo **Stretch** con **árbitro** (2 sites + arbiter)
Objetivo: supervivencia a la caída completa de un site y distribución **2+2** de réplicas entre `site-a` y `site-b`.
1. **Añade el nodo árbitro** y etiqueta:
```bash
kubectl label node <NODO_ARBITRO> topology.kubernetes.io/zone=arbiter --overwrite
```
2. **Actualiza el CephCluster** a stretch (5 MON):
```yaml
# parche del CephCluster (fragmento spec)
mon:
count: 5
allowMultiplePerNode: false
stretchCluster:
failureDomainLabel: topology.kubernetes.io/zone
subFailureDomain: host
zones:
- name: arbiter
arbiter: true
- name: site-a
- name: site-b
```
> Mantén `placement.osd` restringido a `site-a`/`site-b` para no crear OSDs en el árbitro.
3. **(Opcional recomendado)** Cambia el `CephBlockPool` para que el *failure domain* vuelva a **`zone`** con `size: 4` (2 por zona). Si prefieres asegurar la regla, crea una CRUSH rule específica y asígnala al pool.
```bash
# Ejemplo: regla por zona
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- bash -lc '
set -e
# Crea regla "rbd-4x-zone" (elige leaves de tipo zone)
ceph osd crush rule create-replicated rbd-4x-zone default zone || true
# Asigna la regla al pool y ajusta size
ceph osd pool set rbd-2x2-sites crush_rule rbd-4x-zone
ceph osd pool set rbd-2x2-sites size 4
ceph osd pool get rbd-2x2-sites crush_rule
'
```
> Tras el cambio a `zone`, Ceph reubica PGs para cumplir **2+2** entre `site-a` y `site-b`. Hazlo en ventana si ya hay mucho dato.
---
## 11) Troubleshooting rápido
* **PGs `active+undersized` con pool size=4**: ocurre si la regla CRUSH elige `zone` y solo hay 2 zonas (sin stretch). Solución: usa `failureDomain: host` o asigna una regla a `host` (sección 6) hasta activar stretch.
* **Ingress 503** al abrir el dashboard: el Service `rook-ceph-mgr-dashboard` usa **puerto 7000** (HTTP). Ajusta Ingress a `backend-protocol: "HTTP"` y puerto `7000`.
* **Cert TLS no emite**: revisa ClusterIssuer, DNS público hacia el Ingress y que el solver HTTP01 use `class: nginx`. Evita redirecciones que interfieran `/.well-known/acme-challenge/`.
---
## 12) Apéndice Comandos útiles
Estado general:
```bash
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph -s
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd tree
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph df
```
Ver pools y reglas:
```bash
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd pool ls detail
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd pool get rbd-2x2-sites crush_rule
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd crush rule dump rbd-4x-host
```
Dashboard:
```bash
kubectl -n rook-ceph get secret rook-ceph-dashboard-password -o jsonpath="{.data.password}" | base64 -d; echo
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- bash -lc 'echo -n "NuevaPass" | ceph dashboard ac-user-set-password admin -i -'
```
---
> **Resumen**: despliegas RookCeph con red de almacenamiento dedicada, discos por **byid**, pool RBD **size 4** sobre **host** para evitar PGs undersized sin árbitro, dashboard por **Ingress** (TLS en NGINX, backend HTTP:7000) y, cuando añadas el **árbitro**, pasas el clúster a **stretch** y el pool a **`failureDomain: zone`** con **2+2** por site.