El problema#
Hace tres semanas perdí dos de tres nodos Consul en producción por corrupción de datos. El tercero seguía en pie, pero sin quórum el servicio de descubrimiento colapsó. Swarm siguió funcionando, pero sin service discovery todo se fue al infierno.
Este artículo documenta cómo lo arreglé sin parar ni un segundo de servicio.
Entender el estado actual#
Lo primero es saber dónde estamos:
docker exec consul-leader consul membersCon dos nodos caídos, obtendrás algo así:
Node Address Status Type Build Protocol DC
consul-node1 10.0.9.x:8301 alive server 1.15.2 2 dc1
consul-node2 10.0.9.y:8301 failed server 1.15.2 2 dc1
consul-node3 10.0.9.z:8301 failed server 1.15.2 2 dc1Sin quórum (necesitas mayoría: 2 de 3), Consul rechazará escrituras. Verificalo:
docker exec consul-leader consul kv put test/key "valor"Si te dice que no hay líder elegido, confirmas que estás sin quórum.
Paso 1: Backup del nodo superviviente#
Antes de tocar nada:
docker exec consul-leader consul snapshot save /tmp/backup.snap
docker cp consul-leader:/tmp/backup.snap ./consul-backup-$(date +%s).snapGuarda esto en tu máquina host. Es tu única línea de defensa si algo sale mal.
Paso 2: Forzar nuevas elecciones (the nuclear option)#
Si los nodos caídos no van a volver, necesitas decirle a Consul que olvide esos peers. Esto es irreversible:
docker exec consul-leader consul operator raft list-peersVerás los peers caídos. Para removerlos:
docker exec consul-leader consul operator raft remove-peer -id "ID_DEL_NODO_CAIDO"Repite para cada nodo muerto. Después:
docker exec consul-leader consul operator raft list-peersDeberías ver solo el nodo vivo.
Paso 3: Reconstruir los nodos perdidos#
Aquí es donde se pone interesante. No reutilices los volúmenes antiguos. Crea volúmenes nuevos:
docker volume rm consul-node2-data consul-node3-data
docker volume create consul-node2-data
docker volume create consul-node3-dataRedeploy en Swarm con la configuración correcta:
version: '3.8'
services:
consul:
image: consul:1.15.2
command: agent -server -ui -node=consul-node2 -bootstrap-expect=3
-client=0.0.0.0 -bind=0.0.0.0
-retry-join=consul-node1
volumes:
- consul-node2-data:/consul/data
environment:
- CONSUL_BIND_INTERFACE=eth0
networks:
- consul-net
deploy:
placement:
constraints:
- node.hostname == node2
volumes:
consul-node2-data:
driver: localClave: bootstrap-expect=3 sigue siendo 3, pero ahora Consul auto-descubre los peers vía retry-join. Los nodos nuevos se unirán automáticamente.
Paso 4: Validar sin downtime#
Mientras los nodos se sincronizan, prueba que el quórum funciona:
docker exec consul-leader consul members
# Deberías ver los nuevos nodos en estado "alive"
docker exec consul-leader consul kv put test/validation "ok"
docker exec consul-leader consul kv get test/validationSi esto funciona, tienes quórum de nuevo.
Paso 5: Migración de datos#
Los datos clave (servicios registrados, KV) deberían replicarse automáticamente. Verifica:
docker exec consul-leader consul catalog services¿Tus servicios están ahí? Si no, necesitas re-registrarlos. En mi caso usaba Consul DNS y el failover fue transparente porque los clientes ya tenían cached.
Lecciones aprendidas#
- Nunca reutilices volúmenes Consul corrompidos. Mejor perder datos que propagar corrupción.
- El backup que hice me salvó de tener que investigar qué estaba registrado.
bootstrap-expectdebe ser consistente en todos los nodos, incluso si algunos están down.- Los clientes Docker conectados a Consul en modo DNS followeaban automáticamente. El downtime percibido fue cero.
Esto me tomó 2 horas start-to-finish. Sin este proceso hubiera sido un desastre de 8 horas.