Configuração de Replicação do Kafka: Garantindo Durabilidade e Disponibilidade dos Dados

Configure replicação do Kafka, ISR, confirmações do produtor e consciência de rack sem enfraquecer a durabilidade.

Configuração de Replicação do Kafka: Garantindo Durabilidade e Disponibilidade dos Dados

A configuração de replicação do Kafka é onde um cluster deixa de ser uma pilha de brokers e começa a se comportar como um sistema confiável durante falhas. As configurações não são complicadas por si só: fator de replicação, réplicas em sincronia, confirmações do produtor, eleição de líder e posicionamento em racks. A parte complicada é que elas só fazem sentido juntas.

Um tópico com três réplicas ainda pode perder dados confirmados se os produtores usarem confirmações fracas. Um produtor usando acks=all ainda pode falhar em gravações se min.insync.replicas for muito restritivo para o número de brokers atualmente ativos. Um cluster distribuído entre zonas de disponibilidade ainda pode ter um dia ruim se todas as réplicas de uma partição quente estiverem no mesmo domínio de falha. Replicação não é uma única caixa de seleção.

A maneira como gosto de pensar sobre a replicação do Kafka é simples: para cada partição, o Kafka mantém várias cópias, escolhe uma cópia para aceitar leituras e gravações, e mantém as outras cópias suficientemente próximas para que uma delas possa assumir. Seu trabalho é decidir quantas cópias são suficientes, quantas devem estar atualizadas antes que uma gravação seja considerada bem-sucedida e se o cluster deve preferir disponibilidade à segurança dos dados.

Um tópico do Kafka é dividido em partições. Cada partição tem uma réplica líder e zero ou mais réplicas seguidoras. Os produtores escrevem no líder. Os consumidores normalmente leem do líder. Os seguidores buscam registros do líder e mantêm seus logs locais alinhados. Se o broker do líder falhar, o Kafka elege um novo líder entre as réplicas consideradas candidatas seguras.

Essa lista de candidatos seguros é o ISR, abreviação de in-sync replicas (réplicas em sincronia). Uma réplica está no ISR quando está acompanhando o líder de perto o suficiente, de acordo com as regras de lag de réplica do Kafka. Se um seguidor parar de buscar, ficar muito atrasado ou o broker desaparecer, o Kafka o remove do ISR. Quando ele se atualiza, pode reentrar.

Esse detalhe é importante porque o ISR é o que torna a durabilidade do Kafka mais do que um pensamento positivo. Com acks=all, o líder não confirma uma solicitação de produção até que o registro tenha sido replicado para as réplicas em sincronia necessárias. O requisito exato é controlado por min.insync.replicas. Se o tópico tem replication.factor=3 e min.insync.replicas=2, o Kafka exige pelo menos duas réplicas em sincronia antes que uma gravação com acks=all possa ser bem-sucedida.

Essa combinação é comum em produção porque oferece um equilíbrio prático. Um broker pode falhar e o tópico ainda pode aceitar gravações fortemente confirmadas. Se um segundo broker falhar antes que o primeiro volte, os produtores usando acks=all devem começar a ver erros como NotEnoughReplicas ou NotEnoughReplicasAfterAppend. Isso é irritante durante um incidente, mas geralmente é o comportamento correto. O Kafka está se recusando a fingir que uma gravação é durável quando não há cópias seguras suficientes.

Aqui está a linha de base típica de produção para um cluster normal de três ou mais brokers:

default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false

Esses valores não tornam todas as cargas de trabalho seguras automaticamente, mas oferecem um ponto de partida sensato. default.replication.factor=3 significa que novos tópicos recebem três cópias, a menos que o comando de criação do tópico diga o contrário. min.insync.replicas=2 significa que pelo menos duas réplicas devem estar em sincronia para gravações fortes. unclean.leader.election.enable=false diz ao Kafka para não eleger uma réplica desatualizada como líder apenas para manter uma partição gravável.

Não defina o fator de replicação maior que o número de brokers. O Kafka não pode colocar três réplicas em três brokers diferentes se apenas dois brokers existirem. Em clusters de desenvolvimento pequenos, replication.factor=1 é aceitável porque a conveniência é mais importante que a tolerância a falhas. Em produção, 1 significa que a perda de um único broker pode tornar os dados indisponíveis e pode perder permanentemente registros armazenados apenas nesse broker.

O lado do produtor deve corresponder ao lado do tópico. Para dados importantes, use acks=all. Também ative a idempotência, a menos que você tenha um motivo específico para não fazê-lo. Em clientes Kafka modernos, produtores idempotentes são a escolha normal para reduzir duplicatas causadas por novas tentativas.

