↓Pular para o conteúdo principal
  1. blog/
ARTIGO TÉCNICO

Como pensar observabilidade para uma frota de VPS Docker sem partir direto para Kubernetes

Uma arquitetura incremental de observabilidade para hosts Docker com Prometheus, Grafana, Loki, Promtail, cAdvisor e node-exporter, sem transformar Kubernetes em pré-requisito.

Quando a operação cresce em VPS e Docker Compose, é comum aparecer uma conclusão automática:

Agora precisa colocar tudo em Kubernetes para conseguir monitorar direito.

Não necessariamente.

Uma stack real que usei como base de observabilidade rodava no próprio Docker Compose e já separava métricas de host, métricas de containers, logs e visualização usando:

1
2
3
4
5
6
7
Prometheus
node-exporter
cAdvisor
Loki
Promtail
Grafana
Traefik

A fonte recuperada prova essa arquitetura em um host de produção. Ela não prova “centenas de VPS”. Por isso reduzi o título original desta pauta para uma frota de VPS Docker.

A ideia que vale reaproveitar é arquitetural: começar por componentes simples e replicáveis antes de transformar orquestrador em requisito da observabilidade.

O que eu queria enxergar #

Em um host Docker típico, as perguntas básicas são previsíveis:

1
2
3
4
5
6
7
8
host está sem CPU ou memória?
disco está enchendo?
container está reiniciando?
qual container está consumindo recurso?
a aplicação está emitindo erro?
logs estão acessíveis depois que o container reinicia?
quanto tempo de histórico existe?
quem pode abrir Grafana externamente?

Nenhuma dessas perguntas exige Kubernetes para existir.

Elas exigem coleta consistente e uma forma de centralizar contexto.

Métrica de host: node-exporter #

O node-exporter cobre a visão do sistema operacional.

Na stack real, ele rodava com acesso read-only ao filesystem do host e pid: host.

Conceitualmente:

1
2
3
4
5
node-exporter:
  image: prom/node-exporter:latest
  pid: host
  volumes:
    - /:/host:ro,rslave

Com isso, Prometheus consegue acompanhar métricas como:

  • CPU;
  • memória;
  • filesystem;
  • load;
  • rede;
  • pressão do host.

É a camada que responde se o problema está afetando a máquina inteira ou só uma aplicação.

Métrica de container: cAdvisor #

Para enxergar o runtime Docker, a stack usava cAdvisor.

Ele precisa de uma visão bem mais privilegiada do host, montando recursos como:

1
2
3
4
5
6
/
/var/run
/sys
/var/lib/docker
/dev/disk
/dev/kmsg

Na fonte, o container estava como privileged.

Isso merece ser tratado como decisão de segurança, não só como detalhe de Compose.

cAdvisor entrega informação muito útil sobre consumo por container, mas a permissão que recebe também é maior que a de uma aplicação comum.

Então eu prefiro deixá-lo apenas em rede interna e não publicar a UI/porta diretamente na internet.

Prometheus: histórico curto e previsível #

Na stack recuperada, Prometheus estava configurado com:

1
--storage.tsdb.retention.time=15d

E persistia os dados em volume local:

1
./observability/prometheus/data:/prometheus

A configuração era montada em modo read-only:

1
./observability/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro

Quinze dias não são uma lei.

É uma decisão de capacidade.

O ponto é ter retenção explícita. Se ninguém define, o armazenamento cresce até a infraestrutura decidir por você.

Logs: Loki + Promtail #

Métrica mostra quanto algo desviou.

Log costuma mostrar o que aconteceu.

A stack usava Loki com persistência local:

1
./observability/loki/data:/loki

E Promtail coletava fontes do host/containers usando mounts read-only, incluindo:

1
2
/var/log
/var/lib/docker/containers

Além disso, havia acesso ao socket Docker para enriquecer a descoberta/contexto dos containers.

Esse último ponto merece o mesmo cuidado do cAdvisor: montar /var/run/docker.sock dá ao processo uma interface extremamente sensível do Docker daemon.

Se a arquitetura puder obter os metadados necessários sem expor o socket, melhor. Se ele for necessário, precisa permanecer restrito à rede interna e ao host controlado.

Rede interna para coletores #

Um detalhe do Compose que considero bom era a separação de redes.

Prometheus, node-exporter, cAdvisor, Loki e Promtail ficavam em uma bridge interna da stack.

Conceitualmente:

1
n8n_internal

Sem publicação direta dessas portas no host.

As portas existiam dentro da rede Docker:

1
2
3
4
5
Prometheus     9090
Loki           3100
cAdvisor       8080
node-exporter  9100
Grafana        3000

Mas isso é diferente de fazer:

1
2
3
0.0.0.0:9090
0.0.0.0:3100
0.0.0.0:8080

No host observado, apenas a camada de proxy publicava 80/443 externamente.

Isso reduz bastante a superfície acidental da observabilidade.

Grafana é a exceção deliberada #

Grafana precisa ser acessado por pessoas.

Na arquitetura, ele participava de duas redes:

1
2
3
rede interna da stack
+
rede pública do reverse proxy

A publicação era feita por Traefik com TLS, encaminhando para a porta interna 3000.

Isso cria uma fronteira útil:

1
2
3
4
5
6
7
internet
   ↓
Traefik :443
   ↓
Grafana :3000
   ↓
Prometheus / Loki internos

