Apache Zookeeper é um serviço centralizado para manter informações de configuração, nomenclatura, sincronização distribuída e serviços de grupo em sistemas distribuídos. Desenvolvido originalmente pelo Yahoo!, tornou-se um projeto Apache de alto nível e é amplamente usado em ecossistemas de big data.
Características principais
- Coordenação distribuída: sincronização entre múltiplos nós
- Consistência forte: garantias ACID para operações críticas
- Alta disponibilidade: tolerância a falhas com replicação
- Performance: baixa latência para operações de leitura
- Simplicidade: API simples baseada em sistema de arquivos
O problema que o Zookeeper resolve
Desafios de sistemas distribuídos
Problema da eleição de líder:
Cenário: 3 brokers Kafka precisam eleger um líder para uma partição
Sem Zookeeper: Como garantir que apenas um seja eleito?
Com Zookeeper: Algoritmo de consenso garante eleição única e consistente
Problema da configuração distribuída:
Cenário: 10 serviços precisam da mesma configuração atualizada
Sem Zookeeper: Cada serviço mantém sua própria cópia (inconsistência)
Com Zookeeper: Configuração centralizada com notificações automáticas
Problema da descoberta de serviços:
Cenário: Microserviços precisam encontrar uns aos outros
Sem Zookeeper: IPs hardcoded ou DNS complexo
Com Zookeeper: Registro dinâmico com health checks
Antes do Zookeeper: soluções inadequadas
Arquivos de configuração estáticos:
# broker1.properties
broker.id=1
listeners=PLAINTEXT://192.168.1.10:9092
# broker2.properties
broker.id=2
listeners=PLAINTEXT://192.168.1.11:9092
Problemas:
- Configuração manual: alterações exigem restart de serviços
- Inconsistência: diferentes versões de configuração
- Falta de coordenação: sem sincronização entre nós
- Detecção de falhas: sem mecanismo automático
Bancos de dados relacionais:
CREATE TABLE cluster_config (
key VARCHAR(255),
value TEXT,
updated_at TIMESTAMP
);
Problemas:
- Single point of failure: banco centralizado
- Latência: queries SQL para cada operação
- Complexidade: transações distribuídas complexas
- Escalabilidade: limitada pela capacidade do banco
Como o Zookeeper funciona
Modelo de dados hierárquico
O Zookeeper organiza dados em uma estrutura de árvore similar a um sistema de arquivos:
/
├── kafka/
│ ├── brokers/
│ │ ├── ids/
│ │ │ ├── 1 → {"host":"kafka1","port":9092}
│ │ │ ├── 2 → {"host":"kafka2","port":9092}
│ │ │ └── 3 → {"host":"kafka3","port":9092}
│ │ └── topics/
│ │ └── ecommerce.produtos/
│ │ └── partitions/
│ │ ├── 0 → {"leader":1,"replicas":[1,2]}
│ │ └── 1 → {"leader":2,"replicas":[2,3]}
│ ├── controller/
│ │ └── epoch → {"brokerid":1,"epoch":15}
│ └── config/
│ └── topics/
│ └── ecommerce.produtos → {"retention.ms":604800000}
└── debezium/
└── connectors/
└── ecommerce-connector/
├── status → "RUNNING"
└── config → {"database.hostname":"sqlserver"}
Tipos de nós (znodes)
Nós persistentes:
- Permanecem até serem explicitamente deletados
- Usados para configurações e metadados
- Exemplo:
/kafka/config/topics/meu-topico
Nós efêmeros:
- Deletados automaticamente quando a sessão termina
- Usados para detecção de falhas e presença
- Exemplo:
/kafka/brokers/ids/1
(broker ativo)
Nós sequenciais:
- Recebem sufixo numérico automático
- Usados para eleição de líder e filas
- Exemplo:
/kafka/controller/election/n_0000000001
Algoritmo de consenso
Zookeeper usa uma variação do algoritmo Zab (Zookeeper Atomic Broadcast):
- Fase de eleição: nós elegem um líder
- Fase de descoberta: líder coleta estado dos seguidores
- Fase de sincronização: líder sincroniza todos os nós
- Fase de broadcast: líder processa escritas e replica
Líder (Zookeeper 1) Seguidor (Zookeeper 2) Seguidor (Zookeeper 3)
| | |
1. Proposta ----------------> | |
| 2. ACK |
| | |
3. Proposta ---------------------------------> |
| | 4. ACK
5. Commit -----------------> | |
6. Commit ---------------------------------> |
Papel do Zookeeper no ecossistema Kafka
Gerenciamento de brokers
Registro de brokers:
// /kafka/brokers/ids/1
{
"version": 4,
"host": "kafka-broker-1",
"port": 9092,
"jmx_port": 9999,
"timestamp": "1640995200000",
"endpoints": ["PLAINTEXT://kafka-broker-1:9092"],
"rack": "rack1"
}
Detecção de falhas:
- Brokers mantêm sessões efêmeras com Zookeeper
- Se broker falha, nó efêmero é removido automaticamente
- Outros brokers são notificados da mudança
Eleição de controller
O Kafka Controller é o broker responsável por:
- Gerenciar partições e réplicas
- Coordenar eleições de líder de partição
- Processar mudanças de metadados
Processo de eleição:
1. Broker 1 tenta criar /kafka/controller (nó efêmero)
2. Se sucesso → Broker 1 vira controller
3. Se falha → outro broker já é controller
4. Todos os brokers assistem /kafka/controller para mudanças
Metadados de tópicos e partições
Configuração de tópico:
// /kafka/config/topics/ecommerce-produtos
{
"version": 1,
"config": {
"retention.ms": "604800000",
"cleanup.policy": "delete",
"compression.type": "snappy"
}
}
Estado de partições:
// /kafka/brokers/topics/ecommerce-produtos
{
"version": 1,
"partitions": {
"0": [1, 2, 3], // broker 1 é líder, 2 e 3 são réplicas
"1": [2, 3, 1], // broker 2 é líder, 3 e 1 são réplicas
"2": [3, 1, 2] // broker 3 é líder, 1 e 2 são réplicas
}
}
Arquitetura e componentes
Ensemble Zookeeper
Um ensemble é um cluster de servidores Zookeeper que trabalham juntos:
Cliente 1 ──┐
Cliente 2 ──┼─── Zookeeper 1 (Líder)
Cliente 3 ──┘ │
│ Replicação
├─── Zookeeper 2 (Seguidor)
│
└─── Zookeeper 3 (Seguidor)
Características do ensemble:
- Número ímpar: 3, 5, 7 nós (evita split-brain)
- Quorum: maioria deve estar ativa (2/3, 3/5, 4/7)
- Líder único: processa todas as escritas
- Seguidores: processam leituras e replicam escritas
Sessões e watches
Sessões:
- Conexão entre cliente e ensemble
- Timeout configurável (heartbeat)
- Estado mantido enquanto sessão ativa
Watches (observadores):
// Cliente Java registra watch
zk.exists("/kafka/brokers/ids/1", new Watcher() {
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
System.out.println("Broker 1 falhou!");
}
}
});
Tipos de eventos:
-
NodeCreated
: nó foi criado -
NodeDeleted
: nó foi removido -
NodeDataChanged
: dados do nó mudaram -
NodeChildrenChanged
: filhos do nó mudaram
Configuração e deployment
Configuração básica
zoo.cfg:
# Identificação do servidor
tickTime=2000
initLimit=10
syncLimit=5
# Diretório de dados
dataDir=/var/lib/zookeeper
# Porta para clientes
clientPort=2181
# Configuração do ensemble
server.1=zk1:2888:3888
server.2=zk2:2888:3888
server.3=zk3:2888:3888
# Configurações de performance
maxClientCnxns=60
autopurge.snapRetainCount=3
autopurge.purgeInterval=1
Explicação dos parâmetros:
-
tickTime
: unidade básica de tempo (ms) -
initLimit
: tempo para seguidores se conectarem ao líder -
syncLimit
: tempo para seguidores sincronizarem -
2888
: porta para comunicação entre servidores -
3888
: porta para eleição de líder
Configuração para produção
Otimizações de performance:
# Aumentar heap JVM
export JVMFLAGS="-Xmx4G -Xms4G"
# Configurações de rede
maxClientCnxns=0
minSessionTimeout=4000
maxSessionTimeout=40000
# Configurações de disco
preAllocSize=65536
snapCount=100000
# Configurações de log
zookeeper.log.threshold=INFO
Monitoramento:
# Habilitar métricas JMX
com.sun.management.jmxremote=true
com.sun.management.jmxremote.port=9999
com.sun.management.jmxremote.authenticate=false
com.sun.management.jmxremote.ssl=false
Operações e comandos essenciais
Cliente de linha de comando
Conectar ao Zookeeper:
# Conectar ao ensemble
zkCli.sh -server zk1:2181,zk2:2181,zk3:2181
Comandos básicos:
# Listar nós filhos
ls /kafka/brokers/ids
# Obter dados de um nó
get /kafka/controller
# Criar nó
create /teste "dados do teste"
# Atualizar nó
set /teste "novos dados"
# Deletar nó
delete /teste
# Estatísticas do nó
stat /kafka/brokers/ids/1
Comandos administrativos
Status do ensemble:
# Verificar status de cada servidor
echo stat | nc zk1 2181
echo stat | nc zk2 2181
echo stat | nc zk3 2181
# Verificar líder
echo srvr | nc zk1 2181 | grep Mode
Limpeza de dados:
# Limpar snapshots antigos
zkCleanup.sh -n 3
# Verificar integridade
zkServer.sh status
Monitoramento e observabilidade
Métricas importantes
Métricas de performance:
-
zk_avg_latency
: latência média de operações -
zk_max_latency
: latência máxima -
zk_packets_received
: pacotes recebidos por segundo -
zk_packets_sent
: pacotes enviados por segundo
Métricas de saúde:
-
zk_followers
: número de seguidores conectados -
zk_pending_syncs
: sincronizações pendentes -
zk_open_file_descriptor_count
: descritores de arquivo abertos -
zk_max_file_descriptor_count
: limite de descritores
Ferramentas de monitoramento
JMX com Prometheus:
# prometheus.yml
- job_name: 'zookeeper'
static_configs:
- targets: ['zk1:9999', 'zk2:9999', 'zk3:9999']
Comandos de diagnóstico:
# Verificar conexões ativas
echo cons | nc localhost 2181
# Verificar watches ativos
echo wchs | nc localhost 2181
# Verificar estatísticas detalhadas
echo mntr | nc localhost 2181
Troubleshooting comum
Problemas de conectividade
Sintoma: Clientes não conseguem conectar
# Verificar se porta está aberta
telnet zk1 2181
# Verificar logs
tail -f /var/log/zookeeper/zookeeper.log
# Verificar configuração de rede
netstat -tlnp | grep 2181
Solução:
- Verificar firewall e regras de rede
- Confirmar configuração de
clientPort
- Validar DNS/resolução de nomes
Split-brain e quorum
Sintoma: Ensemble não consegue formar quorum
2024-01-15 10:30:00 WARN [QuorumPeer] Not enough followers present, giving up
Diagnóstico:
# Verificar status de cada nó
for server in zk1 zk2 zk3; do
echo "=== $server ==="
echo stat | nc $server 2181 | grep Mode
done
Solução:
- Garantir que maioria dos nós esteja ativa
- Verificar conectividade entre servidores
- Reiniciar nós em sequência se necessário
Performance degradada
Sintoma: Alta latência em operações
# Verificar métricas de latência
echo mntr | nc localhost 2181 | grep latency
Otimizações:
- Aumentar heap JVM se necessário
- Configurar
preAllocSize
adequado - Mover
dataDir
para SSD - Ajustar
snapCount
conforme carga
Evolução: Kafka sem Zookeeper (KRaft)
Limitações do Zookeeper
Complexidade operacional:
- Componente adicional para gerenciar
- Configuração e monitoramento separados
- Ponto de falha adicional
Limitações de escalabilidade:
- Metadados limitados pela capacidade do Zookeeper
- Latência adicional para operações administrativas
- Complexidade de backup e recovery
KRaft (Kafka Raft)
A partir do Kafka 2.8, foi introduzido o KRaft mode:
Kafka com Zookeeper:
Kafka Brokers ←→ Zookeeper Ensemble
Kafka com KRaft:
Kafka Controllers + Kafka Brokers (self-managed)
Vantagens do KRaft:
- Simplicidade: menos componentes para gerenciar
- Performance: menor latência para metadados
- Escalabilidade: suporte a milhões de partições
- Consistência: modelo de dados unificado
Status atual:
- Kafka 3.3+: KRaft production-ready
- Migração gradual do Zookeeper para KRaft
- Zookeeper ainda amplamente usado em produção
Casos de uso além do Kafka
Descoberta de serviços
Registro dinâmico:
// Serviço se registra no Zookeeper
String servicePath = "/services/user-service/" + instanceId;
zk.create(servicePath, serviceInfo.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
Descoberta por clientes:
// Cliente descobre serviços disponíveis
List<String> instances = zk.getChildren("/services/user-service", true);
Configuração distribuída
Configuração centralizada:
// Atualizar configuração
zk.setData("/config/app-settings", newConfig.getBytes(), -1);
// Clientes recebem notificação automática via watch
zk.getData("/config/app-settings", new ConfigWatcher(), null);
Coordenação de tarefas
Lock distribuído:
// Implementação de mutex distribuído
String lockPath = "/locks/resource-x/" + Thread.currentThread().getId();
zk.create(lockPath, new byte[0],
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
Boas práticas
Configuração de produção
Ensemble sizing:
- 3 nós: desenvolvimento e testes
- 5 nós: produção com alta disponibilidade
- 7+ nós: apenas para casos muito críticos
Hardware:
- CPU: 4+ cores para ensemble de produção
- RAM: 8GB+ com heap JVM de 4GB
-
Disco: SSD para
dataDir
edataLogDir
- Rede: baixa latência entre nós do ensemble
Segurança
Autenticação:
# SASL/Kerberos
authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
requireClientAuthScheme=sasl
Autorização:
// ACLs para controle de acesso
zk.create("/secure/path", data,
Arrays.asList(new ACL(ZooDefs.Perms.ALL, new Id("digest", "user:password"))),
CreateMode.PERSISTENT);
Backup e recovery
Backup de dados:
# Backup do dataDir
tar -czf zk-backup-$(date +%Y%m%d).tar.gz /var/lib/zookeeper
# Backup via snapshot
zkServer.sh snapshot /var/lib/zookeeper/version-2
Recovery:
# Restaurar dados
service zookeeper stop
rm -rf /var/lib/zookeeper/version-2/*
tar -xzf zk-backup-20240115.tar.gz -C /
service zookeeper start
Conclusão
Apache Zookeeper é o alicerce que permite a operação confiável de sistemas distribuídos como Apache Kafka. Sua capacidade de fornecer coordenação, consistência e detecção de falhas de forma transparente torna possível construir arquiteturas complexas e resilientes.
Top comments (0)