Disaster Recovery em Kubernetes: Velero, Backups e Restore de Cluster na Prática Já leu

Entendendo Disaster Recovery em Kubernetes Disaster Recovery (DR) é a capacidade de recuperar sua infraestrutura e dados após um evento catastrófico. Em ambientes Kubernetes, isso vai muito além de fazer backup de um banco de dados — você precisa proteger toda a configuração do cluster, volumes persistentes, secrets, ConfigMaps, objetos personalizados e o estado da aplicação. A realidade é que perder um cluster inteiro ou ter dados corrompidos é mais comum do que gostaríamos de admitir, especialmente em ambientes de produção. O desafio aumenta porque Kubernetes é distribuído e stateless por design. Seus deployments, services e ingresses são efêmeros. Quando você perde um cluster, perde toda essa declaração e o estado das aplicações. É por isso que uma estratégia de DR bem pensada não apenas salva seus dados, mas também permite reconstruir o cluster inteiro em outro lugar, com a mesma configuração e estado. Vamos explorar como o Velero resolve isso de forma elegante e prática. O que é Velero

Entendendo Disaster Recovery em Kubernetes

Disaster Recovery (DR) é a capacidade de recuperar sua infraestrutura e dados após um evento catastrófico. Em ambientes Kubernetes, isso vai muito além de fazer backup de um banco de dados — você precisa proteger toda a configuração do cluster, volumes persistentes, secrets, ConfigMaps, objetos personalizados e o estado da aplicação. A realidade é que perder um cluster inteiro ou ter dados corrompidos é mais comum do que gostaríamos de admitir, especialmente em ambientes de produção.

O desafio aumenta porque Kubernetes é distribuído e stateless por design. Seus deployments, services e ingresses são efêmeros. Quando você perde um cluster, perde toda essa declaração e o estado das aplicações. É por isso que uma estratégia de DR bem pensada não apenas salva seus dados, mas também permite reconstruir o cluster inteiro em outro lugar, com a mesma configuração e estado. Vamos explorar como o Velero resolve isso de forma elegante e prática.

O que é Velero e como funciona

O Velero é uma ferramenta open-source mantida pela comunidade Kubernetes que automatiza backup e restore de clusters Kubernetes. Ele funciona criando snapshots de todo o seu cluster — recursos, volumes persistentes, namespaces, e até mesmo objetos customizados — e os armazena em um storage externo como Amazon S3, Google Cloud Storage ou MinIO.

A arquitetura do Velero é baseada em dois componentes principais: um servidor que roda dentro do cluster (como um Deployment) e a CLI que você usa para interagir com ele. Quando você inicia um backup, o servidor Velero lê todos os recursos do cluster através da API Kubernetes, serializa-os em formato JSON, e envia para o storage backend. Para volumes persistentes, ele usa plugins específicos que conseguem fazer snapshots do storage subjacente (EBS, GCP Persistent Disks, ou cria backups dinâmicos através de restic). No restore, o processo é invertido: ele lê os dados do storage, recria os recursos na ordem correta e restaura os volumes.

O ponto crucial é que Velero entende a semântica dos seus recursos. Ele não faz apenas cópia byte-a-byte. Ele sabe que um Deployment precisa ser criado antes de um Pod, que um ConfigMap pode ser referenciado por múltiplos Deployments, e que certos recursos como Nodes não devem ser restaurados (pois o cluster-alvo terá seus próprios Nodes). Isso permite recuperação inteligente e sem conflitos.

Arquitetura e fluxo de backup

O backup no Velero funciona através de um modelo push onde o servidor dentro do cluster coleta recursos, aplica filtros e transformações, e envia tudo para um backend de armazenamento. O pipeline é composto por hooks (que podem executar comandos antes/depois do backup), filtros de recursos (você escolhe o que fazer backup), e drivers de plugin para diferentes tipos de storage.

# Exemplo de um Backup customizado no Velero
apiVersion: velero.io/v1
kind: Backup
metadata:
  name: my-cluster-backup
  namespace: velero
spec:
  # Define o TTL antes de deletar automaticamente
  ttl: 720h

  # Inclui esses namespaces
  includedNamespaces:
    - production
    - staging

  # Exclui recursos específicos
  excludedResources:
    - nodes
    - events

  # Incluir volumes persistentes
  storageLocation: default

  # Hook para pausar aplicação antes do backup
  hooks:
    resources:
      - name: pause-app
        includedNamespaces:
          - production
        pre:
          - exec:
              container: "app-container"
              command: ["/bin/sh", "-c", "kill -SIGSTOP 1"]

