Plan2026/09-decision-almacenamiento.md

626 lines
28 KiB
Markdown
Raw Permalink Normal View History

2026-03-14 19:33:00 +00:00
# Decision: Capa de almacenamiento y datos (punto 6)
## Contexto
Infraestructura de backup distribuida geográficamente:
- **PVE** (producción): Francia, datacenters OVH
- **PBS1** (backup tránsito): Alemania, OVH
- **PBS2** (backup archivo): Polonia, OVH (nuevo, por contratar)
ZFS se usa en PBS para crear datastores por cliente con quotas/reservations (modelo multiplicador 6x). En PVE se usa ZFS estándar para almacenamiento de VMs.
2 subcapas:
- 6.1 Gestión de ZFS a escala
- 6.2 Backup del backup (estrategia de retención y protección)
---
## 6.1 Gestión de ZFS a escala
### Uso actual de ZFS
ZFS se usa como herramienta de gestión de almacenamiento:
- **En PBS**: un dataset por cliente con quota (size×6) y reservation (size×5)
- **En PVE**: almacenamiento estándar de VMs
- Propiedades optimizadas: recordsize=1M, compression=lz4, atime=off
### Herramientas evaluadas
| Herramienta | Qué hace | Estado |
|-------------|----------|--------|
| **Sanoid/Syncoid** | Snapshots automáticos + replicación ZFS | **Descartado**: PBS gestiona snapshots y replicación nativamente |
| **zrepl** | Replicación ZFS continua | **Descartado**: PBS sync jobs cubren esto |
| **zfswatcher** | Monitoring dedicado ZFS con alertas | **Descartado**: node_exporter ya exporta métricas ZFS |
### DECISION 6.1 (confirmada)
**Solo monitorización.** No se necesitan herramientas adicionales para ZFS. PBS y PVE gestionan sus snapshots y replicación con sus mecanismos nativos.
Monitorización ya cubierta por el stack existente:
| Métrica | Fuente | Alerta |
|---------|--------|--------|
| Pool health (degraded, faulted) | node_exporter → VictoriaMetrics | ZFSPoolDegraded (critical, 1min) |
| Pool space > 85% | node_exporter → VictoriaMetrics | ZFSPoolSpaceHigh (warning, 30min) |
| Scrub errors | node_exporter → VictoriaMetrics | ZFSScrubErrors (warning) |
| IO latency | node_exporter → VictoriaMetrics | Dashboard Grafana |
| ARC hit rate | node_exporter → VictoriaMetrics | Dashboard Grafana |
Gestión ZFS via Ansible:
- Playbook `optimizar_zfs.sh` convertido a role Ansible
- Aplicar recordsize=1M, compression=lz4, atime=off a nuevos servidores
- Crear/modificar datasets de clientes (role pbs_customer)
---
## 6.2 Estrategia de backup: modelo de 3 capas geográficas
### El problema
- Los PVE hacen backup en PBS, pero si PBS se pierde, se pierden todos los backups
- Regla 3-2-1: 3 copias, 2 medios, 1 offsite
- OVH Estrasburgo ardió en 2021. Un solo DC no es suficiente.
- Si ransomware entra en PVE y PBS1, debe haber una copia inaccesible
### DECISION 6.2 (confirmada)
#### Arquitectura de 3 capas geográficas
```
Francia (OVH) Alemania (OVH) Polonia (OVH, nuevo)
┌──────────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ PVE │ │ PBS1 (tránsito) │ │ PBS2 (archivo) │
│ (producción) │──backup─►│ │◄─pull──│ │
│ │ WG │ Retención corta: │ WG │ Retención larga: │
│ Permisos: │ │ keep-last: 5 │ │ keep-daily: 7 │
│ - Backup │ │ keep-daily: 3 │ │ keep-weekly: 4 │
│ - Reader │ │ │ │ keep-monthly: 5 │
│ - NO delete │ │ ~8 snapshots/VM │ │ ~16 snapshots/VM │
│ │ │ │ │ │
│ │ │ Acceso: │ │ Acceso: │
│ │ │ - PVE escribe │ │ - MUY restrictivo │
│ │ │ - PBS2 lee (sync)│ │ - Solo PBS2 inicia │
│ │ │ - Técnicos leen │ │ conexiones │
│ │ │ │ │ - Invisible al resto │
└──────────────┘ └──────────────────┘ │ - Verify semanal │
└──────────────────────┘
```
#### Principio 1: PVE no puede borrar
El usuario PVE en PBS1 tiene permisos mínimos:
```
Rol en PBS1 para PVE:
- DatastoreBackup (puede crear backups)
- DatastoreReader (puede restaurar)
NO tiene:
- DatastoreAdmin (puede borrar, podar)
- DatastorePowerUser (puede podar)
```
Si ransomware compromete PVE, puede crear backups (inofensivo) y restaurar, pero **no puede borrar los existentes**.
#### Principio 2: PBS2 PULL de PBS1 (no push)
PBS2 es quien inicia la conexión hacia PBS1:
```
PBS2 tiene sync jobs:
- remote: PBS1 (Alemania)
- remote-store: cada datastore
- credenciales: usuario read-only en PBS1
- schedule: diario (o cada 12h)
PBS1 NO tiene:
- Credenciales de PBS2
- Conectividad hacia PBS2
- Conocimiento de que PBS2 existe
```
Si PBS1 es comprometido, el atacante no puede alcanzar PBS2 porque PBS1 no tiene credenciales ni ruta de red hacia PBS2.
#### Principio 3: PBS2 es invisible
```
Firewall PBS2 (Polonia):
INPUT:
- ACCEPT SSH desde IP bastion (solo emergencia)
- ACCEPT WG UDP desde peer de gestión
- DROP todo lo demás
OUTPUT:
- ACCEPT hacia PBS1:8007 (sync, por WG)
- ACCEPT DNS, NTP
- DROP todo lo demás
→ PBS2 no tiene puertos abiertos a internet
→ PBS2 no es alcanzable desde PVE ni desde PBS1
→ Solo PBS2 decide cuándo hablar con PBS1
```
#### Principio 4: PBS2 verifica integridad
PBS2 ejecuta verify jobs sobre los backups sincronizados:
```
PBS2 verify jobs:
- schedule: semanal
- outdated-after: 14 días
- ignore-verified: true
Si verify falla → alerta Telegram + ticket Zammad (crítico)
```
Valida que lo que llegó de PBS1 no está corrupto. Si un backup corrupto se sincroniza, lo sabéis antes de necesitarlo.
#### Conectividad entre DCs
| Ruta | Transporte | Cifrado |
|------|-----------|---------|
| PVE (Francia) → PBS1 (Alemania) | WireGuard sobre vRack cross-DC o internet | WG cifrado |
| PBS2 (Polonia) → PBS1 (Alemania) | WireGuard sobre vRack cross-DC o internet | WG cifrado |
OVH vRack puede extenderse entre DCs y países (sin coste extra si ya está contratado). Si no, WireGuard sobre internet funciona bien: el tráfico de sync es eficiente (PBS deduplica, solo transfiere chunks nuevos).
Sync inicial (primera vez): puede ser grande. Syncs diarios: solo incrementales, poco tráfico.
#### Retención detallada
```yaml
# PBS1 (Alemania) - TRÁNSITO
# Objetivo: buffer rápido, pocas copias, rota frecuente
prune-schedule: daily
keep-last: 5 # últimos 5 backups independiente de fecha
keep-daily: 3 # 3 días adicionales
# Total estimado: ~8 snapshots por VM
# Espacio: moderado (pocos días de retención)
# PBS2 (Polonia) - ARCHIVO
# Objetivo: retención profunda, recuperación ante desastre
prune-schedule: daily
keep-daily: 7 # última semana completa
keep-weekly: 4 # último mes en semanas
keep-monthly: 5 # últimos 5 meses
# Total estimado: ~16 snapshots por VM
# Espacio: mayor (meses de retención, pero PBS deduplica)
```
PBS1 y PBS2 aplican su política de retención **independientemente**. PBS1 puede borrar snapshots viejos sin afectar a PBS2 (PBS2 ya tiene su copia).
#### Escenarios de desastre y procedimientos de recuperación
| Desastre | Impacto | Recuperación |
|----------|---------|-------------|
| Fallo HW en PVE (Francia) | VMs caídas | Restaurar desde PBS1 Alemania (minutos/horas) |
| Fallo HW en PBS1 (Alemania) | Backups de tránsito perdidos | PBS2 Polonia tiene todo con retención de meses |
| Ransomware en PVE | VMs cifradas | PVE no puede borrar PBS1. Restaurar desde PBS1. |
| Ransomware en PVE + PBS1 | VMs + backups tránsito | PBS2 inaccesible desde ambos. Restaurar desde PBS2. |
| Ransomware en PVE + PBS1 + PBS2 | Catastrófico | Extremadamente improbable: PBS2 no es alcanzable desde los otros |
| DC Francia cae (tipo OVH Estrasburgo 2021) | PVE caído | PBS1 (Alemania) + PBS2 (Polonia) intactos |
| DC Francia + Alemania caen | PVE + PBS1 caídos | PBS2 (Polonia) intacto |
| Error humano borra datastore en PBS1 | Backups cliente borrados | PBS2 tiene copias con retención de meses |
| Fallo HW en PBS2 (Polonia) | Copias históricas perdidas | Solo quedan copias de tránsito en PBS1 (retención corta). Opción PBS3 para clientes críticos. |
#### Procedimiento si PBS1 se pierde
1. Contratar/levantar un PBS1 nuevo (Alemania)
2. Crear datasets ZFS, datastores y usuarios (playbook Ansible `pbs_customer`)
3. PVE empieza a hacer backups al PBS1 nuevo → se rellena con copias frescas
4. Configurar WG peer en PBS1 nuevo para que PBS2 pueda conectar
5. PBS2 empieza a sincronizar (pull) del PBS1 nuevo cuando hay datos
**Hueco**: ~1 día sin capa de tránsito. Las VMs siguen funcionando. PBS2 conserva las copias históricas intactas. El riesgo real es perder una VM durante ese hueco (sin backup disponible en PBS1), pero PBS2 tiene la última sincronización previa al fallo.
#### Procedimiento si PBS2 se pierde
1. Se pierden las copias históricas (retención larga: semanas/meses)
2. PBS1 sigue funcionando con retención corta (3d/5 copias)
3. PVE sigue haciendo backups normalmente a PBS1
4. Contratar/levantar un PBS2 nuevo (Polonia) y configurar sync desde PBS1
**Impacto**: durante el período sin PBS2, solo hay 1 capa de backup (PBS1, retención corta). Si PBS1 también falla durante ese periodo → datos perdidos.
#### Opción PBS3 para clientes críticos (decisión de producto)
Clientes que requieran máxima protección pueden contratar una capa adicional:
```
PBS3 (opcional por cliente):
- Ubicación: otro país/proveedor (ej. Hetzner Finlandia, Scaleway París)
- Modelo: PULL de PBS2 (misma lógica)
- Retención: igual o más larga que PBS2
- Coste: repercutido al cliente
```
Esto es una **decisión de producto/SLA**, no técnica:
- El producto base incluye 2 capas (PBS1 tránsito + PBS2 archivo)
- El producto premium añade PBS3 como 3ª capa
- Documentar claramente en la definición de producto los riesgos de cada nivel
- El cliente decide su nivel de protección según su criticidad y presupuesto
#### Monitorización del modelo de backup
| Alerta | Condición | Severidad |
|--------|-----------|-----------|
| PBSBackupOld48h | VM sin backup en 48h en PBS1 | Critical |
| PBSSyncFailed | Sync PBS2←PBS1 falla | Critical |
| PBSSyncOld48h | PBS2 no sincroniza en 48h | Critical |
| PBSVerifyErrors | Verify en PBS2 encuentra errores | Critical |
| PBS1DatastoreSpace90 | Datastore PBS1 > 90% quota | Warning |
| PBS2DatastoreSpace90 | Datastore PBS2 > 90% | Warning |
---
## 6.3 Backup de configuraciones PVE/PBS a git
### El problema
Si un PVE o PBS se pierde, los datos de VMs están en PBS (backup). Pero la **configuración del propio nodo** (qué VMs había, cómo estaban configuradas, qué datastores, qué usuarios, qué red) está solo en el nodo. ICSManager sabe qué clientes hay, pero no tiene el detalle de cada `.conf` de VM ni cada regla de firewall.
Reconstruir un nodo sin tener su config es posible pero lento: hay que averiguar qué había, preguntar, deducir. Tenerla guardada en git permite saber exactamente qué había y reconstruir rápido.
### DECISION 6.3 (confirmada)
**Copia diaria de directorios de configuración a un repo en Forgejo.**
Son ficheros de texto, poca información (KB), y git muestra exactamente qué cambió y cuándo.
#### Qué se copia
| Servidor | Ruta | Contenido |
|----------|------|-----------|
| **PVE** | `/etc/pve/qemu-server/` | Configs de VMs (.conf: disco, RAM, red, opciones) |
| **PVE** | `/etc/pve/lxc/` | Configs de contenedores |
| **PVE** | `/etc/pve/storage.cfg` | Definiciones de almacenamiento |
| **PVE** | `/etc/pve/user.cfg` | Usuarios y permisos |
| **PVE** | `/etc/pve/datacenter.cfg` | Configuración del datacenter |
| **PVE** | `/etc/pve/firewall/` | Reglas de firewall por nodo y VM |
| **PVE** | `/etc/pve/corosync.conf` | Configuración del cluster |
| **PVE** | `/etc/network/interfaces` | Configuración de red (vmbr, vRack, VLANs) |
| **PBS** | `/etc/proxmox-backup/datastore.cfg` | Definiciones de datastores |
| **PBS** | `/etc/proxmox-backup/user.cfg` | Usuarios PBS |
| **PBS** | `/etc/proxmox-backup/acl.cfg` | Permisos por datastore |
| **PBS** | `/etc/proxmox-backup/remote.cfg` | Remotos configurados |
| **PBS** | `/etc/proxmox-backup/sync.cfg` | Sync jobs |
| **PBS** | `/etc/proxmox-backup/verify.cfg` | Verify jobs |
| **Ambos** | `/etc/wireguard/` | Configs WG + claves |
| **Ambos** | ZFS properties (exportadas) | Quotas, reservations, recordsize por dataset |
#### Implementación
Playbook Ansible ejecutado diariamente desde Semaphore:
```yaml
# playbook: backup-configs.yml
# Semaphore: diario, 06:00
- hosts: all_pve:all_pbs
tasks:
- name: Exportar ZFS properties
shell: "zfs get -Hp quota,reservation,recordsize,compression -r pool9 2>/dev/null || true"
register: zfs_props
changed_when: false
- name: Guardar ZFS properties en fichero temporal
copy:
content: "{{ zfs_props.stdout }}"
dest: /tmp/zfs-properties.txt
changed_when: false
- name: Recoger configs PVE
fetch:
src: "{{ item }}"
dest: "backup-configs/{{ inventory_hostname }}/"
flat: no
loop:
- /etc/pve/storage.cfg
- /etc/pve/user.cfg
- /etc/pve/datacenter.cfg
- /etc/pve/corosync.conf
- /etc/network/interfaces
ignore_errors: true
when: "'pve' in group_names"
- name: Recoger configs de VMs PVE
shell: "ls /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf /etc/pve/firewall/*.fw 2>/dev/null || true"
register: vm_configs
changed_when: false
when: "'pve' in group_names"
- name: Fetch configs de VMs
fetch:
src: "{{ item }}"
dest: "backup-configs/{{ inventory_hostname }}/"
flat: no
loop: "{{ vm_configs.stdout_lines | default([]) }}"
when: "'pve' in group_names"
- name: Recoger configs PBS
fetch:
src: "{{ item }}"
dest: "backup-configs/{{ inventory_hostname }}/"
flat: no
loop:
- /etc/proxmox-backup/datastore.cfg
- /etc/proxmox-backup/user.cfg
- /etc/proxmox-backup/acl.cfg
- /etc/proxmox-backup/remote.cfg
- /etc/proxmox-backup/sync.cfg
- /etc/proxmox-backup/verify.cfg
ignore_errors: true
when: "'pbs' in group_names"
- name: Fetch WG configs
fetch:
src: "{{ item }}"
dest: "backup-configs/{{ inventory_hostname }}/"
flat: no
loop:
- /etc/wireguard/
ignore_errors: true
- name: Fetch ZFS properties
fetch:
src: /tmp/zfs-properties.txt
dest: "backup-configs/{{ inventory_hostname }}/"
flat: no
- hosts: localhost
tasks:
- name: Commit y push al repo Forgejo
shell: |
cd backup-configs/
git add -A
git diff --cached --quiet || git commit -m "Config backup {{ ansible_date_time.date }}"
git push
```
#### Resultado en Forgejo
```
infra/backup-configs/
├── pve-node1/
│ ├── etc/pve/qemu-server/
│ │ ├── 100.conf ← VM cliente ACME
│ │ ├── 101.conf ← VM cliente BETA
│ │ └── ...
│ ├── etc/pve/storage.cfg
│ ├── etc/pve/user.cfg
│ ├── etc/network/interfaces
│ └── tmp/zfs-properties.txt
├── pve-node2/
│ └── ...
├── pbs-alemania/
│ ├── etc/proxmox-backup/datastore.cfg
│ ├── etc/proxmox-backup/sync.cfg
│ └── tmp/zfs-properties.txt
└── pbs-polonia/
└── ...
```
Cada commit muestra el diff: "se añadió VM 105, se cambió la RAM de la 100 de 4GB a 8GB, se creó datastore nuevo".
#### Valor operativo
| Escenario | Sin backup de configs | Con backup en git |
|-----------|----------------------|-------------------|
| PVE se pierde | "¿Qué VMs había? ¿Con qué config?" | `git show pve-node1/etc/pve/qemu-server/` → lista exacta |
| Alguien cambia config de VM | Nadie se entera | Diff en el commit diario |
| Técnico nuevo necesita ver infra | SSH a cada nodo | Lee el repo en Forgejo |
| Auditoría: "¿cuándo se cambió esto?" | No hay registro | `git log --follow fichero.conf` |
| Reconstruir nodo | Horas/días adivinando | Minutos leyendo configs exactas |
#### Complemento con Ansible (evolución)
Fase 1 (día 1): backup de configs a git = **foto de lo que hay**
Fase 2 (mes 1-2): Ansible gestiona configs = **declaración de lo que debe haber**
Ambos conviven: Ansible aplica la config deseada, el backup a git verifica que lo real coincide con lo declarado. Si alguien cambia algo manualmente, el diff del backup lo delata.
---
## 6.4 Réplica de VMs de gestión a oficina (contingencia fuera de OVH)
### El problema
Toda la infraestructura de gestión (monitoring, seguridad, SSOT, SSO) vive en un único servidor en OVH. Si OVH pierde ese servidor, nos quedamos ciegos: sin métricas, sin logs, sin alertas, sin Netbox, sin Authentik. Los datos de clientes están protegidos por 3 capas geográficas (6.2), pero la infra que nos da visibilidad no.
### DECISION 6.4 (confirmada)
**Replicación ZFS incremental de las VMs críticas de gestión al servidor de oficina (Xeon 96GB existente).**
Coste: cero (hardware que ya existe). Las VMs están apagadas en oficina, solo se encienden en contingencia.
#### Qué se replica
| VM | Replicar | Razón |
|----|----------|-------|
| **Monitoring** | Sí | Sin ella: sin Grafana, sin métricas, sin logs, ceguera total |
| **Wazuh** | Sí | Sin ella: sin detección de seguridad, sin FIM |
| **Servicios** | Sí | Netbox (SSOT), Authentik (SSO), Semaphore, Outline |
| **Bastion** | No | Sin estado, se recrea en minutos con Ansible |
| **WG Hub A/B** | No | Sin estado, configs en git (6.3), se recrean |
#### Arquitectura
```
OVH (servidor gestión) Oficina (Xeon 96GB, existente)
┌──────────────────────┐ ┌──────────────────────┐
│ PVE gestión │ │ PVE contingencia │
│ pool: local-zfs │ ZFS send/recv │ pool: local-zfs │
│ │ incremental │ │
│ VM Monitoring ──────│──── WG ────────►│ VM Monitoring (rpl) │
│ VM Wazuh ───────────│──── WG ────────►│ VM Wazuh (rpl) │
│ VM Servicios ───────│──── WG ────────►│ VM Servicios (rpl) │
│ │ │ │
│ │ Cada 4h │ VMs apagadas │
│ │ ~1-5 GB/sync │ Solo encienden en │
│ │ cifrado por WG │ contingencia │
└──────────────────────┘ └──────────────────────┘
```
#### Implementación
Playbook Ansible ejecutado desde Semaphore cada 4 horas:
```yaml
# playbook: replicate-to-office.yml
# Semaphore: cada 4h
- hosts: pve_gestion
vars:
office_host: "oficina-pve.mgmt.vpn6.com.es" # por WG
vms_to_replicate:
- vm-monitoring
- vm-wazuh
- vm-servicios
tasks:
- name: Crear snapshot incremental
shell: "zfs snapshot local-zfs/{{ item }}@replicate-$(date +%Y%m%d-%H%M)"
loop: "{{ vms_to_replicate }}"
- name: Enviar incremental a oficina
shell: |
LATEST=$(zfs list -t snapshot -o name -s creation -r local-zfs/{{ item }} | grep replicate | tail -1)
PREV=$(zfs list -t snapshot -o name -s creation -r local-zfs/{{ item }} | grep replicate | tail -2 | head -1)
if [ "$PREV" != "$LATEST" ]; then
zfs send -i "$PREV" "$LATEST" | ssh {{ office_host }} zfs recv -F tank/{{ item }}
else
zfs send "$LATEST" | ssh {{ office_host }} zfs recv -F tank/{{ item }}
fi
loop: "{{ vms_to_replicate }}"
- name: Rotar snapshots antiguos (mantener últimos 12 = 48h)
shell: |
zfs list -t snapshot -o name -s creation -r local-zfs/{{ item }} | grep replicate | head -n -12 | xargs -r -n1 zfs destroy
loop: "{{ vms_to_replicate }}"
```
#### Ancho de banda
| Parámetro | Valor |
|-----------|-------|
| Tamaño total VMs a replicar | ~400-700 GB |
| Sync inicial (primera vez) | Grande: programar de noche o seed con disco físico |
| Sync incremental cada 4h | ~1-5 GB (solo bloques cambiados) |
| Ancho de banda sostenido | ~5-10 Mbps (factible con fibra de oficina) |
| Transporte | WireGuard cifrado (red de gestión) |
#### Plan de contingencia
| Paso | Acción | Tiempo |
|------|--------|--------|
| 1 | Detectar que el servidor OVH está caído | Minutos (Uptime Kuma externo, ping, o aviso OVH) |
| 2 | Encender VMs réplica en PVE oficina | 2 minutos |
| 3 | Actualizar DNS/WG para apuntar a oficina | 5 minutos |
| 4 | Monitoring + Wazuh + Servicios operativos | ~10 minutos total |
**No es HA automático** (no hace falta). Es un plan B manual que se activa en emergencia. Runbook documentado en Outline.
#### Qué NO se replica aquí
- VMs de clientes (producción): protegidas por 3 capas geográficas PBS (6.2)
- Configs de PVE/PBS: protegidas por backup a git (6.3)
- WG Hub / Bastion: sin estado, se recrean con Ansible
Esta capa protege específicamente la **infraestructura de gestión** para no quedarse ciego si OVH tiene un fallo mayor.
#### Monitorización
| Alerta | Condición | Severidad |
|--------|-----------|-----------|
| ReplicaOfficeOld8h | Última réplica a oficina > 8h | Warning |
| ReplicaOfficeFailed | Playbook de réplica falla | Critical |
---
## Resumen Capa 6 completa (DECISIONES FINALES)
```
┌─────────────────────────────────────────────────────┐
│ CAPA 6: ALMACENAMIENTO Y DATOS │
├─────────────────────────────────────────────────────┤
│ │
│ 6.1 ZFS (confirmada) │
│ ┌───────────────────────────────────┐ │
│ │ Solo monitorización │ │
│ │ - node_exporter → métricas ZFS │ │
│ │ - Alertas: degraded, space, scrub│ │
│ │ - Ansible: optimización nuevos │ │
│ │ Sanoid/Syncoid/zrepl: DESCARTADO │ │
│ │ (PBS gestiona nativamente) │ │
│ └───────────────────────────────────┘ │
│ │
│ 6.2 BACKUP 3 CAPAS (confirmada) │
│ ┌───────────────────────────────────┐ │
│ │ PVE (Francia) │ │
│ │ → backup a PBS1 (Alemania) │ │
│ │ → permisos: solo Backup+Reader │ │
│ │ → NO puede borrar │ │
│ │ │ │
│ │ PBS1 (Alemania) - TRÁNSITO │ │
│ │ → retención corta (3d/5 copias) │ │
│ │ → PVE escribe, PBS2 lee │ │
│ │ │ │
│ │ PBS2 (Polonia) - ARCHIVO │ │
│ │ → retención larga (7d/4w/5m) │ │
│ │ → PULL de PBS1 (no push) │ │
│ │ → Invisible: no alcanzable │ │
│ │ → Verify semanal │ │
│ │ │ │
│ │ Conectividad: WG sobre vRack │ │
│ │ cross-DC o internet │ │
│ │ │ │
│ │ 3 países, 3 jurisdicciones │ │
│ │ Francia → Alemania → Polonia │ │
│ │ │ │
│ │ Recuperación PBS1 perdido: │ │
│ │ → Nuevo PBS1 + PVE replica │ │
│ │ → PBS2 pull cuando hay datos │ │
│ │ → Hueco ~1 día │ │
│ │ │ │
│ │ Recuperación PBS2 perdido: │ │
│ │ → Copias históricas perdidas │ │
│ │ → PBS1 sigue con retención corta│ │
│ │ → Opción PBS3 para clientes │ │
│ │ críticos (decisión de producto)│ │
│ └───────────────────────────────────┘ │
│ │
│ 6.3 CONFIGS A GIT (confirmada) │
│ ┌───────────────────────────────────┐ │
│ │ Backup diario de /etc/pve/ y │ │
│ │ /etc/proxmox-backup/ a Forgejo │ │
│ │ - Configs VMs, storage, users │ │
│ │ - Red, firewall, WG, ZFS props │ │
│ │ - Playbook Ansible + Semaphore │ │
│ │ - Cada commit = diff de cambios │ │
│ │ - Coste: 0 (texto, KB, Forgejo │ │
│ │ ya existe) │ │
│ └───────────────────────────────────┘ │
│ │
│ 6.4 RÉPLICA OFICINA (confirmada) │
│ ┌───────────────────────────────────┐ │
│ │ ZFS send/recv incremental cada │ │
│ │ 4h a servidor oficina (Xeon 96GB)│ │
│ │ - VMs: Monitoring, Wazuh, │ │
│ │ Servicios │ │
│ │ - VMs apagadas, solo emergencia │ │
│ │ - Transporte: WG cifrado │ │
│ │ - Contingencia: ~10 min levant. │ │
│ │ - Coste: 0 (HW ya existe) │ │
│ │ - Protege: infra de gestión │ │
│ │ (no datos de clientes) │ │
│ └───────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
Recursos Capa 6:
- PBS1: ya existente (Alemania)
- PBS2: servidor nuevo por contratar (Polonia)
- Dimensionado según volumen de datos × retención
- Solo necesita disco (CPU/RAM mínimos, PBS es eficiente)
- PBS3 (opcional): por cliente premium, otro proveedor/país
- ZFS monitoring: ya cubierto por node_exporter (0 extra)
- Réplica oficina: servidor Xeon 96GB existente (0 coste)
```