IFCE Horizonte · Portal
ADS23 · Sistemas Operacionais

Mão na massa · ~48 min

Medindo page faults, swap — e dimensionando um container

Hoje você mede quantas vezes o sistema teve que buscar páginas no disco, aprende a ler as colunas de swap que denunciam thrashing, e fecha descobrindo empiricamente o menor limite de memória em que um container ainda funciona — em vez de chutar um número.

Passos 1–4 · Page faults, vmstat e swapPassos 5–7 · Reforço Docker: dimensionar o limite

Passo 1 de 7

Contando page faults de um comando

Vamos rodar um comando que percorre muitos arquivos e medir quantas vezes o sistema precisou buscar páginas.
1/usr/bin/time -v ls -R /usr 2>&1 | grep -i "page faults"

O que é

/usr/bin/time -v (caminho completo, não o time embutido do shell) mostra estatísticas detalhadas do comando, incluindo page faults.

Por que importa

Separa page faults minor (resolvidos na RAM, baratos) de major (exigiram disco, caros) — a distinção que importa no diagnóstico.

$ /usr/bin/time -v ls -R /usr 2>&1 | grep -i "page faults"
Major (requiring I/O) page faults: 3
Minor (reclaiming a frame) page faults: 4821
O que esperar:

muitos minor e pouquíssimos major. Isso é o padrão saudável — a maioria das páginas já estava disponível na RAM, sem precisar tocar o disco.

Deu "comando não encontrado" ou não mostrou page faults

Use o caminho completo /usr/bin/time — o time sozinho chama a versão embutida do shell, que não tem a opção -v. Se o pacote não existir, instale com sudo apt install time.

quantos page faults minor e major seu comando gerou? Por que a diferença entre os dois números é tão grande?

0 / 40 caracteres

Passo 2 de 7

Os contadores globais do kernel

O sistema inteiro mantém contadores acumulados de page fault desde o boot — em /proc, como sempre.
2grep -E "^pgfault|^pgmajfault" /proc/vmstat

O que é

pgfault é o total de page faults desde o boot; pgmajfault é só a parcela que exigiu leitura de disco.

Por que importa

É a fonte que ferramentas de monitoramento consultam pra montar gráficos de page fault ao longo do tempo.

$ grep -E "^pgfault|^pgmajfault" /proc/vmstat
pgfault 2841096
pgmajfault 4127
O que esperar:

números grandes (acumulados desde que a VM ligou) e, de novo, pgmajfault muito menor que pgfault. Rode duas vezes com alguns segundos de intervalo e veja os valores subirem.

Passo 3 de 7

As colunas que denunciam thrashing

O vmstat é a ferramenta clássica pra ver troca de páginas acontecendo em tempo real.
3vmstat 1 5

O que é

Mostra 5 amostras, uma por segundo. As colunas si (swap in) e so (swap out) indicam páginas movendo entre RAM e disco.

Por que importa

si/so altos e constantes é a assinatura de thrashing — o diagnóstico que o slide de hoje ensinou a reconhecer.

$ vmstat 1 5
procs -------memory------- ---swap--
r  b     free   buff  cache   si   so
1  0   438292  83104 1912440    0    0
0  0   438160  83104 1912440    0    0
O que esperar:

si e so zerados. Isso é o resultado correto numa VM ociosa — o valor do exercício é saber onde olhar quando não estiverem zerados.

Zerado não significa que o comando falhou?

Não — significa que sua VM está saudável, sem pressão de memória. Um sistema em thrashing mostraria centenas ou milhares nessas colunas, continuamente. O objetivo aqui é reconhecer o padrão normal pra depois identificar o anormal.

Passo 4 de 7

O estado do swap

Confirme se sua VM tem área de swap configurada e quanto dela está em uso.
4swapon --show && free -h | grep -i swap

O que é

swapon --show lista as áreas de swap ativas; o free filtrado mostra total e uso.

Por que importa

Swap alocado com pouca troca é normal e saudável; o alarme é a taxa de troca do passo 3, não o total alocado.

$ swapon --show && free -h | grep -i swap
NAME      TYPE  SIZE  USED  PRIO
/swap.img file  2.0G    0B   -2
Swap:     2.0Gi   0B  2.0Gi
O que esperar:

uma área de swap listada, com uso próximo de zero. Se swapon --show não retornar nada, sua VM está sem swap — anote isso, é uma configuração possível e vale discutir suas implicações.

Reforço Docker · dimensionando o limite

Passo 5 de 7

Descobrindo o limite mínimo viável