Este manifesto define um backup que inclui apenas namespaces de produção e staging, exclui Nodes e Events (que não faz sentido restaurar), e executa um comando para pausar a aplicação antes do snapshot. Essa abordagem garante consistência dos dados.

Armazenamento e retenção

O Velero precisa de um backend de armazenamento externo ao cluster. Os principais são Amazon S3, Google Cloud Storage, Microsoft Azure Blob Storage, e qualquer storage S3-compatível como MinIO. Você configura a credencial e o bucket uma única vez, e todos os backups vão para lá.

# Exemplo de configuração de storage backend (Secret + BackupStorageLocation)
apiVersion: v1
kind: Secret
metadata:
  name: velero-aws-credentials
  namespace: velero
type: Opaque
data:
  # Base64 encoded AWS credentials
  cloud: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkID0gQUtJQVhYWFhYWFhYWFhYWFhYCmF3c19zZWNyZXRfYWNjZXNzX2tleSA9IHlkKzZhK3hYWFhYWFhYWFhY=
---
apiVersion: velero.io/v1
kind: BackupStorageLocation
metadata:
  name: default
  namespace: velero
spec:
  provider: aws
  bucket: my-velero-backups
  config:
    region: us-east-1
    s3Url: https://s3.amazonaws.com
  credential:
    name: velero-aws-credentials
    key: cloud
  # Retenção automática após 30 dias
  accessMode: ReadWrite

A retenção é crucial em DR. Você não quer guardar backups antigos indefinidamente (custa dinheiro) nem quer deletar muito rápido (precisa de histórico). O Velero permite definir TTL (Time To Live) por backup ou por política, garantindo limpeza automática.

Instalação e Configuração Prática

Para começar com Velero, você precisa ter um cluster Kubernetes rodando e acesso ao kubectl. A instalação envolve adicionar o repositório Helm, configurar credenciais para o storage backend, e fazer deploy do Velero.

Instalação via Helm

# Adicionar o repositório Helm do Velero
helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
helm repo update

# Criar namespace
kubectl create namespace velero

# Criar arquivo com credenciais AWS (se usando S3)
cat > credentials-velero <<EOF
[default]
aws_access_key_id = YOUR_AWS_ACCESS_KEY
aws_secret_access_key = YOUR_AWS_SECRET_ACCESS_KEY
EOF

# Instalar Velero com configuração para AWS S3
helm install velero vmware-tanzu/velero \
  --namespace velero \
  --set configuration.backupStorageLocation.bucket=my-velero-backups \
  --set configuration.backupStorageLocation.provider=aws \
  --set configuration.backupStorageLocation.config.region=us-east-1 \
  --set configuration.schedules.daily.schedule="0 2 * * *" \
  --set configuration.schedules.daily.template.ttl=720h \
  --set credentials.useSecret=true \
  --set-file credentials.extraSecret=./credentials-velero

Este comando instala o Velero configurado para fazer backup em um bucket S3 todo dia às 2 da manhã, mantendo backups por 30 dias. O Velero vai criar um Deployment com os controladores necessários, um CronJob para backups agendados, e um serviço para expor a API.

Instalação manual com manifests

Se preferir mais controle, você pode usar os manifests YAML diretamente:

# Fazer download dos manifests
kubectl apply -f https://github.com/vmware-tanzu/velero/releases/download/v1.12.0/velero-v1.12.0-kubernetes.tar.gz

# Isso instala:
# - CRDs (Custom Resource Definitions)
# - Namespace velero
# - RBAC (ServiceAccount, ClusterRole, ClusterRoleBinding)
# - Deployment do servidor Velero
# - ConfigMap com configuração

Depois você precisa editar o ConfigMap para adicionar suas credenciais e informações de storage. A abordagem manual é mais verbosa, mas oferece visibilidade total.

Verificação da instalação

# Verificar se o Velero está rodando
kubectl get deployment -n velero

# Ver logs do servidor
kubectl logs -n velero -l app.kubernetes.io/name=velero -f

# Testar conectividade com o storage
velero backup-location get

# Saída esperada:
# NAME      PROVIDER   BUCKET/CONTAINER         PHASE       LAST VALIDATED
# default   aws        my-velero-backups        Available   2024-01-15T10:30:00Z

Se o PHASE for "Available", você está conectado corretamente ao storage.

Criando Backups: Estratégia e Implementação

