Introdução ao ELK Stack
O ELK Stack é uma solução de código aberto composta por três componentes principais: Elasticsearch, Logstash e Kibana. Juntos, formam uma plataforma poderosa para coleta, processamento, armazenamento e visualização de logs em tempo real. Se você trabalha com sistemas distribuídos, microsserviços ou qualquer aplicação que gere grandes volumes de dados de log, entender o ELK Stack é fundamental para diagnosticar problemas rapidamente e manter a saúde da sua infraestrutura.
A razão pela qual o ELK Stack se tornou padrão da indústria é simples: sem uma solução centralizada de logs, você estaria conectando em múltiplos servidores, usando ferramentas diferentes e perdendo horas procurando por um erro que ocorreu há dias. O Elasticsearch resolve isso indexando logs de forma rápida e escalável, o Logstash processa e normaliza esses dados, e o Kibana oferece uma interface visual intuitiva para exploração e análise.
Compreendendo os Componentes Fundamentais
Elasticsearch: O Mecanismo de Busca e Armazenamento
O Elasticsearch é um mecanmotor de busca distribuído construído sobre o Apache Lucene. Funciona como um banco de dados NoSQL otimizado para buscas em tempo real e análises. Cada documento armazenado no Elasticsearch é indexado — isso significa que seus campos são analisados e organizados em estruturas que permitem buscas extremamente rápidas, mesmo em bilhões de registros. Você não faz buscas sequenciais como em um banco de dados relacional tradicional; o Elasticsearch usa índices invertidos que mapeiam termos para os documentos que os contêm.
A unidade fundamental no Elasticsearch é o índice. Um índice é similar a uma tabela em um banco de dados relacional, mas muito mais flexível. Dentro de um índice, você tem documentos (registros JSON) e tipos (em versões mais antigas; versões recentes usam apenas um tipo por índice). Documentos são armazenados em shards (partições) que podem ser distribuídos entre múltiplos nós (servidores), permitindo escalabilidade horizontal.
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "logs-2024-01-15",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"timestamp": "2024-01-15T10:30:45Z",
"level": "ERROR",
"message": "Connection timeout",
"service": "payment-api",
"host": "server-01"
}
}
]
}
}
Logstash: Processamento e Transformação de Dados
O Logstash é um pipeline de processamento de dados que extrai, transforma e enriquece logs antes de serem armazenados. Ele funciona em três etapas: input (coleta de dados), filter (transformação e enriquecimento) e output (envio para Elasticsearch ou outro destino). A maior parte do poder do Logstash está nos filters, onde você pode fazer parse de dados não estruturados, adicionar campos calculados, descartar informações desnecessárias ou correlacionar dados de múltiplas fontes.
Uma configuração típica do Logstash começa recebendo logs de um arquivo, socket, API ou qualquer outra fonte, depois aplica padrões regex ou plugins específicos para extrair estrutura, e finalmente envia para o Elasticsearch com campos bem definidos. Isso torna seus logs pesquisáveis e analisáveis de forma eficiente, em vez de serem apenas strings brutas.
input {
file {
path => "/var/log/application/*.log"
start_position => "beginning"
codec => multiline {
pattern => "^\["
negate => true
what => "previous"
}
}
}
filter {
grok {
match => {
"message" => "\[%{TIMESTAMP_ISO8601:timestamp}\] %{LOGLEVEL:level} \[%{DATA:service}\] %{GREEDYDATA:message}"
}
}
date {
match => [ "timestamp", "ISO8601" ]
target => "@timestamp"
}
if [level] == "ERROR" {
mutate {
add_field => { "alert" => true }
}
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "logs-%{+YYYY.MM.dd}"
}
}
Neste exemplo, o Logstash lê logs de arquivos, usa o filtro grok (que é uma linguagem de pattern matching) para extrair campos estruturados, converte o timestamp para o formato padrão do Elasticsearch, adiciona um campo de alerta para erros, e envia tudo para o Elasticsearch em índices diários.
Kibana: Visualização e Exploração Interativa
O Kibana é a interface visual do ELK Stack. Ele permite que você crie dashboards, execute buscas complexas no Elasticsearch, explore dados em tempo real e configure alertas. A força do Kibana não está apenas em exibir dados bonitos — está em permitir que você faça perguntas ao seu sistema de logs rapidamente, sem escrever código.
Em Kibana, você trabalha com índices, visualizações e dashboards. Um índice é o padrão de índices do Elasticsearch que você quer analisar (por exemplo, logs-* para todos os índices que começam com "logs"). Uma visualização é uma representação gráfica de uma query (gráfico de barras, linhas, tabelas, maps, etc.). Um dashboard é uma coleção de visualizações que conta uma história sobre sua infraestrutura.
Arquitetura Prática e Padrões de Implementação
Topologia de Produção Recomendada
Em ambientes de produção, o ELK Stack raramente é instalado em um único servidor. A topologia típica envolve múltiplos nós de Elasticsearch para alta disponibilidade, múltiplas instâncias de Logstash para processamento paralelo e escalável, e pelo menos uma instância de Kibana para visualização. Você também pode adicionar um Filebeat (agente leve) em cada servidor da sua aplicação para coletar logs de forma eficiente, ao invés de usar o Logstash diretamente para leitura de arquivos.
A razão para separar componentes é garantir que um deles fique indisponível sem derrubar todo o sistema. Se o Kibana cair, você ainda consegue enviar logs. Se um nó Elasticsearch sair, outros replicam os dados. Se o Logstash ficar congestionado, as mensagens ficam em fila até ele processar — o importante é não perder logs.
# Exemplo de topologia com Docker Compose
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.0.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
logstash:
image: docker.elastic.co/logstash/logstash:8.0.0
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
ports:
- "5000:5000"
depends_on:
- elasticsearch
kibana:
image: docker.elastic.co/kibana/kibana:8.0.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
volumes:
es_data:
Filtragem e Enriquecimento Avançado
A verdadeira potência do Logstash surge quando você começa a enriquecer logs com contexto externo. Imagine que seus servidores web geram logs com endereços IP de clientes — você pode usar o filtro geoip para adicionar localização geográfica, ou o filtro translate para mapear códigos de erro para mensagens legíveis. Você também pode usar expressões condicionais para aplicar diferentes transformações dependendo do tipo de log.
filter {
# Extrair informações do log
grok {
match => {
"message" => "%{IP:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}\" %{NUMBER:status_code} %{NUMBER:bytes}"
}
}
# Adicionar geolocalização
geoip {
source => "client_ip"
target => "geoip"
}
# Traduzir códigos de status HTTP
translate {
field => "status_code"
destination => "status_description"
dictionary => {
"200" => "OK"
"404" => "Not Found"
"500" => "Internal Server Error"
}
}
# Remover logs de healthcheck (ruído)
if [request] =~ /\/health/ {
drop { }
}
# Marcar requisições lentas
if [response_time] > 1000 {
mutate {
add_tag => [ "slow_request" ]
}
}
}
Casos de Uso Reais e Melhores Práticas
Correlação de Eventos Distribuídos
Um dos maiores desafios em sistemas distribuídos é rastrear uma requisição através de múltiplos serviços. Se uma requisição falha em sua API, ela pode ter passado pela API gateway, serviço de autenticação, banco de dados e cache — você precisa encontrar o log exato em cada um desses serviços. A solução é usar um correlation ID ou trace ID que acompanha a requisição por toda sua jornada.
Seu código deve gerar um identificador único quando a requisição entra no sistema e incluir esse ID em cada log subsequente. Quando você procura por esse ID no Kibana, todos os logs relacionados àquela requisição aparecem juntos, permitindo você acompanhar exatamente onde o problema ocorreu.
import uuid
import logging
from elasticsearch import Elasticsearch
from pythonjsonlogger import jsonlogger
# Configurar logging estruturado com trace ID
class TraceIDFilter(logging.Filter):
def filter(self, record):
record.trace_id = getattr(record, 'trace_id', str(uuid.uuid4()))
return True
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter('%(timestamp)s %(level)s %(trace_id)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.addFilter(TraceIDFilter())
# Uso em aplicação Flask
from flask import Flask, request, g
app = Flask(__name__)
@app.before_request
def set_trace_id():
g.trace_id = request.headers.get('X-Trace-ID', str(uuid.uuid4()))
@app.route('/api/users/<user_id>')
def get_user(user_id):
extra = {'trace_id': g.trace_id}
logger.info(f"Fetching user {user_id}", extra=extra)
# Passar trace ID para serviços downstream
headers = {'X-Trace-ID': g.trace_id}
# ... fazer chamadas HTTP com headers
return {"user_id": user_id}
Configuração de Índices Inteligente
Uma prática fundamental em produção é usar índices por tempo (diários, semanais). Isso permite que você mantenha dados recentes no armazenamento rápido e arquive ou delete dados antigos sem afetar o desempenho. O Elasticsearch tem uma feature chamada Index Lifecycle Management (ILM) que automatiza essa rotação de índices, aplicando políticas como: mover índices para armazenamento mais lento após 30 dias, deletar após 90 dias, ou fazer rollover quando atingem um tamanho específico.
{
"policy": "logs-policy",
"phases": {
"hot": {
"min_age": "0d",
"actions": {
"rollover": {
"max_primary_shard_size": "50gb",
"max_age": "1d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"set_priority": {
"priority": 50
},
"shrink": {
"number_of_shards": 1
}
}
},
"cold": {
"min_age": "90d",
"actions": {
"set_priority": {
"priority": 0
}
}
},
"delete": {
"min_age": "365d",
"actions": {
"delete": {}
}
}
}
}
Segurança e Acesso Controlado
Em produção, você não quer que qualquer pessoa acesse seus logs — eles contêm informações sensíveis como tokens, senhas e dados pessoais. O Elasticsearch X-Pack (agora integrado nas versões mais recentes) oferece autenticação baseada em usuário, criptografia em trânsito (TLS/SSL) e controle granular de acesso (Role-Based Access Control — RBAC). O Kibana respeita esses controles de acesso, permitindo que você restrinja quais índices e dashboards cada usuário pode ver.
# elasticsearch.yml - Configuração básica de segurança
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.http.ssl.enabled: true
xpack.security.transport.ssl.keystore.path: /path/to/elastic-certificates.p12
xpack.security.transport.ssl.keystore.password: "your-password"
xpack.security.http.ssl.keystore.path: /path/to/elastic-certificates.p12
xpack.security.http.ssl.keystore.password: "your-password"
# Definir senha do usuário elástico (padrão)
# bin/elasticsearch-setup-passwords interactive
Conclusão
Você aprendeu que o ELK Stack é uma solução de três camadas: o Elasticsearch indexa e armazena dados de forma distribuída e escalável, o Logstash transforma logs brutos em dados estruturados e ricos em contexto, e o Kibana oferece a visualização e exploração necessária para tomar decisões baseadas em dados. Esses três componentes trabalham em harmonia para resolver um problema crítico: como você encontra uma agulha em um palheiro de bilhões de registros de log.
A implementação prática exige atenção a padrões como correlação de eventos (trace IDs), gestão inteligente de índices para controlar custo e performance, e segurança adequada para proteger dados sensíveis. Começar com uma topologia simples (tudo em um servidor) é aceitável para aprendizado, mas em produção, você precisará pensar em alta disponibilidade, escalabilidade horizontal e retenção de dados.
O que distingue profissionais competentes é a capacidade de pensar além de "apenas fazer funcionar" — é entender por que cada componente existe, como ele escala, e quais padrões evitar. Com essa mentalidade, você conseguirá diagnosticar problemas complexos em minutos, ao invés de horas, e contribuir significativamente para a estabilidade de sistemas críticos.