etcd es el almacén distribuido de clave-valor en el corazón de cada clúster Kubernetes, almacenando todo el estado del clúster desde las definiciones de pods hasta los secretos. Cuando etcd cae o pierde datos, todo el plano de control deja de funcionar. Esta guía te lleva a través del despliegue de etcd como servicio independiente y como clúster de tres nodos, su segurización con TLS mutuo, la creación y restauración de instantáneas, y la comprensión de cómo Kubernetes lo utiliza — para que puedas operarlo con confianza en producción.

Requisitos Previos

  • Servidor(es) Linux con Ubuntu 22.04 o posterior (uno para nodo único, tres para clúster)
  • Acceso root o sudo
  • Familiaridad básica con la gestión de servicios systemd
  • curl y tar instalados
  • Para TLS: cfssl u openssl disponible
  • Puertos 2379 (cliente) y 2380 (par) abiertos entre los nodos etcd

Qué Es etcd y Cómo Funciona

etcd es un almacén distribuido de clave-valor de código abierto y fuertemente consistente, desarrollado originalmente por CoreOS y ahora un proyecto graduado de la Cloud Native Computing Foundation (CNCF). Utiliza el algoritmo de consenso Raft para garantizar que todos los nodos coincidan en el estado actual incluso cuando ocurran particiones de red o fallos de nodos.

Las propiedades clave que debes comprender:

  • Consistencia fuerte: cada lectura devuelve la última escritura confirmada, nunca un valor desactualizado
  • API Watch: los clientes se suscriben a cambios de claves y reciben notificaciones en tiempo real
  • Transacciones atómicas: las operaciones de comparar-e-intercambiar permiten construir bloqueos distribuidos
  • Arrendamientos: las claves pueden expirar automáticamente, habilitando la elección de líder basada en latidos

Kubernetes depende de etcd para cada parte del estado del clúster. El servidor de API es el único componente que lee y escribe en etcd directamente — todos los demás componentes (planificador, gestor de controladores, kubelet) se comunican a través del servidor de API.

CaracterísticaetcdRedisConsulZooKeeper
ConsensoRaftNinguno (independiente)RaftZAB
Consistencia fuerteNo (eventual)
API WatchSolo pub/sub
Nativo en KubernetesNoNoNo
TLS integradoOpcionalOpcional
Complejidad operacionalBajaMuy bajaMediaAlta

etcd gana para Kubernetes porque fue diseñado específicamente para casos de uso del plano de control: valores pequeños, alta tasa de lectura, escrituras poco frecuentes y corrección sobre el rendimiento bruto.

Instalación de etcd

Descarga la última versión del repositorio oficial de GitHub. Al momento de escribir esto, 3.5.x es la serie estable recomendada para Kubernetes 1.29+.

ETCD_VER=v3.5.12
DOWNLOAD_URL=https://github.com/etcd-io/etcd/releases/download

curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz \
  -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz

tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/

sudo mv /tmp/etcd-${ETCD_VER}-linux-amd64/etcd /usr/local/bin/
sudo mv /tmp/etcd-${ETCD_VER}-linux-amd64/etcdctl /usr/local/bin/
sudo mv /tmp/etcd-${ETCD_VER}-linux-amd64/etcdutl /usr/local/bin/

etcd --version
etcdctl version

Crea el directorio de datos y un usuario de sistema dedicado:

sudo groupadd --system etcd
sudo useradd -s /sbin/nologin --system -g etcd etcd
sudo mkdir -p /var/lib/etcd
sudo chown -R etcd:etcd /var/lib/etcd
sudo chmod 700 /var/lib/etcd

Generación de Certificados TLS

Nunca ejecutes etcd sin TLS en ningún entorno más allá de un portátil local. etcd almacena secretos y credenciales — cualquier listener sin cifrar es un riesgo de seguridad inmediato.

Instala cfssl:

curl -Lo /usr/local/bin/cfssl \
  https://github.com/cloudflare/cfssl/releases/latest/download/cfssl_linux-amd64
curl -Lo /usr/local/bin/cfssljson \
  https://github.com/cloudflare/cfssl/releases/latest/download/cfssljson_linux-amd64
chmod +x /usr/local/bin/cfssl /usr/local/bin/cfssljson

Crea una CA y un certificado de servidor. Guarda lo siguiente como ca-config.json:

{
  "signing": {
    "default": { "expiry": "87600h" },
    "profiles": {
      "etcd": {
        "expiry": "87600h",
        "usages": ["signing","key encipherment","server auth","client auth"]
      }
    }
  }
}

Genera los certificados CA y de servidor:

# CA
cfssl gencert -initca ca-csr.json | cfssljson -bare ca

# Certificado de servidor + par (agrega todas las IPs/hostnames de los nodos en el array hosts del CSR)
cfssl gencert \
  -ca=ca.pem -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=etcd \
  etcd-csr.json | cfssljson -bare etcd

sudo mkdir -p /etc/etcd/pki
sudo cp ca.pem etcd.pem etcd-key.pem /etc/etcd/pki/
sudo chown -R etcd:etcd /etc/etcd/pki
sudo chmod 600 /etc/etcd/pki/*-key.pem

Configuración de un Clúster etcd de Tres Nodos

Un clúster etcd en producción necesita tres o cinco miembros. Con tres miembros, tolera un fallo y mantiene el quórum. Con cinco, tolera dos fallos simultáneos.

Suponemos tres nodos con las siguientes IPs:

  • etcd-1: 10.0.0.11
  • etcd-2: 10.0.0.12
  • etcd-3: 10.0.0.13

Crea /etc/etcd/etcd.conf.yml en cada nodo, sustituyendo los valores específicos del nodo:

# /etc/etcd/etcd.conf.yml — ejemplo para etcd-1
name: etcd-1
data-dir: /var/lib/etcd

listen-peer-urls: https://10.0.0.11:2380
listen-client-urls: https://10.0.0.11:2379,https://127.0.0.1:2379

advertise-client-urls: https://10.0.0.11:2379
initial-advertise-peer-urls: https://10.0.0.11:2380

initial-cluster: >-
  etcd-1=https://10.0.0.11:2380,
  etcd-2=https://10.0.0.12:2380,
  etcd-3=https://10.0.0.13:2380
initial-cluster-token: etcd-cluster-prod-01
initial-cluster-state: new

client-transport-security:
  cert-file: /etc/etcd/pki/etcd.pem
  key-file: /etc/etcd/pki/etcd-key.pem
  trusted-ca-file: /etc/etcd/pki/ca.pem
  client-cert-auth: true

peer-transport-security:
  cert-file: /etc/etcd/pki/etcd.pem
  key-file: /etc/etcd/pki/etcd-key.pem
  trusted-ca-file: /etc/etcd/pki/ca.pem
  peer-client-cert-auth: true

Crea la unidad systemd /etc/systemd/system/etcd.service en todos los nodos:

[Unit]
Description=etcd distributed key-value store
Documentation=https://etcd.io
After=network.target

[Service]
User=etcd
Group=etcd
Type=notify
ExecStart=/usr/local/bin/etcd --config-file /etc/etcd/etcd.conf.yml
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Inicia el clúster — los tres nodos deben iniciarse dentro del tiempo de espera de elección (predeterminado 1 segundo) para que el arranque inicial tenga éxito:

# Ejecutar en los tres nodos en rápida sucesión
sudo systemctl daemon-reload
sudo systemctl enable --now etcd

Verifica que el clúster se formó correctamente:

export ETCDCTL_API=3
export ETCDCTL_ENDPOINTS=https://10.0.0.11:2379,https://10.0.0.12:2379,https://10.0.0.13:2379
export ETCDCTL_CACERT=/etc/etcd/pki/ca.pem
export ETCDCTL_CERT=/etc/etcd/pki/etcd.pem
export ETCDCTL_KEY=/etc/etcd/pki/etcd-key.pem

etcdctl endpoint health
etcdctl endpoint status --write-out=table
etcdctl member list --write-out=table

Backup de etcd y Restauración desde Instantánea

El backup de etcd no es negociable en producción. Una instantánea captura todo el almacén de clave-valor en un momento dado. Realiza siempre un backup antes de actualizar etcd o Kubernetes.

Tomar una Instantánea

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

# Verificar la instantánea
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot-*.db --write-out=table

Automatiza esto con un trabajo cron que rote las instantáneas antiguas:

# /etc/cron.d/etcd-backup
0 2 * * * etcd /usr/local/bin/etcdctl snapshot save \
  /backup/etcd-$(date +\%Y\%m\%d).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/etcd/pki/ca.pem \
  --cert=/etc/etcd/pki/etcd.pem \
  --key=/etc/etcd/pki/etcd-key.pem && \
  find /backup/ -name "etcd-*.db" -mtime +7 -delete

Restauración desde una Instantánea

La restauración reemplaza el directorio de datos. Realiza esto en cada miembro del clúster:

# 1. Detener etcd en TODOS los nodos primero
sudo systemctl stop etcd

# 2. Restaurar en cada nodo (usa un --name y --initial-advertise-peer-urls únicos por nodo)
ETCDCTL_API=3 etcdutl snapshot restore /backup/etcd-snapshot.db \
  --name etcd-1 \
  --initial-cluster "etcd-1=https://10.0.0.11:2380,etcd-2=https://10.0.0.12:2380,etcd-3=https://10.0.0.13:2380" \
  --initial-cluster-token etcd-cluster-prod-01 \
  --initial-advertise-peer-urls https://10.0.0.11:2380 \
  --data-dir /var/lib/etcd-restored

# 3. Intercambiar el directorio de datos
sudo mv /var/lib/etcd /var/lib/etcd-old
sudo mv /var/lib/etcd-restored /var/lib/etcd
sudo chown -R etcd:etcd /var/lib/etcd

# 4. Iniciar etcd en TODOS los nodos
sudo systemctl start etcd

Escenario Real: Recuperación de un Plano de Control de Kubernetes

Tienes un clúster Kubernetes de tres nodos donde un nodo del plano de control perdió su disco. El miembro etcd en ese nodo ha desaparecido, y kubectl get nodes ahora se cuelga porque el servidor de API no puede alcanzar quórum en las escrituras.

Aquí está la ruta de recuperación:

  1. Verifica la membresía actual desde un nodo saludable: etcdctl member list. Verás el miembro fallido con una URL vacía.
  2. Elimina el miembro muerto: etcdctl member remove <MEMBER_ID>
  3. Aprovisiona un nuevo nodo con el mismo hostname e IP (o actualiza DNS).
  4. Agrega el reemplazo: etcdctl member add etcd-1-new --peer-urls=https://10.0.0.11:2380
  5. Inicia etcd en el nuevo nodo con ETCD_INITIAL_CLUSTER_STATE=existing en la configuración — no uses new.
  6. Observa la sincronización del nuevo miembro: etcdctl endpoint status mostrará el número de revisión al día en tiempo real.

Todo el proceso típicamente tarda de cinco a diez minutos para un clúster con menos de 1 GB de datos etcd. Durante la eliminación y antes de que el nuevo miembro alcance quórum, Kubernetes continúa sirviendo lecturas pero bloquea las escrituras.

Errores Comunes y Casos Especiales

Nunca ejecutes un número par de miembros etcd. Un clúster de dos miembros no tiene tolerancia a fallos — perder un miembro pierde inmediatamente el quórum. Un clúster de cuatro miembros tolera solo un fallo, igual que tres miembros, pero requiere un nodo extra. Usa 3 o 5.

La E/S de disco es el principal cuello de botella de etcd. etcd llama a fdatasync en cada escritura. Si la latencia de tu disco supera los 10ms de forma constante, verás elecciones de líder y errores de request timeout en Kubernetes. Usa almacenamiento SSD o NVMe. Comprueba la latencia de disco con: fio --rw=write --ioengine=sync --fdatasync=1 --directory=/var/lib/etcd --size=22m --bs=2300 --name=etcd-bench.

etcd no es una base de datos de propósito general. La cuota de almacenamiento predeterminada es 2 GB. Los clústeres Kubernetes con muchos objetos o uso intensivo de recursos personalizados pueden alcanzar este límite. Monitoriza etcd_mvcc_db_total_size_in_bytes en Prometheus y compacta/desfragmenta regularmente:

# Compactar revisiones antiguas (mantener las últimas 1000)
rev=$(etcdctl endpoint status --write-out=json | jq '.[0].Status.header.revision')
etcdctl compact $((rev - 1000))
etcdctl defrag --cluster

El sesgo de reloj provoca elecciones de líder. etcd usa el tiempo de reloj de pared para la expiración de arrendamientos. Si los nodos divergen más de unos pocos cientos de milisegundos, verás elecciones de líder espurias. Ejecuta NTP (chrony o systemd-timesyncd) en todos los nodos etcd y verifica con chronyc tracking.

La restauración de instantáneas borra la membresía del clúster. Después de restaurar, todos los miembros se tratan como un clúster completamente nuevo con la membresía codificada en la instantánea. Nunca restaures una instantánea en un clúster en ejecución sin detener primero todos los miembros — crearás una situación de cerebro dividido (split-brain).

Solución de Problemas

etcdserver: request timed out — Generalmente latencia de disco. Comprueba iostat -x 1 en los nodos etcd. También verifica la conectividad entre pares: etcdctl endpoint health desde cada nodo individualmente.

etcdserver: mvcc: database space exceeded — Se alcanzó la cuota de almacenamiento de 2 GB. Ejecuta compactación y desfragmentación como se muestra arriba, o aumenta la cuota con --quota-backend-bytes=4294967296 (4 GB) en la configuración de etcd.

raft: failed to send message — Firewall bloqueando el puerto 2380 entre pares, o una discrepancia en el CN del certificado de par. Verifica con openssl s_client -connect 10.0.0.12:2380 desde otro nodo etcd.

certificate has expired — Los certificados de par y cliente de etcd necesitan rotación antes de su expiración. Los clústeres Kubernetes administrados con kubeadm rotan automáticamente estos certificados entre los 90 días y 1 año, pero los clústeres gestionados manualmente no. Verifica la expiración: openssl x509 -in /etc/etcd/pki/etcd.pem -noout -dates.

etcd usando demasiada memoria — etcd almacena en caché el conjunto de trabajo en memoria. En clústeres con muchos objetos, el RSS puede superar los 8 GB. Esto es normal. Configura --snapshot-count=5000 (predeterminado 100000) para activar instantáneas más frecuentes y reducir el tamaño del registro raft en memoria.

Resumen

  • etcd almacena todo el estado del clúster Kubernetes; es el servicio más crítico en el plano de control
  • Despliega siempre tres o cinco miembros para producción — solo números impares
  • Habilita TLS mutuo en los puertos de cliente y de par; nunca expongas etcd sin autenticación
  • Toma instantáneas diarias con etcdctl snapshot save y verifícalas con snapshot status
  • Restaura deteniendo todos los miembros, ejecutando etcdutl snapshot restore en cada uno con parámetros de nodo únicos, intercambiando el directorio de datos y reiniciando
  • Monitoriza la latencia de disco (mantén por debajo de 10ms), el tamaño de la base de datos (compacta cuando se acerque a 2 GB) y la estabilidad del líder
  • Usa etcdctl member remove + member add para reemplazar un nodo fallido sin una restauración completa

Artículos Relacionados