Existem duas abordagens para criar backups: sob demanda (manual) e agendados (automáticos). A estratégia ideal combina ambas — backups automáticos diários para recuperação rápida e backups manuais antes de operações críticas como upgrades ou mudanças arquiteturais.

Backup sob demanda

Um backup manual é perfeito para antes de uma mudança arriscada. Você cria, valida, e só depois procede:

# Criar um backup imediato de todo o cluster
velero backup create full-cluster-backup-$(date +%Y%m%d-%H%M%S)

# Criar backup apenas de um namespace específico
velero backup create production-backup --include-namespaces production

# Criar backup excluindo namespaces do sistema
velero backup create app-backup \
  --exclude-namespaces kube-system,kube-public,velero \
  --wait

# Listar todos os backups
velero backup get

# Ver detalhes de um backup específico
velero backup describe full-cluster-backup-20240115-143022 --details

# Ver logs de um backup (útil para debugar)
velero backup logs full-cluster-backup-20240115-143022

O --wait faz o comando bloquear até o backup terminar. Para operações grandes, isso pode levar minutos. Sem ele, o comando retorna imediatamente e você pode monitorar com describe.

Backups agendados

Para operações contínuas, você configura um schedule no Velero que cria backups automaticamente:

# Exemplo de Schedule - backup diário
apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: daily-backup
  namespace: velero
spec:
  # Executa todo dia às 2 da manhã
  schedule: "0 2 * * *"
  template:
    includedNamespaces:
      - "*"
    excludedNamespaces:
      - kube-system
      - kube-node-lease
      - kube-public
      - velero
    storageLocation: default
    # Backup automático de volumes
    defaultVolumesToFsBackup: true
    # Retenção de 30 dias
    ttl: 720h
---
# Schedule para backup semanal completo
apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: weekly-full-backup
  namespace: velero
spec:
  # Executa todo domingo às 3 da manhã
  schedule: "0 3 * * 0"
  template:
    includedNamespaces:
      - "*"
    excludedNamespaces:
      - velero
    storageLocation: default
    defaultVolumesToFsBackup: true
    # Retenção de 90 dias para backups semanais
    ttl: 2160h

Crie esses recursos com kubectl apply -f schedule.yaml. O Velero criará um CronJob que dispara backups nos horários especificados. A sintaxe é padrão cron: minuto, hora, dia do mês, mês, dia da semana.

Monitorando backups

# Ver o progresso de um backup em tempo real
watch -n 5 "velero backup describe full-cluster-backup-20240115-143022"

# Ou verificar via kubectl
kubectl get backups -n velero -o wide

# Saída:
# NAME                                STATUS      ERRORS   WARNINGS   CREATED                         EXPIRES
# full-cluster-backup-20240115-143022 Completed   0        0          2024-01-15T14:30:22Z            2024-02-14T14:30:22Z

Um STATUS de "Completed" com 0 errors significa sucesso. "Warnings" indicam pequenos problemas (um recurso que não pôde ser copiado, por exemplo) mas o backup continuou. Se ver "Failed", verifique os logs.

Hooks para consistência

Para aplicações stateful, você pode querer pausar operações durante o backup:

apiVersion: velero.io/v1
kind: Backup
metadata:
  name: mysql-consistent-backup
  namespace: velero
spec:
  includedNamespaces:
    - databases
  hooks:
    resources:
      - name: mysql-backup-hook
        includedNamespaces:
          - databases
        includedResources:
          - pods
        labelSelector:
          matchLabels:
            app: mysql
        pre:
          # Executar FLUSH TABLES WITH READ LOCK antes do backup
          - exec:
              container: mysql
              command: ["/bin/bash", "-c", "mysql -uroot -p$MYSQL_ROOT_PASSWORD -e 'FLUSH TABLES WITH READ LOCK;'"]
              onError: Fail
              timeout: 30s
        post:
          # Liberar lock após backup
          - exec:
              container: mysql
              command: ["/bin/bash", "-c", "mysql -uroot -p$MYSQL_ROOT_PASSWORD -e 'UNLOCK TABLES;'"]
              onError: Warn
              timeout: 30s

Os hooks de pre executam antes do backup (para preparar a aplicação), e post após (para limpeza). Use onError: Fail para hooks críticos e onError: Warn para opcionais.

Restauração e Testes de DR

Fazer backup é apenas metade do trabalho. A verdadeira medida de uma estratégia de DR é se você consegue restaurar rapidamente quando precisa. Por isso é crucial testar restaurações regularmente — não apenas deixar backups acumulando.

