TL;DR — Resumen Rápido

Backup y restauración de etcd en Kubernetes: snapshots, verificación, restauración multi-nodo, certificados kubeadm, monitoreo y script de producción.

etcd es el corazón de todo clúster de Kubernetes: un almacén de clave-valor distribuido y fuertemente consistente que guarda el estado completo — deseado y observado — de tu clúster. Cuando etcd funciona correctamente, los comandos kubectl responden en milisegundos y los controladores reconcilian de forma continua. Cuando etcd se pierde sin un backup, el clúster desaparece: cada definición de Deployment, cada Secret, cada política RBAC, cada CRD y cada ConfigMap quedan borrados. Esta guía cubre todo lo que necesitas para construir una estrategia de backup y restauración de etcd de nivel productivo.

Requisitos Previos

  • Un clúster de Kubernetes gestionado con kubeadm (v1.22+) o acceso a los certificados de etcd
  • etcdctl instalado en el nodo del plano de control (la versión debe coincidir con la de etcd)
  • Acceso root o sudo en el nodo del plano de control
  • kubectl configurado con permisos de cluster-admin
  • Familiaridad básica con los componentes del plano de control de Kubernetes
  • Para backups automáticos: acceso a S3, GCS o almacenamiento equivalente externo

El Rol de etcd en Kubernetes

Cada vez que ejecutas kubectl apply, el API server de Kubernetes valida la solicitud y escribe el objeto resultante en etcd. Todos los controladores observan etcd a través del mecanismo de watch del API server y reconcilian el clúster en consecuencia. etcd es el único componente con estado en el plano de control — todos los demás componentes son sin estado y pueden reiniciarse desde cero siempre que etcd esté intacto.

Qué reside en etcd:

  • Todas las definiciones de objetos de la API: Pods, Deployments, StatefulSets, DaemonSets, Services, Ingresses
  • Secrets y ConfigMaps
  • RBAC: Roles, ClusterRoles, RoleBindings, ClusterRoleBindings
  • Custom Resource Definitions y todas las instancias de recursos personalizados
  • Definiciones de Namespaces, ResourceQuotas, LimitRanges
  • ServiceAccounts y tokens asociados
  • Registros de nodos y objetos de lease
  • Registros de elección de líder para kube-controller-manager y kube-scheduler

Lo que no reside en etcd: los datos reales almacenados en los PersistentVolumes. etcd solo guarda los objetos PVC y PV (metadatos y enlace), no los bytes en disco.

Arquitectura de etcd: Raft, WAL y Snapshots

etcd utiliza el algoritmo de consenso Raft para replicar el estado en un clúster de miembros de número impar (típicamente 3 o 5). Raft elige un líder que procesa todas las escrituras; los seguidores replican el registro del líder. El clúster tolera (n-1)/2 fallos de miembros — un clúster de 3 nodos sobrevive 1 fallo, uno de 5 nodos sobrevive 2.

Las escrituras se añaden primero al Write-Ahead Log (WAL) en disco y luego se aplican a un B-tree en memoria (bbolt). Periódicamente, etcd toma un snapshot interno del B-tree en disco y trunca el WAL para evitar un crecimiento ilimitado.

El comando etcdctl snapshot save activa un snapshot del estado actual del B-tree. Este snapshot es un backup completo y autocontenido de todos los datos de etcd en el momento en que se tomó.

Métodos de Backup

Método 1: etcdctl snapshot save (Recomendado)

El método canónico de backup. En un clúster kubeadm, etcd corre como pod estático con TLS. Los certificados se encuentran en /etc/kubernetes/pki/etcd/.

ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

Método 2: CronJob Automatizado

Despliega un CronJob de Kubernetes en el plano de control que monte los certificados de etcd del host y escriba los snapshots en un PVC o en almacenamiento en la nube:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: etcd-backup
  namespace: kube-system
spec:
  schedule: "0 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          hostNetwork: true
          containers:
          - name: etcd-backup
            image: bitnami/etcd:3.5
            command:
            - /bin/sh
            - -c
            - |
              ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db \
                --endpoints=https://127.0.0.1:2379 \
                --cacert=/etc/kubernetes/pki/etcd/ca.crt \
                --cert=/etc/kubernetes/pki/etcd/server.crt \
                --key=/etc/kubernetes/pki/etcd/server.key
            volumeMounts:
            - name: etcd-certs
              mountPath: /etc/kubernetes/pki/etcd
              readOnly: true
            - name: backup-dir
              mountPath: /backup
          volumes:
          - name: etcd-certs
            hostPath:
              path: /etc/kubernetes/pki/etcd
          - name: backup-dir
            persistentVolumeClaim:
              claimName: etcd-backup-pvc
          restartPolicy: OnFailure
          nodeSelector:
            node-role.kubernetes.io/control-plane: ""
          tolerations:
          - key: node-role.kubernetes.io/control-plane
            effect: NoSchedule

