Instalación y Gestión de Clúster etcd
etcd es un almacén de clave-valor distribuido y consistente que sirve como base de datos de configuración para Kubernetes y otros sistemas distribuidos, garantizando consistencia fuerte mediante el algoritmo de consenso Raft. Un clúster etcd de producción requiere un número impar de nodos (3, 5 o 7) para mantener quórum y tolerancia a fallos. Esta guía cubre el despliegue de un clúster etcd en Linux con TLS, copias de seguridad y monitorización.
Requisitos Previos
- 3 servidores Linux para producción (Ubuntu 22.04/Debian 12 o CentOS 9/Rocky 9)
- Mínimo 2 GB de RAM y 20 GB de disco SSD por nodo
- Latencia de red entre nodos inferior a 10ms
- NTP sincronizado en todos los nodos
- Puertos 2379 (cliente) y 2380 (peer) accesibles entre nodos
Instalación de etcd
# Descargar la última versión estable de etcd
ETCD_VERSION="v3.5.12"
# Ubuntu/Debian y CentOS/Rocky (binarios estáticos)
wget "https://github.com/etcd-io/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz"
tar xzf "etcd-${ETCD_VERSION}-linux-amd64.tar.gz"
# Instalar los binarios en el PATH del sistema
install -m 755 etcd-${ETCD_VERSION}-linux-amd64/etcd /usr/local/bin/
install -m 755 etcd-${ETCD_VERSION}-linux-amd64/etcdctl /usr/local/bin/
install -m 755 etcd-${ETCD_VERSION}-linux-amd64/etcdutl /usr/local/bin/
# Verificar la instalación
etcd --version
etcdctl version
# Crear usuario de sistema y directorios
useradd -r -d /var/lib/etcd -s /bin/false etcd
mkdir -p /var/lib/etcd /etc/etcd
chown etcd:etcd /var/lib/etcd
chmod 700 /var/lib/etcd
Configuración del Clúster
Configuración de cada nodo del clúster (ajustar IPs y nombres según el nodo):
# En etcd-01 (IP: 10.0.1.10)
cat > /etc/etcd/etcd.env << 'EOF'
# Nombre único de este nodo en el clúster
ETCD_NAME="etcd-01"
# Directorio de datos persistentes
ETCD_DATA_DIR="/var/lib/etcd"
# Dirección de escucha para clientes
ETCD_LISTEN_CLIENT_URLS="https://10.0.1.10:2379,https://127.0.0.1:2379"
ETCD_ADVERTISE_CLIENT_URLS="https://10.0.1.10:2379"
# Dirección de escucha para comunicación entre peers
ETCD_LISTEN_PEER_URLS="https://10.0.1.10:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.0.1.10:2380"
# Topología inicial del clúster
ETCD_INITIAL_CLUSTER="etcd-01=https://10.0.1.10:2380,etcd-02=https://10.0.1.11:2380,etcd-03=https://10.0.1.12:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-produccion"
# Certificados TLS
ETCD_CERT_FILE="/etc/etcd/tls/server.crt"
ETCD_KEY_FILE="/etc/etcd/tls/server.key"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_TRUSTED_CA_FILE="/etc/etcd/tls/ca.crt"
ETCD_PEER_CERT_FILE="/etc/etcd/tls/peer.crt"
ETCD_PEER_KEY_FILE="/etc/etcd/tls/peer.key"
ETCD_PEER_CLIENT_CERT_AUTH="true"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/tls/ca.crt"
# Límites de rendimiento
ETCD_QUOTA_BACKEND_BYTES="8589934592" # 8 GB máximo de datos
ETCD_SNAPSHOT_COUNT="10000"
ETCD_HEARTBEAT_INTERVAL="250"
ETCD_ELECTION_TIMEOUT="2500"
EOF
Crea el servicio systemd:
cat > /etc/systemd/system/etcd.service << 'EOF'
[Unit]
Description=etcd - Distributed Key-Value Store
Documentation=https://etcd.io/docs/
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
User=etcd
Group=etcd
EnvironmentFile=/etc/etcd/etcd.env
ExecStart=/usr/local/bin/etcd
Restart=on-failure
RestartSec=5s
LimitNOFILE=40000
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now etcd
Seguridad TLS
Genera una CA y certificados para el clúster:
# Instalar cfssl para generación de certificados
wget -O /usr/local/bin/cfssl \
"https://github.com/cloudflare/cfssl/releases/latest/download/cfssl_linux-amd64"
wget -O /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
mkdir -p /etc/etcd/tls && cd /etc/etcd/tls
# Configuración de la CA
cat > ca-config.json << 'EOF'
{
"signing": {
"default": { "expiry": "87600h" },
"profiles": {
"etcd": {
"expiry": "87600h",
"usages": ["signing", "key encipherment", "server auth", "client auth"]
}
}
}
}
EOF
# Generar la CA
cat > ca-csr.json << 'EOF'
{"CN":"etcd-ca","key":{"algo":"rsa","size":2048},"names":[{"O":"etcd cluster"}]}
EOF
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
# Generar certificado de servidor/peer para cada nodo
for i in 01 02 03; do
IP="10.0.1.1${i#0}" # Ajustar al esquema de IPs real
cat > etcd-${i}-csr.json << EOFJ
{"CN":"etcd-${i}","hosts":["127.0.0.1","${IP}","etcd-${i}"],"key":{"algo":"rsa","size":2048}}
EOFJ
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
-config=ca-config.json -profile=etcd \
etcd-${i}-csr.json | cfssljson -bare etcd-${i}
done
# Copiar los certificados correctos a cada nodo
# En etcd-01: ca.pem, etcd-01.pem, etcd-01-key.pem
# En etcd-02: ca.pem, etcd-02.pem, etcd-02-key.pem
# etc.
chown -R etcd:etcd /etc/etcd/tls/
chmod 600 /etc/etcd/tls/*-key.pem
Operaciones Básicas
# Variable de entorno para autenticación TLS en etcdctl
export ETCDCTL_API=3
export ETCDCTL_ENDPOINTS="https://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379"
export ETCDCTL_CACERT=/etc/etcd/tls/ca.pem
export ETCDCTL_CERT=/etc/etcd/tls/etcd-01.pem
export ETCDCTL_KEY=/etc/etcd/tls/etcd-01-key.pem
# Verificar el estado del clúster
etcdctl endpoint status --write-out=table
etcdctl endpoint health
# Operaciones básicas de clave-valor
etcdctl put /config/app/database "postgresql://10.0.2.10:5432/app"
etcdctl get /config/app/database
etcdctl get /config/ --prefix # Obtener todas las claves bajo /config/
etcdctl del /config/app/database
# Observar cambios en tiempo real
etcdctl watch /config/ --prefix
# Listar miembros del clúster
etcdctl member list --write-out=table
Copia de Seguridad y Restauración
# Crear snapshot (copia de seguridad) del estado actual
etcdctl snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db
# Verificar la integridad del snapshot
etcdctl snapshot status /backup/etcd-20240115-120000.db --write-out=table
# Script de backup automatizado
cat > /usr/local/bin/etcd-backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backup/etcd"
RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
# Crear snapshot con timestamp
SNAPSHOT="$BACKUP_DIR/snapshot-$(date +%Y%m%d-%H%M%S).db"
ETCDCTL_API=3 etcdctl snapshot save "$SNAPSHOT" \
--endpoints="https://127.0.0.1:2379" \
--cacert=/etc/etcd/tls/ca.pem \
--cert=/etc/etcd/tls/etcd-01.pem \
--key=/etc/etcd/tls/etcd-01-key.pem
# Eliminar backups anteriores al período de retención
find "$BACKUP_DIR" -name "snapshot-*.db" -mtime +$RETENTION_DAYS -delete
echo "Backup completado: $SNAPSHOT"
EOF
chmod +x /usr/local/bin/etcd-backup.sh
# Programar backup diario
echo "0 2 * * * root /usr/local/bin/etcd-backup.sh" > /etc/cron.d/etcd-backup
# Restaurar desde snapshot (DETENER etcd primero en TODOS los nodos)
systemctl stop etcd
rm -rf /var/lib/etcd/*
etcdutl snapshot restore /backup/snapshot-20240115-120000.db \
--name etcd-01 \
--initial-cluster "etcd-01=https://10.0.1.10:2380,etcd-02=https://10.0.1.11:2380,etcd-03=https://10.0.1.12:2380" \
--initial-cluster-token etcd-cluster-produccion \
--initial-advertise-peer-urls https://10.0.1.10:2380 \
--data-dir /var/lib/etcd
chown -R etcd:etcd /var/lib/etcd
systemctl start etcd
Monitorización y Métricas
etcd expone métricas en formato Prometheus en el puerto 2381:
# Ver métricas expuestas
curl -s http://localhost:2381/metrics | grep -E "^etcd_"
# Métricas clave a monitorizar
# etcd_server_leader_changes_seen_total - Cambios de líder (debería ser bajo)
# etcd_disk_backend_commit_duration_seconds - Latencia de escritura a disco
# etcd_network_peer_round_trip_time_seconds - Latencia entre peers
# etcd_server_proposals_failed_total - Propuestas fallidas de Raft
# Verificar el tamaño de la base de datos
etcdctl endpoint status --write-out=json | python3 -m json.tool | grep dbSize
# Compactar y desfragmentar la base de datos (mantenimiento periódico)
REVISION=$(etcdctl endpoint status --write-out=json | \
python3 -c "import sys,json; data=json.load(sys.stdin); print(data[0]['Status']['header']['revision'])")
etcdctl compact $REVISION
etcdctl defrag --cluster
Ajuste de Rendimiento
# Configurar la prioridad de I/O para etcd (mejora la estabilidad)
ionice -c 2 -n 0 -p $(pgrep etcd)
# En el archivo de servicio systemd, añadir:
# IOSchedulingClass=best-effort
# IOSchedulingPriority=0
# Verificar la latencia del disco (crucial para etcd)
# Valores esperados: fsync en SSD < 10ms, NVMe < 1ms
dd if=/dev/zero of=/var/lib/etcd/test-latency bs=4096 count=1000 oflag=sync
rm /var/lib/etcd/test-latency
# Parámetros de heartbeat y elección (ajustar si hay latencia alta)
# ETCD_HEARTBEAT_INTERVAL: tiempo en ms entre heartbeats del líder
# ETCD_ELECTION_TIMEOUT: tiempo sin heartbeat antes de iniciar elección
# Regla: ELECTION_TIMEOUT = 10 * HEARTBEAT_INTERVAL mínimo
Solución de Problemas
# Ver logs del servicio etcd
journalctl -u etcd -f
# Verificar el estado del líder y quórum
etcdctl endpoint status --write-out=table
etcdctl endpoint health
# El clúster perdió quórum (solo 1 de 3 nodos activo)
# Arrancar en modo forzado de un solo nodo (SOLO para recuperación de desastre)
# ETCD_FORCE_NEW_CLUSTER=true en el archivo de entorno, luego reiniciar
# Ver alarmas activas en el clúster
etcdctl alarm list
# Desactivar alarma de espacio en disco (después de compactar/desfragmentar)
etcdctl alarm disarm
# Comprobar si hay claves grandes que consumen espacio
etcdctl get "" --from-key --write-out=fields | \
awk '/^.Value/ {print length($0)}' | sort -n | tail -20
Conclusión
Un clúster etcd correctamente configurado con TLS mutuo, backups automatizados y monitorización es la base de cualquier despliegue Kubernetes en producción. La clave del rendimiento y la estabilidad de etcd está en la calidad del almacenamiento en disco y la latencia de red entre nodos, factores que deben evaluarse cuidadosamente al diseñar la infraestructura subyacente.