Restore completo em um novo cluster

Este é o cenário de disaster total: seu cluster foi destruído e você precisa reconstruir em outro lugar:

# Listar todos os backups disponíveis
velero backup get

# Restaurar um backup completo
velero restore create --from-backup full-cluster-backup-20240115-143022 --wait

# Monitorar progresso
velero restore describe full-cluster-backup-20240115-143022-<timestamp>

# Ou via kubectl
kubectl get restores -n velero -o wide

Quando você restaura em um novo cluster, o Velero recria todos os Deployments, Services, ConfigMaps, Secrets, Volumes — tudo. A única coisa que ele não restaura são os Nodes (o novo cluster já tem seus próprios). Dependendo do tamanho do cluster, isso pode levar de minutos a horas.

Restore seletivo de namespace

Frequentemente você não quer restaurar o cluster inteiro, apenas um aplicativo específico:

# Restaurar apenas o namespace 'production'
velero restore create \
  --from-backup full-cluster-backup-20240115-143022 \
  --include-namespaces production \
  --wait

# Restaurar múltiplos namespaces
velero restore create \
  --from-backup full-cluster-backup-20240115-143022 \
  --include-namespaces production,staging \
  --wait

# Restaurar tudo EXCETO certos namespaces
velero restore create \
  --from-backup full-cluster-backup-20240115-143022 \
  --exclude-namespaces kube-system,velero \
  --wait

Isso é útil quando há corrupção apenas em uma parte do cluster. Você isola o problema e restaura apenas aquela seção.

Restore com transformações

Às vezes você quer restaurar para um ambiente diferente (ex: produção → staging). Você pode aplicar transformações:

# Exemplo de Policy que renomeia namespaces durante restore
apiVersion: velero.io/v1
kind: RestoreItemActionTemplate
metadata:
  name: change-storage-class
  namespace: velero
spec:
  resourceIncludeString: "persistentvolumeclaims"
  action: |
    $patch = [{"op":"replace","path":"/spec/storageClassName","value":"fast-ssd"}]
    $patchType = "JSONPatch"
---
# Aplicar a transformação durante restore
velero restore create \
  --from-backup production-backup \
  --include-namespaces production \
  --restore-volumes=true \
  --wait

Você também pode usar um RestoreItemAction custom (em Go) para transformações mais complexas, como alterar domínios em Ingress ou mudar variáveis de ambiente.

Teste periódico de restore

Uma estratégia recomendada é fazer um "restore test" regularmente em um cluster separado:

#!/bin/bash
# Script para testar restore semanalmente

# Encontrar o backup mais recente
LATEST_BACKUP=$(velero backup get --sort-by='.metadata.creationTimestamp' -o json | jq -r '.items[-1].metadata.name')

echo "Testing restore from backup: $LATEST_BACKUP"

# Criar namespace de teste
kubectl create namespace dr-test

# Restaurar para namespace de teste
velero restore create \
  --from-backup "$LATEST_BACKUP" \
  --include-namespaces production \
  --namespace-mappings production=dr-test \
  --wait

# Aguardar estabilização
sleep 30

# Verificar que os pods estão rodando
RUNNING=$(kubectl get pods -n dr-test -o json | jq '.items[] | select(.status.phase=="Running") | .metadata.name' | wc -l)
echo "Running pods in dr-test: $RUNNING"

# Rodar testes de saúde
kubectl run -n dr-test smoke-test --image=curlimages/curl --rm -it -- \
  curl -s http://app-service:8080/health | jq .

# Limpar
kubectl delete namespace dr-test

echo "DR test completed"

Execute isso semanalmente em um ambiente de teste. Se o restore falha em teste, você saberá antes que precisar fazer para real.

Verificando a integridade do restore

# Após restaurar, verificar que todos os recursos foram criados
velero restore describe <restore-name> --details

# Contar recursos por tipo
kubectl get all -A | tail -20

# Verificar que volumes foram restaurados
kubectl get pvc -A