Prometheus e Loki não precisam ficar públicos só porque Grafana consulta os dois.

Grafana também precisa de persistência #

Na stack real:

1
./observability/grafana/data:/var/lib/grafana

Sem persistência, reiniciar o container pode transformar dashboard, datasource e configuração em estado efêmero dependendo de como o provisioning foi feito.

O mesmo vale para Loki e Prometheus.

Observabilidade sem persistência pode falhar justamente quando você precisa investigar algo que aconteceu antes do restart.

O que essa stack resolve em um único host #

Mesmo antes de centralizar uma frota inteira, um Compose desses já cria uma base consistente:

1
2
3
4
5
6
7
node-exporter → host
cAdvisor      → containers
Promtail      → logs
Prometheus    → métricas
Loki          → logs
Grafana       → exploração/dashboards
Traefik       → publicação controlada

A vantagem é começar entendendo a telemetria local antes de resolver a distribuição em escala.

Como eu evoluiria de um host para uma frota #

A próxima etapa não precisa ser “instalar Kubernetes”.

Primeiro eu decidiria qual modelo operacional a frota precisa.

Modelo A — coletores locais, backend central #

Em cada VPS:

1
2
3
node-exporter
cAdvisor ou equivalente
agente de logs

Em um ponto central:

1
2
3
4
Prometheus / armazenamento de métricas
Loki / armazenamento de logs
Grafana
alertas

Modelo B — Prometheus local + agregação #

Pode fazer sentido quando cada ambiente precisa continuar consultável mesmo com perda de conectividade ao centro.

Modelo C — agente único/OTel Collector #

Conforme a operação amadurece, OpenTelemetry Collector pode reduzir a quantidade de agentes e abrir espaço para traces.

A escolha depende de volume, isolamento entre clientes, conectividade, custo e retenção.

O problema que aparece quando a frota cresce #

Em poucos hosts, editar target manualmente ainda parece aceitável.

Em uma frota, começam os problemas:

1
2
3
4
5
6
quem adiciona o host novo?
quem remove host desativado?
como descubro IP/hostname automaticamente?
como separo cliente/projeto/região?
qual label identifica o serviço?
qual host parou de enviar telemetria?

A partir daí, o desafio deixa de ser instalar Prometheus.

Vira service discovery e governança de metadados.

Isso ainda não obriga Kubernetes. Pode ser resolvido com inventário, geração de targets, Consul, file-based discovery, automação ou outro catálogo operacional.

E quando Kubernetes passa a fazer sentido? #

Kubernetes pode trazer vantagens fortes quando você já precisa dele por motivos de plataforma:

  • scheduler;
  • autoscaling;
  • abstração de serviços;
  • deployment padronizado;
  • operadores;
  • service discovery nativo;
  • políticas de workload.

O que eu evitaria é adotar Kubernetes só para instalar Prometheus e Grafana.

Você troca um problema de observabilidade por um novo domínio operacional inteiro.

Se as aplicações continuam bem atendidas por VPS + Docker Compose, a observabilidade pode evoluir incrementalmente sobre esse modelo.

Segurança precisa entrar no desenho desde o começo #

Na stack real, alguns pontos pediam atenção explícita:

  • cAdvisor privilegiado;
  • Promtail com acesso ao Docker socket;
  • senha administrativa do Grafana parametrizada;
  • signup desabilitado;
  • Grafana publicado só atrás de Traefik/TLS;
  • coletores sem portas expostas diretamente.

Esses detalhes importam mais do que escolher o dashboard bonito.

Uma stack de monitoramento com acesso ao host pode virar uma excelente fonte de telemetria — ou uma nova superfície administrativa crítica.

O que eu padronizaria por VPS #

Para uma frota Docker, eu tentaria manter um contrato pequeno por host:

1
2
3
4
5
6
7
8
9
identidade do host
cliente/projeto
provedor/região
node metrics
container metrics
logs
health da própria telemetria
labels consistentes
TLS/autenticação quando houver envio remoto

O backend pode mudar com o tempo.

Esse contrato deveria mudar muito menos.

Por que tirei “centenas” do título #

O plano editorial original tinha:

Como pensar observabilidade para centenas de VPS Docker sem partir direto para Kubernetes

Mas a fonte técnica recuperada prova em detalhe uma stack Compose em um host, e o histórico prova uma operação com múltiplos VPS/hosts Docker — não uma evidência técnica desta implementação em centenas deles.

Então o artigo ficou deliberadamente menor:

uma frota de VPS Docker

A arquitetura continua útil.

A escala afirmada agora fica do tamanho da evidência disponível.

O principal aprendizado #

O primeiro passo para observar vários hosts não é necessariamente escolher um orquestrador.

É decidir:

1
2
3
4
5
quais sinais preciso coletar?
como eles são identificados?
onde ficam armazenados?
quem pode acessá-los?
como sei que o próprio monitoramento está saudável?

Prometheus, Loki, Grafana, node-exporter, cAdvisor e Promtail em Docker Compose já conseguem responder boa parte disso.

Depois, se Kubernetes fizer sentido para a plataforma, a observabilidade acompanha.

Não precisa ser o motivo para começar a usá-lo.

TEM UM CENÁRIO PARECIDO?

Me chama.

Manda o contexto, os sintomas e o que já foi testado. Bora organizar as evidências antes de sair mexendo.

Falar com Castro →
Sem enrolação. Com evidência.