Em vez de chutar um valor pro --memory, vamos descobrir empiricamente qual é o menor limite em que o container ainda funciona.
5Rode a mesma carga com limites decrescentes

O que é

O mesmo container, executado quatro vezes, com --memory cada vez menor: 256m → 128m → 64m → 32m. A carga é sempre a mesma: gravar 100 MB numa pasta --tmpfs, que vive na RAM. Gravar ali é ocupar memória do container, de forma previsível.

Por que importa

É assim que se dimensiona um serviço de verdade: medindo, não adivinhando. Limite alto demais desperdiça; baixo demais derruba em produção.

$ for M in 256m 128m 64m 32m; do
  echo "--- testando $M ---"
  docker run --rm --memory=$M --memory-swap=$M --tmpfs /dados:size=512m alpine \
    sh -c "head -c 100m /dev/zero > /dados/carga" \
    && echo "OK em $M" || echo "FALHOU em $M"
done
--- testando 256m ---
OK em 256m
--- testando 128m ---
OK em 128m
--- testando 64m ---
FALHOU em 64m
--- testando 32m ---
FALHOU em 32m
O que esperar:

sucesso nos limites maiores e falha em algum ponto conforme aperta. Anote o menor valor que ainda funcionou — esse é o limite mínimo viável para essa carga. Pode aparecer uma mensagem de erro de escrita ou nenhuma mensagem antes do "FALHOU": nos dois casos, o limite venceu.

Por que não usar só head -c 100m /dev/zero | wc -c?

Porque não ocuparia memória nenhuma: os dados passam pelo pipe em pedaços pequenos e o wc só conta. O container passaria em qualquer limite. Para medir memória, a carga precisa guardar os dados — por isso o tmpfs.

Funcionou em todos os valores, até 32m

Confira se o --memory-swap=$M está no comando: sem ele, o container empurra o excesso para o swap em vez de falhar. Se estiver, aumente a carga para head -c 200m e rode de novo.

qual foi o menor limite de memória em que o container ainda funcionou? Por que não seria prudente usar exatamente esse valor em produção?

0 / 40 caracteres

Passo 6 de 7

Consumo contra o limite, ao vivo

Suba um container duradouro com o limite que você encontrou e observe o consumo em relação ao teto.
6docker run -d --name dimensionado --memory=128m alpine sleep 300 depois docker stats --no-stream

O que é

O mesmo docker stats do Encontro 6 — mas agora a coluna de memória mostra consumo contra o limite definido, não contra a RAM total da máquina.

Por que importa

É esse percentual que ferramentas de monitoramento usam pra alertar antes de um serviço ser morto por OOM.

$ docker stats --no-stream
NAME           CPU %   MEM USAGE / LIMIT   MEM %
dimensionado   0.00%   1.1MiB / 128MiB      0.86%
O que esperar:

o limite (128MiB) aparecendo no lugar da RAM total da VM. Compare com o Encontro 6, onde o container sem limite mostrava a memória inteira da máquina como teto.

Passo 7 de 7

Limpando

Encerre e remova o container antes de fechar.
7docker stop dimensionado && docker rm dimensionado

O que é

Encerra e remove. Os containers do passo 5 usaram --rm, então já se removeram sozinhos.

Por que importa

Boa prática: não deixar containers de teste ocupando recursos após a aula.

O que esperar:

o nome impresso duas vezes. Confirme com docker ps -a que não sobrou nada dos testes de hoje.

livre: algo que te surpreendeu ou uma dúvida que sobrou de hoje

0 / 40 caracteres

Exemplo de observação forte × fraca

Observação fraca

"O container funcionou com 64m e falhou com 32m."

Observação forte

"O menor limite viável foi 128m — abaixo disso a carga de 100 MB não coube e o container falhou. Mas usar exatamente 128m em produção seria arriscado: qualquer variação de carga, atualização de dependência ou pico de requisições ultrapassaria o teto e derrubaria o serviço. O sensato é deixar uma folga sobre o mínimo medido, justamente porque o valor foi obtido numa carga controlada e previsível, diferente do mundo real."

Checklist final

  • Medi page faults minor e major de um comando
  • Conferi os contadores globais em /proc/vmstat
  • Rodei vmstat e identifiquei as colunas si/so
  • Verifiquei o estado do swap da minha VM
  • Descobri o menor limite de memória viável pro container
  • Observei o consumo contra o limite no docker stats
  • Limpei os containers de teste
  • Preenchi as 3 observações acima (vou colar no quiz semanal)
0 / 8 verificados