# Testar conectividade de uma aplicação restaurada
POD=$(kubectl get pods -n <namespace> -l app=myapp -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it $POD -n <namespace> -- /bin/sh

# Dentro do pod, verificar que dados persistentes estão presentes
ls -la /data/
cat /data/important-file.txt

Se tudo estiver em ordem, seus backups são confiáveis.

Casos Avançados e Troubleshooting

Nem todo cenário é simples. Às vezes você enfrenta clusters gigantes, aplicações com estado complexo, ou erros durante backup/restore. Vamos cobrir os problemas mais comuns.

Exclusões inteligentes

Nem tudo precisa (ou deveria) ser feito backup:

apiVersion: velero.io/v1
kind: Backup
metadata:
  name: smart-backup
  namespace: velero
spec:
  includedNamespaces:
    - "*"
  excludedNamespaces:
    - kube-system
    - kube-node-lease
    - kube-public
    - velero
  excludedResources:
    # Não fazer backup desses tipos de recurso
    - nodes
    - events
    - events.events.k8s.io
    - backups.velero.io
    - restores.velero.io
    - resticrepositories.velero.io
    - csinodes.storage.k8s.io
    - csidrivers.storage.k8s.io
    - csistoragecapacities.storage.k8s.io
  # Também pode excluir recursos por label
  labelSelector:
    matchExpressions:
      - key: backup
        operator: NotIn
        values:
          - "false"

A regra de ouro: não faça backup de dados que podem ser recriados (caches, índices, logs) ou que são infraestrutura do cluster (Nodes, componentes do kube-system).

Debugando falhas de backup

# Ver logs detalhados do servidor Velero
kubectl logs -n velero -l app.kubernetes.io/name=velero --tail=200

# Ver logs específicos de um backup
velero backup logs <backup-name>

# Descrever backup com todos os erros
velero backup describe <backup-name> --details

# Aumentar nível de verbosidade
kubectl set env deployment/velero -n velero LOG_LEVEL=debug
kubectl logs -n velero -l app.kubernetes.io/name=velero -f

Erros comuns incluem: credenciais inválidas (não consegue escrever no S3), volumes usando storage classes não suportadas, ou aplicações que não permitem snapshots.

Otimizando backups grandes

Para clusters com centenas de GB de dados:

apiVersion: velero.io/v1
kind: Backup
metadata:
  name: optimized-backup
  namespace: velero
spec:
  includedNamespaces:
    - production
  # Usar compressão
  snapshotMoveData: true
  # Tirar snapshots paralelos
  parallelFilesUpload: 10
  # Aumentar chunk size para volumes grandes
  defaultVolumesToFsBackup: true
  fsBackupTimeout: 1h

Também considere:

# Fazer backups incrementais (apenas mudanças desde o último)
# Isso é suportado nativamente pelo Velero em volumes que usam restic

# Para storage dinâmico, configure restic:
kubectl set env deployment/velero -n velero RESTIC_PRUNE_FREQUENCY=weekly

Restauração seletiva por label

# Restaurar apenas recursos com label 'tier=critical'
velero restore create \
  --from-backup full-backup \
  --selector "tier=critical" \
  --wait

# Restaurar tudo EXCETO o que tem label 'tier=temporary'
velero restore create \
  --from-backup full-backup \
  --exclude-resources "pods" \
  --wait

Use labels estrategicamente para categorizar seus recursos. Isso permite restaurações granulares.

Migração entre clusters

Um use case poderoso é migração para um novo cluster (upgrade de versão, mudar cloud provider, etc.):

# Cluster antigo: fazer backup final
velero backup create migration-backup-$(date +%s)

# Copiar credenciais do storage para o novo cluster
# (o novo cluster precisa acesso ao mesmo S3 bucket)

# Novo cluster: instalar Velero apontando para mesmo storage
# (usar mesmo bucket, mesma região, mesmas credenciais)

# Novo cluster: restaurar
velero restore create --from-backup migration-backup-<timestamp> --wait

# Verificar que tudo foi restaurado
kubectl get ns
kubectl get all -A

O Velero faz essa migração ser trivial. Você não precisa fazer kubectl drain manualmente ou mexer em volumes.

Conclusão

Aprendemos que Disaster Recovery em Kubernetes requer uma abordagem sistemática onde o Velero é a ferramenta central. Os três pontos principais são: primeiro, backup não é útil sem restore — você deve testar regularmente que consegue restaurar rapidamente, idealmente em ambientes separados. Segundo, estratégia de retenção importa — backups muito antigos custam dinheiro e backups deletados muito rápido deixam você sem opções. Combine backups diários com retenção de semanas, backups semanais com retenção de meses. Terceiro, uma boa DR strategy é incrementalista — comece protegendo apenas dados críticos, estenda para aplicações inteiras, eventualmente backup completo do cluster. Não tente fazer tudo de uma vez; implemente, teste, valide, depois expanda.

Referências


Artigos relacionados