Resumo — Este artigo documenta a engenharia por trás de um template Zabbix que monitora Apache NiFi sem agente, consumindo a API REST do NiFi (
/nifi-api) com itensHTTP agente parsing JSON em JavaScript embutido no preprocessing. Cobrimos a arquitetura em 4 templates (master + recursos de cluster + filas + counters), a descoberta automática de Process Groups e filas via LLD, o cálculo de back pressure (o sinal que importa de verdade em um pipeline de dados) e um problema que qualquer automação contra NiFi cedo ou tarde enfrenta: tokens que expiram em 8 horas — e a solução de rotação automática via Ansible + API do Zabbix.
1. Por que monitorar NiFi sem agente?
O Apache NiFi é a espinha dorsal de muitos pipelines de ingestão ETL/dados. Quando um fluxo "para silenciosamente" — um processador parado, uma fila saturada, um heap estourando — o sintoma não é um erro HTTP de application: é um dado que deixa de chegar no destino. Descobrir isso no dashboard do consumidor, horas depois, é tarde demais.
NiFi já expõe nativamente uma API REST rica (/flow/status, /flow/process-groups/{id}, /system-diagnostics, /counters) que agrega tudo o que a UI mostra — o bulletin board, o status dos processadores, as filas com seus thresholds de back pressure, as métricas da JVM. Instalar agente Zabbix nos nodes do NiFi (frequentemente em containers, clusters ou ambientes gerenciados) desnecessário e intrusivo: tudo o que importa já está na API.
O desenho adotado é pull-based, sem lado esquerdo de coleta:
- Zabbix Server/Proxy 7.4+ (itens
HTTP agentcom preprocessing JavaScript); - Conectividade HTTP(S) do Zabbix até
/nifi-api— nada instalado no NiFi; - Credenciais (login/token) em macros do tipo
SECRET_TEXT.
O template (Apache NiFi API by JS) é distribuído como um único YAML de export Zabbix 7.4 com 4 templates encadeados.
2. O que importa em um NiFi: traduzindo a UI para sinais
Antes do template, a decisão de engenharia mais importante: quais sinais realmente indicam problemas em um fluxo NiFi? A resposta mapeia diretamente para os conceitos da ferramenta:
| Conceito do NiFi | Sinal de monitoramento | Por que importa |
|---|---|---|
| Back pressure | % do threshold de objetos/bytes na fila | É o "quase-acidente" nº 1 do NiFi: quando a fila atinge o threshold, o upstream pausa e o pipeline inteiro trava em cascata |
| Idade do dado mais antigo | Segundos do FlowFile mais velho na fila | Detecta dados "presos" mesmo sem saturar a fila (fallbacks, relações de retry, loops lentos) |
| Bulletin board | Bullets de WARN/ERROR | É o log operacional do NiFi: processadores que falham, yields, validações quebradas aparecem aqui primeiro |
| Processadores inválidos | Contagem de processadores INVALID/com erros de validação | Fluxo "rodando" mas com componentes quebrados = perda silenciosa de dados |
| JVM | Heap usado/%, uptime | NiFi é Java: heap > 85% precede GC storms e OOM |
| Repositórios | Uso de flowfile/content/provenance repository | Disco cheio no repositório para o fluxo no nível de arquitetura |
| Counters | Contadores customizados (UpdateCounter) | Métricas de negócio embutidas nos fluxos pelo próprio time de dados |
| API viva | /flow/status responde | Separa "fluxo doente" de "coleta doente" |
O template cobre os oito — e o back pressure merece uma nota técnica: o NiFi define, por conexão, um objectThreshold e um bytesThreshold. O cálculo do template é:
var obj = parseInt((q.queueSize && q.queueSize.objectCount) || 0, 10);
var bp = parseInt((q.backPressure && q.backPressure.objectThreshold) || 0, 10);
if (bp <= 0) { return 0; }
return Math.round(100 * obj / bp * 100) / 100;Monitorar o percentual do threshold (e não o valor absoluto) é o que torna o trigger correto em qualquer fluxo — uma fila configurada com threshold 10.000 e 8.500 objetos é mais crítica do que uma com threshold 1 milhão e 50 mil.
3. Arquitetura do template: master + três domínios
O export contém 4 templates com responsabilidade única — o operador vincula apenas o master no host:
| Template | Função |
|---|---|
Apache NiFi API by JS (master) | nifi.api.status (health + trigger nodata HIGH), bulletin board (contagem WARN/ERROR), item de login/token, e a LLD de Process Groups (threads, filas, flowfiles in/out, bytes lidos/escritos, processadores com erro, uptime) |
... Cluster Resources | JVM via /system-diagnostics: heap usado/máx/%, uptime, GC %, nodes do cluster, threads totais + storage dos repositórios (%) |
... Queues | LLD de conexões (filas): objetos/bytes em fila, % de back pressure (objetos e bytes), idade do dado mais antigo |
... Counters | LLD de counters registrados (ex.: UpdateCounter), com filtro por regex ({$NIFI.COUNTERS_FILTER}) |
Duas decisões de desenho valem destaque:
1. Raw master + dependent items no domínio de cluster. Em /system-diagnostics, o padrão é um item HTTP_AGENT que persiste o snapshot inteiro como texto (nifi.jvm.raw, history 1d) e itens DEPENDENT que extraem cada métrica via preprocessing JS. Uma única requisição HTTP rende heap usado, heap máx, heap %, uptime e storage — economia de requests e consistência temporal entre as métricas.
2. Itens autônomos por métrica nos domínios de PG/fila. Nos itens prototipados da LLD, cada item faz sua própria chamada HTTP com seu próprio JS de extração. É mais verboso, mas cada item é independente: um parsing quebrado não derruba as métricas irmãs, e o debugging no item é direto ("esse item faz esse request e esse parse" — sem cadeia de master/dependent para rastrear).
O parsing do uptime da JVM é um exemplo do trabalho "sujo" que o preprocessing absorve — o NiFi devolve uptime como string HH:MM:SS.mmm, não como segundos:
var b = JSON.parse(value);
var u = String(b.uptime || '00:00:00.000');
var p = u.split(':');
var s = parseFloat(p[0]) * 3600 + parseFloat(p[1]) * 60 + parseFloat(p[2] || 0);
return Math.floor(s);E a idade do dado mais antigo na fila usa o relógio do Zabbix como referência, com defesa contra timestamps inválidos:
var ts = parseInt((q.oldestFlowFileTimestamp || q.oldestFlowFileEventTimestamp || 0), 10);
if (ts <= 0) { return 0; }
return Math.max(0, Math.floor(Date.now() / 1000) - Math.floor(ts / 1000));4. LLD: o template descobre o seu fluxo sozinho
A parte que elimina a manutenção manual é a descoberta. A LLD do master (nifi.discovery.process_groups, delay de 1h) consulta /flow/process-groups/{$NIFI.ROOT_PG_ID} e emite uma linha por Process Group filho:
{ "data": [
{ "{#PGID}": "80a4b7...", "{#PGNAME}": "ingestao-vendas", "{#PGPATH}": "/" },
{ "{#PGID}": "0ff3c2...", "{#PGNAME}": "enriquecimento", "{#PGPATH}": "/" }
] }Cada linha prototipa 10 itens (threads, filas em objetos/bytes, flowfiles in/out, bytes lidos/escritos, processadores com erro, uptime, raw) e 2 triggers. A LLD de filas (...Queues) faz o mesmo para as conexões, emitindo {#CONNID}, {#CONNNAME}, {#SRC} e {#DEST} — o que produz triggers nomeados com contexto legível, tipo "Fila ConvertToAvro → PublishKafka: back pressure objetos > 80%".
Detalhes de robustez no desenho da LLD:
- Fallback explícito: se nenhum PG filho é encontrado, a LLD emite uma linha para o próprio grupo raiz — nunca retorna vazio silenciosamente (itens órfãos e host inflado são o anti-padrão clássico);
- Lista estática opcional:
{$NIFI.PROCESS_GROUPS}permite fixar IDs de PG quando a política é monitorar apenas grupos específicos; - Lifetime de 30d: PGs/filas removidas do fluxo têm seus itens limpos automaticamente;
- Filtro por regex nos counters:
{$NIFI.COUNTERS_FILTER}(case-insensitive) evita descobrir dezenas de counters irrelevantes — vazio = todos.
5. Autenticação: o problema do token de 8h (e a rotação automatizada)
Aqui mora a lição de engenharia mais interessante do projeto — e o motivo pelo qual o item de login existe.
O template embute um item nifi.api.token que faz POST {$NIFI.URL}/access/token (application/x-www-form-urlencoded com username/password, espera 201) e registra o token gerado. As demais chamadas seguem com header Authorization: Bearer {$NIFI.TOKEN} — modos basic e token são configuráveis via {$NIFI.AUTH}.
O problema: NiFi não emite tokens de longa duração. O JWT tem expiração máxima hardcoded de 12h (StandardBearerTokenProvider.MAXIMUM_EXPIRATION) e o login single-user gera tokens de 8h fixos. Não existe "API token"/"service token" na UI nem via CLI. Traduzindo: qualquer integração de monitoramento baseada em token quebra a cada 8 horas — e um trigger de "API sem dados" disparado por token expirado é o pior tipo de alerta (falso positivo do próprio monitor).
A recomendação oficial do NiFi para automação é mTLS com certificado de cliente (X.509) — sem tokens e sem renovação. Porém, itens HTTP do Zabbix não suportam mTLS com certificado de cliente, o que obrigaria a um script proxy externo. A solução pragmática adotada foi rotacionar o token automaticamente antes da expiração, por duas vias equivalentes:
Playbook Ansible (renovar_nifi_token.yml, recomendado):
- Lê um
.envlocal (token_zabbix,nifi_username,nifi_password) comassertde variáveis obrigatórias; - Faz login no NiFi (
POST /access/token,201) e valida que o token tem >= 100 chars; - Resolve
hostidehostmacroidde{$NIFI.TOKEN}via API JSON-RPC do Zabbix (host.get→usermacro.get); - Atualiza a macro com
usermacro.update— toda a manipulação do segredo sobno_log: true.
Agendado no cron para rodar a cada 7h — 1h de folga antes da expiração de 8h:
( crontab -l 2>/dev/null; \
echo "0 */7 * * * cd /opt/zabbix-nifi && venv/bin/ansible-playbook renovar_nifi_token.yml >> $HOME/renovar-nifi-token.log 2>&1" ) | crontab -Script shell legado (renovar_nifi_token.sh): mesmo fluxo com curl + python3 para quem não roda Ansible.
As macross do segredo são SECRET_TEXT no Zabbix — o valor nunca aparece na UI, em export de template nem em logs de item. E a validação manual do fluxo cabe em um curl:
curl -sk -X POST "https://nifi-host:8443/nifi-api/access/token" \
-d 'username=USUARIO&password=SENHA'
# corpo da resposta = o JWT em texto puro6. Triggers: calibrados por janela e com escopo
Todos os thresholds vivem em macros — zero hardcode no template:
| Trigger | Prioridade | Função / janela |
|---|---|---|
| NiFi API sem dados | HIGH | nodata(..., {$NIFI.STATUS_NS_TRIGGER}) |
| Bullets de ERROR no bulletin board | AVERAGE | find(..., 5m, "like", ...) >= {$NIFI.BULLETS_ERROR} |
Fila em back pressure (objetos ou bytes) > {$NIFI.QUEUE_BACKPRESSURE_PCT}% | AVERAGE | min(..., 10m) |
Fila com dados parados > {$NIFI.QUEUE_AGE_MIN}s | AVERAGE | min(..., 10m) |
Processadores inválidos/parados no PG > {$NIFI.PROC_STOPPED} | AVERAGE | min(..., 10m) |
JVM heap > {$NIFI.HEAP_PCT}% | WARNING | min(..., 5m) |
Storage dos repositórios > {$NIFI.QUEUE_BACKPRESSURE_PCT}% | WARNING | min(..., 5m) |
Threads totais > {$NIFI.THREADS_MAX} (0 = desativado) | WARNING | min(..., 10m) |
Padrões aplicados a todos: funções de janela (min/max de 5–10m) para evitar flapping em blips; manual_close: YES para o operador fechar com contexto; e tags estruturadas em todos os eventos — scope: availability/performance/capacity + processgroup/queue com o nome descoberto — prontas para filtragem, routing e integração com ITSM.
Em ambientes clusterizados, um detalhe prático: os itens usam os aggregate snapshots do NiFi (aggregateSnapshot do status do PG e do diagnostics), que consolidam o cluster inteiro na visão do nó coordenador.
7. Alinhamento com as boas práticas SRE (para pipelines de dados)
Os Golden Signals clássicos foram desenhados para serviços web; a tradução para um pipeline de dados fica:
- Latência → idade do dado mais antigo na fila (quanto tempo o dado espera para ser processado);
- Tráfego → flowfiles in/out (5m), bytes lidos/escritos por segundo;
- Erros → bullets de ERROR no bulletin board + processadores inválidos;
- Saturação → % de back pressure, heap da JVM, storage dos repositórios, threads totais.
E o "alerting econômico" segue o mesmo princípio do caso OpenShift: janelas de avaliação que filtram blips, prioridades calibradas (HIGH apenas para indisponibilidade, AVERAGE/WARNING para degradação) e um item master de health que separa falha da coleta de falha do fluxo — API caindo ≠ pipeline doente.
8. Pontos fortes
- Zero footprint no NiFi: sem agente, sem JAR de extensão, sem reporter de métricas instalado. Apenas connectividade HTTP(S) do Zabbix até
/nifi-api; - LLD que acompanha o fluxo: PGs, filas e counters descobertos automaticamente — fluxo novo na tela do NiFi = métricas novas no Zabbix, sem toque manual;
- Back pressure como cidadão de primeira classe: percentual do threshold (não valor absoluto), separado em objetos e bytes, com idade do dado como sinal complementar;
- Raw master + dependent items: uma chamada HTTP rende todas as métricas de JVM/storage, com consistência temporal;
- Segredos seguros e rotacionados: macros
SECRET_TEXT+ rotação automatizada via Ansible/no_log, resolvendo a limitação real de 8h dos tokens NiFi; - JavaScript versionado no Git: todo parsing vive no YAML exportado — auditável em
git log, sem script externo no filesystem do Zabbix; - Thresholds 100% em macros — o mesmo YAML serve lab, staging e produção.
9. Conclusão
O case mostra o caminho completo para transformar o Zabbix em consumidor first-class da API REST do NiFi: master template com LLD de Process Groups, templates dependentes para filas (com back pressure em % do threshold), recursos de cluster (JVM/storage via dependent items) e counters de negócio — tudo agentless, com parsing em JavaScript embutido e versionado em Git.
A lição que vale o artigo inteiro, porém, é a de autenticação: quando a plataforma alvo impõe tokens de vida curta por design (e não oferece serviço token), a solução não é reduzir o intervalo de coleta nem aceitar alertas falsos a cada 8h — é automatizar a rotação do segredo no próprio sistema de monitoramento, usando a API do Zabbix para atualizar a macro antes da expiração. O mesmo padrão se aplica a qualquer integração com APIs que sofrem do mesmo mal (e há muitas).
Apêndice A — Quick start
# 1. Validar conectividade a partir do host do Zabbix
curl -sk https://nifi-host:8443/nifi-api/flow/status
# 2. Validar login (retorna o JWT)
curl -sk -X POST https://nifi-host:8443/nifi-api/access/token \
-d 'username=USUARIO&password=SENHA'
# 3. Importar o template e vincular o master ao host
# Configurar no host:
# {$NIFI.URL} = https://nifi-host:8443/nifi-api (sem barra final)
# {$NIFI.USER} = usuario do NiFi
# {$NIFI.PASSWORD} = (SECRET_TEXT)
# 4. Agendar a rotação do token (cron a cada 7h)
venv/bin/ansible-playbook renovar_nifi_token.yml --syntax-checkApêndice B — Requisitos no NiFi
- Usuário com permissões read em
/flow,/controller,/counters; - Endpoints utilizados:
/access/token,/flow/status,/flow/process-groups/{id},/connections/{id},/system-diagnostics,/counters; - LLDs: PGs a cada 1h, filas e counters a cada 5m, com
lifetimede 30d para limpeza automática de itens órfãos.