Comparativa de Herramientas de Backup

HerramientaEstado etcdDatos PVGranularidad de RestauraciónComplejidad
etcdctl snapshotNoClúster completoBaja
Velero + plugin etcdNamespace o completoMedia
etcd-backup-operatorNoClúster completoMedia
kube-backupNoClúster completoBaja
CronJob manualNoClúster completoBaja

Verificación del Snapshot

Nunca confíes en un backup que no has verificado. Después de cada snapshot:

ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot.db \
  --write-out=table

Si TOTAL KEYS es 0 o el hash tiene formato incorrecto, el snapshot está corrupto. Incorpora la verificación en tu CronJob de backup y configura alertas en caso de fallo.

Procedimientos de Restauración

Clúster kubeadm de Un Solo Nodo

Paso 1: Detener el API server y etcd

mkdir -p /tmp/k8s-manifests-backup
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/k8s-manifests-backup/
mv /etc/kubernetes/manifests/etcd.yaml /tmp/k8s-manifests-backup/
sleep 10
ps aux | grep etcd

Paso 2: Respaldar el directorio de datos existente

mv /var/lib/etcd /var/lib/etcd.bak

Paso 3: Restaurar el snapshot

ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db \
  --data-dir=/var/lib/etcd \
  --initial-cluster=default=https://127.0.0.1:2380 \
  --initial-cluster-token=etcd-cluster-1 \
  --initial-advertise-peer-urls=https://127.0.0.1:2380

Paso 4: Restaurar los manifests y verificar

mv /tmp/k8s-manifests-backup/etcd.yaml /etc/kubernetes/manifests/
mv /tmp/k8s-manifests-backup/kube-apiserver.yaml /etc/kubernetes/manifests/
sleep 30
kubectl get nodes

Clúster kubeadm Multi-Nodo

Para un clúster de 3 nodos del plano de control (HA), debes restaurar en todos los miembros de etcd simultáneamente usando valores consistentes de --initial-cluster e --initial-cluster-token:

# En cada nodo del plano de control, ejecuta la restauración con el MISMO snapshot y token
# pero con el --name e --initial-advertise-peer-urls correctos para ese nodo

# Nodo 1
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db \
  --name=etcd-cp1 \
  --data-dir=/var/lib/etcd \
  --initial-cluster=etcd-cp1=https://10.0.0.1:2380,etcd-cp2=https://10.0.0.2:2380,etcd-cp3=https://10.0.0.3:2380 \
  --initial-cluster-token=etcd-restore-token-$(date +%s) \
  --initial-advertise-peer-urls=https://10.0.0.1:2380

Usa un --initial-cluster-token único y diferente del original para evitar que el clúster restaurado se una accidentalmente al clúster anterior degradado.

Consideraciones para Kubernetes Administrado

EKS, GKE, AKS — El proveedor de nube gestiona etcd internamente. No puedes acceder a etcd directamente. Usa los mecanismos nativos del proveedor:

  • EKS: Velero con S3; AWS no expone etcd directamente
  • GKE: Velero; Google gestiona etcd con backups automáticos en Autopilot
  • AKS: Velero + Azure Blob; Microsoft gestiona etcd para los node pools administrados

Monitoreo de Salud de etcd

# Verificar salud del endpoint
ETCDCTL_API=3 etcdctl endpoint health \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# Verificar líder y estado de miembros
ETCDCTL_API=3 etcdctl endpoint status --write-out=table \
  --endpoints=https://10.0.0.1:2379,https://10.0.0.2:2379,https://10.0.0.3:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

Alertas de Prometheus recomendadas para etcd:

  • etcd_server_has_leader == 0 — sin líder elegido (crítico)
  • etcd_disk_wal_fsync_duration_seconds{quantile="0.99"} > 0.01 — escrituras WAL lentas
  • etcd_server_proposals_failed_total > 0 — fallos de consenso
  • etcd_mvcc_db_total_size_in_bytes > 8589934592 — BD cerca del límite de 8 GB

Compactación y Desfragmentación

etcd mantiene un historial de todas las revisiones de claves. Con el tiempo, esto consume espacio en disco significativo. Habilita la compactación automática:

--auto-compaction-mode=periodic
--auto-compaction-retention=1h

Ejecuta la desfragmentación periódicamente durante horas de bajo tráfico:

ETCDCTL_API=3 etcdctl defrag \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

Ajuste de Rendimiento

etcd es extremadamente sensible a la latencia de disco. Recomendaciones:

  • SSD dedicado: Nunca compartas el disco de datos de etcd con cargas de trabajo de aplicaciones. Usa NVMe o SSD con IOPS de escritura aleatoria sostenida > 2000.
  • Timeouts de heartbeat y elección: El heartbeat-interval predeterminado es 100ms y el election-timeout es 1000ms. En entornos de alta latencia, aumenta a 250ms / 1250ms.
  • Cuota de BD: El valor predeterminado es 2 GB. Aumenta a 8 GB para clústeres grandes: --quota-backend-bytes=8589934592

