--- title: "Cómo desplegar un clúster etcd con TLS en Ubuntu 24.04" description: "Despliega un clúster etcd de tres nodos con TLS mutuo en Ubuntu 24.04, gestiónalo con etcdctl, programa copias de seguridad automáticas y restaura un snapshot." category: "Descubrimiento de Servicios" language: es source: https://cubepath.com/es/docs/descubrimiento-de-servicios/instalacion-y-gestion-de-cluster-etcd updated: 2026-09-25 --- # Cómo desplegar un clúster etcd con TLS en Ubuntu 24.04 etcd es un almacén clave-valor distribuido y con consistencia fuerte que usa el algoritmo de consenso Raft. Es la base de datos de Kubernetes y de muchos otros sistemas que necesitan guardar configuración o coordinar procesos. En este tutorial desplegarás un clúster etcd de tres nodos en Ubuntu 24.04 con TLS mutuo entre nodos y clientes, lo gestionarás con `etcdctl` y dejarás programadas copias de seguridad diarias con un temporizador de systemd. ## Requisitos previos Para seguir esta guía necesitas: - Tres servidores con Ubuntu 24.04 LTS (x86_64), por ejemplo tres VPS de CubePath, con al menos 2 GB de RAM y disco SSD o NVMe. etcd es muy sensible a la latencia de escritura en disco. - Un usuario no root con privilegios `sudo` en cada servidor. - Una red privada entre los tres nodos con baja latencia. En los ejemplos se usan estas direcciones, sustitúyelas por las tuyas: | Nombre | IP privada | |---|---| | etcd-01 | 10.0.1.10 | | etcd-02 | 10.0.1.11 | | etcd-03 | 10.0.1.12 | - Acceso SSH desde etcd-01 a los otros dos nodos para copiar certificados con `scp`. Un clúster de tres nodos tolera la caída de uno. Usa siempre un número impar de miembros (3 o 5). ## Paso 1: Instalar los binarios de etcd Ubuntu incluye un paquete `etcd-server`, pero suele ir varias versiones por detrás. Usarás los binarios oficiales publicados en GitHub. Consulta la última versión estable en la [página de releases de etcd](https://github.com/etcd-io/etcd/releases) y ajusta la variable. Ejecuta este paso en los tres nodos: ```bash ETCD_VER=v3.6.4 cd /tmp curl -fsSLO "https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz" curl -fsSLO "https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/SHA256SUMS" sha256sum --check --ignore-missing SHA256SUMS ``` ```output etcd-v3.6.4-linux-amd64.tar.gz: OK ``` Extrae el archivo e instala los tres binarios (`etcd`, el cliente `etcdctl` y la utilidad offline `etcdutl`): ```bash tar xzf "etcd-${ETCD_VER}-linux-amd64.tar.gz" sudo install -m 0755 "etcd-${ETCD_VER}-linux-amd64/etcd" "etcd-${ETCD_VER}-linux-amd64/etcdctl" "etcd-${ETCD_VER}-linux-amd64/etcdutl" /usr/local/bin/ etcd --version ``` ```output etcd Version: 3.6.4 Git SHA: ... Go Version: ... Go OS/Arch: linux/amd64 ``` Crea el usuario de sistema y los directorios de datos y configuración: ```bash sudo useradd --system --home-dir /var/lib/etcd --shell /usr/sbin/nologin etcd sudo install -d -o etcd -g etcd -m 0700 /var/lib/etcd sudo install -d -o etcd -g etcd -m 0750 /etc/etcd/pki ``` ## Paso 2: Crear la CA y los certificados TLS Con TLS mutuo, cada nodo y cada cliente se identifica con un certificado firmado por una CA propia del clúster. Genera todo **en etcd-01**, en un directorio de trabajo: ```bash mkdir -p ~/etcd-pki cd ~/etcd-pki openssl genrsa -out ca.key 4096 openssl req -x509 -new -key ca.key -sha256 -days 3650 -subj "/CN=etcd-ca" -out ca.crt ``` Genera un certificado por nodo. Cada certificado incluye la IP privada del nodo, su nombre y `127.0.0.1` en el campo `subjectAltName`, y sirve tanto para escuchar como servidor como para conectarse a los otros nodos: ```bash while read -r name ip; do openssl genrsa -out "${name}.key" 2048 openssl req -new -key "${name}.key" -subj "/CN=${name}" -out "${name}.csr" printf 'subjectAltName=DNS:%s,IP:%s,IP:127.0.0.1\nextendedKeyUsage=serverAuth,clientAuth\n' "$name" "$ip" > "${name}.ext" openssl x509 -req -in "${name}.csr" -CA ca.crt -CAkey ca.key -CAcreateserial \ -days 825 -sha256 -extfile "${name}.ext" -out "${name}.crt" done < admin.ext openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -days 825 -sha256 -extfile admin.ext -out admin.crt ``` Comprueba que el certificado de un nodo contiene las direcciones correctas: ```bash openssl x509 -in etcd-02.crt -noout -ext subjectAltName ``` ```output X509v3 Subject Alternative Name: DNS:etcd-02, IP Address:10.0.1.11, IP Address:127.0.0.1 ``` Copia a cada uno de los otros nodos la CA, su certificado y el certificado de administración. **No copies `ca.key`**: guárdala fuera del servidor en un lugar seguro, ya que permite emitir certificados válidos para el clúster. ```bash scp ca.crt etcd-02.crt etcd-02.key admin.crt admin.key your_user@10.0.1.11:~/ scp ca.crt etcd-03.crt etcd-03.key admin.crt admin.key your_user@10.0.1.12:~/ ``` En **cada nodo**, instala los archivos en `/etc/etcd/pki` con nombres uniformes. Sustituye `etcd-01` por el nombre del nodo en el que estés (en etcd-01 ejecútalo desde `~/etcd-pki`, en los demás desde tu directorio personal): ```bash sudo install -o etcd -g etcd -m 0644 ca.crt /etc/etcd/pki/ca.crt sudo install -o etcd -g etcd -m 0644 etcd-01.crt /etc/etcd/pki/server.crt sudo install -o etcd -g etcd -m 0600 etcd-01.key /etc/etcd/pki/server.key sudo install -o root -g root -m 0600 admin.crt admin.key /etc/etcd/pki/ ``` Por último, deja el certificado de administración accesible para tu usuario, que es el que ejecutará `etcdctl`: ```bash install -d -m 0700 ~/.etcd install -m 0600 ca.crt admin.crt admin.key ~/.etcd/ ``` ## Paso 3: Configurar etcd en cada nodo etcd lee su configuración de variables de entorno con el prefijo `ETCD_`. Crea el archivo de entorno: ```bash sudo nano /etc/etcd/etcd.env ``` Este es el contenido para etcd-01. En los otros nodos cambia `ETCD_NAME` y todas las URL que contienen `10.0.1.10` por el nombre y la IP de ese nodo; `ETCD_INITIAL_CLUSTER` es idéntico en los tres: ```ini ETCD_NAME="etcd-01" ETCD_DATA_DIR="/var/lib/etcd" ETCD_LISTEN_PEER_URLS="https://10.0.1.10:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.0.1.10:2380" 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" ETCD_LISTEN_METRICS_URLS="http://127.0.0.1:2381" 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-1" ETCD_CERT_FILE="/etc/etcd/pki/server.crt" ETCD_KEY_FILE="/etc/etcd/pki/server.key" ETCD_TRUSTED_CA_FILE="/etc/etcd/pki/ca.crt" ETCD_CLIENT_CERT_AUTH="true" ETCD_PEER_CERT_FILE="/etc/etcd/pki/server.crt" ETCD_PEER_KEY_FILE="/etc/etcd/pki/server.key" ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/pki/ca.crt" ETCD_PEER_CLIENT_CERT_AUTH="true" ETCD_AUTO_COMPACTION_MODE="periodic" ETCD_AUTO_COMPACTION_RETENTION="1h" ``` Puntos importantes: - El puerto 2379 atiende a los clientes y el 2380 la comunicación entre nodos. - `ETCD_CLIENT_CERT_AUTH` y `ETCD_PEER_CLIENT_CERT_AUTH` obligan a presentar un certificado firmado por la CA; sin él, la conexión se rechaza. - `ETCD_LISTEN_METRICS_URLS` publica las métricas de Prometheus y el endpoint `/health` solo en local y sin TLS, para poder monitorizar sin certificados. - La compactación automática descarta cada hora las revisiones antiguas de las claves, lo que evita que la base de datos crezca sin límite. - `ETCD_INITIAL_CLUSTER*` solo se usa en el primer arranque; con el directorio de datos ya creado, etcd lo ignora. Protege el archivo: ```bash sudo chown root:etcd /etc/etcd/etcd.env sudo chmod 0640 /etc/etcd/etcd.env ``` ## Paso 4: Crear el servicio systemd Crea la unidad systemd en los tres nodos: ```bash sudo nano /etc/systemd/system/etcd.service ``` ```ini [Unit] Description=etcd key-value store Documentation=https://etcd.io/docs/ Wants=network-online.target After=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=65536 [Install] WantedBy=multi-user.target ``` Abre los puertos de etcd solo para la red privada. Si UFW no está activo todavía, permite antes SSH: ```bash sudo ufw allow OpenSSH sudo ufw allow from 10.0.1.0/24 to any port 2379:2380 proto tcp sudo ufw enable ``` ## Paso 5: Arrancar el clúster Con `Type=notify`, etcd solo avisa a systemd de que está listo cuando el clúster tiene quórum, por lo que el primer nodo esperaría a los demás. Usa `--no-block` para que el comando no se quede bloqueado y ejecútalo en los tres nodos: ```bash sudo systemctl daemon-reload sudo systemctl enable --now --no-block etcd ``` Tras unos segundos, comprueba el estado en cada nodo: ```bash sudo systemctl status etcd ``` ```output ● etcd.service - etcd key-value store Loaded: loaded (/etc/systemd/system/etcd.service; enabled; preset: enabled) Active: active (running) since ... ``` Si el servicio no arranca, revisa los mensajes con `sudo journalctl -u etcd -e`. Los errores más habituales son rutas de certificados mal escritas o una IP que no coincide con el `subjectAltName`. ## Paso 6: Gestionar el clúster con etcdctl Configura `etcdctl` mediante variables de entorno para no repetir los certificados en cada comando. Añádelas a tu `~/.bashrc`: ```bash nano ~/.bashrc ``` ```bash export ETCDCTL_ENDPOINTS="https://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379" export ETCDCTL_CACERT="$HOME/.etcd/ca.crt" export ETCDCTL_CERT="$HOME/.etcd/admin.crt" export ETCDCTL_KEY="$HOME/.etcd/admin.key" ``` Carga las variables y comprueba la salud de los tres endpoints: ```bash source ~/.bashrc etcdctl endpoint health ``` ```output https://10.0.1.10:2379 is healthy: successfully committed proposal: took = 8.1ms https://10.0.1.11:2379 is healthy: successfully committed proposal: took = 9.4ms https://10.0.1.12:2379 is healthy: successfully committed proposal: took = 9.0ms ``` Lista los miembros: ```bash etcdctl member list --write-out=table ``` ```output +------------------+---------+---------+------------------------+------------------------+------------+ | ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER | +------------------+---------+---------+------------------------+------------------------+------------+ | 3a57933972cb5131 | started | etcd-01 | https://10.0.1.10:2380 | https://10.0.1.10:2379 | false | | 8e9e05c52164694d | started | etcd-02 | https://10.0.1.11:2380 | https://10.0.1.11:2379 | false | | c1b2e4f7a0d39e6b | started | etcd-03 | https://10.0.1.12:2380 | https://10.0.1.12:2379 | false | +------------------+---------+---------+------------------------+------------------------+------------+ ``` `etcdctl endpoint status --write-out=table` muestra además qué nodo es el líder y el tamaño de la base de datos de cada miembro. Prueba las operaciones básicas. Escribe una clave, léela y lee todas las que comparten prefijo: ```bash etcdctl put /config/app/log_level info etcdctl get /config/app/log_level etcdctl get /config/ --prefix ``` ```output OK /config/app/log_level info /config/app/log_level info ``` Para ver cómo reaccionan los clientes a los cambios, abre una segunda sesión y observa el prefijo con `etcdctl watch /config/ --prefix`. Cada `put` o `del` que hagas en la primera sesión aparecerá al instante. Comprueba también que un cliente sin certificado queda rechazado: ```bash curl -s --cacert ~/.etcd/ca.crt https://10.0.1.10:2379/version ``` La conexión falla durante el handshake TLS porque el servidor exige certificado de cliente. Con `--cert ~/.etcd/admin.crt --key ~/.etcd/admin.key` añadidos, la misma petición devuelve la versión en JSON. ## Paso 7: Programar copias de seguridad Un snapshot de etcd contiene todo el estado del clúster y se puede tomar en caliente desde un solo miembro. Crea un script de copia en **etcd-01**: ```bash sudo nano /usr/local/sbin/etcd-backup ``` ```bash #!/usr/bin/env bash set -euo pipefail BACKUP_DIR="/var/backups/etcd" RETENTION_DAYS=7 SNAPSHOT="${BACKUP_DIR}/etcd-$(date +%Y%m%d-%H%M%S).db" mkdir -p "$BACKUP_DIR" chmod 0700 "$BACKUP_DIR" /usr/local/bin/etcdctl \ --endpoints="https://127.0.0.1:2379" \ --cacert=/etc/etcd/pki/ca.crt \ --cert=/etc/etcd/pki/admin.crt \ --key=/etc/etcd/pki/admin.key \ snapshot save "$SNAPSHOT" /usr/local/bin/etcdutl snapshot status "$SNAPSHOT" --write-out=table find "$BACKUP_DIR" -name 'etcd-*.db' -mtime +"$RETENTION_DAYS" -delete ``` Hazlo ejecutable y pruébalo: ```bash sudo chmod 0750 /usr/local/sbin/etcd-backup sudo /usr/local/sbin/etcd-backup ``` ```output Snapshot saved at /var/backups/etcd/etcd-20260925-101500.db +----------+----------+------------+------------+ | HASH | REVISION | TOTAL KEYS | TOTAL SIZE | +----------+----------+------------+------------+ | 5e1f0a2b | 14 | 9 | 25 kB | +----------+----------+------------+------------+ ``` Programa la ejecución diaria con systemd. Crea la unidad de servicio: ```bash sudo nano /etc/systemd/system/etcd-backup.service ``` ```ini [Unit] Description=etcd snapshot backup [Service] Type=oneshot ExecStart=/usr/local/sbin/etcd-backup ``` Y el temporizador: ```bash sudo nano /etc/systemd/system/etcd-backup.timer ``` ```ini [Unit] Description=Daily etcd snapshot backup [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=timers.target ``` Actívalo y comprueba la próxima ejecución: ```bash sudo systemctl daemon-reload sudo systemctl enable --now etcd-backup.timer systemctl list-timers etcd-backup.timer ``` Copia los snapshots fuera del servidor (almacenamiento de objetos u otro host): una copia que vive en el mismo disco que los datos no protege frente a la pérdida del nodo. ## Paso 8: Restaurar el clúster desde un snapshot La restauración crea un clúster nuevo con los datos del snapshot. Se hace en todos los nodos con el mismo archivo. Copia el snapshot a los tres nodos y, primero, detén etcd en todos: ```bash sudo systemctl stop etcd ``` En cada nodo, aparta el directorio de datos actual (el restore necesita que el destino no exista) y restaura. Ajusta `--name` y `--initial-advertise-peer-urls` al nodo en el que estés: ```bash sudo mv /var/lib/etcd /var/lib/etcd.old sudo etcdutl snapshot restore /var/backups/etcd/etcd-20260925-101500.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-1 \ --initial-advertise-peer-urls https://10.0.1.10:2380 \ --data-dir /var/lib/etcd sudo chown -R etcd:etcd /var/lib/etcd sudo chmod 0700 /var/lib/etcd ``` Arranca etcd en los tres nodos y verifica que los datos están de vuelta: ```bash sudo systemctl start --no-block etcd etcdctl endpoint health etcdctl get /config/ --prefix ``` Cuando confirmes que todo funciona, puedes borrar `/var/lib/etcd.old`. ## Paso 9: Monitorización y mantenimiento El endpoint de métricas configurado en el paso 3 permite comprobar la salud del nodo sin certificados: ```bash curl -s http://127.0.0.1:2381/health ``` ```output {"health":"true","reason":""} ``` Las métricas en formato Prometheus están en `http://127.0.0.1:2381/metrics`. Las más útiles para alertar son `etcd_server_has_leader` (debe valer 1), `etcd_server_leader_changes_seen_total` (debe crecer muy poco), `etcd_disk_wal_fsync_duration_seconds` y `etcd_disk_backend_commit_duration_seconds`: ```bash curl -s http://127.0.0.1:2381/metrics | grep -E '^etcd_server_(has_leader|leader_changes_seen_total)' ``` La compactación automática libera revisiones antiguas, pero el espacio en disco solo se recupera al desfragmentar. Hazlo de un nodo en cada paso, porque el miembro queda bloqueado mientras dura: ```bash etcdctl defrag --endpoints=https://10.0.1.10:2379 ``` Si la base de datos alcanza su cuota (2 GB por defecto), etcd activa una alarma `NOSPACE` y deja de aceptar escrituras. Compruébalo con: ```bash etcdctl alarm list ``` Tras compactar y desfragmentar, desactiva la alarma con `etcdctl alarm disarm`. Para saber si el disco es lo bastante rápido, la documentación de etcd recomienda medir la latencia de `fdatasync` con `fio` en el mismo sistema de archivos que `/var/lib/etcd`. El percentil 99 debería estar por debajo de 10 ms: ```bash sudo apt install -y fio sudo mkdir -p /var/lib/etcd-fio-test sudo fio --rw=write --ioengine=sync --fdatasync=1 --directory=/var/lib/etcd-fio-test --size=22m --bs=2300 --name=etcd-disk-test sudo rm -rf /var/lib/etcd-fio-test ``` En la salida, busca la sección `fsync/fdatasync/sync_file_range` y el valor `99.00th`. ## Solución de problemas - **`x509: certificate is valid for ..., not 10.0.1.11`:** el certificado del nodo no incluye esa IP. Regenera el certificado con el `subjectAltName` correcto y reinicia etcd. - **`request cluster ID mismatch` o el nodo no se une:** el nodo tiene datos de un clúster anterior. Detén etcd, aparta `/var/lib/etcd` y vuelve a arrancarlo con la configuración inicial correcta. - **Cambios de líder frecuentes:** casi siempre es latencia de disco o de red. Revisa `etcd_disk_wal_fsync_duration_seconds` y la prueba de `fio`, y evita compartir el disco con cargas intensivas. - **`database space exceeded`:** la base de datos llegó a la cuota. Compacta, desfragmenta y ejecuta `etcdctl alarm disarm`; si el volumen de datos es legítimo, aumenta `ETCD_QUOTA_BACKEND_BYTES`. ## Conclusión Tienes un clúster etcd de tres nodos con TLS mutuo, compactación automática, copias de seguridad diarias programadas con systemd y un procedimiento de restauración probado. Como siguientes pasos, envía las métricas del puerto 2381 a tu sistema de monitorización, copia los snapshots a un almacenamiento externo y activa la autenticación por usuarios y roles de etcd (`etcdctl user` y `etcdctl role`) si varias aplicaciones van a compartir el clúster.