Apache ZooKeeper es un servicio de coordinación distribuida: guarda pequeños datos en un árbol de nodos (znodes) replicado en varios servidores y ofrece primitivas como nodos efímeros, nodos secuenciales y notificaciones (watches), sobre las que se construyen la elección de líder, el registro de servicios o los bloqueos distribuidos. Lo usan Apache HBase, Solr, Hadoop y muchas aplicaciones propias. En este tutorial instalarás un ensemble de tres servidores ZooKeeper en Ubuntu 24.04, lo gestionarás con systemd y aprenderás a trabajar con znodes y ACLs.
Requisitos previos
Para seguir esta guía necesitas:
- Tres servidores con Ubuntu 24.04 LTS, por ejemplo tres VPS de CubePath, con al menos 2 GB de RAM cada uno.
- Un usuario no root con privilegios
sudoen cada servidor. - Una red privada entre los tres. En los ejemplos se usan estas direcciones, sustitúyelas por las tuyas:
| Nombre | IP privada | ID de ZooKeeper |
|---|---|---|
| zk-01 | 10.0.1.10 | 1 |
| zk-02 | 10.0.1.11 | 2 |
| zk-03 | 10.0.1.12 | 3 |
Un ensemble necesita que la mayoría de servidores estén activos para funcionar. Con tres servidores tolera la caída de uno; con cinco, de dos. Salvo que se indique lo contrario, ejecuta cada paso en los tres servidores.
NotaApache Kafka 4.0 y posteriores ya no usan ZooKeeper (funcionan solo en modo KRaft). Si instalas ZooKeeper únicamente para un Kafka nuevo, no lo necesitas.
Paso 1: Instalar Java
ZooKeeper es una aplicación Java. Instala el entorno de ejecución de OpenJDK 17 sin componentes gráficos:
sudo apt update
sudo apt install -y openjdk-17-jre-headless
java -version
openjdk version "17.0.x" ...
OpenJDK Runtime Environment (build 17.0.x+...-Ubuntu-...)
OpenJDK 64-Bit Server VM (build 17.0.x+..., mixed mode, sharing)
Paso 2: Descargar e instalar ZooKeeper
Usarás el paquete binario oficial de Apache. Consulta la versión estable actual en la página de releases de ZooKeeper y ajusta la variable. El archivo de Apache (archive.apache.org) conserva todas las versiones, así que la URL no deja de funcionar cuando sale una nueva:
ZK_VERSION=3.9.3
cd /tmp
curl -fsSLO "https://archive.apache.org/dist/zookeeper/zookeeper-${ZK_VERSION}/apache-zookeeper-${ZK_VERSION}-bin.tar.gz"
curl -fsSLO "https://archive.apache.org/dist/zookeeper/zookeeper-${ZK_VERSION}/apache-zookeeper-${ZK_VERSION}-bin.tar.gz.sha512"
sha512sum "apache-zookeeper-${ZK_VERSION}-bin.tar.gz"
cat "apache-zookeeper-${ZK_VERSION}-bin.tar.gz.sha512"
Los dos comandos deben mostrar el mismo hash. Si no coinciden, borra el archivo y vuelve a descargarlo.
Extrae el paquete en /opt y crea un enlace simbólico estable, para que las actualizaciones consistan en cambiar el enlace:
sudo tar -xzf "apache-zookeeper-${ZK_VERSION}-bin.tar.gz" -C /opt
sudo ln -sfn "/opt/apache-zookeeper-${ZK_VERSION}-bin" /opt/zookeeper
Crea un usuario de sistema sin shell y el directorio de datos:
sudo useradd --system --home-dir /var/lib/zookeeper --shell /usr/sbin/nologin zookeeper
sudo install -d -o zookeeper -g zookeeper -m 0750 /var/lib/zookeeper /var/lib/zookeeper/data /var/log/zookeeper
Los binarios quedan como propiedad de root; el usuario zookeeper solo necesita escribir en su directorio de datos y de logs.
Paso 3: Configurar el ensemble
Crea el archivo de configuración principal:
sudo nano /opt/zookeeper/conf/zoo.cfg
El contenido es idéntico en los tres servidores:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper/data
clientPort=2181
maxClientCnxns=60
autopurge.snapRetainCount=5
autopurge.purgeInterval=24
4lw.commands.whitelist=ruok,srvr,stat,mntr,conf
admin.serverAddress=127.0.0.1
admin.serverPort=8080
server.1=10.0.1.10:2888:3888
server.2=10.0.1.11:2888:3888
server.3=10.0.1.12:2888:3888
Qué hace cada bloque:
tickTimees la unidad de tiempo en milisegundos.initLimitysyncLimitson los ticks que un seguidor puede tardar en conectarse al líder y en mantenerse sincronizado (20 y 10 segundos).dataDirguarda los snapshots y el registro de transacciones.autopurge.*borra cada 24 horas los snapshots antiguos y conserva los 5 últimos. Sin esto, el disco se llena con el tiempo.4lw.commands.whitelisthabilita los comandos de diagnóstico de cuatro letras. Por defecto solo está permitidosrvr.admin.serverAddresslimita la AdminServer HTTP a127.0.0.1. Por defecto escucha en todas las interfaces en el puerto 8080.- Cada línea
server.Ndefine un miembro: su ID, su IP, el puerto por el que los seguidores hablan con el líder (2888) y el de elección de líder (3888). Usa IP y no nombres de host: en Ubuntu, el nombre propio del servidor suele resolver a127.0.1.1y ZooKeeper escucharía solo en local.
Cada servidor necesita saber cuál es su ID. Escríbelo en el archivo myid del directorio de datos, con el valor que corresponda a cada nodo (1 en zk-01, 2 en zk-02, 3 en zk-03):
echo 1 | sudo tee /var/lib/zookeeper/data/myid
sudo chown zookeeper:zookeeper /var/lib/zookeeper/data/myid
Paso 4: Crear el servicio systemd
Crea la unidad systemd. zkServer.sh start-foreground mantiene el proceso en primer plano, que es lo que espera systemd, y los logs van directamente al journal:
sudo nano /etc/systemd/system/zookeeper.service
[Unit]
Description=Apache ZooKeeper
Documentation=https://zookeeper.apache.org
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=zookeeper
Group=zookeeper
Environment=ZOO_LOG_DIR=/var/log/zookeeper
Environment="SERVER_JVMFLAGS=-Xms1g -Xmx1g"
ExecStart=/opt/zookeeper/bin/zkServer.sh start-foreground
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
SERVER_JVMFLAGS fija la memoria de la JVM. Un valor fijo (mismo -Xms y -Xmx) de la mitad de la RAM del servidor es un buen punto de partida; ZooKeeper guarda todo el árbol en memoria, pero con datos de coordinación rara vez necesita más de unos pocos GB.
Paso 5: Abrir los puertos y arrancar
Permite el tráfico de ZooKeeper solo desde la red privada: 2181 para clientes, 2888 y 3888 entre servidores. Si UFW no está activo, permite antes SSH:
sudo ufw allow OpenSSH
sudo ufw allow from 10.0.1.0/24 to any port 2181 proto tcp
sudo ufw allow from 10.0.1.0/24 to any port 2888 proto tcp
sudo ufw allow from 10.0.1.0/24 to any port 3888 proto tcp
sudo ufw enable
Arranca el servicio en los tres servidores:
sudo systemctl daemon-reload
sudo systemctl enable --now zookeeper
sudo systemctl status zookeeper
Cuando los tres estén en marcha, comprueba el rol de cada uno:
/opt/zookeeper/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower
Uno de los tres servidores mostrará Mode: leader y los otros dos Mode: follower. Si ves Mode: standalone, las líneas server.N no se han leído; si el comando no puede conectarse, revisa los logs con sudo journalctl -u zookeeper -e.
Comprueba también la salud con un comando de cuatro letras:
echo ruok | nc 127.0.0.1 2181
imok
Paso 6: Trabajar con znodes
El cliente zkCli.sh permite explorar y modificar el árbol. Conéctate al ensemble indicando los tres servidores; si uno cae, el cliente se reconecta a otro:
/opt/zookeeper/bin/zkCli.sh -server 10.0.1.10:2181,10.0.1.11:2181,10.0.1.12:2181
Los siguientes comandos se escriben dentro del shell de ZooKeeper. Crea un znode persistente con datos y léelo:
create /app ""
create /app/config "log_level=info"
get /app/config
Created /app
Created /app/config
log_level=info
Actualiza el valor y consulta los metadatos. El campo dataVersion aumenta con cada cambio, lo que permite actualizaciones condicionales:
set /app/config "log_level=debug"
stat /app/config
Los nodos efímeros desaparecen cuando se cierra la sesión del cliente que los creó. Son la base del registro de servicios: cada instancia crea el suyo y, si se cae, desaparece solo. Los nodos secuenciales reciben un sufijo numérico creciente, útil para colas y elección de líder:
create /app/workers ""
create -e /app/workers/worker-01 "10.0.2.10:8080"
create -s /app/workers/job- "tarea"
ls /app/workers
[job-0000000001, worker-01]
Para recibir una notificación cuando cambie un nodo, registra un watch. Ejecuta esto, cambia el valor desde otra sesión de zkCli.sh y verás el aviso WatchedEvent:
get -w /app/config
Sal del shell con quit. Puedes comprobar desde otro servidor que los datos se han replicado:
/opt/zookeeper/bin/zkCli.sh -server 10.0.1.12:2181 get /app/config
Paso 7: Proteger znodes con ACLs
Por defecto cualquier cliente que llegue al puerto 2181 puede leer y modificar todo el árbol. Las ACLs se definen por znode con el formato esquema:id:permisos, donde los permisos son c (crear hijos), d (borrar hijos), r (leer), w (escribir) y a (administrar ACLs).
El esquema digest autentica con usuario y contraseña. Conéctate con zkCli.sh y, dentro del shell, autentícate y crea un znode que solo ese usuario pueda usar. El esquema auth aplica la ACL a los usuarios autenticados en la sesión actual. Sustituye your_strong_password por una contraseña real:
addauth digest appuser:your_strong_password
create /secretos "dato-privado" auth::cdrwa
getAcl /secretos
Created /secretos
'digest,'appuser:...
: cdrwa
Abre una segunda sesión de zkCli.sh sin autenticarte e intenta leerlo:
get /secretos
Insufficient permission : /secretos
Tras ejecutar addauth digest appuser:your_strong_password en esa sesión, la lectura funciona. Para un znode que todos puedan leer pero solo el usuario autenticado pueda modificar, combina dos ACLs: setAcl /app world:anyone:r,auth::cdrwa.
Importantelas credenciales
digestviajan en claro por la red. Mantén el puerto 2181 limitado a la red privada, como en el paso 5, o configura TLS entre clientes y servidores.
Paso 8: Monitorizar el ensemble
El comando mntr devuelve métricas en formato clave-valor, fáciles de recoger con cualquier sistema de monitorización:
echo mntr | nc 127.0.0.1 2181 | grep -E 'zk_server_state|zk_avg_latency|zk_outstanding_requests|zk_synced_followers'
zk_server_state leader
zk_avg_latency 0.4
zk_outstanding_requests 0
zk_synced_followers 2
Vigila especialmente:
zk_server_state: debe haber exactamente unleaderen el ensemble.zk_synced_followers(solo en el líder): debe ser el número de servidores menos uno.zk_outstanding_requests: si crece de forma sostenida, el ensemble no da abasto.zk_avg_latency: latencia media en milisegundos; valores altos suelen indicar disco lento.
La AdminServer ofrece la misma información en JSON a través de HTTP, solo en local:
curl -s http://127.0.0.1:8080/commands/ruok
{
"command" : "ruok",
"error" : null
}
Solución de problemas
- El servidor muestra
Mode: standalone: falta el archivomyido las líneasserver.Nno están enzoo.cfg. Revisa ambos y reinicia consudo systemctl restart zookeeper. - No se elige líder (
Error contacting serviceen todos los nodos): los servidores no se ven por el puerto 3888. Pruebanc -zv 10.0.1.11 3888y revisa las reglas de UFW. Comprueba también que cadamyidcoincide con su líneaserver.N. ruok is not executed because it is not in the whitelist: añade el comando a4lw.commands.whitelistenzoo.cfgy reinicia.Address already in useen el puerto 8080: otra aplicación usa ese puerto. Cambiaadmin.serverPorto desactiva la AdminServer conadmin.enableServer=false.- Disco lleno en
/var/lib/zookeeper: falta la purga automática. Comprueba los valoresautopurge.*.
Conclusión
Tienes un ensemble de tres servidores ZooKeeper replicado, gestionado con systemd, con purga automática de snapshots, comandos de diagnóstico habilitados y ACLs para proteger los znodes. Como siguientes pasos, recoge las métricas de mntr en tu sistema de monitorización, configura TLS para las conexiones de clientes si salen de la red privada y conecta tus aplicaciones al ensemble con la cadena 10.0.1.10:2181,10.0.1.11:2181,10.0.1.12:2181.