acks=all
enable.idempotence=true
retries=2147483647
max.in.flight.requests.per.connection=5

Não copie o valor de retries cegamente para todos os clientes sem entender sua versão do cliente e os requisitos de entrega. A ideia importante é que a produção durável do Kafka geralmente precisa de novas tentativas, idempotência e acks=all juntos. Se você definir acks=1, o líder pode confirmar um registro antes que os seguidores o copiem. Se esse líder falhar no momento errado, um registro confirmado pode desaparecer. Isso é aceitável para alguns fluxos de telemetria. Não é aceitável para pagamentos, trilhas de auditoria, movimentos de estoque ou qualquer coisa que uma equipe downstream trate como uma fonte da verdade.

Ao criar um tópico, defina as escolhas de replicação deliberadamente, em vez de confiar nos padrões do broker que estiverem presentes:

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic orders.v1   --partitions 12   --replication-factor 3   --config min.insync.replicas=2

O número de partições é separado da replicação. Doze partições com fator de replicação três significam trinta e seis réplicas de partição no total. Isso tem custos de armazenamento, rede, manipuladores de arquivos e metadados do controlador. A replicação melhora a durabilidade, mas não é gratuita.

Para tópicos existentes, alterar min.insync.replicas é simples:

kafka-configs.sh --alter   --bootstrap-server broker1:9092   --entity-type topics   --entity-name orders.v1   --add-config min.insync.replicas=2

Alterar o fator de replicação para um tópico existente depende da versão do Kafka e das ferramentas. Versões mais recentes do Kafka suportam kafka-reassign-partitions.sh e, em alguns casos, fluxos de trabalho de alteração de tópico que facilitam os aumentos. Em clusters mais antigos, aumentar a replicação geralmente significa gerar e executar um plano de realocação de partições. Diminuir a replicação é mais sensível porque você está removendo cópias. Trate isso como uma operação planejada, não um comando casual digitado durante um incidente barulhento.

Uma realocação deve ser limitada se o tópico for grande ou o cluster já estiver ocupado. A atualização da replicação lê dados antigos das réplicas existentes e os escreve em novas. Isso pode roubar capacidade de disco e rede de produtores e consumidores ativos. Um runbook seguro geralmente inclui uma janela de manutenção, saída --describe antes e depois, limites de realocação e um plano de reversão.

Você pode inspecionar um tópico assim:

kafka-topics.sh --describe   --bootstrap-server broker1:9092   --topic orders.v1

Observe três campos na saída: Leader, Replicas e Isr. Replicas é o conjunto atribuído. Isr é o conjunto atualmente atualizado. Se Replicas for 1,2,3 mas Isr for 1,2, o broker 3 está atrasado ou indisponível para essa partição. Se muitas partições mostrarem um broker ausente no ISR, verifique o disco, a rede, a saúde do processo e os logs desse broker. Se apenas algumas partições quentes forem afetadas, o líder pode estar sobrecarregado ou a partição pode ter tráfego excepcionalmente alto.

A eleição de líder suja (unclean leader election) merece cuidado especial. Se todas as réplicas em sincronia para uma partição desaparecerem, o Kafka tem duas opções. Ele pode deixar a partição indisponível até que uma réplica segura retorne, ou pode eleger uma réplica dessincronizada e arriscar perder registros que foram confirmados no líder antigo. unclean.leader.election.enable=false escolhe segurança. true escolhe disponibilidade ao risco de perda de dados.

Existem cargas de trabalho onde a eleição suja pode ser defensável: dados de clickstream de curta duração, métricas descartáveis ou um pipeline onde sistemas upstream podem reproduzir tudo. Para a maioria dos dados de negócios, deixe-o desabilitado. Perder a disponibilidade de uma partição é doloroso, mas a perda silenciosa de dados é pior porque os consumidores podem continuar como se nada tivesse acontecido.

A replicação com consciência de rack ajuda com um tipo diferente de falha. Se seus brokers estão divididos entre racks, zonas ou hosts com caminhos de energia/rede compartilhados, informe ao Kafka onde cada broker está:

broker.rack=zone-a

Defina o valor correto em cada broker. O Kafka tentará espalhar as réplicas entre os racks para que uma única falha de zona tenha menos probabilidade de remover todas as cópias de uma partição. Isso não é mágica. Você ainda precisa de brokers suficientes em cada zona, disco suficiente e posicionamento cuidadoso das partições. Mas sem broker.rack, o Kafka não tem como saber que dois brokers compartilham o mesmo domínio de falha.

