Guia completo de limites de recursos no Docker: evite que vazamentos de memória derrubem o servidor

O alerta do celular dispara: “servidor sem resposta”, “CPU em 100%”, “tempo limite da conexão SSH”. Depois de três tentativas, a VPN finalmente conecta. Você digita o comando ssh — e recebe outro timeout. O servidor travou.
Após a reinicialização forçada, os logs mostram a causa: um contêiner que rodava havia seis meses sofreu um vazamento de memória. O consumo subiu de 500 MB para 16 GB e esgotou toda a memória do servidor, deixando até o SSH sem recursos para responder. Outros três contêineres que funcionavam normalmente também caíram — o ambiente de produção entrou em colapso. Em 2024, a versão 27.0.3 do Docker apresentou um grave bug de vazamento de memória que fez o OOM Killer encerrar 68 contêineres de uma vez.
O objetivo deste artigo é resolver uma questão: como impedir que um único contêiner derrube o servidor inteiro. Vamos dos fundamentos de cgroups à prática com os parâmetros --memory e --cpus, além de três ferramentas de monitoramento: docker stats, cAdvisor e Prometheus. Ao final, você saberá pelo menos como fazer o próprio contêiner cair quando surgirem sinais de vazamento, em vez de levar o servidor inteiro junto.
Por que um contêiner pode derrubar o servidor?
O Docker tem uma “característica”: por padrão, não há qualquer limite para o uso de recursos pelos contêineres. Parece liberdade, não é? Tanta liberdade que um contêiner pode consumir toda a memória e a CPU do host e acabar com tudo de uma vez.
Caso real do Docker 27.4.0: um usuário percebeu que o daemon dockerd passou de algumas centenas de MB para 8 GB em poucos dias e deixou o servidor extremamente lento. Não dá para simplesmente culpar a instabilidade do Docker. O problema é que, sem limites definidos por você, o contêiner se comporta como um cavalo sem rédeas e consome todos os recursos que conseguir.
Nesse momento, o kernel Linux aciona um mecanismo “assassino”: o OOM Killer (Out Of Memory Killer). Quando falta memória no sistema, ele escolhe o processo “mais adequado” para encerrar e liberar recursos. Como faz essa escolha? O kernel atribui uma pontuação a cada processo (oom_score); quanto maior o valor, maior a chance de o processo ser eliminado. Os processos dentro de contêineres costumam ter pontuações altas, mas o daemon do Docker reduz a própria prioridade de OOM (oom_score_adj é definido como -500). Por isso, quem normalmente acaba atingido são os seus contêineres.
No docker ps, você vê que o contêiner desapareceu. Ao consultar os logs, encontra o Exit Code 137. Esse número significa 128 + 9 (sinal SIGKILL). Em termos simples: o processo foi encerrado à força. Não foi um desligamento elegante; o kernel o eliminou imediatamente.
Os sintomas de um vazamento de memória costumam ser estes:
- O consumo de memória do contêiner dispara de algumas centenas de MB para vários GB e não para de crescer
- O servidor começa a usar swap intensamente, com atividade constante do disco
- Os demais contêineres respondem cada vez mais devagar até pararem por completo
- O gráfico de monitoramento exibe uma bela linha inclinada para cima
Em 2024, houve um caso especialmente grave na comunidade Storj: a memória de um contêiner passou de algumas centenas de MB para 37 GB e quase derrubou todo o nó de armazenamento. Se um limite de memória tivesse sido configurado, o OOM Killer teria encerrado o contêiner ao chegar a 1 GB, e o servidor teria permanecido estável.
[Imagem: gráfico de um vazamento de memória]
Prompt: gráfico de uso de memória de servidor com pico acentuado para cima, zona crítica vermelha em 90%, fundo escuro, estilo de painel de monitoramento, alta qualidade
cgroups: a base dos limites de recursos
Talvez você já tenha ouvido que o Docker usa “cgroups” para limitar recursos, mas o que isso significa? Em resumo, cgroups (Control Groups) é um recurso do kernel Linux criado para definir cotas de recursos para grupos de processos. É como entregar a cada processo um “cartão de recursos”: quando o saldo termina, não é possível ultrapassar o limite.
Ao criar um contêiner, o Docker cria automaticamente um cgroup e coloca nele os processos daquele contêiner. Quando você executa docker run -m 512m nginx, nos bastidores o Docker grava o valor 536870912 — a quantidade de bytes em 512 MB — no arquivo memory.limit_in_bytes, dentro do diretório /sys/fs/cgroup/memory/docker/<ID do contêiner>/. O kernel lê o arquivo, entende que o contêiner pode usar no máximo 512 MB e o encerra se o limite for ultrapassado.
Diferenças entre cgroups v1 e v2:
- v1: separa memória, CPU e I/O de disco em subsistemas independentes, como departamentos distintos que cuidam cada um da sua área
- v2: unifica o gerenciamento em uma hierarquia mais clara, adequada para cenários como contêineres, que exigem controle integrado
- Sistemas antigos, como o RHEL 7, ainda usam v1; sistemas mais novos, como Ubuntu 20.04+ e RHEL 8+, em geral já migraram para v2
Se quiser ver como é a configuração real de cgroup de um contêiner, use estes comandos:
# Localize o ID completo do contêiner
docker inspect --format='{{.Id}}' my_container
# Consulte o limite de memória (cgroups v1)
cat /sys/fs/cgroup/memory/docker/<ID do contêiner>/memory.limit_in_bytes
# Consulte a cota de CPU (cgroups v1)
cat /sys/fs/cgroup/cpu/docker/<ID do contêiner>/cpu.cfs_quota_us
Na primeira vez que vi esses arquivos, fiquei confuso: como os limites de recursos podiam ser implementados pelo sistema de arquivos? Depois entendi que isso segue a filosofia do Linux de que “tudo é arquivo”. O kernel expõe a configuração dos cgroups como arquivos, o Docker grava valores neles e o kernel lê esses valores para aplicar as restrições. É um projeto bastante elegante.
[Imagem: diagrama da hierarquia de cgroups]
Prompt: diagrama da hierarquia de cgroups do Linux, contêineres agrupados em um cgroup do Docker, subsistemas de memória e CPU, estrutura em árvore, ilustração técnica, design limpo, alta qualidade
Todos os parâmetros de limite de memória
O Docker oferece vários parâmetros de memória, mas apenas alguns são realmente comuns. Vamos examinar cada um deles.
1. --memory / -m (limite rígido e mais importante)
Este é o parâmetro que pode salvar o servidor. Quando o contêiner atinge esse valor de memória, o OOM Killer é acionado e o contêiner é encerrado. O mínimo é 6 MB — embora quase nada funcione com tão pouco — e, em produção, normalmente é necessário começar com algumas centenas de MB.
# Limite o contêiner a no máximo 512 MB de memória
docker run -m 512m nginx
# Também é possível usar GB como unidade
docker run -m 2g my-app
Como escolher um valor adequado? Pela minha experiência, primeiro faça um teste de carga e meça o consumo normal de memória da aplicação. Depois multiplique esse valor por 1,2 a 1,5. Se a aplicação costuma usar 300 MB, por exemplo, um limite entre 400 e 450 MB tende a ser razoável. Um valor baixo demais faz o contêiner ser encerrado com frequência; um valor alto demais perde a função de proteção.
2. --memory-swap (espaço de troca, fácil de interpretar errado)
Este parâmetro confunde muita gente. Ele define o valor total de memória + swap, e não apenas o tamanho do swap.
# 512 MB de memória + 512 MB de swap (1 GB disponível no total)
docker run -m 512m --memory-swap 1g nginx
# Desative o swap (use somente memória)
docker run -m 512m --memory-swap 512m nginx
# Permita swap ilimitado (perigoso!)
docker run -m 512m --memory-swap -1 nginx
Se você não definir --memory-swap, o comportamento padrão será swap = memory. Isso significa que o total disponível equivale ao dobro da memória. Um contêiner com -m 512m, por exemplo, pode usar até 1 GB no total: 512 MB de memória + 512 MB de swap.
Recomendação para produção: desative o swap — fazendo --memory-swap ser igual a --memory — ou limite-o a no máximo metade da memória. Não use -1, pois um contêiner que consome swap sem controle pode sobrecarregar o disco.
3. --memory-reservation (limite flexível)
É uma “cota elástica”. Quando há memória disponível no servidor, o contêiner pode ultrapassar esse valor. Quando a memória fica escassa, o kernel tenta reduzir o consumo do contêiner para abaixo desse limite. O valor precisa ser menor que --memory.
# Limite flexível de 750 MB e limite rígido de 1 GB
docker run -m 1g --memory-reservation 750m nginx
É útil para aplicações que ocasionalmente consomem muita memória por pouco tempo, como durante um processamento em lote, mas usam pouco no dia a dia. O limite flexível permite distribuir os recursos com mais eficiência.
4. --kernel-memory (memória do kernel, use com cuidado)
Limita a memória do kernel usada pelo contêiner, incluindo buffers de rede e cache do sistema de arquivos. Essa parcela não pode ser movida para swap. Sinceramente, a menos que você saiba exatamente o que está fazendo, não mexa nesse parâmetro. Uma configuração inadequada pode impedir até a inicialização do contêiner.
5. --oom-kill-disable (parâmetro perigoso)
Este parâmetro desativa o OOM Killer e impede que o contêiner seja encerrado ao ultrapassar a memória. Parece uma boa ideia? É justamente o contrário. Se o consumo de memória sair do controle e o contêiner não puder ser encerrado, ele poderá esgotar a memória do servidor inteiro.
# Isto pode causar um desastre! O contêiner pode consumir memória sem limite
docker run --oom-kill-disable nginx
# Se você realmente precisar usar o parâmetro, defina também um limite de memória
docker run -m 512m --oom-kill-disable nginx
Quando esse parâmetro faz sentido? Quase nunca. Talvez em uma aplicação especial cujo processo não possa ser encerrado de forma repentina, como durante um checkpoint de banco de dados, e somente quando você tiver certeza de que a própria aplicação controla a memória.
Caso real: um usuário da AWS derrubou uma instância EC2. Depois de uma longa investigação, descobriu que um contêiner sem limite de memória havia consumido 30 GB e deixado a instância totalmente sem resposta. Depois de adicionar -m 2g, o contêiner passou a cair e reiniciar ao atingir o limite, enquanto o servidor continuava estável.
[Imagem: relação entre os parâmetros de memória]
Prompt: diagrama dos parâmetros de memória do Docker mostrando a relação entre memória e swap, gráfico visual com barras e rótulos, ilustração técnica, cores azul e laranja, alta qualidade
Todos os parâmetros de limite de CPU
Os limites de CPU são menos agressivos que os de memória: ao ultrapassá-los, o contêiner não é encerrado, apenas tem a velocidade reduzida. Mesmo assim, uma CPU fora de controle também pode travar o servidor.
1. --cpus (a opção mais intuitiva)
Define diretamente quantos núcleos de CPU o contêiner pode usar e aceita valores decimais.
# Use no máximo 1,5 núcleo de CPU
docker run --cpus="1.5" nginx
# Use apenas metade de um núcleo
docker run --cpus="0.5" my-app
Nos bastidores, esse parâmetro funciona por meio de --cpu-period e --cpu-quota; o Docker calcula a proporção para você. Com 1,5, o contêiner pode usar no máximo a capacidade computacional de um núcleo e meio a qualquer momento: um núcleo inteiro mais 50% de outro.
2. --cpu-shares (peso relativo, não um limite rígido)
Este parâmetro define a prioridade no escalonamento de CPU e tem o valor padrão 1024. O ponto mais importante é: ele só entra em ação quando há disputa por CPU. Se houver capacidade ociosa no servidor, o contêiner poderá usar o que estiver disponível.
# O contêiner A recebe o dobro do tempo de CPU do contêiner B
docker run --cpu-shares 2048 --name app_a my-app
docker run --cpu-shares 1024 --name app_b my-app
Imagine um servidor com apenas dois núcleos, ambos sob carga máxima. Os dois contêineres acima dividiriam a CPU na proporção 2:1: o contêiner A receberia cerca de 1,33 núcleo, e o B, 0,67. Se a CPU do servidor estiver ociosa, porém, os dois poderão funcionar em velocidade máxima.
Quando usar? Quando houver vários contêineres e você quiser priorizar um serviço importante durante a disputa por recursos, como uma API em relação a uma tarefa em segundo plano.
3. --cpuset-cpus (vinculação a núcleos específicos)
Prende o contêiner a determinados núcleos de CPU e impede o uso dos demais.
# Use apenas os núcleos 0 e 3
docker run --cpuset-cpus="0,3" nginx
# Use os núcleos de 2 a 5
docker run --cpuset-cpus="2-5" my-app
Casos de uso:
- Arquitetura NUMA: em servidores com múltiplas CPUs, vincule o contêiner aos núcleos do mesmo socket para reduzir acessos à memória entre sockets
- Evitar invalidação de cache: ao manter os processos em núcleos fixos, aumenta-se a taxa de acerto do cache da CPU
- Isolar serviços críticos: vincule contêineres importantes a núcleos dedicados para evitar interferência de outros contêineres
Já vi um ambiente Kubernetes em que o contêiner do banco de dados foi vinculado aos quatro últimos núcleos de um servidor com oito, deixando os quatro primeiros para o serviço Web. A melhoria de desempenho foi evidente.
4. --cpu-period e --cpu-quota (controle detalhado)
Estes são os parâmetros de baixo nível encapsulados por --cpus.
--cpu-period: período de escalonamento do CFS (Completely Fair Scheduler), com padrão de 100000 microssegundos (100 milissegundos)--cpu-quota: quantidade de microssegundos de CPU que o contêiner pode usar dentro de cada período
# Em um período de 100 ms, use apenas 50 ms de CPU (equivale a 0,5 núcleo)
docker run --cpu-period=100000 --cpu-quota=50000 nginx
# Equivale a
docker run --cpus="0.5" nginx
Na maioria dos casos, --cpus é suficiente. Use os parâmetros de baixo nível somente quando precisar de controle muito preciso, como um período mais curto para aumentar a frequência do escalonamento.
Caso real: um bug colocou o pool de threads de um contêiner em loop infinito, e a CPU chegou a 800% em um servidor de oito núcleos. Sem limites, o servidor inteiro foi derrubado e nem o SSH respondia. Depois que todos os contêineres receberam um limite --cpus="2", uma falha semelhante passou a afetar apenas o próprio contêiner, sem desestabilizar os demais.
[Imagem: comparação dos limites de CPU]
Prompt: gráfico comparativo de uso de CPU com e sem limites, antes e depois mostrando a prevenção de picos de CPU, painel de monitoramento de desempenho, visualização limpa, alta qualidade
Soluções práticas de monitoramento
Definir limites de recursos é apenas o primeiro passo. Você também precisa saber quanto cada contêiner realmente consome. Caso contrário, só perceberá o vazamento quando a memória já tiver atingido o limite e acionado o OOM.
Ferramenta 1: docker stats (integrada e sem custo)
É a opção mais simples e já vem com o Docker.
# Atualize em tempo real; pressione Ctrl+C para sair
docker stats
# Exiba apenas uma medição, útil para scripts
docker stats --no-stream
# Consulte somente contêineres específicos
docker stats nginx_container mysql_container
A saída tem este formato:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O
a1b2c3d4e5f6 nginx 0.50% 45.2MiB / 512MiB 8.83% 1.2kB / 0B
Vantagem: funciona imediatamente, sem instalar nada.
Desvantagem: mostra apenas o estado atual, sem histórico, alertas ou visualização. É adequada para investigar um problema pontual, não para monitoramento contínuo.
Ferramenta 2: cAdvisor (especialista em contêineres criado pelo Google)
O cAdvisor (Container Advisor) detecta automaticamente todos os contêineres do host e coleta métricas de CPU, memória, rede e I/O de disco. Também oferece uma interface Web e um endpoint de métricas no formato do Prometheus.
Para executá-lo:
docker run -d \
--name=cadvisor \
--restart=always \
-p 8080:8080 \
-v /:/rootfs:ro \
-v /var/run:/var/run:ro \
-v /sys:/sys:ro \
-v /var/lib/docker/:/var/lib/docker:ro \
-v /dev/disk/:/dev/disk:ro \
gcr.io/cadvisor/cadvisor:latest
Depois da inicialização, abra http://IP-do-servidor:8080 para consultar os gráficos de tendência de uso de recursos de cada contêiner. Em http://IP-do-servidor:8080/metrics, você encontra os dados no formato do Prometheus.
Vantagens: é especializado, completo e compatível com o ecossistema Prometheus.
Desvantagem: mantém apenas os dois minutos mais recentes de dados. Para consultar tendências históricas, é necessário combiná-lo com o Prometheus.
Ferramenta 3: Prometheus + Grafana (solução empresarial)
Este é um sistema completo de monitoramento:
- cAdvisor: coleta as métricas dos contêineres
- Prometheus: extrai e armazena os dados das métricas
- Grafana: oferece visualização e alertas
Configuração completa do docker-compose.yml:
version: '3.8'
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
restart: always
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
restart: always
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
restart: always
volumes:
prometheus_data:
grafana_data:
O arquivo prometheus.yml correspondente:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
Depois da implantação:
- O Prometheus coleta os dados do cAdvisor a cada 15 segundos
- Acesse o Grafana em
http://IP-do-servidor:3000(usuário e senha padrão: admin/admin) - Adicione uma fonte de dados Prometheus, usando o endereço
http://prometheus:9090 - Importe um modelo de dashboard do Grafana — recomendo o Dashboard ID 19908, criado especificamente para contêineres Docker
Principais métricas de monitoramento:
container_memory_usage_bytes: uso atual de memória do contêinercontainer_memory_max_usage_bytes: maior uso histórico de memória do contêinercontainer_cpu_load_average_10s: carga média de CPU nos últimos 10 segundoscontainer_fs_io_time_seconds_total: tempo de I/O de disco
Configure uma regra de alerta para enviar uma notificação por e-mail ou DingTalk quando o uso de memória ultrapassar 80%. Assim, você pode detectar os primeiros sinais antes que o problema se agrave.
Para ser sincero, montar esse sistema dá algum trabalho, mas é um esforço feito uma só vez. Hoje monitoro mais de 20 contêineres com o Grafana e não preciso mais acordar no meio da noite por causa de alertas.
[Imagem: dashboard de monitoramento de contêineres no Grafana]
Prompt: dashboard do Grafana mostrando métricas de contêineres Docker, gráficos de memória e CPU, interface moderna e limpa, tema escuro, painéis de monitoramento com gráficos coloridos, alta qualidade
Processo completo para diagnosticar vazamentos de memória
Quando o alerta de monitoramento dispara ou um contêiner cai sem motivo aparente, como localizar o problema rapidamente? Siga este processo.
Etapa 1: detectar a anomalia
Primeiro, use docker stats para descobrir qual contêiner apresenta crescimento descontrolado de memória:
docker stats --no-stream | grep -v "0.00%"
Encontrou um contêiner cujo consumo de memória já está próximo do limite? Verifique se ele foi encerrado recentemente pelo OOM Killer:
# Consulte o log de eventos do contêiner
docker events --filter 'event=oom' --since '24h'
# Consulte o estado de saída do contêiner
docker inspect <nome do contêiner> --format='{{.State.ExitCode}}'
# Se o retorno for 137, o contêiner foi encerrado pelo OOM Killer
Etapa 2: analisar o consumo de memória
Entre no contêiner e descubra qual processo está consumindo a memória:
# Entre no contêiner
docker exec -it <nome do contêiner> /bin/bash
# Ordene os processos pelo uso de memória (top/htop precisa estar instalado)
top -o %MEM
# Ou use ps
ps aux --sort=-%mem | head -n 10
Em uma aplicação Java, você pode exportar um heap dump para análise:
# Localize o PID do processo Java
jps
# Exporte o heap dump
jcmd <PID> GC.heap_dump /tmp/heap.hprof
# Copie o arquivo para fora do contêiner e analise-o
docker cp <nome do contêiner>:/tmp/heap.hprof ./
Etapa 3: aplicar uma correção emergencial
Se o problema já estiver afetando o serviço, faça primeiro uma mitigação temporária:
# Reinicie o contêiner (os dados temporários dentro dele serão perdidos)
docker restart <nome do contêiner>
# Se o contêiner ainda estiver em execução, ajuste dinamicamente o limite de memória
docker update --memory 1g --memory-swap 1g <nome do contêiner>
# Remova recursos não utilizados do sistema (cuidado: imagens e contêineres sem uso serão excluídos)
docker system prune -a
Etapa 4: corrigir a causa raiz
As medidas temporárias apenas contêm o problema. Para resolvê-lo de verdade:
- Corrija o código da aplicação: encontre a origem do vazamento — conexões que não foram fechadas, cache que cresce sem limite, objetos grandes que não foram liberados — e altere o código
- Defina limites de recursos: se ainda não existirem, adicione o parâmetro
--memoryimediatamente - Implante o monitoramento: configure Prometheus + Grafana para receber alertas antes que o problema se torne crítico
- Reinicie o contêiner automaticamente: adicione
--restart=on-failure:3para permitir no máximo três reinicializações após um OOM
Lista rápida de comandos:
# Consulte a configuração de limites de recursos de todos os contêineres
docker ps --format "{{.Names}}" | xargs docker inspect \
--format='{{.Name}}: Memory={{.HostConfig.Memory}} CPU={{.HostConfig.NanoCpus}}'
# Consulte o número histórico de reinicializações do contêiner
docker inspect --format='{{.RestartCount}}' <nome do contêiner>
# Consulte o log detalhado do contêiner (últimas 100 linhas)
docker logs --tail 100 <nome do contêiner>
# Consulte os detalhes do uso de memória do contêiner
docker stats --no-stream --format \
"table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}" <nome do contêiner>
Revisão do caso real: no vazamento de 37 GB da Storj, o usuário resolveu a situação assim:
- Ao perceber o aumento da memória, executou
docker restartpara restaurar temporariamente o serviço - Adicionou um limite
-m 1gpara impedir que o host fosse derrubado novamente - Exportou os logs e o heap dump do contêiner e os enviou à equipe de desenvolvimento
- Depois que o bug foi corrigido em uma nova versão, atualizou a imagem e fez uma nova implantação
- Implantou o cAdvisor e configurou um alerta quando o uso de memória ultrapassasse 70%
O processo deu trabalho, mas, depois dessa lição, o mesmo problema nunca mais se repetiu.
[Imagem: fluxograma de diagnóstico de memória]
Prompt: fluxograma mostrando o processo de diagnóstico de vazamento de memória, passo a passo da detecção à resolução, setas ligando caixas, estilo de infográfico limpo, cores azul e verde, alta qualidade
Boas práticas e erros a evitar
Depois de tantos parâmetros e ferramentas, vamos resumir como usar tudo corretamente em produção.
Checklist obrigatório para produção
✅ Defina um limite de memória para todos os contêineres
Não conte com a sorte. Até um servidor Nginx de arquivos estáticos deve receber, por exemplo, um limite de 512 MB. É melhor ser um pouco conservador do que corrigir tudo depois de um incidente.
✅ Simule os limites de produção no ambiente de desenvolvimento
Não execute contêineres localmente sem restrições para só descobrir em produção que a memória é insuficiente. Configure o ambiente de desenvolvimento com 80% dos valores de produção e encontre os problemas com antecedência.
✅ Revise regularmente o uso de recursos
Consulte o docker stats uma vez por mês. As necessidades de alguns contêineres podem ter mudado: aumente os recursos quando necessário e reduza-os quando houver sobra.
❌ Não desative o OOM Killer
A menos que você tenha 100% de certeza de que a aplicação controla a própria memória, não use --oom-kill-disable. É um parâmetro autodestrutivo.
❌ Não use swap ilimitado
--memory-swap -1 pode parecer tentador, mas é uma armadilha para o servidor. O uso descontrolado de swap pode sobrecarregar o disco; é melhor deixar o OOM Killer encerrar o contêiner.
❌ Não defina um limite de memória baixo demais
Um valor inferior à necessidade real da aplicação causa OOMs frequentes e reduz a disponibilidade. Faça testes de carga antes de escolher o limite.
A forma correta no Docker Compose
services:
web:
image: nginx:latest
deploy:
resources:
limits:
cpus: '1.5'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart: on-failure:3
Observe o campo deploy, da sintaxe do Docker Compose v3. limits define limites rígidos, enquanto reservations define limites flexíveis. O contêiner pode usar 256 MB normalmente e chegar a 512 MB em períodos de maior atividade.
Gerenciamento em lote de vários contêineres
Se você tiver um conjunto de contêineres de microsserviços e quiser definir um pool de recursos comum, use cgroup-parent:
# Crie um grupo cgroup pai e limite os recursos totais
docker run --cgroup-parent=/my-services -m 2g service-a
docker run --cgroup-parent=/my-services -m 2g service-b
# Os dois contêineres compartilham um cgroup, e a memória total não ultrapassa o limite do grupo pai
Esse recurso é útil quando vários contêineres relacionados formam uma unidade de negócio e você quer controlar o consumo total.
Valores práticos para limites de recursos
| Tipo de aplicação | Limite de memória sugerido | Limite de CPU sugerido |
|---|---|---|
| Serviço estático Nginx | 256-512 MB | 0,5-1 núcleo |
| API Node.js | 512 MB-1 GB | 1-2 núcleos |
| Microsserviço Java | 1-2 GB | 2-4 núcleos |
| Banco de dados (MySQL/PostgreSQL) | 2-4 GB | 2-4 núcleos |
| Fila de mensagens (RabbitMQ/Kafka) | 1-2 GB | 1-2 núcleos |
Essas são estimativas conservadoras; os valores reais dependem do volume de uso. Observe o pico de consumo durante o teste de carga e multiplique-o por 1,5 para chegar a um limite razoável.
Comparação com o gerenciamento de recursos do Kubernetes
Se você já usou Kubernetes, perceberá que os conceitos de requests e limits se parecem com os parâmetros do Docker:
- requests: semelhante a
--memory-reservationno Docker - limits: semelhante a
--memoryno Docker
A vantagem do K8s é uma configuração de limites mais padronizada, gerenciada em YAML. O Docker, por sua vez, oferece mais flexibilidade, pois permite ajustar valores a qualquer momento com docker update.
Um último conselho
Limites de recursos não são algo que você configura uma vez e esquece para sempre. A aplicação muda, o volume de uso cresce, e os dados de monitoramento mostram quando é hora de adicionar recursos ou otimizar o código. Faça revisões periódicas e não deixe a configuração virar enfeite.
[Imagem: tabela comparativa de configuração de limites de recursos]
Prompt: tabela comparativa mostrando boas práticas de limites de recursos no Docker, marcas de certo e errado, estilo de infográfico limpo, layout profissional, alta qualidade
Resumo
Voltemos à história do início: um alerta às três da manhã porque um contêiner derrubou o servidor. Essa experiência dolorosa me ensinou que a “liberdade” padrão do Docker é uma armadilha. Se você não definir limites, estará entregando ao contêiner o poder de decidir o destino do servidor.
Agora, a estratégia de defesa fica clara:
Primeira linha de defesa: limites de recursos (prevenção)
Defina --memory e --cpus para cada contêiner, como quem coloca rédeas em um cavalo. Se o contêiner sair do controle, ele próprio cairá sem afetar todo o servidor. Esta é a medida mais básica e mais importante.
Segunda linha de defesa: monitoramento e alertas (detecção)
O docker stats mostra o estado atual; cAdvisor + Prometheus + Grafana mostram tendências e histórico. Um alerta quando o uso de memória ultrapassa 80% pode revelar os primeiros sinais 48 horas antes do desastre.
Terceira linha de defesa: processo de diagnóstico (resposta)
Se o problema acontecer, siga o processo: detectar a anomalia → analisar o consumo → aplicar uma correção emergencial → corrigir a causa raiz. Não entre em pânico; a lista de comandos está neste artigo.
Dos fundamentos de cgroups aos detalhes de --memory-swap, da configuração do Docker Compose à comparação com Kubernetes, este artigo cobre os principais pontos dos limites de recursos no Docker. Agora falta colocá-los em prática.
Faça estas três coisas agora:
- Verifique seu ambiente de produção. Execute este comando para descobrir quais contêineres não têm limites:
docker ps --format "{{.Names}}" | xargs docker inspect \
--format='{{.Name}}: Memory={{.HostConfig.Memory}} CPU={{.HostConfig.NanoCpus}}'
Contêineres com Memory=0 são bombas-relógio.
-
Implante o monitoramento. Copie o docker-compose.yml deste artigo e coloque a solução para rodar; em 30 minutos ela estará pronta.
-
Crie um lembrete no calendário. Reserve uma hora por mês para consultar o
docker statse avaliar se o uso de recursos continua adequado.
Sinceramente, espero que você não precise aprender essa lição com um alerta às três da manhã, como aconteceu comigo. Quanto antes você configurar os limites de recursos, menos preocupação terá. Não espere o incidente acontecer para se arrepender.
Processo completo para configurar limites de recursos no Docker
Evite que vazamentos de memória em contêineres derrubem o servidor, dos fundamentos de cgroups à prática com --memory e --cpus e às opções de monitoramento
⏱️ Estimated time: 1 hr
- 1
Step 1: Entenda a gravidade do problema e os fundamentos de cgroups
Gravidade do problema:
• O vazamento de memória de um contêiner pode subir de 500 MB para 16 GB
• Ele pode consumir toda a memória do servidor, deixando até o SSH sem recursos para responder
• Outros contêineres que funcionavam normalmente também podem cair, derrubando o ambiente de produção
Fundamentos de cgroups:
• O mecanismo cgroups do kernel Linux controla o uso de recursos dos contêineres
• O Docker usa cgroups para limitar CPU, memória, I/O e outros recursos
• Isso impede que um único contêiner derrube o servidor inteiro
Em 2024, a versão 27.0.3 do Docker apresentou um grave bug de vazamento de memória que fez o OOM Killer do kernel Linux encerrar 68 contêineres de uma vez. - 2
Step 2: Configure limites de memória e CPU
Limite de memória:
• Use --memory para limitar a memória: docker run --memory=512m container-name
• Configure limites de recursos no docker-compose: deploy.resources.limits.memory: 512m
• Defina o limite de memória swap com --memory-swap
Limite de CPU:
• Use --cpus para limitar a CPU: docker run --cpus=1.0 container-name
• Use --cpu-shares para definir o peso de CPU
• Configure no docker-compose: deploy.resources.limits.cpus: '1.0'
Verificação dos limites:
• Use docker stats para consultar o uso de recursos do contêiner
• Confirme se os limites entraram em vigor - 3
Step 3: Implante o monitoramento e aplique as boas práticas
Monitoramento:
• Use docker stats para acompanhar os recursos do contêiner em tempo real: docker stats container-name
• O cAdvisor oferece uma interface visual de monitoramento:
docker run -d -p 8080:8080 --name=cadvisor google/cadvisor
• Prometheus + Grafana fornecem monitoramento e alertas de nível de produção:
configure o Prometheus para coletar métricas do cAdvisor e visualize os dados no Grafana
Boas práticas:
• Sempre configure limites de recursos em produção
• Defina valores adequados para memória e CPU
• Configure monitoramento e alertas
• Verifique regularmente o uso de recursos dos contêineres (reserve uma hora por mês para consultar o docker stats)
• Trate qualquer anomalia assim que ela for detectada
FAQ
Por que é necessário configurar limites de recursos no Docker?
• O vazamento de memória de um contêiner pode subir de 500 MB para 16 GB
• Ele pode consumir toda a memória do servidor, deixando até o SSH sem recursos para responder
• Outros contêineres que funcionavam normalmente também podem cair, derrubando o ambiente de produção
Em 2024, a versão 27.0.3 do Docker apresentou um grave bug de vazamento de memória que fez o OOM Killer do kernel Linux encerrar 68 contêineres de uma vez.
Os limites de recursos impedem que um único contêiner derrube o servidor inteiro. Quando surgem os primeiros sinais de vazamento, o próprio contêiner é encerrado, em vez de levar o servidor junto.
Como configurar limites de recursos em contêineres Docker?
• Use --memory para limitar a memória: docker run --memory=512m container-name
• Configure limites de recursos no docker-compose: deploy.resources.limits.memory: 512m
• Defina o limite de memória swap com --memory-swap
Limite de CPU:
• Use --cpus para limitar a CPU: docker run --cpus=1.0 container-name
• Use --cpu-shares para definir o peso de CPU
• Configure no docker-compose: deploy.resources.limits.cpus: '1.0'
Verificação dos limites:
• Use docker stats para consultar o uso de recursos do contêiner
• Confirme se os limites entraram em vigor
Como monitorar o uso de recursos de contêineres Docker?
Monitoramento em tempo real com docker stats:
• docker stats container-name
Interface visual com cAdvisor:
• docker run -d -p 8080:8080 --name=cadvisor google/cadvisor
Monitoramento e alertas de nível de produção com Prometheus + Grafana:
• Configure o Prometheus para coletar métricas do cAdvisor
• Visualize os dados no Grafana
Implantação: copie o docker-compose.yml do artigo e coloque a solução para rodar; em 30 minutos ela estará pronta.
Verificação periódica: reserve uma hora por mês para consultar o docker stats e avaliar se o uso de recursos continua adequado.
Quais são as boas práticas para limites de recursos no Docker?
• Sempre configure limites de recursos em produção
• Defina valores adequados para memória e CPU
• Configure monitoramento e alertas
• Verifique regularmente o uso de recursos dos contêineres (reserve uma hora por mês para consultar o docker stats)
• Trate qualquer anomalia assim que ela for detectada
Quanto antes você configurar os limites de recursos, menos preocupação terá. Não espere o incidente acontecer para se arrepender. Crie um lembrete no calendário e reserve uma hora por mês para consultar o docker stats e avaliar se o uso de recursos continua adequado.
22 min de leitura · Publicado em: 18 dez 2025 · Atualizado em: 4 set 2026
Guia prático Docker
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Guia completo de Docker Secrets: práticas recomendadas para proteger senhas e chaves de API em contêineres
Pare de colocar senhas de banco de dados no Dockerfile. Este guia explica como proteger informações sensíveis com Docker Secrets, compara Docker, Kubernetes e Vault e traz um checklist completo para produção.
Parte 28 de 34
Próximo
Comando docker logs: 7 dicas para diagnosticar problemas em contêineres
Conheça 7 dicas práticas do comando docker logs, incluindo acompanhamento em tempo real, filtro por horário, busca com grep, localização dos arquivos de log e boas práticas para produção.
Parte 30 de 34



Comentários
Entre com GitHub para comentar