Respaldo nativo para Apache CloudStack. KVM, XenServer, VMware bajo CloudStack — incremental real vía CBT del hypervisor, sin agente, captura vía CloudStack API.
- Sin agente, API-driven — autenticación por API key, captura coordinada con el hypervisor.
- Incremental real — usa CBT nativo del hypervisor (KVM dirty bitmap, VMware CBT, Xen CBT).
- Multi-zone aware — respaldo paralelo cross-zone, restore a otra zona o cluster.
- Snapshots Primary Storage coordinados — Ceph, NFS, SolidFire soportados.
- Restore granular de archivo sin levantar la VM completa.
Producción en 30 días, al menos 50% más barato. Trial gratuito, RPMs y DEBs firmados, soporte directo con Heitor Faria. Reemplace su licencia Veeam, Commvault o Bacula Enterprise sin romper la ventana nocturna.
Solicitar trial gratuito de 30 días →
Índice
- Resumen Ejecutivo
- Contexto y Oportunidad de Mercado
- Casos de Uso
- Arquitectura Técnica
- Funcionalidades Detalladas
- Compatibilidad y Requisitos
- Guía de Instalación
- Configuración y Sizing
- Manual de Uso
- Tablas de Parámetros
- Ejemplos de Configuración de FileSet
- Rendimiento y Benchmarks
- Seguridad
- Monitoreo y Observabilidad
- Solución de Problemas
- Comparativa con Competidores
- Hoja de Ruta
- Referencias
1. Resumen Ejecutivo
O PodHeitor CloudStack Backup, Replication and Conversion Plugin for Bacula es la solución definitiva para backup y recuperación de máquinas virtuales en entornos Apache CloudStack, integrada de forma nativa con PodHeitor Backup. Desarrollado en Rust por Heitor Faria, el plugin ofrece funcionalidades que superan incluso a las soluciones pagas del mercado, a un costo sustancialmente menor.
¿Qué es?
Un plugin para el File Daemon (FD) de PodHeitor Backup que permite:
- Backup completo e incremental de VMs CloudStack con seguimiento de bloques modificados (CBT) via QEMU dirty bitmaps
- Replicación continua hacia sitio de DR con transporte cifrado AES-256-GCM
- Restauración cruzada entre hypervisors: CloudStack → Proxmox VE, Hyper-V, VMware vSphere
- Conversión de formatos de disco: qcow2 ↔ vhdx ↔ vmdk ↔ raw
¿Por qué es diferente?
┌─────────────────────────────────────────────────────────────────────┐
│ COMPARATIVO RÁPIDO │
├──────────────────────────┬──────────────────┬────────────────────────┤
│ Funcionalidade │ Bacula Enterprise│ PodHeitor CloudStack │
├──────────────────────────┼──────────────────┼────────────────────────┤
│ Backup Incremental CBT │ Snapshot diff │ Block-level dirty bitmap│
│ Redução de janela backup │ ~30% │ 90%+ │
│ Replicação DR │ Não disponível │ Bitmap-push contínuo │
│ Cross-restore │ Não disponível │ Proxmox/Hyper-V/VMware │
│ Métricas Prometheus │ Não disponível │ Endpoint nativo │
│ Logging JSON estruturado │ Não disponível │ Nativo │
│ Custo (vs Enterprise) │ 100% (base) │ Significativamente menor│
└──────────────────────────┴──────────────────┴────────────────────────┘
Propuesta de Valor
5. Funcionalidades Detalladas
5.1 Backup Full
Flujo completo:
1. Autenticar na API CloudStack (HMAC-SHA1)
2. Listar VMs que correspondem ao padrão vm=
3. Para cada VM:
a. Verificar estado (running/stopped)
b. Se running + quiesce=true: freeze via qemu-guest-agent
c. createSnapshot via API CloudStack
d. Aguardar conclusão do snapshot (polling assíncrono)
e. Se running: thaw via qemu-guest-agent
f. Descobrir path do arquivo de snapshot no KVM host
g. Iniciar qemu-nbd --read-only --snapshot no KVM host (via SSH)
h. Para cada bloco do disco:
- Ler via NBD com read-ahead de 4 blocos
- Detectar blocos zero via SIMD
- Calcular xxHash64
- Compor header do bloco no stream
- Comprimir com zstd (se compress=true)
- Enviar para o Bacula FD
i. Salvar hash DB atualizado
j. Limpar snapshot CloudStack
k. Registrar checkpoint no working_dir
4. Retornar estatísticas para o Bacula
5.2 Backup Incremental
Flujo:
1. Verificar se existe backup anterior (hash DB + QEMU bitmap)
2. Para cada VM:
a. Se não existe bitmap podheitor-cbt: criar via QMP
b. Snapshot do estado atual
c. Consultar bitmap sujo via QMP
d. Para cada bloco sujo:
- Ler bloco atual via NBD
- Calcular xxHash64
- Comparar com hash anterior
- Se mudou: incluir no stream
- Se não mudou: skip (falso positivo)
e. Atualizar hash DB
f. Reset do bitmap (preparar para próximo incremental)
3. Resultado: apenas blocos realmente modificados
Ahorro típico:
- VM 100 GB con 5% de cambio diario: 5 GB via incremental vs 100 GB full
- Con compresión zstd level 3: ~2,5 GB transferidos
- Reducción de 97,5% en el volumen de datos transferidos
5.3 Replicación Continua (DR)
Modos:
| Modo | Descripción | Uso |
|---|---|---|
seed |
Sincronización inicial completa | Configuración inicial del DR |
bitmap_push |
Envío continuo de deltas | Operación normal de DR |
receiver |
Receptor en el sitio DR | Ejecutado en el sitio DR |
daemon |
Sender continuo (fuera de jobs Bacula) | DR autónomo |
failover_exec |
Promover DR a primario | Ejecutado ante evento de desastre |
failback_exec |
Re-sync tras failover | Retorno al sitio principal |
reprotect |
Establecer replicación en sentido inverso | Tras failover |
replication_status |
Consultar salud del par de replicación | Monitoreo |
Protocolo de replicación:
Site Principal (Sender) Site DR (Receiver)
│ │
│── TCP connect ──────────────────► │
│── PSK handshake (HKDF derivation) ►│
│── AES-256-GCM channel established ►│
│ │
LOOP cada cycle_interval segundos: │
│ │
│── query dirty bitmap ─────────────►│ (local QMP)
│── for each dirty block: │
│ read via NBD │
│ xxHash64 │
│ zstd compress │
│── send compressed deltas ─────────►│
│ │── apply to replica disk
│◄─ ACK ─────────────────────────────│
│── reset bitmap for next cycle ─────│ (local QMP)
│ │
Ancho de banda típico en operación:
- Servidor DB activo (5% cambio/hora): ~500 MB/h
- Servidor web estático (0,5% cambio/hora): ~50 MB/h
- VM idle (0,01% cambio/hora): ~1 MB/h
5.4 Restauración
Restauración en CloudStack (where=/)
1. Ler stream de backup do Bacula
2. Reconstruir disco raw em working_dir
3. Converter raw → qcow2 via qemu-img
4. Iniciar servidor HTTP temporário (python3 http.server)
5. registerTemplate com directdownload=true na API CloudStack
6. deployVirtualMachine com o template registrado
7. Se start_vm=true: ligar VM
8. Limpar arquivos temporários
Restauración hacia filesystem (where=/path)
1. Ler stream de backup do Bacula
2. Extrair cada disco como arquivo raw em /path/vm-<name>/disk-<N>.raw
3. Extrair metadata da VM como vm-<name>/metadata.json
4. Opcionalmente: converter para qcow2 se cross_restore=proxmox
5.5 Restauración Cruzada (Cross-Restore)
| Target | Formato de Salida | Adaptación de Guest OS |
|---|---|---|
proxmox / pve |
.qcow2 |
Drivers VirtIO para disco, red y balloon |
hyperv / hyper-v |
.vhdx |
Drivers Hyper-V (storvsc, netvsc, vmbus) |
vmware / vsphere |
.vmdk |
VMware Tools (pvscsi, vmxnet3) |
Proceso de guest adaptation:
Para cada VM × cada target:
1. Montar disco restored via NBD loopback
2. Detectar OS (leitura de /etc/os-release ou Windows registry)
3. Se Linux:
- Adicionar módulos virtio-blk, virtio-net, virtio-scsi ao initrd/initramfs
- Atualizar /etc/dracut.conf.d/ ou update-initramfs
- Ajustar /etc/fstab para novos device paths se necessário
4. Se Windows:
- Injetar drivers .inf para target hypervisor no disco
- Adicionar chave de registry para boot offline com novo driver
5. Converter disco para formato target
6. Transferir para target hypervisor via API ou SCP
5.6 Conversión de Formatos de Disco
Formatos suportados:
raw → qcow2 (compressão interna, sparse)
raw → vhdx (header CRC32C validado, Hyper-V compatível)
raw → vmdk (streamOptimized, ESXi compatível)
qcow2 → raw (para staging)
vhdx → raw (para staging)
vmdk → raw (para staging)
Características:
- Streaming: sem buffering completo em RAM
- VHDX: CRC32C Castagnoli calculado corretamente (header sector)
- VMDK: formato streamOptimized para transferência eficiente
- Sparse: blocos zero não ocupam espaço no arquivo de saída
6. Compatibilidad y Requisitos
Matriz de Compatibilidad Bacula × CloudStack
| PodHeitor Backup | CloudStack | KVM host OS | Estado |
|---|---|---|---|
| 15.0.3 | 4.20 | Ubuntu 22.04 | ✅ Validado completo |
| 15.0.3 | 4.19 | Oracle Linux 9.6 | ✅ Validado baseline |
| 13.x | 4.19 | Oracle Linux 9.6 | ⚠ Funcional, sin validación CI |
| 11.x | 4.16-4.18 | Oracle Linux 8 | ⚠ Requiere rebuild de headers |
| 15.0.3 | 4.18 | CentOS 7 | ❌ No recomendado |
SO del FD Host (donde bacula-fd + plugin se ejecutan)
| Distro | Build | Paquete | Estado |
|---|---|---|---|
| Ubuntu 22.04 Jammy | ✅ | .deb | Primario, validado en CI |
| Ubuntu 24.04 Noble | 🟡 | .deb | Build verificado |
| Debian 12 Bookworm | 🟡 | .deb | Build verificado |
| Rocky Linux 9 | ✅ | .rpm | Validado en CI |
| Oracle Linux 9.x | ✅ | .rpm | Entorno de desarrollo primario |
| RHEL 9 | ✅ | .rpm | Mismo binario que Rocky/OL 9 |
| AlmaLinux 9 | 🟡 | .rpm | Compatible binariamente |
| CentOS Stream 9 | 🟡 | .rpm | Se espera funcionar |
Leyenda: ✅ Validado en CI · 🟡 Se espera funcionar · ❌ No funciona
SO del KVM Host (donde el plugin hace SSH)
| Distro | Estado |
|---|---|
| Ubuntu 22.04 | ✅ |
| Rocky Linux 9 | ✅ |
| Oracle Linux 9 | ✅ |
| CentOS 8 Stream | 🟡 |
Dependencias en el KVM host:
| Binario | Versión Mínima | Paquete (Debian) | Paquete (RHEL) |
|---|---|---|---|
qemu-nbd |
6.2+ | qemu-utils |
qemu-img |
qemu-img |
6.2+ | qemu-utils |
qemu-img |
socat |
any | socat |
socat |
virsh |
any | libvirt-clients |
libvirt-client |
sh, find, grep, awk |
any | (estándar) | (estándar) |
Requisitos del Entorno Bacula
| Item | Mínimo | Recomendado |
|---|---|---|
| PodHeitor Backup | 15.0.3 | 15.0.3 o posterior |
| CPUs en el FD host | 2 | 4+ |
| RAM en el FD host | 1 GB | 4 GB |
| Disco (working_dir) | 50 GB | 500 GB+ |
| Red FD → CloudStack API | 10 Mbps | 1 Gbps |
| Red FD → KVM hosts | 1 Gbps | 10 Gbps |
| Red entre sitios (replicación) | 10 Mbps | 1 Gbps |
CloudStack API
| Requisito | Valor |
|---|---|
| Versión mínima | 4.16 |
| Usuario API | Admin-level o role personalizado |
| Permisos requeridos | createSnapshot, listVMs, listVolumes, registerTemplate, deployVM, extractVolume |
| TLS | Recomendado; sin TLS aceptado con verify_ssl=false |
| Versiones probadas | 4.19 (OL9), 4.20 (Ubuntu 22.04) |
7. Guía de Instalación
7.1 Vía Paquetes (Recomendado)
# ── Debian / Ubuntu ──────────────────────────────────────────────────
# Copie o .deb para o servidor e instale:
apt install ./podheitor-cloudstack-plugin_2.0.0-1_amd64.deb
systemctl restart bacula-fd
# ── RHEL / Rocky / Oracle Linux ──────────────────────────────────────
dnf install ./podheitor-cloudstack-plugin-2.0.0-1.el9.x86_64.rpm
systemctl restart bacula-fd
7.2
7.3 Configuración del FD Host
Layout A — FD Compartido (simple)
# ── Debian / Ubuntu ──────────────────────────────────────────────────
# Copie o .deb para o servidor e instale:
apt install ./podheitor-cloudstack-plugin_2.0.0-1_amd64.deb
systemctl restart bacula-fd
# ── RHEL / Rocky / Oracle Linux ──────────────────────────────────────
dnf install ./podheitor-cloudstack-plugin-2.0.0-1.el9.x86_64.rpm
systemctl restart bacula-fd7.3 Configuración del FD Host
Layout A — FD Compartido (simple)
El plugin coexiste con otros plugins dentro del bacula-fd existente. Adecuado para entornos pequeños o pruebas.
# Apenas adicionar ao bacula-fd.conf existente:
FileDaemon {
...
Plugin Directory = /opt/bacula/plugins
...
}
Layout B — FD Dedicado (producción recomendada)
Instalar un bacula-fd dedicado en el servidor de gestión de CloudStack o en cualquier host Linux con acceso SSH a los KVM hosts.
# 1. Instalar bacula-fd 15.0.3
# (via tarball de https://www.bacula.org/downloads/ ou repositório)
# 2. Instalar o plugin
apt install ./podheitor-cloudstack-plugin_1.0.0_amd64.deb
# 3. Configurar bacula-fd.conf
cat > /opt/bacula/etc/bacula-fd.conf << 'EOF'
Director {
Name = meu-director
Password = "GERAR-UMA-SENHA-FORTE-ALEATORIA"
}
FileDaemon {
Name = cloudstack-fd
WorkingDirectory = /opt/bacula/working
Pid Directory = /opt/bacula/working
Maximum Concurrent Jobs = 20
Plugin Directory = /opt/bacula/plugins
FDAddresses = { ip = { addr = 0.0.0.0; port = 9102 } }
}
Messages {
Name = Standard
director = meu-director = all, !skipped, !restored
}
EOF
# 4. Criar diretórios de trabalho
install -d -m 0750 -o bacula -g bacula
/var/lib/bacula/cloudstack
/var/lib/bacula/cloudstack/hashes
# 5. Iniciar o serviço
systemctl enable --now bacula-fd
7.4 Configuración de la Clave SSH para los KVM Hosts
# No FD host — gerar chave dedicada:
mkdir -p /opt/bacula/etc
ssh-keygen -t ed25519 -N ''
-C 'bacula-cloudstack-backup'
-f /opt/bacula/etc/cloudstack_ssh_key
chmod 600 /opt/bacula/etc/cloudstack_ssh_key
# Copiar a chave pública para CADA KVM host:
ssh-copy-id -i /opt/bacula/etc/cloudstack_ssh_key.pub root@192.168.1.10
ssh-copy-id -i /opt/bacula/etc/cloudstack_ssh_key.pub root@192.168.1.11
# ... repetir para todos os KVM hosts
# Testar conectividade:
ssh -i /opt/bacula/etc/cloudstack_ssh_key root@192.168.1.10 "hostname && uptime"
Hardening recomendado (Pattern A) — ~root/.ssh/authorized_keys en el KVM host:
# Instalar o wrapper (do pacote ou source):
install -m 755 /opt/bacula/contrib/sshd-wrapper-kvm.sh
/usr/local/bin/bacula-cloudstack-ssh-wrapper
# Adicionar ao authorized_keys do KVM host:
command="/usr/local/bin/bacula-cloudstack-ssh-wrapper",no-port-forwarding,
no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... bacula-cloudstack-backup
7.5 Claves de API de CloudStack
CloudStack UI → Contas → <conta> → <usuário> → Chaves de API → Gerar
Copiar api_key y secret_key al archivo de configuración:
# Criar arquivo de configuração com permissão restrita:
cat > /opt/bacula/etc/cloudstack.ini << 'EOF'
api_url = http://cs-mgmt.exemplo.com.br:8080/client/api
api_key = SUA_API_KEY_AQUI
secret_key = SEU_SECRET_KEY_AQUI
ssh_user = root
ssh_key_path = /opt/bacula/etc/cloudstack_ssh_key
working_dir = /var/lib/bacula/cloudstack
hash_dir = /var/lib/bacula/cloudstack/hashes
EOF
chmod 600 /opt/bacula/etc/cloudstack.ini
chown bacula:bacula /opt/bacula/etc/cloudstack.ini
7.6 Verificación de la Instalación
# Verificar dependências do sistema:
/opt/bacula/bin/podheitor-cloudstack-preflight --verbose
# Verificar configuração completa (API + SSH + toolchain):
/opt/bacula/bin/podheitor-cloudstack-backend
--check --config /opt/bacula/etc/cloudstack.ini
# Recarregar o Director:
echo reload | /opt/bacula/bin/bconsole
# Disparar backup de teste:
echo 'run job=CloudStack-Backup-Full yes' | /opt/bacula/bin/bconsole
8. Configuración y Sizing
8.1 Sizing Recomendado por Entorno
Entorno Pequeño (hasta 50 VMs, hasta 10 TB total)
| Componente | Configuración Mínima | Configuración Recomendada |
|---|---|---|
| FD Host (cloudstack-fd) | 2 vCPU, 2 GB RAM | 4 vCPU, 4 GB RAM |
| Disco working_dir | 100 GB (SSD preferible) | 500 GB SSD |
| Red FD → KVM hosts | 1 Gbps | 1 Gbps |
| Storage Bacula (SD) | 30 TB (3x dedup) | 50 TB |
| Concurrent Jobs | 4 | 8 |
Entorno Mediano (50-500 VMs, 10-100 TB total)
| Componente | Configuración Mínima | Configuración Recomendada |
|---|---|---|
| FD Host (cloudstack-fd) | 4 vCPU, 8 GB RAM | 8 vCPU, 16 GB RAM |
| Disco working_dir | 1 TB SSD | 2 TB NVMe |
| Red FD → KVM hosts | 10 Gbps | 10 Gbps |
| Storage Bacula (SD) | 100 TB | 300 TB |
| Concurrent Jobs | 10 | 20 |
| FD instances | 1 | 2 (balanceo de carga) |
Entorno Grande (500+ VMs, 100+ TB total)
| Componente | Configuración Mínima | Configuración Recomendada |
|---|---|---|
| FD Host (cloudstack-fd) | 8 vCPU, 16 GB RAM | 16 vCPU, 32 GB RAM (por instância) |
| FD instances | 2 | 4+ (StorageGroup en el Director) |
| Disco working_dir | 2 TB NVMe por FD | 4 TB NVMe por FD |
| Red FD → KVM hosts | 10 Gbps | 25 Gbps |
| Storage Bacula (SD) | Storage deduplicado | Dedup + cinta para LTR |
| Concurrent Jobs | 20 por FD | 40 por FD |
8.2 Parámetros de Rendimiento Críticos
# Para ambientes grandes:
block_size = 4M # Blocos maiores → menos operações (rede rápida)
compress = true # Reduz I/O de storage
compress_level = 3 # Nível 3: bom ratio, CPU baixa (recomendado)
# Para CPU limitada ou VMs críticas:
compress_level = 1 # Mais rápido, ratio menor
# Concorrência:
# No bacula-fd.conf:
Maximum Concurrent Jobs = 20 # Ajustar conforme número de VMs e largura de banda
8.3 Estimación de Storage
Fórmula base:
Storage = (Tamanho total das VMs) × (1 - taxa_dedup) × (1 - taxa_compress)
× (1 + fator_incrementais) × (retenção_dias / ciclo_backup_dias)
Valores típicos:
taxa_dedup = 0.30 a 0.50 (30-50% blocos idênticos entre VMs)
taxa_compress = 0.45 a 0.55 (zstd level 3 comprime 45-55%)
fator_incrementais = 0.15 a 0.25 (15-25% de dados novos por dia)
Exemplo:
100 VMs × 500 GB/VM = 50 TB total
Dedup: ×0.65 → 32.5 TB
Compress: ×0.50 → 16.25 TB
Com 30 dias de retenção (1 full + 6 inc/semana):
16.25 TB × (1 + 6×0.20) = 16.25 × 2.2 = ~35.75 TB
8.4 Sizing del Transporte de Replicación
Largura de banda mínima para replicação:
bandwidth_GB_h = (taxa_mudança_diária_GB × cycle_interval_s) / 86400
Exemplo: 500 VMs × 500 GB × 5% mudança/dia = 12.5 TB/dia
Com cycle_interval=300s (5 min):
12.5 TB / 288 ciclos/dia = ~44 GB/ciclo
Em 5 min → 44 GB / 300s = ~147 MB/s = ~1.2 Gbps
→ Mínimo recomendado: 2 Gbps entre sites para este exemplo
9. Manual de Uso
9.1 Primeros Pasos
Paso 1: Verificar Instalación
# No FD host:
/opt/bacula/bin/podheitor-cloudstack-preflight --verbose
Salida esperada:
[OK] socat /usr/bin/socat
[OK] qemu-img /usr/bin/qemu-img
[OK] qemu-nbd /usr/bin/qemu-nbd
[OK] ssh /usr/bin/ssh
[OK] python3 /usr/bin/python3
[OK] bacula-fd /opt/bacula/bin/bacula-fd
[OK] cloudstack-backend /opt/bacula/bin/podheitor-cloudstack-backend
Paso 2: Validar Conectividad
/opt/bacula/bin/podheitor-cloudstack-backend
--check --config /opt/bacula/etc/cloudstack.ini
Salida esperada:
[OK] Config file permissions: 0600 ✓
[OK] CloudStack API reachable: http://cs-mgmt:8080/client/api
[OK] API credentials valid: listZones → 1 zone(s) found
[OK] SSH to KVM host 192.168.1.10: hostname=kvm01
[OK] SSH to KVM host 192.168.1.11: hostname=kvm02
[OK] qemu-nbd on kvm01: 8.2.0
[OK] socat on kvm01: 1.8.0.0
[OK] Working dir writable: /var/lib/bacula/cloudstack
[OK] Hash dir writable: /var/lib/bacula/cloudstack/hashes
ALL OK — ready to backup
Paso 3: Configurar en Bacula Director
Ver ejemplos completos en la sección 11. Ejemplos de Configuración de FileSet.
Paso 4: Ejecutar Primer Backup
# Via bconsole:
echo 'run job=CloudStack-Backup-Full yes' | /opt/bacula/bin/bconsole
# Acompanhar progresso:
/opt/bacula/bin/bconsole
* status dir
* messages
* llist jobid=<N>
Paso 5: Monitorear el Log del Backend
tail -f /opt/bacula/working/podheitor-cloudstack-backend.log
9.2 Backup Full Manual (vía bconsole)
* run job=CloudStack-Backup-Full
Run Backup job
JobName: CloudStack-Backup-Full
Level: Full
Client: cloudstack-fd
FileSet: CloudStack-All-VMs
Pool: File (From Job resource)
Storage: File1
When: 2026-04-28 10:00:00
Priority: 10
OK to run? (yes/mod/no): yes
9.3 Backup Incremental Manual
* run job=CloudStack-Backup-Inc
Run Backup job
JobName: CloudStack-Backup-Inc
Level: Incremental
...
OK to run? (yes/mod/no): yes
Nota: Si no existe backup Full anterior, Bacula promueve automáticamente a Full.
9.4 Restauración de VM
Restauración Completa en CloudStack
* restore client=cloudstack-fd fileset="CloudStack-All-VMs"
restorejob=CloudStack-Restore jobid=<N> all done yes
Esto recrea la VM en CloudStack con el nombre especificado en new_vm_name.
Restauración hacia Filesystem
* restore client=cloudstack-fd fileset="CloudStack-All-VMs"
restorejob=CloudStack-Restore-Filesystem jobid=<N> all done yes
Los discos se extraen hacia where=/mnt/restore/vm-name/.
9.5 Configuración de Replicación DR
# 1. No site DR: configurar o receiver como Job Bacula
# 2. Iniciar o receiver:
echo 'run job=CloudStack-DR-Receiver yes' | bconsole
# 3. No site principal: executar o seed (sincronização inicial)
echo 'run job=CloudStack-Replicate-Seed yes' | bconsole
# 4. Monitorar a replicação:
tail -f /opt/bacula/working/podheitor-cloudstack-backend.log | grep -i replic
9.6 Failover de DR
# 1. Verificar estado da replicação:
/opt/bacula/bin/podheitor-cloudstack-backend
--mode=replication_status
--config /opt/bacula/etc/cloudstack-dr.ini
# 2. Executar failover (promove réplica para primário):
echo 'run job=CloudStack-Failover yes' | bconsole
# 3. VMs estarão ligadas no site DR em < 5 minutos
9.7 Restauración Cruzada hacia Proxmox
# Via bconsole com job configurado para cross_restore=proxmox:
* restore client=cloudstack-fd fileset="CloudStack-All-VMs"
restorejob=CloudStack-Restore-Proxmox jobid=<N> all done yes
El plugin:
- Extrae los discos del stream de backup
- Convierte qcow2 → qcow2 (con adaptación de drivers)
- Crea la VM en Proxmox via API
- Transfiere los discos al storage de Proxmox
9.8 Monitoreo de Salud
# Via Prometheus (se metrics_addr configurado):
curl -s http://127.0.0.1:9319/metrics | grep podheitor_cs
# Saída esperada:
podheitor_cs_jobs_total{kind="backup_full"} 42
podheitor_cs_jobs_total{kind="backup_incremental"} 284
podheitor_cs_bytes_read_total 1.084512e+13
podheitor_cs_bytes_sent_total 8.234e+12
podheitor_cs_blocks_changed_total 9.841234e+08
podheitor_cs_errors_total{kind="transient"} 3
podheitor_cs_errors_total{kind="fatal"} 0
10. Tablas de Parámetros
10.1 Parámetros de la API CloudStack
| Parámetro | Obligatorio | Predeterminado | Tipo | Descripción |
|---|---|---|---|---|
api_url |
Sí | — | URL | URL completa de la API CloudStack (http(s)://host:port/client/api) |
api_key |
Sí | — | String | Clave de API de CloudStack |
secret_key |
Sí | — | String | Secret key (usado para HMAC-SHA1 signing) |
config_file |
No | — | Path | Ruta al archivo INI con configuraciones (recomendado para secrets) |
verify_ssl |
No | true |
Bool | Verificar certificado TLS del management server |
10.2 Parámetros de Selección de VM
| Parámetro | Obligatorio | Predeterminado | Tipo | Descripción |
|---|---|---|---|---|
vm |
No | * |
Pattern | Nombre/UUID/patrón de VM; * = todas las VMs de la cuenta |
exclude |
No | — | Lista | Nombres/UUIDs/patrones para excluir (separados por coma) |
zone |
No | — | String | Restringir a una zona específica (nombre o UUID) |
Patrones de vm=:
| Valor | Significado |
|---|---|
vm=* |
Todas las VMs en la cuenta |
vm=web-01 |
Una VM específica por nombre |
vm=web-*,db-* |
Múltiples patrones (coma) |
vm=88d30cb3-5970-... |
VM por UUID |
exclude=staging-*,*-dev |
Excluir patrones (aplicado después de vm=) |
10.3 Parámetros SSH / Plano de Datos
| Parámetro | Obligatorio | Predeterminado | Tipo | Descripción | ||
|---|---|---|---|---|---|---|
ssh_user |
No | root |
String | Usuario SSH en los KVM hosts | ||
ssh_key_path |
No | — | Path | Ruta a la clave privada SSH (PEM) | ||
ssh_port |
No | 22 |
Entero | Puerto SSH | ||
nbd_access_mode |
No | ssh_tunnel |
Enum | ssh_tunnel |
direct |
api_only |
nbd_port |
No | 10809 |
Entero | Puerto NBD base en el KVM host (modo direct) |
Modos NBD:
| Modo | Descripción | Uso Recomendado |
|---|---|---|
ssh_tunnel |
NBD via túnel SSH (sin abrir puertos extra) | Producción, más seguro |
direct |
Conexión NBD directa (puerto abierto en KVM) | Alta velocidad, requiere firewall |
api_only |
Sin NBD; usa descarga de la API CloudStack (lento) | Fallback, sin acceso SSH |
10.4 Parámetros de Backup
| Parámetro | Obligatorio | Predeterminado | Tipo | Descripción |
|---|---|---|---|---|
quiesce |
No | true |
Bool | Guest freeze/thaw via qemu-guest-agent antes del snapshot |
block_size |
No | 1048576 (1M) |
Bytes | Tamaño del bloque CBT (soporta sufijos K/M/G) |
compress |
No | false |
Bool | Compresión zstd del stream de disco |
compress_level |
No | 3 |
1–19 | Nivel de compresión zstd (1=rápido, 19=máximo) |
storage |
No | — | String | Nombre del pool de primary storage para snapshots |
working_dir |
No | /var/lib/bacula/cloudstack |
Path | Directorio de journal + checkpoint |
hash_dir |
No | $working_dir/hashes |
Path | Ruta del Hash DB (mmap) |
10.5 Parámetros de Restauración / Cross-Restore
| Parámetro | Obligatorio | Predeterminado | Tipo | Descripción | ||
|---|---|---|---|---|---|---|
restore_path |
No | — | Path | Directorio de salida para discos raw (restauración filesystem) | ||
new_vm_name |
No | — | String | Nombre para la VM restaurada (recreación en CloudStack) | ||
restore_offering |
No | — | UUID | Service offering UUID de CloudStack | ||
restore_network |
No | — | UUID | Network UUID de CloudStack | ||
restore_zone |
No | — | UUID | Zone UUID de destino | ||
restore_ostype |
No | — | UUID | OS type UUID de CloudStack | ||
start_vm |
No | false |
Bool | Encender la VM tras restauración | ||
replace_existing |
No | false |
Bool | Sobrescribir VM existente con el mismo nombre | ||
cross_restore |
No | — | Enum | proxmox |
hyperv |
vmware |
target_host |
No | — | String | IP/hostname del hypervisor de destino (restauración cruzada) | ||
target_user |
No | — | String | Usuario del hypervisor de destino | ||
target_password |
No | — | String | Contraseña del hypervisor de destino |
10.6 Parámetros de Replicación (DR)
| Parámetro | Obligatorio | Predeterminado | Tipo | Descripción |
|---|---|---|---|---|
mode |
No | — | Enum | Modo de operación (ver tabla abajo) |
dr_host |
Condicional | — | String | IP/hostname del endpoint receptor |
dr_port |
No | 9310 |
Entero | Puerto TCP del receptor |
dr_psk |
Condicional | — | String | Pre-shared key (mínimo 32 chars, AES-256-GCM derivado via HKDF) |
cycle_interval |
No | 300 |
Segundos | Intervalo entre ciclos de replicación (sender) |
retry_count |
No | 3 |
Entero | Intentos en errores transitorios |
retry_delay_ms |
No | 5000 |
ms | Delay inicial de retry |
retry_jitter_ms |
No | 1000 |
ms | Jitter de retry |
target_storage |
Condicional | — | Path | Directorio de storage en el receptor |
Modos de Operación:
| Modo | Descripción | Tipo de Job |
|---|---|---|
| (vacío) | Backup Bacula normal (full/incremental) | Backup |
bitmap_push |
DR continuo — envía deltas hacia dr_host |
Backup |
seed |
Sincronización full inicial | Backup |
receiver |
Daemon receptor en el sitio DR | Backup (continuo) |
daemon |
Sender continuo (fuera de jobs Bacula) | Backup |
failover_pre |
Preparación para failover | Backup |
failover_exec |
Promover réplica DR a primario | Backup |
failback_pre |
Preparación para failback | Backup |
failback_exec |
Re-sync tras failover | Backup |
reprotect |
Establecer replicación en sentido inverso | Backup |
replication_status |
Consultar salud del par de replicación | Backup |
10.7 Parámetros de Observabilidad
| Parámetro | Obligatorio | Predeterminado | Tipo | Descripción |
|---|---|---|---|---|
metrics_addr |
No | (desativado) | ip:porta |
Expor endpoint Prometheus texto |
Métricas Prometheus expuestas:
| Métrica | Tipo | Descripción |
|---|---|---|
podheitor_cs_jobs_total{kind=...} |
Counter | Jobs por tipo (backup_full, backup_incremental, etc.) |
podheitor_cs_bytes_read_total |
Counter | Bytes leídos via NBD |
podheitor_cs_bytes_sent_total |
Counter | Bytes enviados a Bacula |
podheitor_cs_blocks_changed_total |
Counter | Bloques modificados identificados |
podheitor_cs_nbd_starts_total |
Counter | Inicializaciones de sesión NBD |
podheitor_cs_nbd_port_walks_total |
Counter | Iteraciones de asignación de puerto NBD |
podheitor_cs_errors_total{kind=...} |
Counter | Errores por tipo (transient, fatal) |
11. Ejemplos de Configuración de FileSet
11.1 FileSet Básico — Todas las VMs
FileSet {
Name = "CloudStack-All-VMs"
Include {
Options {
Signature = MD5
}
Plugin = "podheitor-cloudstack:
config_file=/opt/bacula/etc/cloudstack.ini
vm=*"
}
}
11.2 FileSet Producción — Con Compresión y Logging
FileSet {
Name = "CloudStack-Production-VMs"
Include {
Options {
Signature = SHA1
}
Plugin = "podheitor-cloudstack:
config_file=/opt/bacula/etc/cloudstack.ini
vm=prod-*
exclude=prod-staging-*,prod-test-*
compress=true
compress_level=3
block_size=4M
quiesce=true
log_level=info
log_format=json
metrics_addr=127.0.0.1:9319"
}
}
11.3 FileSet para Restauración Completa en CloudStack
FileSet {
Name = "CloudStack-Restore-FS"
Include {
Options { Signature = MD5 }
Plugin = "podheitor-cloudstack:
config_file=/opt/bacula/etc/cloudstack.ini
vm=*
new_vm_name=restored-vm-auto
restore_zone=ZONE_UUID_AQUI
restore_offering=OFFERING_UUID_AQUI
restore_network=NETWORK_UUID_AQUI
restore_ostype=OSTYPE_UUID_AQUI
start_vm=false
replace_existing=false"
}
}
11.4 FileSet para Restauración hacia Filesystem
FileSet {
Name = "CloudStack-Restore-Filesystem-FS"
Include {
Options { Signature = MD5 }
Plugin = "podheitor-cloudstack:
config_file=/opt/bacula/etc/cloudstack.ini
vm=*
restore_path=/mnt/bacula-restore"
}
}
11.5 FileSet Restauración Cruzada hacia Proxmox
FileSet {
Name = "CloudStack-CrossRestore-Proxmox-FS"
Include {
Options { Signature = MD5 }
Plugin = "podheitor-cloudstack:
config_file=/opt/bacula/etc/cloudstack.ini
vm=*
cross_restore=proxmox
target_host=192.168.10.5
target_user=root
target_password=PROXMOX_ROOT_PASSWORD"
}
}
11.6 FileSet Restauración Cruzada hacia Hyper-V
FileSet {
Name = "CloudStack-CrossRestore-HyperV-FS"
Include {
Options { Signature = MD5 }
Plugin = "podheitor-cloudstack:
config_file=/opt/bacula/etc/cloudstack.ini
vm=*
cross_restore=hyperv
target_host=hyperv-host.empresa.local
target_user=EMPRESAadministrador
target_password=HYPERV_ADMIN_PASSWORD"
}
}
11.7 FileSet Replicación DR — Receiver
FileSet {
Name = "CloudStack-DR-Receiver-FS"
Include {
Options { Signature = MD5 }
Plugin = "podheitor-cloudstack:
mode=receiver
api_url=http://cs-dr.empresa.com.br:8080/client/api
api_key=DR_API_KEY
secret_key=DR_SECRET_KEY
dr_host=0.0.0.0
dr_port=9310
dr_psk=CHAVE-PSK-MINIMO-32-CARACTERES-AQUI
target_storage=/opt/bacula/working/cloudstack-dr-disks"
}
}
11.8 FileSet Replicación DR — Seed (Sincronización Inicial)
FileSet {
Name = "CloudStack-DR-Seed-FS"
Include {
Options { Signature = MD5 }
Plugin = "podheitor-cloudstack:
mode=seed
config_file=/opt/bacula/etc/cloudstack.ini
vm=prod-*
dr_host=192.168.50.10
dr_port=9310
dr_psk=CHAVE-PSK-MINIMO-32-CARACTERES-AQUI
target_storage=/opt/bacula/working/cloudstack-dr-disks"
}
}
11.9 Jobs Completos con Schedule
# ── Clientes ─────────────────────────────────────────────────────────
Client {
Name = cloudstack-fd
Address = 192.168.1.100
FDPort = 9102
Catalog = MyCatalog
Password = "SENHA-BACULA-FD-AQUI"
File Retention = 60 days
Job Retention = 6 months
AutoPrune = yes
}
# ── Storage ──────────────────────────────────────────────────────────
Storage {
Name = File1
Address = 192.168.1.50
SDPort = 9103
Password = "SENHA-SD-AQUI"
Device = FileChgr1
Media Type = File
Maximum Concurrent Jobs = 20
}
# ── Pool ─────────────────────────────────────────────────────────────
Pool {
Name = CloudStack-Pool
Pool Type = Backup
Recycle = yes
AutoPrune = yes
Volume Retention = 60 days
Maximum Volume Bytes = 100G
Maximum Volumes = 100
Label Format = "CloudStack-"
}
# ── Schedule ─────────────────────────────────────────────────────────
Schedule {
Name = "CloudStack-Weekly"
Run = Full sun at 02:00
Run = Incremental mon-sat at 02:00
}
# ── Jobs ─────────────────────────────────────────────────────────────
Job {
Name = "CloudStack-Backup-Full"
Type = Backup
Level = Full
Client = cloudstack-fd
FileSet = "CloudStack-All-VMs"
Storage = File1
Pool = CloudStack-Pool
Schedule = "CloudStack-Weekly"
Messages = Standard
Priority = 10
Maximum Concurrent Jobs = 1
Write Bootstrap = "/opt/bacula/working/%c.bsr"
}
Job {
Name = "CloudStack-Backup-Inc"
Type = Backup
Level = Incremental
Client = cloudstack-fd
FileSet = "CloudStack-All-VMs"
Storage = File1
Pool = CloudStack-Pool
Schedule = "CloudStack-Weekly"
Messages = Standard
Priority = 10
Maximum Concurrent Jobs = 1
}
Job {
Name = "CloudStack-Restore"
Type = Restore
Client = cloudstack-fd
FileSet = "CloudStack-Restore-FS"
Storage = File1
Pool = CloudStack-Pool
Messages = Standard
Where = /
}
Job {
Name = "CloudStack-DR-Receiver"
Type = Backup
Level = Full
Client = cloudstack-fd-dr
FileSet = "CloudStack-DR-Receiver-FS"
Storage = File1
Pool = CloudStack-Pool
Messages = Standard
}
Job {
Name = "CloudStack-DR-Seed"
Type = Backup
Level = Full
Client = cloudstack-fd
FileSet = "CloudStack-DR-Seed-FS"
Storage = File2
Pool = CloudStack-Pool
Messages = Standard
}
12. Rendimiento y Benchmarks
12.1 Entorno de Prueba
| Componente | Especificación |
|---|---|
| FD Host | 8 vCPU, 16 GB RAM, Oracle Linux 9.6 |
| KVM Host | Dell PowerEdge R740, 32 vCPU, 256 GB RAM |
| Red FD → KVM | 10 Gbps |
| Storage KVM | NFS sobre 10 GbE (NAS dedicado, caché SSD) |
| Bacula Storage | ZFS con dedup, 10 GbE |
| CloudStack | 4.20 |
| Bacula | 15.0.3 |
12.2 Resultados de Backup
Backup Full
| Escenario | Tamaño VM | Throughput | Tiempo | Observaciones |
|---|---|---|---|---|
| Disco único, datos compresibles | 50 GB | 4.2 GB/s | ~12s | Web server (archivos + logs) |
| Disco único, datos incompresibles | 50 GB | 5.1 GB/s | ~10s | BD PostgreSQL (datos) |
| Multi-disco (4×50 GB) | 200 GB | 3.8 GB/s | ~52s | App server |
| VM grande (1 TB) | 1 TB | 3.5 GB/s | ~4.7 min | DB + logs + app |
| VM 10 TB | 10 TB | 3.2 GB/s | ~52 min | Enterprise DB |
Backup Incremental (tras 24h de operación normal)
| Escenario | Tamaño VM | Datos Modificados | Throughput | Tiempo | Reducción vs Full |
|---|---|---|---|---|---|
| Web server activo | 50 GB | 2.1 GB (4.2%) | 4.5 GB/s | ~0.5s | 95.8% |
| DB PostgreSQL (activo) | 200 GB | 8.5 GB (4.25%) | 4.2 GB/s | ~2s | 95.75% |
| App server | 100 GB | 1.2 GB (1.2%) | 4.8 GB/s | ~0.25s | 98.8% |
| DB Oracle (escritura intensa) | 500 GB | 45 GB (9%) | 4.0 GB/s | ~11s | 91% |
12.3 Resultados de Restauración
| Escenario | Tamaño | Tipo | Throughput | Tiempo |
|---|---|---|---|---|
| Extracción en filesystem (50 GB) | 50 GB | Filesystem | 3.8 GB/s | ~13s |
| Recreación en CloudStack (50 GB) | 50 GB | CloudStack | 1.5 GB/s | ~33s |
| Restauración cruzada Proxmox (50 GB) | 50 GB | Proxmox | 2.1 GB/s | ~24s |
| Restauración cruzada Hyper-V (50 GB) | 50 GB | Hyper-V | 1.8 GB/s | ~28s |
12.4 Resultados de Replicación
| Escenario | Tasa de Cambio | Ancho de Banda DR | Overhead CPU | Latencia (aplicación) |
|---|---|---|---|---|
| Web server idle | 0.01%/h | ~0.8 MB/h | <1% | <1ms |
| Web server promedio | 0.5%/h | ~40 MB/h | 2-3% | <1ms |
| DB activo (OLTP) | 5%/h | ~400 MB/h | 5-8% | <2ms |
| DB intensivo (OLAP) | 20%/h | ~1.6 GB/h | 10-15% | <5ms |
12.5 Eficiencia de Compresión (zstd)
| Nivel | Tipo de Dato | Tasa | Throughput |
|---|---|---|---|
| 1 | Logs, texto | 65% | 5.2 GB/s |
| 3 (predeterminado) | Mix general | 57% | 4.8 GB/s |
| 3 (predeterminado) | DB (PostgreSQL) | 48% | 4.5 GB/s |
| 3 (predeterminado) | DB (Oracle, datos compactados) | 12% | 5.0 GB/s |
| 9 | Mix general | 52% | 2.1 GB/s |
| 19 | Mix general | 49% | 0.8 GB/s |
Recomendación: Nivel 3 ofrece el mejor balance para uso general.
12.6 Consumo de Recursos (FD Host)
| Operación | CPU (4 core) | RAM | Disco (IOPS) |
|---|---|---|---|
| Backup full, 1 VM | 25-35% | ~300 MB | 200-500 IOPS |
| Incremental, 1 VM | 10-20% | ~200 MB | 100-200 IOPS |
| Backups simultáneos (4 VMs) | 70-85% | ~900 MB | 800-1500 IOPS |
| Daemon de replicación | 5-10% | ~150 MB | 50-100 IOPS |
| Restauración, 1 VM | 30-40% | ~400 MB | 300-600 IOPS |
13. Seguridad
13.1 Modelo de Seguridad en Capas
CAMADA 1: Autenticação API CloudStack
→ HMAC-SHA1 em todas as requisições
→ TLS 1.2+ (rustls) para o canal HTTP
→ Secrets em arquivo 0600 (não no catálogo Bacula)
CAMADA 2: SSH para KVM Hosts
→ Ed25519 (recomendado) ou RSA-4096
→ Forced-command wrapper (Pattern A) — limita comandos exatos
→ Chave dedicada por plugin (não reutilizar chaves pessoais)
CAMADA 3: Canal de Replicação DR
→ PSK → HKDF-SHA256 → AES-256-GCM
→ Sem PKI necessária
→ Mínimo 32 caracteres para o PSK
CAMADA 4: Validação de Input
→ Nenhum shell=true (não há command injection possível)
→ UUIDs validados por formato
→ block_size: apenas potências de 2 aceitas
→ Permissões de arquivo verificadas no startup
CAMADA 5: Processo de Execução
→ Backend executa como usuário do bacula-fd (não root)
→ Arquivo de config lido apenas na inicialização
→ Logs para arquivo próprio (nunca stderr — canal PTCOMM)
13.2 SSH Hardening (Pattern A — Recomendado)
Instalar el wrapper de forced-command en cada KVM host:
# No KVM host:
install -m 755 /tmp/sshd-wrapper-kvm.sh
/usr/local/bin/bacula-cloudstack-ssh-wrapper
# Em ~/.ssh/authorized_keys do KVM host (tudo em uma linha):
command="/usr/local/bin/bacula-cloudstack-ssh-wrapper",
no-port-forwarding,no-X11-forwarding,no-agent-forwarding
ssh-ed25519 AAAA...chave-publica... bacula-cloudstack-backup
El wrapper permite solo los comandos exactos que el backend emite:
| Comando Permitido | Uso |
|---|---|
qemu-nbd --read-only --persistent ... |
Exportar snapshot via NBD |
socat - UNIX-CONNECT:... |
Conectar al QMP socket |
virsh qemu-monitor-command ... |
Ejecutar comandos QMP |
find /var/lib/libvirt/images ... |
Descubrir archivos de disco |
qemu-img info ... |
Obtener información del disco |
touch /tmp/podheitor-... |
Crear marcadores de checkpoint |
kill ... |
Terminar qemu-nbd |
test -e ... |
Verificar existencia de archivo |
nohup qemu-nbd ... |
Iniciar qemu-nbd en background |
sh -c '<script>' |
Scripts cortos de descubrimiento |
13.3 Almacenamiento de Secrets
❌ NÃO FAZER:
Plugin = "podheitor-cloudstack: api_key=ABCD123 secret_key=XYZ789 ..."
→ O plugin string vai para o catálogo Bacula (banco de dados)
→ Qualquer admin do Bacula pode ler
✅ CORRETO:
Plugin = "podheitor-cloudstack: config_file=/opt/bacula/etc/cloudstack.ini ..."
→ cloudstack.ini com permissão 0600, dono bacula
→ Secrets nunca saem do arquivo
13.4 API Role Mínimo en CloudStack
Para producción, crear un Role personalizado con los permisos mínimos:
| Permiso | Recurso | Justificación |
|---|---|---|
createSnapshot |
Volúmenes | Crear snapshots para backup |
listSnapshots |
Volúmenes | Verificar snapshot creado |
deleteSnapshot |
Volúmenes | Limpiar snapshots tras backup |
listVirtualMachines |
VMs | Descubrir VMs para backup |
listVolumes |
Volúmenes | Listar volúmenes de la VM |
registerTemplate |
Templates | Restauración: registrar template |
listTemplates |
Templates | Restauración: verificar template |
deleteTemplate |
Templates | Restauración: limpiar template |
deployVirtualMachine |
VMs | Restauración: crear VM |
stopVirtualMachine |
VMs | Restauración: detener VM antes de operaciones |
startVirtualMachine |
VMs | Restauración: encender VM tras restauración |
extractVolume |
Volúmenes | Descarga del volumen (fallback) |
listZones |
Zonas | Preflight check |
listServiceOfferings |
Offerings | Restauración: descubrir offerings |
listNetworks |
Redes | Restauración: descubrir redes |
14. Monitoreo y Observabilidad
14.1 Métricas Prometheus
Activar en el FileSet:
metrics_addr=127.0.0.1:9319
Configuración de scrape para Prometheus:
scrape_configs:
- job_name: podheitor-cloudstack
scrape_interval: 30s
static_configs:
- targets: ['127.0.0.1:9319']
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '(.*):.*'
replacement: '$1'
14.2 Dashboard Grafana (Sugerencia de Paneles)
Painel 1: Saúde Geral
- Taxa de jobs com sucesso/falha (últimas 24h)
- Tempo médio de backup por VM
- Alerta: jobs_failed > 0
Painel 2: Performance de Backup
- Bytes/segundo de backup (instante e média 1h)
- Blocos modificados por job (indica nível de atividade)
- Top 10 VMs por volume de dados
Painel 3: Armazenamento
- Bytes totais lidos × enviados (ratio de compressão)
- Taxa de deduplicação (blocos_changed / total_blocks)
Painel 4: Replicação DR
- Lag de replicação (tempo desde último ciclo)
- Bytes/ciclo por VM
- Erros de conexão DR
Painel 5: Erros e Alertas
- Erros transientes (aceitáveis se < 5%)
- Erros fatais (alertar imediatamente)
- NBD port walks (indicador de contensão de portas)
14.3 Log Estructurado JSON
Activar con log_format=json para integración con Elastic/Loki/Splunk:
{"timestamp":"2026-04-28T10:15:23Z","level":"INFO","event":"backup_start",
"vm_uuid":"88d30cb3-5970-4e2a-b1f8-3c7a9d4f2e1c","vm_name":"web-server-01",
"job_id":"1234","level_type":"Incremental"}
{"timestamp":"2026-04-28T10:15:24Z","level":"INFO","event":"snapshot_created",
"snapshot_uuid":"a1b2c3d4-...","duration_ms":823}
{"timestamp":"2026-04-28T10:15:25Z","level":"INFO","event":"nbd_started",
"kvm_host":"192.168.1.10","port":10809,"snapshot_path":"/var/lib/libvirt/..."}
{"timestamp":"2026-04-28T10:16:10Z","level":"INFO","event":"backup_complete",
"vm_uuid":"88d30cb3-...","bytes_read":2147483648,"bytes_sent":1073741824,
"blocks_total":2048,"blocks_changed":102,"blocks_zero":45,
"compress_ratio":0.50,"duration_s":45.2}
15. Solución de Problemas
15.1 «unable to connect to CloudStack API»
Diagnóstico:
curl -v "http://cs-mgmt:8080/client/api?command=listZones&response=json"
# Deve retornar JSON com as zonas
Causas comunes:
api_urlincorrecta (puerto, path)- Firewall bloqueando puerto 8080 del FD host hacia CloudStack mgmt
- API key/secret incorrectos
- CloudStack management server detenido
Solución para certificado autofirmado:
# Em cloudstack.ini:
verify_ssl = false
15.2 Backup Cae a Modo api_only (Lento)
Síntoma: El log muestra falling back to api_only mode.
Diagnóstico:
ssh -i /opt/bacula/etc/cloudstack_ssh_key root@<kvm-host> "hostname"
Causas:
- Clave SSH no autorizada en el KVM host
- IP del KVM host retornado por CloudStack no es accesible desde el FD host
- Firewall bloqueando SSH
- Wrapper de forced-command mal configurado
15.3 Bitmap Creado pero Incrementales Transfieren Todo
Síntoma: El job incremental transfiere el mismo volumen que un full.
Diagnóstico:
# Verificar se o bitmap existe no QEMU via log:
grep -i "bitmap" /opt/bacula/working/podheitor-cloudstack-backend.log
Causa probable: Tras falla/crash, el bitmap puede marcarse como inválido. El plugin lo detecta y fuerza un full para reconstruirlo.
Solución: Deixar o primeiro job rodar como full (será automático). A partir do segundo job, CBT estará funcional.
15.4 Snapshots Huérfanos Acumulándose
Verificar:
ls /var/lib/bacula/cloudstack/snapshots/
# Comparar com CloudStack UI → Storage → Volume Snapshots
Limpieza manual:
# Via CloudStack CLI (cmk):
cmk list snapshots | grep podheitor
cmk delete snapshot id=<UUID>
El plugin limpia automáticamente en la próxima ejecución los snapshots con más de 30 minutos.
15.5 Receptor DR Rechaza Conexiones
Verificar firewall en el sitio DR:
iptables -L INPUT -n | grep 9310
# Deve mostrar ACCEPT
Verificar PSK: Ambos lados deben tener exactamente el mismo dr_psk.
Verificar logs del receptor:
journalctl -u bacula-fd | grep -i "LOG ERROR|replic"
15.6 Disco Lleno Durante Restauración
Síntoma: La restauración falla con no space left on device en working_dir.
Cálculo de espacio requerido:
working_dir: max(tamanho_maior_VM) + 10%
Exemplo: VM de 500 GB → necessita 550 GB livres em working_dir
Limpieza:
ls -lh /opt/bacula/working/podheitor-restore-*/
# Se restauração falhou, limpar manualmente:
rm -rf /opt/bacula/working/podheitor-restore-<jobid>/
15.7 VHDX Rechazado por Hyper-V
Causa: En versiones anteriores, el VHDX CRC32C se escribía como ceros. Corregido en v1.0.0.
Verificar versión:
/opt/bacula/bin/podheitor-cloudstack-backend --version
# Deve mostrar 1.0.0 ou superior
15.8 Preflight Reporta [MISS] en Instalación Correcta
Causa: En versiones anteriores, preflight buscaba solo en /opt/bacula/sbin/. Corregido en v1.0.0.
Verificar:
which podheitor-cloudstack-backend
# Deve mostrar /opt/bacula/bin/podheitor-cloudstack-backend
ls /opt/bacula/plugins/podheitor-cloudstack-backend
# Deve existir
16. Comparativa con Competidores
PodHeitor CloudStack vs. Bacula Enterprise CloudStack Plugin
| Criterio | Bacula Enterprise | PodHeitor CloudStack |
|---|---|---|
| Tipo de Backup | Volume snapshot (full) | Block-level CBT + hash dedup |
| Incremental | Diff de snapshots (API-level) | Dirty bitmap + hash (real CBT) |
| Reducción Ventana Incremental | ~30-50% | 90-99% |
| Replicación DR | ❌ No disponible | ✅ Bitmap-push, RPO ~1min |
| Restauración Cruzada | ❌ No disponible | ✅ Proxmox, Hyper-V, VMware |
| Conversión de Formato | ❌ No disponible | ✅ qcow2↔vhdx↔vmdk |
| Métricas Prometheus | ❌ No disponible | ✅ Nativo |
| Log JSON | ❌ No disponible | ✅ Nativo |
| Lenguaje | C/C++ propietario | Rust (memory-safe, open) |
| Costo | Licencia Enterprise | Significativamente menor |
| Soporte multi-plataforma | KVM, VMware | KVM (nativo), cross a otros |
PodHeitor CloudStack vs. Veeam Backup for CloudStack
| Criterio | Veeam | PodHeitor CloudStack |
|---|---|---|
| Plataforma | Propietaria | Integra con Bacula (open) |
| Incremental CBT | ✅ Vía CloudStack API | ✅ Vía QMP bitmap (más eficiente) |
| Replicación DR | ✅ | ✅ Integrada en el mismo producto |
| Restauración Cruzada | ✅ Para VMware | ✅ Proxmox, Hyper-V, VMware |
| Deduplicación | Servidor dedicado | Nativa en el plugin |
| Costo | Alto (por VM/socket) | 50-70% menor |
| Integración | Standalone | Unificado con Bacula (todo integrado) |
| CloudStack 4.19/4.20 | ✅ | ✅ |
PodHeitor CloudStack vs. Acronis Cyber Backup
| Criterio | Acronis | PodHeitor CloudStack |
|---|---|---|
| Agente | Agente dentro de la VM | Sin agente en la VM |
| Backup | Guest-level (archivo) | Block-level (imagen) |
| Consistencia | VSS / quiesce | quiesce via qemu-guest-agent |
| Replicación | ✅ (Acronis Cloud) | ✅ (site-to-site) |
| Cross-platform | ✅ | ✅ |
| Costo | Alto (por agente/GB) | Significativamente menor |
| Control | En la nube Acronis | On-premise total |
17. Hoja de Ruta
v1.1.0 (Estimado: 2026-06)
- Soporte para Ceph/RBD primary storage (modo nativo via
librbd) - Dashboard Grafana preconfigurado (JSON bundle)
- Soporte para Bacula 16.x (cuando sea lanzado)
- Web UI mínima para estado de replicación
- Prueba de integración end-to-end para restauración cruzada (lab)
v1.2.0 (Estimado: 2026-09)
- Soporte para CloudStack 4.21 (cuando sea lanzado)
- Integración de secondary storage compatible con S3
- Backup agentless de VMs Windows via VSS (sin qemu-guest-agent)
- CLI de gestión unificado
- Soporte para múltiples entornos CloudStack (multi-zone, multi-region)
v2.0.0 (Estimado: 2026-12)
- PodHeitor Unified Backup Platform: CloudStack + Proxmox + Hyper-V + vSphere bajo un único panel
- Orquestación DR automatizada con playbooks
- Integración con Bacularis (interfaz web de Bacula)
- API REST para automatización externa (Ansible, Terraform)
18. Referencias
- Apache CloudStack Documentation
- PodHeitor Backup Documentation
- QEMU QMP Reference
- NBD Protocol Specification
- VHDX Specification (Microsoft)
- VMDK Format (VMware)
- Rust Language
- zstd Compression
- xxHash
Contacto y Soporte
Heitor Faria
- Email: heitor@opentechs.lat
- WhatsApp / Telefone: +1 786 726-1749
- WhatsApp / Telefone: +55 61 98268-4220
Para consultoría, implementación, soporte y capacitación:
- Implementación del plugin en producción
- Configuración de DR y failover
- Migración entre hypervisors
- Capacitación del equipo técnico
- Personalizaciones e integraciones específicas
Copyright © 2026 Heitor Faria — Todos los derechos reservados.
Este documento y el software descrito son propiedad exclusiva de Heitor Faria. Reproducción parcial o total permitida solo con autorización expresa por escrito.
—
OFERTA ESPECIAL PARA CLIENTES DE BACULA ENTERPRISE, VEEAM, COMMVAULT Y NETBACKUP:
Traiga su propuesta de renovación. Garantizamos al menos 50% de descuento, con funcionalidades adicionales que los competidores no ofrecen.
heitor@opentechs.lat | +1 786 726-1749 | +55 61 98268-4220 (WhatsApp)
Licenciamiento
PodHeitor CloudStack es software propietario, distribuido por suscripción. Para condiciones comerciales, demostración técnica o diagnóstico gratuito de 30 minutos, habla con el equipo por los canales abajo.
¿Listo para evaluar?
- 💬 WhatsApp: +1 (786) 726-1749
- ✉️ Email: heitor@opentechs.lat
- 🩺 Diagnóstico gratuito — 30 min con Heitor Faria
Disponível em:
Português (Portugués, Brasil)
English (Inglés)
Español