Monitorando Consumer Lag do Apache Kafka
Tags: kafka, monitoring
Published: 2020-03-29 20:07:05

O Apache Kafka tem milhares de métricas que podem ser acessadas via interface JMX ([nesse artigo](https://medium.com/@alvarobacelar/monitorando-um-cluster-kafka-com-ferramentas-open-source-a4032836dc79?source=friends_link&sk=2e2ae34d66935565a59932b80099dfc1) eu mostro como instrumentar tais métricas utilizando ferramentas Open Source). Contudo há uma métrica, não menos importante, que não está disponível via JMX: O Consumer Lag.

Mensurada em número de mensagem, basicamente o Lag é a diferença entre a última mensagem produzida em uma partição especifica e a última mensagem processada (*committed*) pelo consumidor.

Segundo o livro [_"kafka - The Definitive Guide"_](https://www.confluent.io/resources/kafka-the-definitive-guide/) o método preferido para monitorar o Lag é através de uma aplicação externa que possa visualizar o estado de uma partição no broker, rastreando o offset mais recente da mensagem produzida e o último offset processado pelo consumidor, atraves do topico interno de controle: __consumer_offsets

Levando isso em consideração, o LinkedIn (empresa responsável pela criação do Apache Kafka) desenvolveu (também) uma aplicação que realiza essa rastreabilidade dos offsets das partições dos brokers. Essa aplicação é o [Burrow](https://github.com/linkedin/Burrow).

O Burrow é uma ferramenta que foi escrita na linguagem [Go](https://golang.org/) e ela nos provê tanto informações de Consumer Lag quanto informações diversas do cluster e disponibiliza tais valores via REST API. Com o Burrow ainda é possível enviar notificações via email ou WebHook com um threshold definido.

Como falei acima, o Burrow provê essas informações via REST API e nosso objetivo aqui é fazer essa informação chegar no Prometheus e consequentemente no Grafana.

Então irei mostrar como instalar e configurar o Burrow juntamente com o [burrow_exporter](https://github.com/alvarobacelar/burrow_exporter) (também escrito em Go) para monitorar o Consumer Lag de um cluster Apache Kafka e ter todas as informações do Lag, entre outras, no Prometheus.

> Para realizar todos os testes desse artigo, eu realizei o setup de um cluster Apache Kafka com o Ansible. [Clicando aqui](https://medium.com/@alvarobacelar/setup-de-cluster-apache-kafka-com-ansible-df62c8b1017b?source=friends_link&sk=5839522ba2bea31a39d527d523592f97) você verá um artigo que escrevi mostrando como realizar um setup de um cluster Apache Kafka e Zookeeper com o Ansible. 

### Instalando e Configurando o Prometheus

Para instalar e configurar o Prometheus vamos seguir os seguintes passos: 

- Baixar a versão mais estável do [Prometheus](https://prometheus.io/download/)
```
# wget https://github.com/prometheus/prometheus/releases/download/v2.12.0/prometheus-2.12.0.linux-amd64.tar.gz
```
- Descompactando no diretório /srv
```
# tar -zxvf prometheus-2.12.0.linux-amd64.tar.gz -C /srv/
``` 
- Adicionar usuário de serviço do prometheus
```
# useradd -s /usr/sbin/nologin prometheus
```
- Mudar o dono da pasta do prometheus
```
# chown prometheus:prometheus /srv/prometheus-2.12.0.linux-amd64/ -R
```
- Adicionar o arquivo de service do prometheus
```
# vim /etc/systemd/system/prometheus.service
```
```
[Unit]
Description=Prometheus
After=network-online.target
[Service]
Type=simple
User=prometheus
Group=prometheus
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/srv/prometheus-2.12.0.linux-amd64/prometheus --config.file=/srv/prometheus-2.12.0.linux-amd64/prometheus.yml --storage.tsdb.path=/srv/prometheus-2.12.0.linux-amd64/data --web.listen-address=0.0.0.0:9090
LimitNOFILE=65000
LockPersonality=true
NoNewPrivileges=true
MemoryDenyWriteExecute=true
PrivateDevices=true
PrivateTmp=true
ProtectHome=true
RemoveIPC=true
RestrictSUIDSGID=true
ProtectSystem=full
SyslogIdentifier=prometheus
Restart=always
[Install]
WantedBy=multi-user.targe
```
- Executar o daemon-reload e iniciar o serviço
```
# systemctl daemon-reload
# systemctl start prometheus
```

Se tudo tiver dado certo o serviço subiu na porta 9090 e podemos acessar nessa porta: 
http://127.0.0.1:9090

### Instalando o Grafana

A instalação do Grafana é mais fácil, para instala-lo basta acessar o seguinte link https://grafana.com/grafana/download e seguir os passos descrito no link de acordo com seu S.O. 

Depois de instalado suba o serviço
```
# systemctl start grafana-server
```

O Grafana por padrão sobe na porta 3000, então vamos acessa-lo:
http://127.0.0.1:3000

O usuário e senha default do Grafana é admin, no seu primeiro acesso é solicitado para trocar.

Após acessar o Grafana, você deve adicionar o datasource do Prometheus.

### Instalando e configurando o Burrow

O primeiro passo para instalar o Burrow é realizar o download do código fonte, para isso vá ao repositório oficial do Burrow no link abaixo:

```
$ git clone https://github.com/linkedin/Burrow.git
```

Com o código fonte do Burrow, vamos entrar no diretório e *buildar* a imagem Docker: 
```
$ cd ${PWD}/Burrow
$ docker build -t burrow-api .
```



>Nesse post irei focar na instalação do Burrow via [Docker](https://www.docker.com/). Para aqueles que preferirem executar o Burrow sem o uso do Docker, é só seguir os passos que estão descritos no [README.rd](https://github.com/linkedin/Burrow#build-and-install) do repositório do Burrow.

Com a imagem do Burrow *buildada* vamos agora realizar o mesmo procedimento com a imagem do burrow_exporter. Baixe o código fonte no link abaixo: 

{% github alvarobacelar/burrow_exporter no-readme %}

```
git clone https://github.com/alvarobacelar/burrow_exporter.git
```

> O repositório acima é um fork do projeto [burrow_exporter](https://github.com/shamil/burrow_exporter) criado por Shamil. Eu solicitei um [Pull Request](https://github.com/shamil/burrow_exporter/pull/1) há um tempo para adicionar alguns recursos extras. Mas o dono do repositório nunca respondeu, então vamos seguir usando o projeto que *forkei*, pois ele retorna um valor a mais que a API do Burrow nos disponibiliza (iremos ver isso logo mais na frente).

Entrando na pasta do do burrow_exporter, que acabamos de baixar, vamos *buildar* a imagem Docker:

```
$ docker build -t burrow-exporter . 
```

Agora que temos as imagens do Burrow e burrow_exporter criadas, vamos 
criar o arquivo docker-compose.yml para subirmos as duas aplicações posteriormente. O arquivo deve conter o seguinte conteúdo:
```yaml
version: "2"
services:
  burrow:
    image: burrow-api
    volumes:
      - ${PWD}/config:/etc/burrow/
    container_name: burrow_api

  burrow_exporter:
    image: burrow-exporter
    container_name: burrow_exporter
    ports:
      - 8090:8237
    depends_on:
      - burrow
    command: --burrow.address http://burrow:8000
```

Antes de subir os containers precisamos adicionar os servidores do kafka e zookeeper no arquivo de configuração do Burrow. Dentro do diretório da API do Burrow vamos editar o arquivo *burrow.toml*
```bash
$ vim ${PWD}/Burrow/config/burrow.toml
```

No arquivo vamos alterar os endereços dos brokers, Zookeeper e outros parâmetros:

```toml
[general]
pidfile="burrow.pid"
stdout-logfile="burrow.out"
access-control-allow-origin="mysite.example.com"

[logging]
filename="logs/burrow.log"
level="info"
maxsize=100
maxbackups=30
maxage=10
use-localtime=false
use-compression=true

[zookeeper]
servers=[ "zkhost01.example.com:2181", "zkhost02.example.com:2181", "zkhost03.example.com:2181" ] # altere para os edereços de seus zookeepers
timeout=6
root-path="/burrow"

############################
####### CLUSTER zoom #######
############################
# altere o nome do profile do cliente caso queira
[client-profile.post]
client-id="burrow-monitor"
kafka-version="2.3.0" # altere para a versão 2.3.0 do Kafka

# Dê um nome para o seu cluster [cluster.nome]
[cluster.zoom]
class-name="kafka"
servers=[ "kafka01.example.com:10251", "kafka02.example.com:10251", "kafka03.example.com:10251" ] # altere para os endereços dos seus brokers
client-profile="post" # coloque aqui o nome do client-profile definido acima 
topic-refresh=120
offset-refresh=30

# Dê um nome para o seu cluster [cluster.nome]
[consumer.zoom]
class-name="kafka"
cluster="zoom" # coloque aqui o nome do cluster definido
servers=[ "kafka01.example.com:10251", "kafka02.example.com:10251", "kafka03.example.com:10251" ] # altere para os endereços dos seus brokers
client-profile="post" # coloque aqui o nome do client-profile definido 
group-blacklist="^(console-consumer-|python-kafka-consumer-|quick-).*$" # coloque aqui os nomes dos consumer groups que não quer que apareça
group-whitelist=""
################################
####### FIM CLUSTER zoom #######
################################
# Repita isso para quantos clusters tiver

[httpserver.default]
address=":8000"

[storage.default]
class-name="inmemory"
workers=20
intervals=15
expire-group=604800
min-distance=1

## Vamos deixar comentado por enquanto
# [notifier.default]
# class-name="http"
# url-open="http://someservice.example.com:1467/v1/event"
# interval=60
# timeout=5
# keepalive=30
# extras={ api_key="REDACTED", app="burrow", tier="STG", fabric="mydc" }
# template-open="conf/default-http-post.tmpl"
# template-close="conf/default-http-delete.tmpl"
# method-close="DELETE"
# send-close=true
# threshold=1
```

Com o arquivo docker-compose.yml criado e o arquivo de configuração burrow.toml alterado com os endereços de seus clusters, podemos subir as duas aplicações: 
```bash
$ docker-compose -f docker-compose up -d
```
Agora que temos os dois containers rodando, vamos configurar o Prometheus para que este capture as métricas.

No arquivo de configuração do Prometheus (vim /srv/prometheus-2.12.0.linux-amd64/prometheus.yml), você deve adicionar as seguintes linhas:
```yaml
- job_name: 'burrow'
    static_configs:
    - targets: ['127.0.0.1:8090']
```

>O IP *127.0.0.1* deve ser substituído pelo o IP do servidor que está executando o container que subimos acima.

Para o Prometheus reconhecer tais configurações precisamos realizar um reload no serviço:
```
# systemctl reload prometheus
```

Agora vamos importar o dashboard no Grafana. O link abaixo é o dash específico para essas métricas:
https://grafana.com/grafana/dashboards/11963

Quando tiver importando o dash acima e tudo tiver ocorrido bem você verá algo parecido com imagem abaixo:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9ssukt9dwyzfwqfq1wsm.png)

>Lembram que eu havia comentado acima que iriamos utilizar o repositório que eu *forkei*? Pois bem, se você analisar o dash que importamos vai ver que tem um campo na tabela chamado *Consumer client*. Foi esse campo que adicionei no PR que abri e o dono do repositório nunca aceitou. Com essa informação sabemos qual é o IP do servidor que está consumindo naquela especifica partição.

## Conclusão
O sucesso para que o seu cluster Apache Kafka (ou qualquer outra aplicação) esteja 100% disponível e seja 100% confiável é ter uma stack madura de monitoração. Como mostrado nesse artigo, não é necessário gastar uma *bala* com licenças em softwares de monitoração achando que tal software vai fazer mágica para você. Com ferramentas Open Source somos capaz de tirar o melhor que cada uma tem a oferecer e montar uma stack muito madura de monitoração e alertas.