Monitore a replicação continuamente. Os sinais de alerta mais úteis são partições sub-replicadas, partições offline, eventos de encolhimento do ISR e erros de produção relacionados a réplicas insuficientes. Em configurações baseadas em Prometheus, as equipes comumente monitoram métricas de brokers Kafka para partições sub-replicadas e offline, e então emparelham esses alertas com métricas de disco, rede e JVM do broker.

Uma boa pergunta de incidente é: o ISR encolheu porque um broker morreu, porque a replicação não consegue acompanhar ou porque a rede não é confiável? A correção difere. Um broker morto precisa de recuperação de serviço. Um broker lento pode precisar de substituição de disco, investigação de I/O ou menos líderes de partição. Um problema de rede pode se manifestar como desconexões repetidas e lag do buscador, mesmo quando CPU e disco parecem normais.

Reinicializações rolantes de brokers são outro lugar onde as configurações de replicação mostram seu valor. Reinicie um broker de cada vez. Aguarde as partições recuperarem um ISR saudável antes de reiniciar o próximo broker. Se você reiniciar brokers muito rapidamente com min.insync.replicas=2, os produtores podem começar a falhar porque muito poucas réplicas estão em sincronia. Essa falha é esperada, mas você pode evitá-la com paciência e monitoramento.

A lista de verificação prática é curta. Use fator de replicação três para a maioria dos tópicos de produção. Use min.insync.replicas=2 com produtor acks=all para dados importantes. Mantenha a eleição de líder suja desabilitada, a menos que os dados sejam explicitamente descartáveis. Espalhe as réplicas entre domínios de falha com consciência de rack. Monitore a saúde do ISR, não apenas o tempo de atividade do broker. E teste suas suposições reiniciando um broker em uma janela controlada antes que uma falha real faça isso por você.

Um detalhe que ajuda durante as revisões é separar durabilidade de disponibilidade em linguagem simples. Durabilidade pergunta: "Depois que o Kafka diz que a gravação foi bem-sucedida, quantas falhas podem acontecer antes que esse registro confirmado esteja em risco?" Disponibilidade pergunta: "Produtores e consumidores ainda podem usar a partição agora?" Configurações fortes às vezes reduzem a disponibilidade porque o Kafka rejeitará gravações em vez de aceitar dados fracamente replicados. Isso não é uma falha do Kafka. Isso é o Kafka honrando o contrato que você configurou.

Por exemplo, imagine um tópico com fator de replicação três, min.insync.replicas=2 e produtores usando acks=all. Broker 1 é líder, brokers 2 e 3 são seguidores. Se o broker 3 cair, o ISR se torna 1,2. As gravações ainda são bem-sucedidas porque duas réplicas estão em sincronia. Se o broker 2 cair antes que o broker 3 retorne, o ISR se torna apenas 1. As gravações falham. Algumas equipes veem isso pela primeira vez em produção e perguntam por que o Kafka está inativo quando o líder ainda está vivo. A resposta é que o tópico ainda está disponível para algumas leituras, mas não é seguro para gravações fortemente confirmadas.

Você também deve pensar na recuperação do consumidor. A replicação protege as cópias dos registros no lado do broker. Ela não protege automaticamente os offsets do consumidor de todos os erros de fluxo de trabalho. Os offsets do consumidor são armazenados no Kafka também, geralmente em __consumer_offsets, então esse tópico interno também precisa de replicação saudável. Se os tópicos do usuário estão cuidadosamente configurados, mas os tópicos internos foram criados com replicação fraca em uma construção inicial do cluster, o comportamento de failover ainda pode ser pior do que o esperado. Verifique a replicação dos tópicos internos como parte de uma revisão de prontidão de produção.

Em clusters multi-inquilinos, nem todo tópico merece a mesma configuração. Um tópico de métricas descartável com alto volume e baixo valor comercial pode usar retenção mais curta e tolerar garantias mais fracas. Um tópico de faturamento não deve. O erro é deixar padrões acidentais decidirem essa distinção. Coloque as classes de tópico por escrito: fluxos de eventos críticos, telemetria reproduzível, tópicos de estado compactados, tópicos temporários de desenvolvimento. Em seguida, mapeie cada classe para replicação, ISR, retenção e configurações do produtor.

Durante incidentes, evite alterar as configurações de durabilidade apenas para silenciar erros, a menos que todos entendam a compensação. Reduzir min.insync.replicas de 2 para 1 pode fazer os produtores funcionarem, mas também significa que gravações confirmadas podem viver em um único broker. Ativar a eleição de líder suja pode restaurar a disponibilidade da partição, mas réplicas desatualizadas podem perder registros. Às vezes, o negócio pode escolher essa compensação. Deve ser uma decisão consciente de incidente, não um atalho oculto do operador.