Monitorando containers com Docker stats

1. Introdução ao Docker stats

No ecossistema DevOps, a visibilidade sobre o comportamento dos containers em tempo real é um requisito fundamental para garantir disponibilidade, performance e uso eficiente de recursos. O comando docker stats é uma ferramenta nativa do Docker que fornece um fluxo contínuo de métricas de uso de CPU, memória, I/O de rede e I/O de bloco para containers em execução.

Diferente de logs históricos, que registram eventos passados, o docker stats oferece monitoramento em tempo real — uma janela ao vivo para o que está acontecendo agora. Enquanto logs são essenciais para análise forense e debugging, as métricas de docker stats são ideais para detectar anomalias instantâneas, como picos de consumo ou vazamentos de memória.

Para o profissional DevOps, monitorar containers não é opcional: é a base para dimensionamento adequado, otimização de custos em ambientes cloud e prevenção de falhas em produção. Sem essa visibilidade, um container que consome toda a CPU disponível pode derrubar serviços inteiros sem aviso prévio.

2. Sintaxe básica e primeiros passos

O uso mais simples do comando é executá-lo sem argumentos:

docker stats

Isso exibe uma tabela com todos os containers em execução, atualizada a cada segundo. Para monitorar um container específico, use seu ID ou nome:

docker stats meu-container-web

Opções úteis incluem:

  • --no-stream: exibe apenas uma única captura (útil para scripts)
  • --format: personaliza a saída com templates Go
  • --all (ou -a): inclui containers parados (exibe métricas zeradas)

Exemplo com --no-stream:

docker stats --no-stream meu-container-web

3. Métricas essenciais exibidas

A saída padrão do docker stats inclui as seguintes colunas:

CPU% — Percentual de uso da CPU em relação aos núcleos disponíveis. Se um container usa 150%, significa que está consumindo 1,5 núcleos completos.

MEM USAGE / LIMIT — Uso atual de memória e o limite imposto. Exemplo: 128.4MiB / 256MiB indica que metade da memória permitida está em uso.

MEM% — Percentual do limite de memória que está sendo consumido.

NET I/O — Tráfego de rede acumulado (entrada/saída) desde o início do container.

BLOCK I/O — Operações de leitura/gravação em disco acumuladas.

Exemplo prático de saída:

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O
a1b2c3d4e5f6   web-app   45.23%    210.5MiB / 512MiB     41.11%    1.2GB / 3.4GB     150MB / 45MB

4. Interpretação e análise das métricas

Identificar gargalos de CPU é direto: se um container mantém uso acima de 80-90% consistentemente, pode estar subdimensionado ou com um loop ineficiente. Vazamentos de memória (memory leak) se manifestam como crescimento contínuo do MEM USAGE sem liberação, mesmo sem aumento de carga.

A relação entre limites configurados e métricas exibidas é crucial. Se um container foi iniciado com --memory=256m --cpus=0.5, o docker stats respeitará esses limites:

docker run -d --name web-limited --memory=256m --cpus=0.5 nginx
docker stats web-limited

Sinais de alerta incluem:
- CPU% consistentemente acima de 90%
- MEM% próximo de 100%
- I/O de bloco excessivo (pode indicar swapping)
- NET I/O crescendo sem aumento de requisições (possível ataque ou vazamento de dados)

5. Formatação de saída para automação

O --format aceita templates Go para extrair métricas específicas. Exemplo básico:

docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

Para capturar apenas valores (sem cabeçalho), remova table:

docker stats --no-stream --format "{{.Name}},{{.CPUPerc}},{{.MemPerc}}"

Campos disponíveis: .Name, .ID, .CPUPerc, .MemUsage, .MemPerc, .NetIO, .BlockIO, .PIDs.

Exemplo prático para gerar um CSV:

docker stats --no-stream --format "{{.Name}},{{.CPUPerc}},{{.MemUsage}},{{.MemPerc}}" >> metrics.csv

6. Integração com pipelines DevOps

Em pipelines CI/CD (GitHub Actions, Jenkins), o docker stats pode ser usado para validar se um container não excede limites antes de promover para produção.

Exemplo de script bash para coleta e alerta:

