Instalación y Configuración de Apache ZooKeeper
Apache ZooKeeper es un servicio de coordinación distribuida de alto rendimiento que proporciona sincronización, configuración centralizada y elección de líder para sistemas distribuidos como Apache Kafka, HBase y Hadoop. Su arquitectura de ensemble con replicación y consistencia garantizada mediante ZAB (ZooKeeper Atomic Broadcast) lo hace esencial en infraestructuras de big data. Esta guía cubre el despliegue de un ensemble ZooKeeper en Linux, configuración, ACLs y monitorización.
Requisitos Previos
- 3 o 5 servidores Linux (Ubuntu 22.04/Debian 12 o CentOS 9/Rocky 9)
- Java 11 o superior instalado en todos los nodos
- Mínimo 2 GB de RAM por nodo (4 GB recomendado en producción)
- Disco separado para datos de ZooKeeper (SSD recomendado)
- Puertos 2181 (cliente), 2888 (follower→leader) y 3888 (elección) abiertos entre nodos
- NTP sincronizado
Instalación de ZooKeeper
# Instalar Java si no está disponible
apt-get update && apt-get install -y openjdk-17-jre-headless
# CentOS/Rocky:
# dnf install -y java-17-openjdk-headless
java -version
# Definir la versión a instalar
ZK_VERSION="3.9.2"
# Descargar ZooKeeper desde Apache
wget "https://downloads.apache.org/zookeeper/zookeeper-${ZK_VERSION}/apache-zookeeper-${ZK_VERSION}-bin.tar.gz"
tar xzf "apache-zookeeper-${ZK_VERSION}-bin.tar.gz" -C /opt/
ln -s "/opt/apache-zookeeper-${ZK_VERSION}-bin" /opt/zookeeper
# Crear usuario de sistema
useradd -r -d /opt/zookeeper -s /bin/false zookeeper
chown -R zookeeper:zookeeper /opt/zookeeper
# Crear directorios de datos y logs en disco separado (si disponible)
mkdir -p /var/lib/zookeeper/data /var/log/zookeeper
chown -R zookeeper:zookeeper /var/lib/zookeeper /var/log/zookeeper
# Añadir ZooKeeper al PATH del sistema
echo 'export PATH=$PATH:/opt/zookeeper/bin' > /etc/profile.d/zookeeper.sh
Configuración del Ensemble
Configura el archivo zoo.cfg en cada nodo del ensemble:
cat > /opt/zookeeper/conf/zoo.cfg << 'EOF'
# Duración de un tick en milisegundos (unidad base de tiempo)
tickTime=2000
# Número de ticks que un servidor follower puede tardar en conectarse al líder
initLimit=10
# Número de ticks para la sincronización entre líder y followers
syncLimit=5
# Directorio donde ZooKeeper almacena los snapshots
dataDir=/var/lib/zookeeper/data
# Directorio para los logs de transacciones (idealmente disco separado)
dataLogDir=/var/lib/zookeeper/datalog
# Puerto de escucha para clientes
clientPort=2181
# Límite de conexiones por cliente
maxClientCnxns=60
# Retención de snapshots (cuántos snapshots mantener)
autopurge.snapRetainCount=5
autopurge.purgeInterval=24
# Configuración del ensemble (todos los nodos deben tenerlo igual)
# Formato: server.ID=hostname:puertoFollower:puertoEleccion
server.1=zk-01.empresa.local:2888:3888
server.2=zk-02.empresa.local:2888:3888
server.3=zk-03.empresa.local:2888:3888
# Métricas con Prometheus (ZooKeeper 3.6+)
metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
metricsProvider.httpPort=7070
metricsProvider.exportJvmInfo=true
# Logs de auditoría (ZooKeeper 3.6+)
audit.enable=true
EOF
# CRÍTICO: Establecer el ID del nodo en el archivo myid
# En zk-01: echo "1" > /var/lib/zookeeper/data/myid
# En zk-02: echo "2" > /var/lib/zookeeper/data/myid
# En zk-03: echo "3" > /var/lib/zookeeper/data/myid
echo "1" > /var/lib/zookeeper/data/myid # Ajustar según el nodo
chown zookeeper:zookeeper /var/lib/zookeeper/data/myid
Configura los parámetros de la JVM:
# Configurar la JVM para producción
cat > /opt/zookeeper/conf/java.env << 'EOF'
# Memoria de la JVM (ajustar según la RAM disponible)
export JVMFLAGS="-Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=50"
# Directorio de logs
export ZOO_LOG_DIR=/var/log/zookeeper
export ZOO_LOG4J_PROP="INFO,ROLLINGFILE"
EOF
Gestión del Servicio
Crea el servicio systemd para ZooKeeper:
cat > /etc/systemd/system/zookeeper.service << 'EOF'
[Unit]
Description=Apache ZooKeeper - Distributed Coordination Service
Documentation=https://zookeeper.apache.org
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
User=zookeeper
Group=zookeeper
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64"
ExecStart=/opt/zookeeper/bin/zkServer.sh start
ExecStop=/opt/zookeeper/bin/zkServer.sh stop
ExecReload=/opt/zookeeper/bin/zkServer.sh restart
PIDFile=/var/lib/zookeeper/data/zookeeper_server.pid
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now zookeeper
# Verificar el estado del servidor
/opt/zookeeper/bin/zkServer.sh status
# Debe mostrar: Mode: leader (en el líder) o Mode: follower
Operaciones con znodes
El shell de ZooKeeper permite administrar el árbol de znodes:
# Conectar al shell de ZooKeeper
zkCli.sh -server localhost:2181
# Dentro del shell (las siguientes líneas son comandos de zkCli):
# Listar el directorio raíz
ls /
# Crear un znode persistente
create /config ""
create /config/app "configuracion-inicial"
# Crear un znode efímero (desaparece cuando el cliente se desconecta)
create -e /workers/worker-01 "10.0.2.10:8080"
# Crear un znode secuencial (útil para colas y elección de líder)
create -s /tasks/task- "datos-tarea"
# Leer el contenido de un znode
get /config/app
# Actualizar el valor de un znode
set /config/app "nueva-configuracion"
# Observar cambios en un znode (watch)
get -w /config/app
ls -w /workers
# Eliminar un znode (debe estar vacío)
delete /config/app
# Eliminar recursivamente
deleteall /config
Desde la línea de comandos del sistema:
# Verificar la conectividad (usando el comando four-letter word "ruok")
echo "ruok" | nc localhost 2181
# Respuesta esperada: imok
# Ver estadísticas del servidor
echo "stat" | nc localhost 2181
# Ver la configuración del ensemble
echo "conf" | nc localhost 2181
# Ver las conexiones activas de clientes
echo "dump" | nc localhost 2181
Configuración de ACLs
ZooKeeper soporta ACLs por znode para control de acceso:
# Conectar al shell con autenticación
zkCli.sh -server localhost:2181
# Dentro del shell de ZooKeeper:
# Añadir autenticación de tipo "digest" (usuario:password)
addauth digest admin:password_seguro
# Crear un znode con ACL restringida
create /secretos "" digest:admin:$(echo -n "admin:password_seguro" | sha1sum | cut -d' ' -f1):rwcda
# Permisos disponibles: r(read), w(write), c(create), d(delete), a(admin)
# ACL "world:anyone:r" permite lectura a todos
create /publico "dato-publico" world:anyone:r
# Ver las ACLs de un znode
getAcl /secretos
Monitorización y Métricas
# Verificar métricas de Prometheus (si está habilitado en zoo.cfg)
curl -s http://localhost:7070/metrics | grep -E "^zookeeper_"
# Comandos four-letter para monitorización básica
echo "mntr" | nc localhost 2181 # Métricas detalladas del servidor
echo "srvr" | nc localhost 2181 # Información del servidor
echo "stat" | nc localhost 2181 # Estadísticas de conexiones
# Métricas clave a vigilar:
# zk_outstanding_requests - Solicitudes en cola (debe ser bajo)
# zk_avg_latency - Latencia media (debe ser < 10ms)
# zk_max_latency - Latencia máxima
# zk_watch_count - Número de watches activos
# zk_followers - Número de followers (debe ser count-1)
# Script de monitorización básica
cat > /usr/local/bin/zk-check.sh << 'EOF'
#!/bin/bash
# Verificar el estado de ZooKeeper y alertar si hay problemas
ZK_HOST="localhost"
ZK_PORT="2181"
STATUS=$(echo "ruok" | nc -w 3 $ZK_HOST $ZK_PORT 2>/dev/null)
if [ "$STATUS" != "imok" ]; then
echo "ALERTA: ZooKeeper no responde en $ZK_HOST:$ZK_PORT"
exit 1
fi
MODE=$(echo "stat" | nc -w 3 $ZK_HOST $ZK_PORT | grep "Mode:" | awk '{print $2}')
echo "ZooKeeper funcionando. Modo: $MODE"
EOF
chmod +x /usr/local/bin/zk-check.sh
Integración con Kafka
# Kafka usa ZooKeeper para gestión de brokers, tópicos y coordinación
# En el archivo server.properties de Kafka:
# zookeeper.connect=zk-01:2181,zk-02:2181,zk-03:2181/kafka
# Los metadatos de Kafka se almacenan bajo /kafka en ZooKeeper
# Verificar que Kafka puede conectarse a ZooKeeper
zkCli.sh -server localhost:2181 ls /kafka
# Debe mostrar: [admin, brokers, cluster, config, consumers, controller, ...]
# Ver los brokers registrados en ZooKeeper
zkCli.sh -server localhost:2181 ls /kafka/brokers/ids
# Nota: Las versiones modernas de Kafka (3.x) pueden operar sin ZooKeeper
# usando KRaft (Kafka Raft Metadata mode) - considera migrar si usas Kafka 3.3+
Solución de Problemas
# Ver logs de ZooKeeper
tail -f /var/log/zookeeper/zookeeper.log
# El ensemble no puede elegir líder
# Verificar que el puerto 3888 está accesible entre todos los nodos
nc -zv zk-02.empresa.local 3888
nc -zv zk-03.empresa.local 3888
# Verificar el contenido del archivo myid
cat /var/lib/zookeeper/data/myid
# Limpiar snapshots y logs de transacción (CUIDADO: solo si es necesario)
# Primero detener ZooKeeper
systemctl stop zookeeper
# Usar la herramienta de limpieza integrada
java -cp /opt/zookeeper/lib/*:/opt/zookeeper/*.jar \
org.apache.zookeeper.server.PurgeTxnLog \
/var/lib/zookeeper/data /var/lib/zookeeper/data -n 5
# Comprobar la integridad del snapshot
java -cp /opt/zookeeper/lib/*:/opt/zookeeper/*.jar \
org.apache.zookeeper.server.SnapshotFormatter \
/var/lib/zookeeper/data/version-2/snapshot.0 | head -50
Conclusión
Apache ZooKeeper es una pieza fundamental en la infraestructura de sistemas distribuidos, proporcionando las primitivas de coordinación que permiten a Kafka, HBase y otros sistemas mantener consistencia y elegir líderes de forma confiable. Su configuración como ensemble de 3 o 5 nodos garantiza alta disponibilidad, y su monitorización activa mediante los comandos four-letter y métricas Prometheus es esencial para operación estable en producción.


