Design clássico · Sênior
Logging distribuído
Projeto de entrevista clássico. Pontos centrais: Monitoramento e Observabilidade, Pipelines de Dados e Sistemas de Busca.
Resposta curta
Projeto de entrevista clássico. Pontos centrais: Monitoramento e Observabilidade, Pipelines de Dados e Sistemas de Busca. Um sistema de logging distribuído recolhe os registros produzidos por muitos serviços em muitas máquinas, guarda esses registros e deixa a equipe procurar neles. O desenho junta as etapas de coleta, transporte, armazenamento e consulta. É um bom exercício de monitoramento e observabilidade.
01 · CONTEXTO E REQUISITOS
O que um sistema de logs distribuído precisa fazer
Um sistema de logging distribuído recolhe os registros produzidos por muitos serviços em muitas máquinas, guarda esses registros e deixa a equipe procurar neles. O desenho junta as etapas de coleta, transporte, armazenamento e consulta. É um bom exercício de monitoramento e observabilidade.
Um log é um registro textual com carimbo de tempo, que pode ser estruturado ou não; a documentação do OpenTelemetry recomenda o formato estruturado. Estruturado, aqui, quer dizer ter um esquema consistente ou campos tipados bem definidos nos quais o processamento posterior possa confiar.
Os requisitos funcionais são receber logs de muitos serviços, permitir busca por intervalo de tempo e filtros, e disparar alertas a partir dos logs. Entre os não funcionais, mandam no desenho: não perder logs em silêncio, absorver picos de escrita, manter o custo de armazenamento sob controle e isolar os dados de cada equipe.
02 · COLETA NA PONTA
Um agente perto do serviço
A documentação do OpenTelemetry recomenda, em geral, rodar um coletor junto ao serviço. Segundo ela, o coletor deixa o serviço entregar os dados rapidamente e cuida de repetição de envio, agrupamento em lotes, criptografia e até filtragem de dados sensíveis.
O mesmo coletor lê logs de qualquer arquivo e oferece recursos para interpretar formatos diferentes, e implantá-lo como agente de logging de uso geral costuma ser o primeiro passo de quem adota o OpenTelemetry. O OpenTelemetry também correlaciona automaticamente logs e traces.
No Loki, a divisão é parecida: o agente raspa os logs, os transforma em streams ao adicionar rótulos e os envia por uma API HTTP. Um stream é um conjunto de logs que compartilham os mesmos rótulos.
03 · TRANSPORTE DURÁVEL
Uma fila entre quem escreve e quem guarda
Entre os agentes e o armazenamento pode entrar uma fila de mensagens durável, que absorve a diferença de ritmo entre quem produz e quem grava.
O Kafka, por exemplo, tem failover automático para as réplicas quando um servidor do cluster falha, e só considera uma mensagem confirmada quando todas as réplicas em sincronia a aplicaram ao seu log.
Quem publica pode esperar ou não essa confirmação, trocando latência por durabilidade. Do lado de leitura, o consumidor controla sua posição no log e pode retroceder para reler os dados; cada partição é consumida por exatamente um consumidor dentro de cada grupo de consumo, o que, segundo a documentação, dá uma combinação de ordem e escalabilidade.
Se o consumidor processa as mensagens e cai antes de salvar a posição, o novo processo recebe de novo mensagens já processadas. Essa sequência corresponde à semântica ao-menos-uma-vez em caso de falha do consumidor.
Cada serviço escreve seus registros e o agente local os recolhe e entrega adiante.
O agente publica os registros na fila durável, que desacopla quem produz de quem guarda.
O consumidor de ingestão lê da fila no seu próprio ritmo e guarda a posição lida.
Os registros são gravados no armazenamento de longo prazo.
Quem investiga abre o painel de busca e o serviço de consulta procura no armazenamento.
Em paralelo, o avaliador de alertas roda consultas repetidas sobre os mesmos logs.
04 · CAMINHO DE ESCRITA
Distribuidor, anel de hash e ingesters
Como referência concreta, o Loki é um sistema de agregação de logs horizontalmente escalável, de alta disponibilidade e multi-tenant. Ele não indexa o conteúdo dos logs, só metadados em forma de rótulos de cada stream, e guarda os dados comprimidos em chunks num armazenamento de objetos.
O distribuidor é o primeiro passo do caminho de escrita: garante que os dados chegam conforme a especificação, limita a taxa de ingestão por tenant e é um componente sem estado. É preciso um balanceador de carga na frente dos distribuidores.
Cada stream pertence a um tenant e tem um conjunto único de rótulos. Os distribuidores usam hash consistente, junto com o fator de replicação, para escolher os ingesters que recebem aquele stream.
O distribuidor tenta gravar os mesmos dados em todos os ingesters escolhidos. Com a escrita confirmada por dois de três ingesters, tolera-se a perda de um, mas não de dois. Se menos de duas gravações tiverem sucesso, o distribuidor devolve erro e a escrita é repetida.
A replicação não está sozinha: o WAL do ingester a complementa, e os dados só se perdem com falhas significativas nos dois mecanismos, por exemplo vários ingesters morrendo e perdendo ou corrompendo os discos.
O ingester persiste os dados e os envia ao armazenamento de longo prazo, e nas consultas devolve os dados recentes que estão em memória. Um chunk é fechado, comprimido e marcado como somente leitura quando atinge a capacidade configurada ou quando passa tempo demais sem receber atualização.
O agente envia o stream por HTTP e o balanceador o entrega a um distribuidor.
O distribuidor valida o stream e aplica o limite de ingestão do tenant.
Pelo hash do stream, o distribuidor escolhe os ingesters e tenta gravar os mesmos dados em todos eles.
Com pelo menos dois dos três ingesters confirmando, a escrita é aceita; com menos que isso, o distribuidor devolve erro e a escrita é repetida.
Se um ingester falhar, a perda de um é tolerada, mas não a de dois.
Os ingesters comprimem os chunks e os enviam ao armazenamento de objetos.
| Componente | Responsabilidade |
|---|---|
| Distribuidor | Valida os dados, limita a taxa por tenant e é um componente sem estado |
| Ingester | Persiste os dados e os envia ao armazenamento de longo prazo; devolve dados recentes em memória nas consultas |
| Query frontend | Divide consultas grandes, junta os resultados e agenda as consultas de forma justa entre tenants |
| Querier | Consulta os ingesters pelos dados em memória antes de recorrer ao armazenamento de fundo |
05 · CAMINHO DE LEITURA
Dividir, paralelizar e deduplicar
Segundo a documentação do Loki, as consultas devem ser dirigidas ao query frontend em vez de aos queriers. O frontend divide consultas grandes em consultas menores, executa em paralelo nos queriers e junta os resultados.
O frontend também agenda as consultas de forma justa entre tenants, evitando que um tenant derrube os outros. Com multi-tenancy, os dados e as requisições de cada tenant ficam isolados dos demais.
Os queriers consultam todos os ingesters pelos dados em memória antes de recorrer ao armazenamento de fundo. O querier pode receber dados duplicados e os deduplica internamente.
Para consultas de logs, o frontend guarda em cache apenas resultados vazios de intervalos de tempo quantizados. A documentação diz que isso é mais eficiente do que cachear resultados reais porque as consultas de log têm limite de resultados.
A consulta vai ao query frontend, não direto aos queriers.
O frontend consulta o cache de resultados e divide o que falta em subconsultas.
Os queriers executam as subconsultas em paralelo.
Os queriers consultam todos os ingesters pelos dados recentes em memória.
Depois, os queriers recorrem ao armazenamento de objetos para o restante.
O frontend junta os resultados parciais; duplicatas vindas da replicação já foram eliminadas pelos queriers.
06 · RETENÇÃO E CUSTO
Quanto tempo guardar e onde
Como o Loki indexa só os rótulos e guarda chunks muito comprimidos, o índice é pequeno, o que simplifica a operação e reduz o custo. Em contrapartida, a documentação diz que um bom conjunto de rótulos é a chave para consultas eficientes.
No Elasticsearch, o ILM automatiza a gestão de índices baseados em tempo, como logs e métricas. Ele pode criar um novo índice de escrita quando o atual atinge certo tamanho, número de documentos ou idade, mover índices para hardware mais barato e apagar os antigos para impor padrões de retenção.
No Loki, a retenção é aplicada pelo compactor, que aceita políticas por tenant ou por stream, e, sem habilitá-la, os logs enviados ao Loki ficam para sempre.
| Aspecto | Elasticsearch (ILM) | Loki (compactor) |
|---|---|---|
| O que é gerenciado | Índices baseados em tempo, como logs e métricas | Logs, com políticas por tenant ou por stream |
| Como o custo cai | Move índices para hardware mais barato e apaga os antigos | Aplica a retenção configurada |
| Ponto de atenção | Todos os nós do cluster devem rodar a mesma versão | Sem habilitar a retenção, os logs ficam para sempre |
07 · FALHAS E TRADE-OFFS
O que pode dar errado
Se um processo de ingester cai de forma abrupta, os dados ainda não enviados ao armazenamento podem se perder. Replicação e WAL são mitigações que se complementam.
Do lado do transporte, a semântica ao-menos-uma-vez do consumidor significa que o mesmo registro pode ser processado de novo depois de uma queda. Do lado de quem publica, é possível escolher entre esperar a confirmação ou não.
O Loki permite que vários tenants compartilhem uma única instância, com dados e requisições isolados.
Perguntas frequentes
O que é Logging distribuído?
Projeto de entrevista clássico. Pontos centrais: Monitoramento e Observabilidade, Pipelines de Dados e Sistemas de Busca.
O que o design de Logging distribuído cobre?
Ele está organizado nestas seções: O que um sistema de logs distribuído precisa fazer, Um agente perto do serviço, Uma fila entre quem escreve e quem guarda, Distribuidor, anel de hash e ingesters, Dividir, paralelizar e deduplicar e Quanto tempo guardar e onde.
Quanto tempo leva Logging distribuído e qual é o nível?
Leva cerca de 16 minutos para ler e tem nível Sênior.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.