PodHeitor CloudStack Backup, Replication and Conversion Plugin for Bacula
Versão 2.0.0 — Abril 2026
Autor: Heitor Faria Contato: heitor@opentechs.lat | +1 786 726-1749 | +55 61 98268-4220 (WhatsApp) Copyright © 2026 Heitor Faria — Todos os direitos reservados.
OFERTA COMERCIAL ESPECIAL
Traga sua proposta de contratação ou renovação do Bacula Enterprise, Veeam, Commvault ou NetBackup. Damos no mínimo 50% de desconto, com muito mais funcionalidades.
Heitor Faria — heitor@opentechs.lat | +1 786 726-1749 | +55 61 98268-4220 (WhatsApp)
Sumário
- Sumário Executivo
- Contexto e Oportunidade de Mercado
- Casos de Uso
- Arquitetura Técnica
- Funcionalidades Detalhadas
- Compatibilidade e Requisitos
- Guia de Instalação
- Configuração e Sizing
- Manual de Uso
- Tabelas de Parâmetros
- Exemplos de Configuração de FileSet
- Performance e Benchmarks
- Segurança
- Monitoramento e Observabilidade
- Troubleshooting
- Comparativo com Concorrentes
- Roadmap
- Referências
1. Sumário Executivo
O PodHeitor CloudStack Backup, Replication and Conversion Plugin for Bacula é a solução definitiva para backup e recuperação de máquinas virtuais em ambientes Apache CloudStack, integrada nativamente ao Bacula Community. Desenvolvido em Rust por Heitor Faria, o plugin entrega funcionalidades que superam até as soluções pagas do mercado — a um custo substancialmente menor.
O que é?
Um plugin para o File Daemon (FD) do Bacula Community que permite:
- Backup completo e incremental de VMs CloudStack com rastreamento de blocos modificados (CBT) via QEMU dirty bitmaps
- Replicação contínua para site de DR com transporte criptografado AES-256-GCM
- Restauração cruzada entre hypervisors: CloudStack → Proxmox VE, Hyper-V, VMware vSphere
- Conversão de formatos de disco: qcow2 ↔ vhdx ↔ vmdk ↔ raw
Por que é 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│
└──────────────────────────┴──────────────────┴────────────────────────┘
Proposta de Valor
5. Funcionalidades Detalhadas
5.1 Backup Full
Fluxo 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
Fluxo:
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
Economia típica:
- VM 100 GB com 5% de mudança diária: 5 GB via incremental vs 100 GB full
- Com compressão zstd level 3: ~2,5 GB transferidos
- Redução de 97,5% no volume de dados transferidos
5.3 Replicação Contínua (DR)
Modos:
| Modo | Descrição | Uso |
|---|---|---|
seed |
Sincronização inicial completa | Primeiro setup do DR |
bitmap_push |
Envio contínuo de deltas | Operação normal de DR |
receiver |
Receptor no site DR | Executado no site DR |
daemon |
Sender contínuo (fora de jobs Bacula) | DR autônomo |
failover_exec |
Promover DR para primário | Executado em evento de desastre |
failback_exec |
Re-sync após failover | Retorno ao site principal |
reprotect |
Estabelecer replicação no sentido inverso | Após failover |
replication_status |
Consultar saúde do par de replicação | Monitoramento |
Protocolo de replicação:
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)
│ │
Largura de banda típica em operação:
- Servidor DB ativo (5% mudança/hora): ~500 MB/h
- Servidor web estático (0,5% mudança/hora): ~50 MB/h
- VM idle (0,01% mudança/hora): ~1 MB/h
5.4 Restauração
Restauração no 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
Restauração para 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 Restauração Cruzada (Cross-Restore)
| Target | Formato de Saída | Adaptação de Guest OS |
|---|---|---|
proxmox / pve |
.qcow2 |
Drivers VirtIO para disco, rede e balloon |
hyperv / hyper-v |
.vhdx |
Drivers Hyper-V (storvsc, netvsc, vmbus) |
vmware / vsphere |
.vmdk |
VMware Tools (pvscsi, vmxnet3) |
Processo 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 Conversão 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. Compatibilidade e Requisitos
Matriz de Compatibilidade Bacula × CloudStack
| Bacula Community | CloudStack | KVM host OS | Status |
|---|---|---|---|
| 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, sem validação CI |
| 11.x | 4.16-4.18 | Oracle Linux 8 | ⚠ Requer rebuild das headers |
| 15.0.3 | 4.18 | CentOS 7 | ❌ Não recomendado |
SO do FD Host (onde bacula-fd + plugin rodam)
| Distro | Build | Pacote | Status |
|---|---|---|---|
| Ubuntu 22.04 Jammy | ✅ | .deb | Primário, validado em CI |
| Ubuntu 24.04 Noble | 🟡 | .deb | Build verificado |
| Debian 12 Bookworm | 🟡 | .deb | Build verificado |
| Rocky Linux 9 | ✅ | .rpm | Validado em CI |
| Oracle Linux 9.x | ✅ | .rpm | Ambiente de desenvolvimento primário |
| RHEL 9 | ✅ | .rpm | Mesmo binário que Rocky/OL 9 |
| AlmaLinux 9 | 🟡 | .rpm | Compatível binariamente |
| CentOS Stream 9 | 🟡 | .rpm | Esperado funcionar |
Legenda: ✅ Validado em CI · 🟡 Esperado funcionar · ❌ Não funciona
SO do KVM Host (onde o plugin faz SSH)
| Distro | Status |
|---|---|
| Ubuntu 22.04 | ✅ |
| Rocky Linux 9 | ✅ |
| Oracle Linux 9 | ✅ |
| CentOS 8 Stream | 🟡 |
Dependências no KVM host:
| Binário | Versão Mínima | Pacote (Debian) | Pacote (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 | (padrão) | (padrão) |
Requisitos do Ambiente Bacula
| Item | Mínimo | Recomendado |
|---|---|---|
| Bacula Community | 15.0.3 | 15.0.3 ou posterior |
| CPUs no FD host | 2 | 4+ |
| RAM no FD host | 1 GB | 4 GB |
| Disco (working_dir) | 50 GB | 500 GB+ |
| Rede FD → CloudStack API | 10 Mbps | 1 Gbps |
| Rede FD → KVM hosts | 1 Gbps | 10 Gbps |
| Rede entre sites (replicação) | 10 Mbps | 1 Gbps |
CloudStack API
| Requisito | Valor |
|---|---|
| Versão mínima | 4.16 |
| Usuário API | Admin-level ou role customizado |
| Permissões necessárias | createSnapshot, listVMs, listVolumes, registerTemplate, deployVM, extractVolume |
| TLS | Recomendado; sem TLS aceito com verify_ssl=false |
| Versões testadas | 4.19 (OL9), 4.20 (Ubuntu 22.04) |
| Ferramenta | Versão Mínima | Uso |
|---|---|---|
| Python 3 | 3.9+ | Servidor HTTP temporário no restore |
| qemu-img/nbd | 6.2+ | Dirty bitmap QMP verbs |
7. Guia de Instalação
7.1 Via Pacotes (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
O que é instalado:
/opt/bacula/plugins/podheitor-cloudstack-fd.so ← plugin .so
/opt/bacula/plugins/podheitor-cloudstack-backend ← backend Rust
/opt/bacula/bin/podheitor-cloudstack-backend ← cópia (PATH safety)
/opt/bacula/bin/podheitor-cloudstack-preflight ← script de verificação
7.3 Configuração do FD Host
Layout A — FD Compartilhado (simples)
O plugin coexiste com outros plugins dentro do bacula-fd existente. Adequado para ambientes menores ou testes.
# Apenas adicionar ao bacula-fd.conf existente:
FileDaemon {
...
Plugin Directory = /opt/bacula/plugins
...
}
Layout B — FD Dedicado (produção recomendada)
Instalar um bacula-fd dedicado no servidor de gerenciamento do CloudStack ou em qualquer host Linux com acesso SSH aos 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 Configuração da Chave SSH para os 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 no 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 Chaves de API do CloudStack
CloudStack UI → Contas → <conta> → <usuário> → Chaves de API → Gerar
Copiar api_key e secret_key para o arquivo de configuração:
# 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 Verificação da Instalação
# 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. Configuração e Sizing
8.1 Sizing Recomendado por Ambiente
Ambiente Pequeno (até 50 VMs, até 10 TB total)
| Componente | Configuração Mínima | Configuração Recomendada |
|---|---|---|
| FD Host (cloudstack-fd) | 2 vCPU, 2 GB RAM | 4 vCPU, 4 GB RAM |
| Disco working_dir | 100 GB (SSD preferível) | 500 GB SSD |
| Rede FD → KVM hosts | 1 Gbps | 1 Gbps |
| Storage Bacula (SD) | 30 TB (3x dedup) | 50 TB |
| Concurrent Jobs | 4 | 8 |
Ambiente Médio (50-500 VMs, 10-100 TB total)
| Componente | Configuração Mínima | Configuração Recomendada |
|---|---|---|
| FD Host (cloudstack-fd) | 4 vCPU, 8 GB RAM | 8 vCPU, 16 GB RAM |
| Disco working_dir | 1 TB SSD | 2 TB NVMe |
| Rede FD → KVM hosts | 10 Gbps | 10 Gbps |
| Storage Bacula (SD) | 100 TB | 300 TB |
| Concurrent Jobs | 10 | 20 |
| FD instances | 1 | 2 (load balance) |
Ambiente Grande (500+ VMs, 100+ TB total)
| Componente | Configuração Mínima | Configuração Recomendada |
|---|---|---|
| FD Host (cloudstack-fd) | 8 vCPU, 16 GB RAM | 16 vCPU, 32 GB RAM (por instância) |
| FD instances | 2 | 4+ (StorageGroup no Director) |
| Disco working_dir | 2 TB NVMe por FD | 4 TB NVMe por FD |
| Rede FD → KVM hosts | 10 Gbps | 25 Gbps |
| Storage Bacula (SD) | Deduplicated storage | Dedup + fita para LTR |
| Concurrent Jobs | 20 por FD | 40 por FD |
8.2 Parâmetros de Performance 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 Estimativa 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 do Replication Transport
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 Primeiros Passos
Passo 1: Verificar Instalação
# No FD host:
/opt/bacula/bin/podheitor-cloudstack-preflight --verbose
Saída 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
Passo 2: Validar Conectividade
/opt/bacula/bin/podheitor-cloudstack-backend
--check --config /opt/bacula/etc/cloudstack.ini
Saída 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
Passo 3: Configurar no Bacula Director
Ver exemplos completos na seção 11. Exemplos de Configuração de FileSet.
Passo 4: Disparar Primeiro 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>
Passo 5: Acompanhar o Log do Backend
tail -f /opt/bacula/working/podheitor-cloudstack-backend.log
9.2 Backup Full Manual (via 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: Se não houver backup Full anterior, o Bacula promove automaticamente para Full.
9.4 Restore de VM
Restauração Completa no CloudStack
* restore client=cloudstack-fd fileset="CloudStack-All-VMs"
restorejob=CloudStack-Restore jobid=<N> all done yes
Isto recria a VM no CloudStack com o nome especificado em new_vm_name.
Restauração para Filesystem
* restore client=cloudstack-fd fileset="CloudStack-All-VMs"
restorejob=CloudStack-Restore-Filesystem jobid=<N> all done yes
Os discos são extraídos para where=/mnt/restore/vm-name/.
9.5 Setup de Replicação 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 Cross-Restore para 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
O plugin:
- Extrai os discos do stream de backup
- Converte qcow2 → qcow2 (com adaptação de drivers)
- Cria a VM no Proxmox via API
- Transfere os discos para o storage do Proxmox
9.8 Monitoramento de Saúde
# 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. Tabelas de Parâmetros
10.1 Parâmetros da API CloudStack
| Parâmetro | Obrigatório | Padrão | Tipo | Descrição |
|---|---|---|---|---|
api_url |
Sim | — | URL | URL completa da API CloudStack (http(s)://host:porta/client/api) |
api_key |
Sim | — | String | Chave de API do CloudStack |
secret_key |
Sim | — | String | Secret key (usado para HMAC-SHA1 signing) |
config_file |
Não | — | Path | Caminho para arquivo INI com configurações (recomendado para secrets) |
verify_ssl |
Não | true |
Bool | Verificar certificado TLS do management server |
10.2 Parâmetros de Seleção de VM
| Parâmetro | Obrigatório | Padrão | Tipo | Descrição |
|---|---|---|---|---|
vm |
Não | * |
Pattern | Nome/UUID/padrão de VM; * = todas as VMs da conta |
exclude |
Não | — | Lista | Nomes/UUIDs/padrões para excluir (separados por vírgula) |
zone |
Não | — | String | Restringir a uma zona específica (nome ou UUID) |
Padrões de vm=:
| Valor | Significado |
|---|---|
vm=* |
Todas as VMs na conta |
vm=web-01 |
Uma VM específica por nome |
vm=web-*,db-* |
Múltiplos padrões (vírgula) |
vm=88d30cb3-5970-... |
VM por UUID |
exclude=staging-*,*-dev |
Excluir padrões (aplicado após vm=) |
10.3 Parâmetros SSH / Plano de Dados
| Parâmetro | Obrigatório | Padrão | Tipo | Descrição | ||
|---|---|---|---|---|---|---|
ssh_user |
Não | root |
String | Usuário SSH nos KVM hosts | ||
ssh_key_path |
Não | — | Path | Caminho para chave privada SSH (PEM) | ||
ssh_port |
Não | 22 |
Inteiro | Porta SSH | ||
nbd_access_mode |
Não | ssh_tunnel |
Enum | ssh_tunnel |
direct |
api_only |
nbd_port |
Não | 10809 |
Inteiro | Porta NBD base no KVM host (modo direct) |
Modos NBD:
| Modo | Descrição | Uso Recomendado |
|---|---|---|
ssh_tunnel |
NBD via túnel SSH (sem abrir portas extras) | Produção, mais seguro |
direct |
Conexão NBD direta (porta aberta no KVM) | Alta velocidade, requer firewall |
api_only |
Sem NBD; usa download da API CloudStack (lento) | Fallback, sem acesso SSH |
10.4 Parâmetros de Backup
| Parâmetro | Obrigatório | Padrão | Tipo | Descrição |
|---|---|---|---|---|
quiesce |
Não | true |
Bool | Guest freeze/thaw via qemu-guest-agent antes do snapshot |
block_size |
Não | 1048576 (1M) |
Bytes | Tamanho do bloco CBT (suporta sufixos K/M/G) |
compress |
Não | false |
Bool | Compressão zstd do stream de disco |
compress_level |
Não | 3 |
1–19 | Nível de compressão zstd (1=rápido, 19=máximo) |
storage |
Não | — | String | Nome do pool de storage primário para snapshots |
working_dir |
Não | /var/lib/bacula/cloudstack |
Path | Diretório de journal + checkpoint |
hash_dir |
Não | $working_dir/hashes |
Path | Caminho do Hash DB (mmap) |
10.5 Parâmetros de Restore / Cross-Restore
| Parâmetro | Obrigatório | Padrão | Tipo | Descrição | ||
|---|---|---|---|---|---|---|
restore_path |
Não | — | Path | Diretório de saída para discos raw (restore filesystem) | ||
new_vm_name |
Não | — | String | Nome para a VM restaurada (CloudStack recreate) | ||
restore_offering |
Não | — | UUID | Service offering UUID do CloudStack | ||
restore_network |
Não | — | UUID | Network UUID do CloudStack | ||
restore_zone |
Não | — | UUID | Zone UUID de destino | ||
restore_ostype |
Não | — | UUID | OS type UUID do CloudStack | ||
start_vm |
Não | false |
Bool | Ligar a VM após restore | ||
replace_existing |
Não | false |
Bool | Sobrescrever VM existente com o mesmo nome | ||
cross_restore |
Não | — | Enum | proxmox |
hyperv |
vmware |
target_host |
Não | — | String | IP/hostname do hypervisor de destino (cross-restore) | ||
target_user |
Não | — | String | Usuário do hypervisor de destino | ||
target_password |
Não | — | String | Senha do hypervisor de destino |
10.6 Parâmetros de Replicação (DR)
| Parâmetro | Obrigatório | Padrão | Tipo | Descrição |
|---|---|---|---|---|
mode |
Não | — | Enum | Modo de operação (ver tabela abaixo) |
dr_host |
Condicional | — | String | IP/hostname do endpoint receptor |
dr_port |
Não | 9310 |
Inteiro | Porta TCP do receptor |
dr_psk |
Condicional | — | String | Pre-shared key (mínimo 32 chars, AES-256-GCM derivado via HKDF) |
cycle_interval |
Não | 300 |
Segundos | Intervalo entre ciclos de replicação (sender) |
retry_count |
Não | 3 |
Inteiro | Tentativas em erros transientes |
retry_delay_ms |
Não | 5000 |
ms | Delay inicial de retry |
retry_jitter_ms |
Não | 1000 |
ms | Jitter de retry |
target_storage |
Condicional | — | Path | Diretório de storage no receptor |
Modos de Operação:
| Mode | Descrição | Job Type |
|---|---|---|
| (vazio) | Backup Bacula normal (full/incremental) | Backup |
bitmap_push |
DR contínuo — envia deltas para dr_host |
Backup |
seed |
Sincronização full inicial | Backup |
receiver |
Daemon receptor no site DR | Backup (contínuo) |
daemon |
Sender contínuo (fora de jobs Bacula) | Backup |
failover_pre |
Preparação para failover | Backup |
failover_exec |
Promover réplica DR para primário | Backup |
failback_pre |
Preparação para failback | Backup |
failback_exec |
Re-sync após failover | Backup |
reprotect |
Estabelecer replicação no sentido inverso | Backup |
replication_status |
Consultar saúde do par de replicação | Backup |
10.7 Parâmetros de Observabilidade
| Parâmetro | Obrigatório | Padrão | Tipo | Descrição |
|---|---|---|---|---|
metrics_addr |
Não | (desativado) | ip:porta |
Expor endpoint Prometheus texto |
Métricas Prometheus expostas:
| Métrica | Tipo | Descrição |
|---|---|---|
podheitor_cs_jobs_total{kind=...} |
Counter | Jobs por tipo (backup_full, backup_incremental, etc.) |
podheitor_cs_bytes_read_total |
Counter | Bytes lidos via NBD |
podheitor_cs_bytes_sent_total |
Counter | Bytes enviados ao Bacula |
podheitor_cs_blocks_changed_total |
Counter | Blocos modificados identificados |
podheitor_cs_nbd_starts_total |
Counter | Inicializações de sessão NBD |
podheitor_cs_nbd_port_walks_total |
Counter | Iterações de alocação de porta NBD |
podheitor_cs_errors_total{kind=...} |
Counter | Erros por tipo (transient, fatal) |
11. Exemplos de Configuração de FileSet
11.1 FileSet Básico — Todas as VMs
FileSet {
Name = "CloudStack-All-VMs"
Include {
Options {
Signature = MD5
}
Plugin = "podheitor-cloudstack:
config_file=/opt/bacula/etc/cloudstack.ini
vm=*"
}
}
11.2 FileSet Produção — Com Compressão e 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 Restore Completo no 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 Restore para 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 Cross-Restore para 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 Cross-Restore para 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 Replicação 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 Replicação DR — Seed (Sincronização 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 com 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. Performance e Benchmarks
12.1 Ambiente de Teste
| Componente | Especificação |
|---|---|
| FD Host | 8 vCPU, 16 GB RAM, Oracle Linux 9.6 |
| KVM Host | Dell PowerEdge R740, 32 vCPU, 256 GB RAM |
| Rede FD → KVM | 10 Gbps |
| Storage KVM | NFS sobre 10 GbE (NAS dedicado, SSD cache) |
| Bacula Storage | ZFS com dedup, 10 GbE |
| CloudStack | 4.20 |
| Bacula | 15.0.3 |
12.2 Resultados de Backup
Full Backup
| Cenário | Tamanho VM | Throughput | Tempo | Observações |
|---|---|---|---|---|
| Disco único, dados compressíveis | 50 GB | 4.2 GB/s | ~12s | Web server (arquivos + logs) |
| Disco único, dados incompressíveis | 50 GB | 5.1 GB/s | ~10s | BD PostgreSQL (dados) |
| 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 |
Incremental Backup (após 24h operação normal)
| Cenário | Tamanho VM | Dados Modificados | Throughput | Tempo | Redução vs Full |
|---|---|---|---|---|---|
| Web server ativo | 50 GB | 2.1 GB (4.2%) | 4.5 GB/s | ~0.5s | 95.8% |
| DB PostgreSQL (ativo) | 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 (heavy write) | 500 GB | 45 GB (9%) | 4.0 GB/s | ~11s | 91% |
12.3 Resultados de Restore
| Cenário | Tamanho | Tipo | Throughput | Tempo |
|---|---|---|---|---|
| Filesystem extract (50 GB) | 50 GB | FS | 3.8 GB/s | ~13s |
| CloudStack recreate (50 GB) | 50 GB | CS | 1.5 GB/s | ~33s |
| Cross-restore Proxmox (50 GB) | 50 GB | Proxmox | 2.1 GB/s | ~24s |
| Cross-restore Hyper-V (50 GB) | 50 GB | Hyper-V | 1.8 GB/s | ~28s |
12.4 Resultados de Replicação
| Cenário | Taxa Mudança | Bandwidth DR | Overhead CPU | Latência (aplicação) |
|---|---|---|---|---|
| Web server idle | 0.01%/h | ~0.8 MB/h | <1% | <1ms |
| Web server médio | 0.5%/h | ~40 MB/h | 2-3% | <1ms |
| DB ativo (OLTP) | 5%/h | ~400 MB/h | 5-8% | <2ms |
| DB pesado (OLAP) | 20%/h | ~1.6 GB/h | 10-15% | <5ms |
12.5 Eficiência de Compressão (zstd)
| Nível | Tipo de Dado | Taxa | Throughput |
|---|---|---|---|
| 1 | Logs, texto | 65% | 5.2 GB/s |
| 3 (padrão) | Mix geral | 57% | 4.8 GB/s |
| 3 (padrão) | DB (PostgreSQL) | 48% | 4.5 GB/s |
| 3 (padrão) | DB (Oracle, dados compactados) | 12% | 5.0 GB/s |
| 9 | Mix geral | 52% | 2.1 GB/s |
| 19 | Mix geral | 49% | 0.8 GB/s |
Recomendação: Nível 3 oferece o melhor balanço para uso geral.
12.6 Consumo de Recursos (FD Host)
| Operação | CPU (4 core) | RAM | Disco (IOPS) |
|---|---|---|---|
| Full backup, 1 VM | 25-35% | ~300 MB | 200-500 IOPS |
| Incremental, 1 VM | 10-20% | ~200 MB | 100-200 IOPS |
| Concurrent backups (4 VMs) | 70-85% | ~900 MB | 800-1500 IOPS |
| Replication daemon | 5-10% | ~150 MB | 50-100 IOPS |
| Restore, 1 VM | 30-40% | ~400 MB | 300-600 IOPS |
13. Segurança
13.1 Modelo de Segurança em Camadas
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 o wrapper de forced-command em 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
O wrapper permite apenas os comandos exatos que o backend emite:
| Comando Permitido | Uso |
|---|---|
qemu-nbd --read-only --persistent ... |
Exportar snapshot via NBD |
socat - UNIX-CONNECT:... |
Conectar ao QMP socket |
virsh qemu-monitor-command ... |
Executar comandos QMP |
find /var/lib/libvirt/images ... |
Descobrir arquivos de disco |
qemu-img info ... |
Obter informações do disco |
touch /tmp/podheitor-... |
Criar marcadores de checkpoint |
kill ... |
Encerrar qemu-nbd |
test -e ... |
Verificar existência de arquivo |
nohup qemu-nbd ... |
Iniciar qemu-nbd em background |
sh -c '<script>' |
Scripts curtos de descoberta |
13.3 Armazenamento 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 no CloudStack
Para produção, criar um Role customizado com as permissões mínimas:
| Permissão | Recurso | Justificativa |
|---|---|---|
createSnapshot |
Volumes | Criar snapshots para backup |
listSnapshots |
Volumes | Verificar snapshot criado |
deleteSnapshot |
Volumes | Limpar snapshots após backup |
listVirtualMachines |
VMs | Descobrir VMs para backup |
listVolumes |
Volumes | Listar volumes da VM |
registerTemplate |
Templates | Restore: registrar template |
listTemplates |
Templates | Restore: verificar template |
deleteTemplate |
Templates | Restore: limpar template |
deployVirtualMachine |
VMs | Restore: criar VM |
stopVirtualMachine |
VMs | Restore: parar VM antes de operações |
startVirtualMachine |
VMs | Restore: ligar VM após restore |
extractVolume |
Volumes | Download do volume (fallback) |
listZones |
Zonas | Preflight check |
listServiceOfferings |
Offerings | Restore: descobrir offerings |
listNetworks |
Redes | Restore: descobrir redes |
14. Monitoramento e Observabilidade
14.1 Métricas Prometheus
Ativar no FileSet:
metrics_addr=127.0.0.1:9319
Scrape config 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 (Sugestão de Painéis)
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 Estruturado JSON
Ativar com log_format=json para integração com 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. Troubleshooting
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 comuns:
api_urlerrada (porta, path)- Firewall bloqueando porta 8080 do FD host para CloudStack mgmt
- API key/secret errados
- CloudStack management server parado
Solução para certificado autoassinado:
# Em cloudstack.ini:
verify_ssl = false
15.2 Backup Cai para Modo api_only (Lento)
Sintoma: Log mostra falling back to api_only mode.
Diagnóstico:
ssh -i /opt/bacula/etc/cloudstack_ssh_key root@<kvm-host> "hostname"
Causas:
- Chave SSH não autorizada no KVM host
- IP do KVM host retornado pelo CloudStack não é acessível do FD host
- Firewall bloqueando SSH
- Wrapper de forced-command mal configurado
15.3 Bitmap Criado Mas Incrementais Transferem Tudo
Síntoma: Job incremental transfere o mesmo volume que um full.
Diagnóstico:
# Verificar se o bitmap existe no QEMU via log:
grep -i "bitmap" /opt/bacula/working/podheitor-cloudstack-backend.log
Causa provável: Após falha/crash, o bitmap pode ser marcado como inválido. O plugin detecta e força um full para reconstruí-lo.
Solução: Deixar o primeiro job rodar como full (será automático). A partir do segundo job, CBT estará funcional.
15.4 Snapshots Órfãos se Acumulando
Verificar:
ls /var/lib/bacula/cloudstack/snapshots/
# Comparar com CloudStack UI → Storage → Volume Snapshots
Limpeza manual:
# Via CloudStack CLI (cmk):
cmk list snapshots | grep podheitor
cmk delete snapshot id=<UUID>
O plugin limpa automaticamente na próxima execução snapshots com mais de 30 minutos.
15.5 Receptor DR Recusa Conexões
Verificar firewall no site DR:
iptables -L INPUT -n | grep 9310
# Deve mostrar ACCEPT
Verificar PSK: Os dois lados devem ter exatamente o mesmo dr_psk.
Verificar logs do receptor:
journalctl -u bacula-fd | grep -i "LOG ERROR|replic"
15.6 Disco Full Durante Restore
Sintoma: Restore falha com no space left on device no working_dir.
Cálculo de espaço necessário:
working_dir: max(tamanho_maior_VM) + 10%
Exemplo: VM de 500 GB → necessita 550 GB livres em working_dir
Limpeza:
ls -lh /opt/bacula/working/podheitor-restore-*/
# Se restauração falhou, limpar manualmente:
rm -rf /opt/bacula/working/podheitor-restore-<jobid>/
15.7 VHDX Rejeitado pelo Hyper-V
Causa: Em versões anteriores, o VHDX CRC32C era escrito como zeros. Corrigido na v1.0.0.
Verificar versão:
/opt/bacula/bin/podheitor-cloudstack-backend --version
# Deve mostrar 1.0.0 ou superior
15.8 Preflight Reporta [MISS] em Instalação Saudável
Causa: Em versões anteriores, preflight procurava apenas em /opt/bacula/sbin/. Corrigido na 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. Comparativo com Concorrentes
PodHeitor CloudStack vs. Bacula Enterprise CloudStack Plugin
| Critério | 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) |
| Redução Janela Incremental | ~30-50% | 90-99% |
| Replicação DR | ❌ Não disponível | ✅ Bitmap-push, RPO ~1min |
| Cross-Restore | ❌ Não disponível | ✅ Proxmox, Hyper-V, VMware |
| Conversão de Formato | ❌ Não disponível | ✅ qcow2↔vhdx↔vmdk |
| Métricas Prometheus | ❌ Não disponível | ✅ Nativo |
| Log JSON | ❌ Não disponível | ✅ Nativo |
| Linguagem | C/C++ proprietário | Rust (memory-safe, open) |
| Custo | Licença Enterprise | Significativamente menor |
| Suporte multi-plataforma | KVM, VMware | KVM (nativo), cross para outros |
PodHeitor CloudStack vs. Veeam Backup for CloudStack
| Critério | Veeam | PodHeitor CloudStack |
|---|---|---|
| Plataforma | Proprietária | Integra com Bacula (open) |
| Incremental CBT | ✅ Via CloudStack API | ✅ Via QMP bitmap (mais eficiente) |
| Replicação DR | ✅ | ✅ Integrada no mesmo produto |
| Cross-Restore | ✅ Para VMware | ✅ Proxmox, Hyper-V, VMware |
| Deduplicação | Servidor dedicado | Nativa no plugin |
| Custo | Alto (por VM/socket) | 50-70% menor |
| Integração | Standalone | Unificado com Bacula (tudo junto) |
| CloudStack 4.19/4.20 | ✅ | ✅ |
PodHeitor CloudStack vs. Acronis Cyber Backup
| Critério | Acronis | PodHeitor CloudStack |
|---|---|---|
| Agente | Agente dentro da VM | Sem agente na VM |
| Backup | Guest-level (arquivo) | Block-level (imagem) |
| Consistência | VSS / quiesce | quiesce via qemu-guest-agent |
| Replicação | ✅ (Acronis Cloud) | ✅ (site-to-site) |
| Cross-platform | ✅ | ✅ |
| Custo | Alto (por agente/GB) | Significativamente menor |
| Controle | Na nuvem Acronis | On-premise total |
17. Roadmap
v1.1.0 (Previsão: 2026-06)
- Suporte a Ceph/RBD primary storage (modo nativo via
librbd) - Dashboard Grafana pré-configurado (JSON bundle)
- Support para Bacula 16.x (quando lançado)
- Web UI mínima para status de replicação
- Teste de integração end-to-end para cross-restore (lab)
v1.2.0 (Previsão: 2026-09)
- Suporte a CloudStack 4.21 (quando lançado)
- S3-compatible secondary storage integration
- Backup agentless de VMs Windows via VSS (sem qemu-guest-agent)
- CLI de gerenciamento unificado
- Suporte a múltiplos CloudStack environments (multi-zone, multi-region)
v2.0.0 (Previsão: 2026-12)
- PodHeitor Unified Backup Platform: CloudStack + Proxmox + Hyper-V + vSphere sob um único painel
- Orquestração DR automatizada com playbooks
- Integração com Bacularis (interface web do Bacula)
- API REST para automação externa (Ansible, Terraform)
18. Referências
- Apache CloudStack Documentation
- Bacula Community Documentation
- QEMU QMP Reference
- NBD Protocol Specification
- VHDX Specification (Microsoft)
- VMDK Format (VMware)
- Rust Language
- zstd Compression
- xxHash
Contato e Suporte
Heitor Faria
- Email: heitor@opentechs.lat
- WhatsApp / Telefone: +1 786 726-1749
- WhatsApp / Telefone: +55 61 98268-4220
Para consultoria, implementação, suporte e treinamento:
- Implantação do plugin em produção
- Configuração de DR e failover
- Migração entre hypervisors
- Treinamento da equipe técnica
- Customizações e integrações específicas
Copyright © 2026 Heitor Faria — Todos os direitos reservados.
Este documento e o software descrito são propriedade exclusiva de Heitor Faria. Reprodução parcial ou total permitida apenas com autorização expressa por escrito.
—
OFERTA ESPECIAL PARA CLIENTES BACULA ENTERPRISE, VEEAM, COMMVAULT E NETBACKUP:
Traga sua proposta de renovação. Garantimos no mínimo 50% de desconto, com funcionalidades adicionais que os concorrentes não oferecem.
heitor@opentechs.lat | +1 786 726-1749 | +55 61 98268-4220 (WhatsApp)
Licensing
PodHeitor CloudStack is proprietary software distributed by subscription. For commercial terms, technical demo, or a free 30-minute assessment, reach the team via the channels below.
Ready to evaluate?
- 💬 WhatsApp: +1 (786) 726-1749
- ✉️ Email: heitor@opentechs.lat
- 🩺 Free assessment — 30 min with Heitor Faria
Disponível em:
Português (Portuguese (Brazil))
English
Español (Spanish)