Script de Backup de Producción con Alertas

#!/bin/bash
set -euo pipefail

BACKUP_DIR="/opt/etcd-backups"
RETENTION_COUNT=24
S3_BUCKET="s3://mi-cluster-etcd-backups"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx/yyy/zzz"
ETCD_ENDPOINTS="https://127.0.0.1:2379"
CACERT="/etc/kubernetes/pki/etcd/ca.crt"
CERT="/etc/kubernetes/pki/etcd/server.crt"
KEY="/etc/kubernetes/pki/etcd/server.key"

TIMESTAMP=$(date +%Y%m%d-%H%M%S)
SNAPSHOT_FILE="${BACKUP_DIR}/etcd-${TIMESTAMP}.db"

alerta() {
  local msg="$1"
  curl -s -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"[etcd-backup] ${msg}\"}" "${SLACK_WEBHOOK}" || true
}

mkdir -p "${BACKUP_DIR}"

if ! ETCDCTL_API=3 etcdctl snapshot save "${SNAPSHOT_FILE}" \
    --endpoints="${ETCD_ENDPOINTS}" \
    --cacert="${CACERT}" --cert="${CERT}" --key="${KEY}"; then
  alerta "CRÍTICO: etcd snapshot save FALLÓ en $(hostname) a las ${TIMESTAMP}"
  exit 1
fi

KEYS=$(ETCDCTL_API=3 etcdctl snapshot status "${SNAPSHOT_FILE}" \
  --write-out=json | python3 -c "import sys,json; print(json.load(sys.stdin)['totalKey'])")
if [ "${KEYS}" -lt 100 ]; then
  alerta "ADVERTENCIA: el snapshot solo tiene ${KEYS} claves — posible snapshot vacío o corrupto"
  exit 1
fi

aws s3 cp "${SNAPSHOT_FILE}" "${S3_BUCKET}/$(basename ${SNAPSHOT_FILE})" \
  --storage-class STANDARD_IA

ls -t "${BACKUP_DIR}"/etcd-*.db | tail -n +$((RETENTION_COUNT + 1)) | xargs -r rm -f

echo "Backup completado: ${SNAPSHOT_FILE} (${KEYS} claves)"

Escenarios de Recuperación ante Desastres

Escenario 1: Fallo de un miembro (quórum intacto) — El clúster continúa operando. Reemplaza el miembro fallido con etcdctl member remove + etcdctl member add sin necesidad de restaurar.

Escenario 2: Pérdida de quórum (mayoría de miembros caídos) — El clúster se vuelve de solo lectura. Si los datos están intactos, restablece la conectividad. Si los datos se perdieron, restaura desde snapshot en todos los miembros.

Escenario 3: Restauración completa del clúster — Detén todos los componentes del plano de control, restaura el snapshot en cada nodo con --initial-cluster-token consistente, reinicia en orden: etcd primero, luego kube-apiserver, luego kube-controller-manager y kube-scheduler.

Casos Especiales y Errores Comunes

Incompatibilidad de versión de etcdctl — Siempre establece ETCDCTL_API=3. Verifica que la versión del cliente coincida con la del servidor. Las incompatibilidades causan fallos silenciosos o restauraciones corruptas.

El snapshot de un seguidor puede estar ligeramente desfasado — Para restauraciones críticas, toma el snapshot del líder.

La restauración sobreescribe el directorio de datosetcdctl snapshot restore escribe en --data-dir. Si el directorio ya existe, la restauración falla. Mueve siempre el directorio existente antes.

Desviación de reloj entre miembros — Los certificados TLS de los pares de etcd son sensibles al tiempo. Asegúrate de que NTP esté configurado y sincronizado en todos los nodos del plano de control.

Resumen

  • etcd almacena todo el estado del clúster de Kubernetes; perderlo sin backup significa reconstruir desde cero
  • Usa etcdctl snapshot save con los flags TLS apuntando a /etc/kubernetes/pki/etcd/ para clústeres kubeadm
  • Siempre ejecuta etcdctl snapshot status para verificar los snapshots tras su creación
  • La restauración requiere detener el API server y etcd, ejecutar etcdctl snapshot restore y reiniciar el plano de control
  • La restauración multi-nodo requiere --initial-cluster-token consistente y --initial-advertise-peer-urls correctas por nodo
  • Habilita la compactación automática y ejecuta etcdctl defrag mensualmente
  • Dedica un SSD de baja latencia a los datos de etcd; monitorea la latencia de WAL fsync con Prometheus
  • Almacena snapshots de forma externa (S3/GCS) con al menos 24h de retención; automatiza con CronJob + script de alertas

Artículos Relacionados