Tutoriel Linux

Cgroups Linux, verificar os limites de CPU e memória de um serviço

Débutant6 min de lecture

Um serviço web que desacelera, um worker morto sem mensagem clara ou um servidor que troca não necessariamente esgotou toda a RAM da máquina. Com o systemd, um limite definido em uma unidade pode ser suficiente para explicar o sintoma.

Os cgroups permitem que o núcleo Linux conte, limite e priorize os recursos de um grupo de processos. Antes de adicionar memória ou reiniciar aleatoriamente, verifique o que o systemd aplica ao serviço em questão.

Tux diante de servidores com um gráfico de recursos de CPU e memória
Cgroups e limites de recursos de um serviço Linux gerenciado pelo systemd.

Ver o cgroup de um serviço

Nas distribuições recentes, o systemd coloca cada serviço em seu próprio cgroup. Você não precisa mover PIDs manualmente para diagnosticar uma unidade clássica como Nginx, PostgreSQL ou um worker de aplicativo.

Comece lendo o estado do serviço e as propriedades relacionadas aos recursos. Substitua nginx.service pelo nome real da sua unidade.

systemctl status nginx.service --no-pager
systemctl show nginx.service -p ControlGroup -p MainPID -p MemoryCurrent -p MemoryMax -p MemoryHigh -p CPUQuotaPerSecUSec

ControlGroup fornece o caminho gerenciado pelo systemd. MemoryCurrent exibe a memória contabilizada para a unidade no momento da leitura. Se MemoryMax for igual a infinity, o systemd não definiu um teto rígido de memória para este serviço.

Se você partir de um PID, rel leia seu cgroup a partir de /proc. No cgroup v2, você verá frequentemente uma linha do tipo 0::/system.slice/nginx.service.

pid=$(systemctl show nginx.service -p MainPID --value)
cat /proc/$pid/cgroup

Se MainPID for igual a 0, o serviço não possui um processo principal ativo. Volte para systemctl status e para journalctl em vez de concluir a partir de um PID vazio.

Ler os limites de memória e CPU

MemoryMax é um limite rígido. Se o cgroup atingir esse teto e o núcleo não recuperar memória suficiente, um processo do grupo pode ser morto pelo OOM killer.

MemoryHigh é menos severo. O núcleo limita as alocações e tenta recuperar memória quando o cgroup ultrapassa esse limite. Para contenção de um serviço sem que ele caia no primeiro pico, esta é frequentemente uma melhor configuração inicial que MemoryMax.

CPUQuota limita o tempo de CPU disponível para o serviço. CPUQuota=25% representa o equivalente a um quarto de um núcleo lógico. Em uma máquina com vários núcleos, CPUQuota=200% permite até dois núcleos de tempo de CPU.

systemctl show nginx.service 
  -p MemoryCurrent -p MemoryMax -p MemoryHigh 
  -p CPUUsageNSec -p CPUQuotaPerSecUSec

Não misture quota e prioridade. Uma quota impede que o serviço exceda uma quantidade de CPU. Uma prioridade, com nice ou CPUWeight, arbitra principalmente a concorrência quando várias tarefas solicitam CPU ao mesmo tempo.

Encontrar a origem de um limite

Um limite pode vir do arquivo fornecido pelo pacote, de um drop-in do systemd ou de uma sobreposição criada localmente. Leia a configuração completa antes de modificar o serviço.

systemctl cat nginx.service
systemctl show nginx.service -p FragmentPath -p DropInPaths

Um arquivo em /etc/systemd/system/nginx.service.d/ normalmente tem precedência sobre a unidade fornecida pelo pacote. Evite modificar diretamente um arquivo em /usr/lib/systemd/system/ ou /lib/systemd/system/, pois uma atualização pode sobrescrevê-lo.

Em seguida, verifique os sintomas. Um serviço lento também pode vir de uma consulta SQL lenta, um disco saturado, uma fila cheia ou um erro de aplicativo.

journalctl -u nginx.service -b --no-pager
journalctl -k -b | grep -Ei 'oom|out of memory|killed process|memory cgroup'

Se os limites permanecerem em infinity e o log do kernel não mostrar nenhum evento de memória, procure em outros locais antes de modificar os cgroups: logs de aplicativo, métricas de disco, latência de rede ou consumo real do processo.

Testar sem tocar no serviço

Para validar uma sintaxe ou medir o efeito de um limite, execute uma unidade temporária com systemd-run. O teste não modifica Nginx, PostgreSQL ou seu serviço de negócios.

sudo systemd-run --unit=teste-limite-memoria 
  --property=MemoryMax=256M 
  --property=CPUQuota=25% 
  /usr/bin/bash -c 'sleep 300'

Em seguida, releia as propriedades aplicadas. Para CPUQuota=25%, o systemd pode exibir um valor equivalente a 250ms por segundo.

systemctl show teste-limite-memoria.service 
  -p MemoryMax -p CPUQuotaPerSecUSec -p ControlGroup -p ActiveState
systemctl cat teste-limite-memoria.service

Interrompa a unidade de teste assim que tiver terminado. Com sleep, o esquecimento não é grave, mas esse reflexo evita surpresas quando o teste executa um comando real.

sudo systemctl stop teste-limite-memoria.service

Aplicar uma sobreposição do systemd

Quando os logs e as medidas confirmam que um limite faz sentido, faça uma sobreposição no systemd. O exemplo abaixo define um limite alto em 800 MB, um teto rígido em 1 GB e limita o serviço ao equivalente a um núcleo de CPU.

sudo systemctl edit meu-servico.service

Adicione apenas as diretivas necessárias. Não copie esses valores para um serviço de produção sem ter observado seu consumo normal e seus picos.

[Service]
MemoryHigh=800M
MemoryMax=1G
CPUQuota=100%

Recarregue o systemd e, em seguida, reinicie o serviço somente se a interrupção for aceitável. Remotamente, mantenha uma segunda sessão SSH aberta ao tocar em um componente crítico.

sudo systemctl daemon-reload
sudo systemctl restart meu-servico.service
systemctl status meu-servico.service --no-pager
systemctl show meu-servico.service -p MemoryCurrent -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec

Se o serviço não iniciar mais ou se tornar muito lento, remova a sobreposição com cautela. sudo systemctl revert meu-servico.service remove as sobreposições locais da unidade. Não a utilize se o mesmo diretório contiver outras configurações a serem mantidas.

Controles após a alteração

  • Verifique o estado com systemctl is-active meu-servico.service.
  • Releia MemoryCurrent, MemoryHigh, MemoryMax e CPUQuotaPerSecUSec após a reinicialização.
  • Monitore journalctl -u meu-servico.service -b e o log do kernel durante o pico de carga que estava apresentando problemas.
  • Meça o efeito do lado da aplicação: tempo de resposta, jobs atrasados, filas, erros do usuário.
  • Registre o motivo da configuração, a data, o valor definido e o sintoma corrigido em seu procedimento operacional.

Os cgroups protegem o restante da máquina e tornam os limites visíveis. Eles não corrigem um vazamento de memória, uma consulta muito cara ou um worker mal dimensionado. Se o teto for atingido, mantenha o log, a carga observada e o valor configurado para escolher entre ajustar o limite e corrigir a aplicação.

Para ir mais longe, a página systemd.resource-control(5) detalha cada diretiva. O guia Nice no Linux ajuda a escolher uma prioridade de CPU quando um teto rígido não é adequado.

sudo apt update && sudo apt upgrade