Backup VM-level com CBT block-level (QEMU dirty bitmaps), replicação contínua DR, cross-restore para Proxmox/Hyper-V/VMware e conversão de formato em streaming — capacidades que o plugin CloudStack do Bacula Enterprise não cobre.
Documento técnico complementar à página do plugin PodHeitor CloudStack.
1. O problema: o plugin CloudStack do Bacula Enterprise é API-only
O Bacula Enterprise CloudStack plugin opera estritamente sobre a API do CloudStack: cria snapshot de volume, faz download via API, restaura via API. Isso tem três limites duros:
- Sem CBT real. O incremental do BEE é diff de snapshot a nível de API — granularidade de volume, não de bloco. Para uma VM de 200 GB com 500 MB modificados, o BEE ainda transfere a diferença de snapshot completa do CloudStack, não os blocos sujos.
- Sem replicação contínua. O BEE não tem modo DR push. Se você quer replicação para site secundário, precisa de uma segunda solução (CloudStack Snapshot Schedule + cópia manual).
- Sem cross-restore. O backup CloudStack do BEE só restaura para CloudStack. Migrar para Proxmox/Hyper-V/VMware exige conversão manual com
qemu-img+ reconstrução de config.
O PodHeitor CloudStack Plugin elimina os três gargalos com acesso direto KVM/libvirt via NBD, dirty bitmap nativo do QEMU, e pipeline de conversão em streaming.
2. Modelo arquitetural — 100% Rust cdylib
A v2.0.0+ é arquitetura 100% Rust. O FD-side .so é um cdylib puro (build a partir de ../PodHeitor Rust cdylib/crates/plugin-cloudstack/) que dlopen-a no bacula-fd. Nenhum source AGPLv3 do Bacula é vinculado estaticamente. O shim C++ legado foi removido — todas as responsabilidades (identidade do plugin, validação de parâmetros, driver PTCOMM, virtual-file buffering, restore replay) estão reimplementadas no framework Rust metaplugin-rs.
┌──────────────────────────────────────────────────────────┐
│ Bacula File Daemon (FD) │
│ ┌────────────────────────────┐ │
│ │ podheitor-cloudstack-fd.so│──PTCOMM──▶┌────────────┐ │
│ │ (Rust cdylib) │◀─────────│ Rust │ │
│ │ │ │ Backend │ │
│ └────────────────────────────┘ └─────┬──────┘ │
│ │ │
│ ┌──────────────────────────────────────────────┴─────┐ │
│ │ CloudStack API (HMAC-SHA1) + SSH/NBD data plane │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
O backend (~12.000 LOC Rust) é spawnado como subprocesso por job — preservando o boundary PTCOMM de crash-isolation. O cdylib é mínimo: identidade de plugin + driver PTCOMM + virtual-file buffering. Toda lógica de backup, restore, replicação e conversão vive no backend.
3. Three-Layer Change Detection (CBT)
O incremental real do PodHeitor não é diff de snapshot — é uma pipeline de três camadas que cobre os caminhos felizes e patológicos:
| Camada | Mecanismo | Função |
|---|---|---|
| L1 | QEMU QMP dirty bitmap | Apenas blocos modificados desde o último backup entram no pipeline |
| L2 | xxHash64 block hash | Elimina falsos-positivos de bitmap resets sujos (hard reboot do KVM, host crash) |
| L3 | SIMD zero-block detection | Elimina regiões esparsas/zeradas — benefício de thin provisioning |
Resultado típico: redução de janela de backup > 90% vs Full em incrementais sucessivos.
4. Modos de operação
| Mode | Propósito |
|---|---|
| (unset) | Backup Bacula normal (Full ou Incremental) |
bitmap_push |
DR contínuo — envia deltas de dirty-bitmap para dr_host |
seed |
Sync full inicial antes de mudar para bitmap_push |
receiver |
Daemon DR-side — aceita bitmap pushes recebidos |
daemon |
Sender de replicação long-running (out-of-band dos jobs Bacula) |
failover_pre / failover_exec |
Promove réplica DR a primary |
failback_pre / failback_exec |
Re-sync primary após failover e swap back |
reprotect |
Re-estabelece replicação na direção reversa |
replication_status |
Query de saúde sender/receiver |
5. Cross-restore — conversão em streaming
O plugin restaura uma VM CloudStack em hipervisor diferente do origem. Detectado pelo parâmetro cross_restore:
| Target | Conversão de disco | Adaptação de guest |
|---|---|---|
proxmox |
qcow2 → qcow2 (passthrough) ou raw | Geração de qm config; ajuste de driver virtio |
hyperv |
qcow2 → vhdx (streaming via qemu-img) | VMCX XML; integração services |
vmware |
qcow2 → vmdk (streaming) | VMX file; VMware Tools handoff |
A conversão é streaming: nenhum arquivo intermediário é materializado. O pipeline é NBD read → qemu-img convert (stdin) → SSH/SMB push (stdout). Para uma VM de 100 GB indo CloudStack→Hyper-V, isso evita 100 GB de I/O temporário no FD host.
6. Data plane — três modos NBD
O parâmetro nbd_access_mode seleciona o caminho de leitura de bytes do hipervisor KVM:
| Modo | Funcionamento | Quando usar |
|---|---|---|
ssh_tunnel (default) |
SSH ao host KVM, abre túnel para porta NBD local, conecta cliente NBD via tunnel | Default seguro — não exige abrir 10809 no firewall |
direct |
Cliente NBD conecta direto a kvm-host:10809 |
Quando rede de backup é segregada e firewall permite |
api_only |
Fallback API-only (download de snapshot via CloudStack) | Quando SSH ao host KVM falha — log warning |
Aritmética típica: direct entrega 3–5 GB/s em LAN 10 GbE; ssh_tunnel ~1.5–2.5 GB/s (limitado por overhead de criptografia); api_only raramente passa de 200 MB/s.
7. Replicação contínua DR (bitmap-push)
O modo bitmap_push transforma o plugin em um agente de replicação out-of-band: ciclos de cycle_interval segundos (default 300) leem o dirty bitmap atual, calculam delta vs último ciclo, e enviam para dr_host:dr_port sob transporte AES-256-GCM com PSK derivado.
- PSK ≥ 32 caracteres obrigatório.
- Retry com backoff exponencial:
retry_count=3,retry_delay_ms=5000, jitterretry_jitter_ms=1000. - Resiliência: se o receiver cair, o sender mantém o bitmap acumulado; reconexão retoma do último ciclo confirmado.
- Economia de banda: 95%+ vs cópia full (delta-only push).
O fluxo de failover automatiza a promoção: failover_pre drena pendências, failover_exec apresenta os disks da réplica para uma nova VM CloudStack no DR site. failback_pre/failback_exec + reprotect fecham o loop.
8. Performance
| Métrica | Valor típico |
|---|---|
| Throughput de Full backup | 1.5–5 GB/s (depende de modo NBD, rede, disco) |
| Redução de janela Incremental | 90%+ vs Full (dirty bitmap + hash dedup) |
| Economia de banda em replicação | 95%+ (delta-only push) |
| Compressão zstd (level 3) | 50–60% |
| Memória por disk backup concorrente | ~17 MB (NBD buffers + zstd context + hash working set) |
| Hash lookup | O(1) — array flat memory-mapped, OS page cache |
9. Hash DB memory-mapped
O hash DB do PodHeitor é um array flat memory-mapped indexado por xxHash64. Diferentemente de DBs tradicionais (LMDB, RocksDB), não há latência SSD no caminho hot — o page cache do kernel mantém o working set residente. Resultado:
- Lookup O(1) sem syscall (mmap-only).
- SSD-latency-independent: rode em HDD se quiser.
- Working set cresce com unique blocks, não com volume total.
10. Anti-patterns documentados
- Não desabilite
quiesceem workloads DB. Sem qemu-guest-agent freeze/thaw, snapshots de DB ficam crash-consistent — válidos mas não application-consistent. Para Postgres/MySQL/MSSQL, mantenhaquiesce=true. - Não use
verify_ssl=falseem produção. Importe a CA do CloudStack management no trust store do FD host.verify_ssl=falseexiste para PoC/lab. - Não rode replicação
bitmap_pushsobre WAN sem cap. O ciclo default de 300s + delta pode saturar 100 Mbps em VMs write-heavy. Configurebandwidth_limitou aumentecycle_interval.
11. License posture
O plugin distribui sob LicenseRef-PodHeitor-Proprietary. Não vincula estaticamente nenhum source AGPLv3 do Bacula. O binding é exclusivamente via extern "C" independente.
Pronto para avaliar?
Trial gratuito de 30 dias para workloads CloudStack qualificadas (KVM, validado em CloudStack 4.16/4.19/4.20). Garantimos no mínimo 50% de desconto vs Bacula Enterprise, Veeam, Commvault ou NetBackup, com mais funcionalidades inclusas — e cross-restore que nenhum dos três oferece.
Heitor Faria — Fundador, PodHeitor International
✉ [email protected]
☎ +1 (789) 726-1749 · +55 (61) 98268-4220 (WhatsApp)
🔗 Página do plugin PodHeitor CloudStack
Disponível em:
Português
English (Inglês)
Español (Espanhol)