#!/bin/bash
CONTAINER="web-app"
THRESHOLD_CPU=80
THRESHOLD_MEM=90

while true; do
  STATS=$(docker stats --no-stream --format "{{.CPUPerc}},{{.MemPerc}}" $CONTAINER)
  CPU=$(echo $STATS | cut -d',' -f1 | tr -d '%')
  MEM=$(echo $STATS | cut -d',' -f2 | tr -d '%')

  if (( $(echo "$CPU > $THRESHOLD_CPU" | bc -l) )); then
    echo "ALERTA: CPU em $CPU% - limite $THRESHOLD_CPU%"
  fi

  if (( $(echo "$MEM > $THRESHOLD_MEM" | bc -l) )); then
    echo "ALERTA: Memória em $MEM% - limite $THRESHOLD_MEM%"
  fi

  sleep 5
done

Para envio a sistemas como Prometheus, um script pode expor as métricas em formato texto para um endpoint HTTP, ou usar ferramentas como cAdvisor que já coletam dados do Docker daemon.

7. Limitações e alternativas

O docker stats tem limitações importantes:

  • Não monitora disco persistente (apenas I/O de bloco do container efêmero)
  • Não verifica saúde da aplicação (apenas recursos do sistema)
  • Não oferece histórico (apenas tempo real ou capturas únicas)
  • Escalabilidade limitada em clusters grandes

Em Kubernetes, as alternativas nativas são:

  • kubectl top pod — métricas de CPU/memória por pod
  • kubectl top node — métricas por nó
  • Metrics Server — componente que coleta dados do kubelet
  • Prometheus + Grafana — stack completo para monitoramento

Boas práticas: combine docker stats com healthchecks (HEALTHCHECK no Dockerfile) e logs estruturados. Para ambientes de produção, ferramentas como Datadog, New Relic ou Prometheus são mais robustas.

8. Exemplo prático completo

Cenário: Monitorar um container Nginx durante um teste de carga com ab (Apache Benchmark).

Passo 1 — Iniciar container com limites definidos:

docker run -d --name nginx-test --memory=128m --cpus=0.5 -p 8080:80 nginx:alpine

Passo 2 — Gerar carga:

ab -n 10000 -c 100 http://localhost:8080/

Passo 3 — Script de monitoramento em tempo real, registrando em CSV:

#!/bin/bash
CONTAINER="nginx-test"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
CSV_FILE="metrics_${TIMESTAMP}.csv"

echo "timestamp,cpu_perc,mem_usage,mem_perc,net_in,net_out" > $CSV_FILE

for i in {1..30}; do
  STATS=$(docker stats --no-stream --format \
    "{{.CPUPerc}},{{.MemUsage}},{{.MemPerc}},{{.NetIO}}" $CONTAINER)

  CPU=$(echo $STATS | cut -d',' -f1)
  MEM=$(echo $STATS | cut -d',' -f2)
  MEM_PERC=$(echo $STATS | cut -d',' -f3)
  NET=$(echo $STATS | cut -d',' -f4)

  echo "$(date +%H:%M:%S),$CPU,$MEM,$MEM_PERC,$NET" >> $CSV_FILE

  # Alerta simples se CPU > 80%
  CPU_VAL=$(echo $CPU | tr -d '%')
  if (( $(echo "$CPU_VAL > 80" | bc -l) )); then
    echo "⚠️  ALERTA: CPU em $CPU_VAL%"
  fi

  sleep 2
done

echo "Métricas salvas em $CSV_FILE"

Passo 4 — Analisar o CSV gerado:

cat metrics_*.csv

Exemplo de saída:

timestamp,cpu_perc,mem_usage,mem_perc,net_in,net_out
14:30:01,12.45%,5.2MiB / 128MiB,4.06%,1.2kB / 2.1kB
14:30:03,78.32%,45.8MiB / 128MiB,35.78%,2.1GB / 1.3GB
14:30:05,92.10%,89.3MiB / 128MiB,69.77%,4.5GB / 2.8GB
14:30:07,45.67%,92.1MiB / 128MiB,71.95%,4.8GB / 3.1GB

O script demonstra como capturar métricas durante um evento de estresse, identificar picos e gerar alertas básicos — tudo com ferramentas nativas do Docker.

Referências