Mensageria e streaming de eventos
Instalar e operar o Apache Kafka em modo KRaft: setup, CLI e monitoramento
Intermediário8 min de leituraAtualizado
Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês
Um cluster Kafka sem ZooKeeper se resume a alguns processos Java com um arquivo de propriedades cada, mas são as configurações desse arquivo que decidem se o cluster sobrevive à perda de um disco ou a um disco cheio. Este guia instala o Kafka 3.x em modo KRaft, primeiro em um único nó e depois como um cluster de três brokers, cobre as ferramentas de linha de comando que você usa no dia a dia para topics, consumer groups e offsets, e explica as configurações de broker, as métricas, as opções de segurança e os passos de atualização que importam em produção.
O que você vai aprender
- Formatar o armazenamento e iniciar um broker KRaft ou um nó combinado broker/controller a partir do tarball ou de um contêiner
- Escrever o server.properties de um cluster de produção com três brokers e três controllers
- Criar, inspecionar e reconfigurar topics e redefinir os offsets de um consumer group com as ferramentas de CLI
- Definir os padrões do broker que protegem os dados: replicação, min.insync.replicas, retenção e criação de topics
- Monitorar as métricas que antecipam quedas: partições sub-replicadas, estado do controller, latência das requisições e lag dos consumidores
- Habilitar TLS, SASL e ACLs e planejar uma atualização gradual ou uma migração do ZooKeeper
O Kafka carrega a fama de ser difícil de operar desde a era do ZooKeeper, quando cada cluster significava dois sistemas distribuídos, duas configurações de segurança e um failover de controller que podia levar minutos. Em modo KRaft, pronto para produção desde o Kafka 3.3 e único modo disponível na 4.0, um cluster é um conjunto de processos Java idênticos cujos papéis são definidos em um único arquivo de propriedades. Este guia instala o Kafka 3.x dessa forma e depois cobre as ferramentas do dia a dia e as configurações de produção que o quickstart não menciona. Todos os comandos são para o tarball de Linux; a variante em contêiner aparece quando for diferente.
Pré-requisitos e estrutura de arquivos
Um broker precisa de um JDK — o 17 é a escolha segura para o 3.x e é obrigatório para o 4.x —, de um usuário dedicado e de um sistema de arquivos com espaço para log.dirs. O disco importa mais que a CPU: o Kafka escreve sequencialmente e lê pelo page cache, então o mais comum é usar vários discos simples ou um volume rápido por broker, com o diretório de dados em seu próprio ponto de montagem. Dê de 4 a 6 GB de heap à JVM e deixe o restante da memória da máquina para o page cache; um heap maior deixa o Kafka mais lento, não mais rápido.
sudo useradd --system --home /opt/kafka --shell /usr/sbin/nologin kafka
curl -LO https://archive.apache.org/dist/kafka/3.9.0/kafka_2.13-3.9.0.tgz
sudo tar -xzf kafka_2.13-3.9.0.tgz -C /opt
sudo ln -sfn /opt/kafka_2.13-3.9.0 /opt/kafka
sudo mkdir -p /var/lib/kafka && sudo chown -R kafka:kafka /var/lib/kafka /opt/kafka_2.13-3.9.0
O 2.13 no nome do arquivo é a versão do Scala com a qual o broker foi compilado; ela não tem efeito nenhum sobre os clientes Java. Dentro do pacote, bin/ contém os scripts de CLI, config/kraft/ as configurações de exemplo do KRaft (server.properties para um nó combinado, broker.properties e controller.properties para papéis separados) e libs/ os jars. O Kafka 4.0 elimina o subdiretório kraft/, porque não existe mais outro modo.
Um cluster de nó único
Antes de iniciar, um nó KRaft precisa saber a qual cluster pertence, e é isso que faz a etapa de formatação do armazenamento. Ela grava em cada diretório de log um arquivo meta.properties com o id do cluster e o id do nó; um broker se recusa a iniciar em um diretório formatado para outro cluster, o que evita o acidente clássico de um broker perdido entrar no cluster de produção.
# config/kraft/server.properties — combined broker and controller for development
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@localhost:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://localhost:9092
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT
log.dirs=/var/lib/kafka
num.partitions=3
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
KAFKA_CLUSTER_ID=$(bin/kafka-storage.sh random-uuid) # once per cluster; keep it
bin/kafka-storage.sh format -t "$KAFKA_CLUSTER_ID" -c config/kraft/server.properties
bin/kafka-server-start.sh config/kraft/server.properties # foreground; add -daemon to background
O mesmo em um contêiner, com a imagem oficial que o projeto publica desde a 3.7:
docker run -d --name kafka -p 9092:9092 \
-e KAFKA_NODE_ID=1 \
-e KAFKA_PROCESS_ROLES=broker,controller \
-e KAFKA_CONTROLLER_QUORUM_VOTERS=1@localhost:9093 \
-e KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
-e KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER \
-e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \
-e KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1 \
-e KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1 \
-e KAFKA_LOG_DIRS=/var/lib/kafka/data \
-v kafka-data:/var/lib/kafka/data \
apache/kafka:3.9.0
Cada propriedade corresponde a uma variável de ambiente: basta colocá-la em maiúsculas, trocar os pontos por underscores e adicionar o prefixo KAFKA_. Na primeira inicialização, a própria imagem formata o armazenamento com um id de cluster gerado.
Verifique o nó com um topic e uma ida e volta:
bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic smoke --partitions 1 --replication-factor 1
echo "hello" | bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic smoke
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic smoke --from-beginning --max-messages 1
Um cluster de produção
Um cluster de verdade separa os papéis. Três controllers em máquinas pequenas formam o quórum de metadados; três ou mais brokers, em máquinas com discos de verdade, guardam os dados. Cada nó tem seu próprio node.id, único entre os dois papéis.
# controller.properties on controller nodes 1, 2, 3 (change node.id on each)
process.roles=controller
node.id=1
controller.quorum.voters[email protected]:9093,[email protected]:9093,[email protected]:9093
listeners=CONTROLLER://:9093
controller.listener.names=CONTROLLER
log.dirs=/var/lib/kafka/metadata
# broker.properties on broker nodes 11, 12, 13 (change node.id and advertised host on each)
process.roles=broker
node.id=11
controller.quorum.voters[email protected]:9093,[email protected]:9093,[email protected]:9093
listeners=INTERNAL://:9092,EXTERNAL://:9094
advertised.listeners=INTERNAL://kafka-11.internal:9092,EXTERNAL://kafka-11.example.com:9094
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:SASL_SSL,CONTROLLER:PLAINTEXT
inter.broker.listener.name=INTERNAL
controller.listener.names=CONTROLLER
log.dirs=/data/kafka-1,/data/kafka-2 # one directory per disk; partitions are spread across them
# Defaults that protect data
default.replication.factor=3
min.insync.replicas=2
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
unclean.leader.election.enable=false
auto.create.topics.enable=false # topics are created deliberately, with the right settings
num.partitions=6
log.retention.hours=168
log.segment.bytes=1073741824
message.max.bytes=1048588
Dois desses parâmetros merecem destaque. auto.create.topics.enable=false impede que um nome de topic digitado errado em um produtor crie silenciosamente um topic com uma única partição e sub-replicado. E min.insync.replicas=2 é a metade do contrato de durabilidade que cabe ao broker: com ele, um produtor com acks=all é recusado quando há menos de duas réplicas ativas, em vez de ouvir que um registro está seguro quando ele existe em uma única máquina.
O Kafka 3.9 introduziu a associação dinâmica ao quórum (controller.quorum.bootstrap.servers no lugar de uma lista estática controller.quorum.voters, além de kafka-metadata-quorum.sh add-controller), que permite substituir um controller sem reiniciar os outros. A lista estática continua funcionando e é mais fácil de entender em um primeiro cluster.
Rode cada nó sob o systemd com Restart=on-failure, um LimitNOFILE generoso (o Kafka mantém um file handle por segmento e por índice) e KAFKA_HEAP_OPTS="-Xms4g -Xmx4g" no ambiente.
Operação diária com a CLI
Todo script em bin/ conversa com o cluster via --bootstrap-server; não existe uma porta de administração separada. Os quatro que você mais vai usar:
Topics. Crie com configurações explícitas, use describe para ver os líderes e as réplicas sincronizadas e alter para adicionar partições ou mudar a configuração.
bin/kafka-topics.sh --bootstrap-server kafka-11.internal:9092 --create --topic orders \
--partitions 12 --replication-factor 3 --config min.insync.replicas=2 --config retention.ms=1209600000
bin/kafka-topics.sh --bootstrap-server kafka-11.internal:9092 --describe --topic orders
# Topic: orders Partition: 0 Leader: 11 Replicas: 11,12,13 Isr: 11,12,13
# A partition whose Isr is shorter than Replicas is under-replicated
bin/kafka-configs.sh --bootstrap-server kafka-11.internal:9092 --alter --entity-type topics \
--entity-name orders --add-config cleanup.policy=compact,delete
Consumer groups. O describe mostra, para cada partição, o offset commitado, o offset de fim de log, o lag e qual membro é dono dela; redefinir os offsets permite reprocessar ou pular mensagens.
bin/kafka-consumer-groups.sh --bootstrap-server kafka-11.internal:9092 --describe --group billing
# GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID
# billing orders 0 1204 1210 6 billing-1-… /10.0.1.5 billing
# Rewind the whole group to two hours ago — the group must be stopped first
bin/kafka-consumer-groups.sh --bootstrap-server kafka-11.internal:9092 --group billing \
--topic orders --reset-offsets --to-datetime 2026-09-07T07:00:00.000 --execute
# Other targets: --to-earliest, --to-latest, --to-offset N, --shift-by -100, --by-duration PT1H
Console producer e console consumer. Servem para conferir se os dados estão fluindo e para dar uma olhada no conteúdo de um topic; --property print.key=true --property print.timestamp=true no consumidor e --property parse.key=true --property key.separator=: no produtor deixam as chaves visíveis.
Quórum de metadados. bin/kafka-metadata-quorum.sh --bootstrap-server kafka-11.internal:9092 describe --status mostra o controller ativo, os votantes, o atraso de cada um em relação ao líder e a versão atual dos metadados; é o primeiro comando a rodar quando o cluster se comporta mal.
Outros dois comandos merecem lugar em um runbook: kafka-reassign-partitions.sh move réplicas entre brokers quando você adiciona ou desativa um nó (ele não rebalanceia automaticamente), e kafka-leader-election.sh restaura os líderes preferenciais depois que um broker volta, para que o tráfego fique novamente bem distribuído.
Monitoramento
Os brokers expõem centenas de métricas JMX; um exporter JMX para Prometheus ou o agente da sua ferramenta de monitoramento as transforma em dashboards. Uma lista curta antecipa a maioria dos incidentes:
| Métrica (JMX) | Valor saudável | O que significa quando não está |
|---|---|---|
kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions | 0 | Um follower está atrasado ou fora do ar; a durabilidade com acks=all fica reduzida e, ao chegar a min.insync.replicas, os produtores serão recusados |
kafka.controller:type=KafkaController,name=ActiveControllerCount | 1 no cluster inteiro | 0 significa nenhum controller — nada de eleições de líder nem de mudanças em topics; mais de 1 indica split-brain |
kafka.server:type=ReplicaManager,name=OfflinePartitionsCount | 0 | Partições sem líder; os produtores e consumidores delas falham |
kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce (p99) | Poucas dezenas de ms | Latência de disco ou de replicação; os produtores se aproximam de delivery.timeout.ms |
kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent | Acima de 0,3 | Threads do broker saturadas; adicione brokers ou reduza o número de partições por broker |
kafka.log:type=LogFlushStats,name=LogFlushRateAndTimeMs | Estável | Um tempo de flush crescente indica que o disco é o gargalo |
Lag dos consumidores por grupo (kafka-consumer-groups.sh ou records-lag-max no consumidor) | Estável ou caindo | Os consumidores estão lentos demais, parados ou travados em uma mensagem envenenada |
Crie alertas também para o uso de disco de cada caminho de log.dirs: um broker cujo disco de dados enche coloca o diretório afetado offline e, com um único diretório, isso significa o broker inteiro. Os limites de retenção são a sua defesa; dimensione-os de acordo com o disco, com folga para o pico do maior topic.
Segurança
Por padrão, um listener do Kafka trafega em texto puro e sem autenticação. Protegê-lo exige três camadas, todas configuradas por listener:
- Criptografia com TLS:
ssl.keystore.locationessl.truststore.locationno broker, eSSLouSASL_SSLemlistener.security.protocol.map. - Autenticação com SASL.
SCRAM-SHA-512armazena as credenciais nos metadados do cluster (kafka-configs.sh --alter --add-config 'SCRAM-SHA-512=[password=...]' --entity-type users --entity-name orders),OAUTHBEARERse integra a um provedor de identidade eGSSAPIao Kerberos. O mTLS com certificados de cliente é uma quarta opção, que dispensa o SASL por completo. - Autorização com ACLs:
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizerem todos os nós,super.userspara os operadores ekafka-acls.sh --add --allow-principal User:orders --operation Write --topic orderspara as aplicações. Para os consumidores, concedaReadnos topics eReadno consumer group; esquecer a ACL do grupo é a causa mais comum de umaGroupAuthorizationException.
Mantenha o listener dos controllers em uma rede privada: ele transporta o log de metadados e, em uma implantação com papéis separados, não precisa ser acessível pelos clientes.
Atualizações e a migração do ZooKeeper
Os brokers são atualizados um de cada vez: pare o broker, instale a nova versão, inicie-o, espere UnderReplicatedPartitions voltar a zero e passe para o próximo. Os clientes continuam funcionando o tempo todo, porque o protocolo é negociado a cada conexão. Quando todos os nós estiverem na nova versão, suba a versão de metadados — bin/kafka-features.sh --bootstrap-server ... upgrade --metadata 3.9 — para liberar os recursos que a nova release traz; até lá, o cluster se comporta como a versão antiga, e esse passo pode ser adiado pelo tempo que você quiser.
Clusters que ainda usam ZooKeeper precisam migrar antes da 4.0, que não consegue mais ler o ZooKeeper. O procedimento documentado provisiona um quórum de controllers KRaft em modo de migração, deixa que ele copie os metadados do ZooKeeper, reinicia os brokers um a um em modo KRaft e, por fim, tira os controllers do modo de migração; há um caminho de rollback até esse último passo. Trate isso como um projeto, com um ensaio em uma cópia da produção, e não como uma janela de manutenção.
Erros comuns
advertised.listenersdeixado em localhost. O problema mais clássico de “funciona na máquina, mas em nenhum outro lugar”; defina um endereço que os clientes consigam alcançar.- Fator de replicação 1 nos topics internos. O
offsets.topic.replication.factor=1do quickstart, em produção, significa que perder um único broker faz perder a posição de todos os consumer groups. Coloque os três fatores de replicação internos em 3. auto.create.topics.enabledeixado ligado. Erros de digitação viram topics sub-replicados permanentes; mais cedo ou mais tarde alguém consome do topic errado.- Heap dimensionado como o de um banco de dados. De 4 a 6 GB é mais do que suficiente; quem faz o trabalho pesado é o page cache. Heaps muito grandes geram pausas longas de garbage collection que, para o controller, parecem um broker morto.
- Adicionar um broker e esperar que ele assuma carga. As partições existentes ficam onde estão até você rodar
kafka-reassign-partitions.sh. - Nenhum alerta de lag. Um consumidor que está morto há uma semana só é descoberto quando a janela de retenção se esgota e os dados já se foram.
Principais pontos
- O KRaft elimina o ZooKeeper: formate o armazenamento de cada nó com o id do cluster, defina
process.roles,node.ide o quórum de controllers e inicie o processo. - Em desenvolvimento, um nó combinado basta; em produção, rodam três controllers dedicados e três ou mais brokers, cada um com
advertised.listenersque os clientes consigam realmente alcançar. default.replication.factor=3,min.insync.replicas=2, topics internos replicados,auto.create.topics.enable=falsee retenção explícita são as configurações de broker que mantêm os dados seguros.kafka-topics.sh,kafka-consumer-groups.sh,kafka-configs.shekafka-metadata-quorum.shcobrem a maior parte do trabalho diário, incluindo redefinir os offsets de um grupo para reprocessar mensagens.- Acompanhe as partições sub-replicadas e offline, a contagem de controllers ativos, a latência de produção, o percentual de ociosidade dos handlers, o uso de disco e o lag dos consumidores.
- Proteja os listeners com TLS, SASL e ACLs; atualize os brokers um de cada vez e suba a versão de metadados depois; migre os clusters que ainda usam ZooKeeper antes de passar para o 4.x.
Fontes e leituras complementares
Perguntas frequentes
- Quantos controllers um cluster KRaft precisa ter?
- Três na maioria dos clusters e cinco nos muito grandes ou muito críticos. O Raft precisa de maioria para avançar, então três controllers toleram uma falha e cinco toleram duas. Para desenvolvimento, um único controller basta. Nós combinados broker/controller são aceitáveis em clusters pequenos, mas em produção o normal é rodar os controllers como processos separados e pouco carregados, para que a carga dos brokers não atrase as decisões sobre os metadados.
- Por que meus clientes conectam a partir do host, mas não de outras máquinas ou contêineres?
- Porque advertised.listeners diz aos clientes onde se conectar nas requisições seguintes, e o valor padrão é o de listeners, muitas vezes localhost. O cliente faz o bootstrap com sucesso, recebe localhost:9092 como endereço do líder e aí falha. Defina advertised.listeners com um hostname ou IP que todos os clientes consigam resolver e alcançar, com listeners separados para o tráfego interno e externo se eles forem diferentes.
- Dá para diminuir o número de partições de um topic?
- Não. Partições só podem ser adicionadas. Para reduzi-las, crie um topic novo com a quantidade desejada, copie os dados com um par consumidor-produtor ou com o MirrorMaker, migre consumidores e produtores para ele e apague o topic antigo. Como as chaves são mapeadas para as partições por hash, a cópia altera a ordem por chave em relação ao topic antigo.
- Preciso atualizar os clientes quando atualizo os brokers?
- Não. O protocolo do Kafka é versionado e os brokers aceitam requisições de clientes mais antigos; clientes mais novos que o broker também funcionam, mas não usam recursos que o broker não tem. Atualize primeiro os brokers, um de cada vez, depois suba a versão de metadados com kafka-features.sh e, por fim, atualize os clientes no seu ritmo.